@ -11,6 +11,7 @@ use log::{info, warn, error};
use protobuf ::parse_from_bytes ;
use libflate ::{ gzip ::{ Decoder , Encoder } , finish ::AutoFinishUnchecked } ;
use secp256k1 ::SecretKey ;
use bip39 ::{ Mnemonic , Language } ;
@ -134,7 +135,7 @@ pub struct LightWallet {
impl LightWallet {
pub fn serialized_version ( ) -> u64 {
return 4 ;
return 5 ;
}
fn get_taddr_from_bip39seed ( config : & LightClientConfig , bip39_seed : & [ u8 ] , pos : u32 ) -> SecretKey {
@ -232,22 +233,32 @@ impl LightWallet {
} )
}
pub fn read < R : Read > ( mut reader : R , config : & LightClientConfig ) -> io ::Result < Self > {
let version = reader . read_u64 ::< LittleEndian > ( ) ? ;
pub fn read < R : Read > ( mut inp : R , config : & LightClientConfig ) -> io ::Result < Self > {
let version = inp . read_u64 ::< LittleEndian > ( ) ? ;
if version > LightWallet ::serialized_version ( ) {
let e = format ! ( "Don't know how to read wallet version {}. Do you have the latest version?" , version ) ;
error ! ( "{}" , e ) ;
return Err ( io ::Error ::new ( ErrorKind ::InvalidData , e ) ) ;
}
println ! ( "Reading wallet version {}" , version ) ;
info ! ( "Reading wallet version {}" , version ) ;
// After version 5, we're writing the rest of the file as a compressed stream (gzip)
let mut reader : Box < dyn Read > = if version < = 4 {
info ! ( "Reading direct" ) ;
Box ::new ( inp )
} else {
info ! ( "Reading libflat" ) ;
Box ::new ( Decoder ::new ( inp ) . unwrap ( ) )
} ;
let encrypted = if version > = 4 {
reader . read_u8 ( ) ? > 0
} else {
false
} ;
info ! ( "Wallet Encryption {:?}" , encrypted ) ;
let mut enc_seed = [ 0 u8 ; 48 ] ;
if version > = 4 {
reader . read_exact ( & mut enc_seed ) ? ;
@ -331,14 +342,17 @@ impl LightWallet {
} )
}
pub fn write < W : Write > ( & self , mut writer : W ) -> io ::Result < ( ) > {
pub fn write < W : Write > ( & self , mut out : W ) -> io ::Result < ( ) > {
if self . encrypted & & self . unlocked {
return Err ( Error ::new ( ErrorKind ::InvalidInput ,
format ! ( "Cannot write while wallet is unlocked while encrypted." ) ) ) ;
}
// Write the version
writer . write_u64 ::< LittleEndian > ( LightWallet ::serialized_version ( ) ) ? ;
out . write_u64 ::< LittleEndian > ( LightWallet ::serialized_version ( ) ) ? ;
// Gzip encoder
let mut writer = AutoFinishUnchecked ::new ( Encoder ::new ( out ) . unwrap ( ) ) ;
// Write if it is locked
writer . write_u8 ( if self . encrypted { 1 } else { 0 } ) ? ;
@ -387,9 +401,7 @@ impl LightWallet {
// While writing the birthday, get it from the fn so we recalculate it properly
// in case of rescans etc...
writer . write_u64 ::< LittleEndian > ( self . get_birthday ( ) ) ? ;
Ok ( ( ) )
writer . write_u64 ::< LittleEndian > ( self . get_birthday ( ) )
}
pub fn note_address ( hrp : & str , note : & SaplingNoteData ) -> Option < String > {
@ -1088,14 +1100,20 @@ impl LightWallet {
} ;
{
info ! ( "A sapling note was sp ent in {}" , tx . txid ( ) ) ;
// Update the WalletTx
// Do it in a short scope because of the write lock.
info ! ( "A sapling note was sent in {}, getting memo " , tx . txid ( ) ) ;
// Do it in a short scope because of the write lock.
let mut txs = self . txs . write ( ) . unwrap ( ) ;
txs . get_mut ( & tx . txid ( ) ) . unwrap ( )
. notes . iter_mut ( )
. find ( | nd | nd . note = = note ) . unwrap ( )
. memo = Some ( memo ) ;
// Update memo if we have this Tx.
match txs . get_mut ( & tx . txid ( ) )
. and_then ( | t | {
t . notes . iter_mut ( ) . find ( | nd | nd . note = = note )
} ) {
None = > ( ) ,
Some ( nd ) = > {
nd . memo = Some ( memo )
}
}
}
}
@ -1137,7 +1155,7 @@ impl LightWallet {
let mut txs = self . txs . write ( ) . unwrap ( ) ;
if txs . get ( & tx . txid ( ) ) . unwrap ( ) . outgoing_metadata . iter ( )
. find ( | om | om . address = = address & & om . value = = note . value )
. find ( | om | om . address = = address & & om . value = = note . value & & om . memo = = memo )
. is_some ( ) {
warn ! ( "Duplicate outgoing metadata" ) ;
continue ;
@ -1453,16 +1471,16 @@ impl LightWallet {
return Err ( "Need at least one destination address" . to_string ( ) ) ;
}
// Check for duplicates in the to list
if tos . len ( ) > 1 {
let mut to_addresses = tos . iter ( ) . map ( | t | t . 0. to_string ( ) ) . collect ::< Vec < _ > > ( ) ;
to_addresses . sort ( ) ;
for i in 0 . . to_addresses . len ( ) - 1 {
if to_addresses [ i ] = = to_addresses [ i + 1 ] {
return Err ( format ! ( "To address {} is duplicated" , to_addresses [ i ] ) ) ;
}
}
}
// Check for duplicates in the to list - We need that for HushChat
// if tos.len() > 1 {
// let mut to_addresses = tos.iter().map(|t| t.0.to_string()).collect::<Vec<_>>();
// to_addresses.sort();
// for i in 0..to_addresses.len()-1 {
// if to_addresses[i] == to_addresses[i+1] {
// return Err(format!("To address {} is duplicated", to_addresses[i]));
// }
// }
// }
let total_value = tos . iter ( ) . map ( | to | to . 1 ) . sum ::< u64 > ( ) as u64 ;
println ! (
@ -1608,7 +1626,17 @@ impl LightWallet {
for ( to , value , memo ) in recepients {
// Compute memo if it exists
let encoded_memo = memo . map ( | s | Memo ::from_str ( & s ) . unwrap ( ) ) ;
let encoded_memo = match memo {
None = > None ,
Some ( s ) = > match Memo ::from_str ( & s ) {
None = > {
let e = format ! ( "Error creating output. Memo {:?} is too long" , s ) ;
error ! ( "{}" , e ) ;
return Err ( e ) ;
} ,
Some ( m ) = > Some ( m )
}
} ;
println ! ( "{}: Adding output" , now ( ) - start_time ) ;