@ -65,7 +65,7 @@ mod walletzkey;
use data ::{ BlockData , WalletTx , Utxo , SaplingNoteData , SpendableNote , OutgoingTxMetadata } ;
use extended_key ::{ KeyIndex , ExtendedPrivKey } ;
use walletzkey ::{ WalletZKey , WalletZKeyType } ;
use walletzkey ::{ WalletZKey , WalletTKey , Wallet ZKeyType } ;
pub const MAX_REORG : usize = 100 ;
pub const GAP_RULE_UNUSED_ADDRESSES : usize = 5 ;
@ -124,11 +124,8 @@ pub struct LightWallet {
// viewing keys and imported spending keys.
zkeys : Arc < RwLock < Vec < WalletZKey > > > ,
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
// but the addresses will be present.
tkeys : Arc < RwLock < Vec < secp256k1 ::SecretKey > > > ,
pub taddresses : Arc < RwLock < Vec < String > > > ,
// Transparent keys.
tkeys : Arc < RwLock < Vec < WalletTKey > > > ,
blocks : Arc < RwLock < Vec < BlockData > > > ,
pub txs : Arc < RwLock < HashMap < TxId , WalletTx > > > ,
@ -149,7 +146,7 @@ pub struct LightWallet {
impl LightWallet {
pub fn serialized_version ( ) -> u64 {
return 7 ;
return 9 ;
}
fn get_taddr_from_bip39seed ( config : & LightClientConfig , bip39_seed : & [ u8 ] , pos : u32 ) -> SecretKey {
@ -255,8 +252,7 @@ impl LightWallet {
nonce : vec ! [ ] ,
seed : seed_bytes ,
zkeys : Arc ::new ( RwLock ::new ( vec ! [ WalletZKey ::new_hdkey ( hdkey_num , extsk ) ] ) ) ,
tkeys : Arc ::new ( RwLock ::new ( vec ! [ tpk ] ) ) ,
taddresses : Arc ::new ( RwLock ::new ( vec ! [ taddr ] ) ) ,
tkeys : Arc ::new ( RwLock ::new ( vec ! [ WalletTKey ::new_hdkey ( tpk , taddr ) ] ) ) ,
blocks : Arc ::new ( RwLock ::new ( vec ! [ ] ) ) ,
txs : Arc ::new ( RwLock ::new ( HashMap ::new ( ) ) ) ,
mempool_txs : Arc ::new ( RwLock ::new ( HashMap ::new ( ) ) ) ,
@ -374,18 +370,29 @@ impl LightWallet {
// Calculate the addresses
let tkeys = Vector ::read ( & mut reader , | r | {
let mut tpk_bytes = [ 0 u8 ; 32 ] ;
r . read_exact ( & mut tpk_bytes ) ? ;
secp256k1 ::SecretKey ::from_slice ( & tpk_bytes ) . map_err ( | e | io ::Error ::new ( ErrorKind ::InvalidData , e ) )
} ) ? ;
let taddresses = if version > = 4 {
// Read the addresses
Vector ::read ( & mut reader , | r | utils ::read_string ( r ) ) ?
let wallet_tkeys = if version > = 9 {
Vector ::read ( & mut reader , | r | {
WalletTKey ::read ( r )
} ) ?
} else {
// Calculate the addresses
tkeys . iter ( ) . map ( | sk | LightWallet ::address_from_prefix_sk ( & config . base58_pubkey_address ( ) , sk ) ) . collect ( )
let tkeys = Vector ::read ( & mut reader , | r | {
let mut tpk_bytes = [ 0 u8 ; 32 ] ;
r . read_exact ( & mut tpk_bytes ) ? ;
secp256k1 ::SecretKey ::from_slice ( & tpk_bytes ) . map_err ( | e | io ::Error ::new ( ErrorKind ::InvalidData , e ) )
} ) ? ;
let taddresses = if version > = 4 {
// Read the addresses
Vector ::read ( & mut reader , | r | utils ::read_string ( r ) ) ?
} else {
// Calculate the addresses
tkeys . iter ( ) . map ( | sk | LightWallet ::address_from_prefix_sk ( & config . base58_pubkey_address ( ) , sk ) ) . collect ( )
} ;
tkeys . iter ( ) . zip ( taddresses . iter ( ) ) . map ( | ( k , a ) |
WalletTKey ::new_hdkey ( * k , a . clone ( ) )
) . collect ( )
} ;
let blocks = Vector ::read ( & mut reader , | r | BlockData ::read ( r ) ) ? ;
@ -414,8 +421,7 @@ impl LightWallet {
nonce : nonce ,
seed : seed_bytes ,
zkeys : Arc ::new ( RwLock ::new ( zkeys ) ) ,
tkeys : Arc ::new ( RwLock ::new ( tkeys ) ) ,
taddresses : Arc ::new ( RwLock ::new ( taddresses ) ) ,
tkeys : Arc ::new ( RwLock ::new ( wallet_tkeys ) ) ,
blocks : Arc ::new ( RwLock ::new ( blocks ) ) ,
txs : Arc ::new ( RwLock ::new ( txs ) ) ,
mempool_txs : Arc ::new ( RwLock ::new ( HashMap ::new ( ) ) ) ,
@ -456,12 +462,7 @@ impl LightWallet {
// Write the transparent private keys
Vector ::write ( & mut writer , & self . tkeys . read ( ) . unwrap ( ) ,
| w , pk | w . write_all ( & pk [ . . ] )
) ? ;
// Write the transparent addresses
Vector ::write ( & mut writer , & self . taddresses . read ( ) . unwrap ( ) ,
| w , a | utils ::write_string ( w , a )
| w , tk | tk . write ( w )
) ? ;
Vector ::write ( & mut writer , & self . blocks . read ( ) . unwrap ( ) , | w , b | b . write ( w ) ) ? ;
@ -533,9 +534,14 @@ impl LightWallet {
/// Get all t-address private keys. Returns a Vector of (address, secretkey)
pub fn get_t_secret_keys ( & self ) -> Vec < ( String , String ) > {
self . tkeys . read ( ) . unwrap ( ) . iter ( ) . map ( | sk | {
( self . address_from_sk ( sk ) ,
sk [ . . ] . to_base58check ( & self . config . base58_secretkey_prefix ( ) , & [ 0x01 ] ) )
self . tkeys . read ( ) . unwrap ( ) . iter ( ) . map ( | wtk | {
let sk = if wtk . tkey . is_some ( ) {
wtk . tkey . unwrap ( ) [ . . ] . to_base58check ( & self . config . base58_secretkey_prefix ( ) , & [ 0x01 ] )
} else {
"" . to_string ( )
} ;
( wtk . address . clone ( ) , sk )
} ) . collect ::< Vec < ( String , String ) > > ( )
}
@ -547,6 +553,10 @@ impl LightWallet {
return "Error: Can't add key while wallet is locked" . to_string ( ) ;
}
if self . encrypted {
return "Error: Can't add key while wallet is encrypted" . to_string ( ) ;
}
// Find the highest pos we have
let pos = self . zkeys . read ( ) . unwrap ( ) . iter ( )
. filter ( | zk | zk . hdkey_num . is_some ( ) )
@ -603,14 +613,49 @@ impl LightWallet {
return "Error: Can't add key while wallet is locked" . to_string ( ) ;
}
if self . encrypted {
return "Error: Can't add key while wallet is encrypted" . to_string ( ) ;
}
let pos = self . tkeys . read ( ) . unwrap ( ) . len ( ) as u32 ;
let bip39_seed = bip39 ::Seed ::new ( & Mnemonic ::from_entropy ( & self . seed , Language ::English ) . unwrap ( ) , "" ) ;
let sk = LightWallet ::get_taddr_from_bip39seed ( & self . config , & bip39_seed . as_bytes ( ) , pos ) ;
let address = self . address_from_sk ( & sk ) ;
self . tkeys . write ( ) . unwrap ( ) . push ( sk ) ;
self . taddresses . write ( ) . unwrap ( ) . push ( address . clone ( ) ) ;
self . tkeys . write ( ) . unwrap ( ) . push ( WalletTKey ::new_hdkey ( sk , address . clone ( ) ) ) ;
address
}
pub fn import_taddr ( & self , sk : String ) -> String {
if ! self . unlocked {
return "Error: Can't add key while wallet is locked" . to_string ( ) ;
}
//// Decode Wif to base58 to hex
let sk_to_bs58 = bs58 ::decode ( sk ) . into_vec ( ) . unwrap ( ) ;
let bs58_to_hex = hex ::encode ( sk_to_bs58 ) ;
//// Manipulate string, to exclude last 4 bytes (checksum bytes), first 2 bytes (secretkey prefix) and the compressed flag (works only for compressed Wifs!)
let slice_sk = & bs58_to_hex [ 2 . . 66 ] ;
//// Get the SecretKey from slice
let secret_key = SecretKey ::from_slice ( & hex ::decode ( slice_sk ) . unwrap ( ) ) ;
let sk_raw = secret_key . unwrap ( ) ;
//// Make sure the key doesn't already exist
if self . tkeys . read ( ) . unwrap ( ) . iter ( ) . find ( | & wk | wk . tkey . is_some ( ) & & wk . tkey . as_ref ( ) . unwrap ( ) = = & sk_raw . clone ( ) ) . is_some ( ) {
return "Error: Key already exists" . to_string ( ) ;
}
//// Get the taddr from key
let address = self . address_from_sk ( & sk_raw ) ;
//// Add to tkeys
self . tkeys . write ( ) . unwrap ( ) . push ( WalletTKey ::import_hdkey ( sk_raw , address . clone ( ) ) ) ;
address
}
@ -791,6 +836,12 @@ impl LightWallet {
}
}
pub fn get_all_taddresses ( & self ) -> Vec < String > {
self . tkeys . read ( ) . unwrap ( )
. iter ( )
. map ( | wtx | wtx . address . clone ( ) ) . collect ( )
}
pub fn get_all_zaddresses ( & self ) -> Vec < String > {
self . zkeys . read ( ) . unwrap ( ) . iter ( ) . map ( | zk | {
encode_payment_address ( self . config . hrp_sapling_address ( ) , & zk . zaddress )
@ -863,6 +914,11 @@ impl LightWallet {
self . nonce = nonce . as_ref ( ) . to_vec ( ) ;
// Encrypt the individual keys
self . tkeys . write ( ) . unwrap ( ) . iter_mut ( )
. map ( | k | k . encrypt ( & key ) )
. collect ::< io ::Result < Vec < ( ) > > > ( ) ? ;
self . zkeys . write ( ) . unwrap ( ) . iter_mut ( )
. map ( | k | k . encrypt ( & key ) )
. collect ::< io ::Result < Vec < ( ) > > > ( ) ? ;
@ -884,7 +940,12 @@ impl LightWallet {
// Empty the seed and the secret keys
self . seed . copy_from_slice ( & [ 0 u8 ; 32 ] ) ;
self . tkeys = Arc ::new ( RwLock ::new ( vec ! [ ] ) ) ;
// Remove all the private key from the tkeys
self . tkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | tk | {
tk . lock ( )
} ) . collect ::< io ::Result < Vec < _ > > > ( ) ? ;
// Remove all the private key from the zkeys
self . zkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | zk | {
zk . lock ( )
@ -922,19 +983,10 @@ impl LightWallet {
// we need to get the 64 byte bip39 entropy
let bip39_seed = bip39 ::Seed ::new ( & Mnemonic ::from_entropy ( & seed , Language ::English ) . unwrap ( ) , "" ) ;
// Transparent keys
let mut tkeys = vec ! [ ] ;
for pos in 0 . . self . taddresses . read ( ) . unwrap ( ) . len ( ) {
let sk = LightWallet ::get_taddr_from_bip39seed ( & self . config , & bip39_seed . as_bytes ( ) , pos as u32 ) ;
let address = self . address_from_sk ( & sk ) ;
if address ! = self . taddresses . read ( ) . unwrap ( ) [ pos ] {
return Err ( io ::Error ::new ( ErrorKind ::InvalidData ,
format ! ( "taddress mismatch at {}. {} vs {}" , pos , address , self . taddresses . read ( ) . unwrap ( ) [ pos ] ) ) ) ;
}
tkeys . push ( sk ) ;
}
// Go over the tkeys, and add the keys again
self . tkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | tk | {
tk . unlock ( & key )
} ) . collect ::< io ::Result < Vec < ( ) > > > ( ) ? ;
// Go over the zkeys, and add the spending keys again
self . zkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | zk | {
@ -942,7 +994,6 @@ impl LightWallet {
} ) . collect ::< io ::Result < Vec < ( ) > > > ( ) ? ;
// Everything checks out, so we'll update our wallet with the decrypted values
self . tkeys = Arc ::new ( RwLock ::new ( tkeys ) ) ;
self . seed . copy_from_slice ( & seed ) ;
self . encrypted = true ;
@ -963,8 +1014,13 @@ impl LightWallet {
self . unlock ( passwd ) ? ;
}
// Remove encryption from individual tkeys
self . tkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | tk | {
tk . remove_encryption ( )
} ) . collect ::< io ::Result < Vec < ( ) > > > ( ) ? ;
// Remove encryption from individual zkeys
self . zkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | zk | {
self . zkeys . write ( ) . unwrap ( ) . iter_mut ( ) . map ( | zk | {
zk . remove_encryption ( )
} ) . collect ::< io ::Result < Vec < ( ) > > > ( ) ? ;
@ -1159,7 +1215,12 @@ impl LightWallet {
// If one of the last 'n' taddress was used, ensure we add the next HD taddress to the wallet.
pub fn ensure_hd_taddresses ( & self , address : & String ) {
let last_addresses = {
self . taddresses . read ( ) . unwrap ( ) . iter ( ) . rev ( ) . take ( GAP_RULE_UNUSED_ADDRESSES ) . map ( | s | s . clone ( ) ) . collect ::< Vec < String > > ( )
self . tkeys . read ( ) . unwrap ( )
. iter ( )
. map ( | t | t . address . clone ( ) )
. rev ( ) . take ( GAP_RULE_UNUSED_ADDRESSES ) . map ( | s |
s . clone ( ) )
. collect ::< Vec < String > > ( )
} ;
match last_addresses . iter ( ) . position ( | s | * s = = * address ) {
@ -1252,7 +1313,8 @@ impl LightWallet {
}
// Scan for t outputs
let all_taddresses = self . taddresses . read ( ) . unwrap ( ) . iter ( )
let all_taddresses = self . tkeys . read ( ) . unwrap ( ) . iter ( )
. map ( | wtx | wtx . address . clone ( ) )
. map ( | a | a . clone ( ) )
. collect ::< Vec < _ > > ( ) ;
for address in all_taddresses {
@ -1279,7 +1341,8 @@ impl LightWallet {
// outgoing metadata
// Collect our t-addresses
let wallet_taddrs = self . taddresses . read ( ) . unwrap ( ) . iter ( )
let wallet_taddrs = self . tkeys . read ( ) . unwrap ( ) . iter ( )
. map ( | wtx | wtx . address . clone ( ) )
. map ( | a | a . clone ( ) )
. collect ::< HashSet < String > > ( ) ;
@ -2037,7 +2100,8 @@ impl LightWallet {
// Create a map from address -> sk for all taddrs, so we can spend from the
// right address
let address_to_sk = self . tkeys . read ( ) . unwrap ( ) . iter ( )
. map ( | sk | ( self . address_from_sk ( & sk ) , sk . clone ( ) ) )
. filter ( | wtk | wtk . tkey . is_some ( ) )
. map ( | wtk | ( wtk . address . clone ( ) , wtk . tkey . unwrap ( ) . clone ( ) ) )
. collect ::< HashMap < _ , _ > > ( ) ;
// Add all tinputs
@ -2050,15 +2114,11 @@ impl LightWallet {
script_pubkey : Script { 0 : utxo . script . clone ( ) } ,
} ;
match address_to_sk . get ( & utxo . address ) {
Some ( sk ) = > builder . add_transparent_input ( * sk , outpoint . clone ( ) , coin . clone ( ) ) ,
None = > {
// Something is very wrong
let e = format ! ( "Couldn't find the secreykey for taddr {}" , utxo . address ) ;
error ! ( "{}" , e ) ;
Err ( zcash_primitives ::transaction ::builder ::Error ::InvalidAddress )
}
if let Some ( sk ) = address_to_sk . get ( & utxo . address ) {
return builder . add_transparent_input ( * sk , outpoint . clone ( ) , coin . clone ( ) )
} else {
info ! ( "Not adding a UTXO because secret key is absent." ) ;
return Ok ( ( ) )
}
} )