wallet: update lmdb from store crate, migrate single backend from trait to struct, update tests cargo deps and design doc

This commit is contained in:
ardocrat
2026-06-04 02:35:10 +03:00
parent 602d79e868
commit 840bde734e
37 changed files with 2393 additions and 2261 deletions
Generated
+1124 -671
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -41,10 +41,10 @@ grin_wallet_util = { path = "./util", version = "5.4.0-alpha.1" }
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_core = "5.4.0" #grin_core = "5.4.0"
grin_keychain = "5.4.0" #grin_keychain = "5.4.0"
grin_util = "5.4.0" #grin_util = "5.4.0"
grin_api = "5.4.0" #grin_api = "5.4.0"
# For beta release # For beta release
@@ -54,10 +54,10 @@ grin_api = "5.4.0"
# grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_keychain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_api = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing
# grin_core = { path = "../grin/core"} # grin_core = { path = "../grin/core"}
+6 -6
View File
@@ -30,9 +30,9 @@ grin_wallet_util = { path = "../util", version = "5.4.0-alpha.1" }
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_core = "5.4.0" #grin_core = "5.4.0"
grin_keychain = "5.4.0" #grin_keychain = "5.4.0"
grin_util = "5.4.0" #grin_util = "5.4.0"
# For beta release # For beta release
@@ -41,9 +41,9 @@ grin_util = "5.4.0"
# grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_keychain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing
# grin_core = { path = "../../grin/core"} # grin_core = { path = "../../grin/core"}
+16 -22
View File
@@ -146,7 +146,7 @@ where
/// // by the reference wallet implementation. /// // by the reference wallet implementation.
/// // These traits can be replaced with alternative implementations if desired /// // These traits can be replaced with alternative implementations if desired
/// ///
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap()) /// let mut wallet = Box::new(DefaultWalletImpl::<HTTPNodeClient>::new(node_client.clone()).unwrap())
/// as Box<dyn WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>; /// as Box<dyn WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
/// ///
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc... /// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
@@ -286,7 +286,7 @@ where
)?; )?;
} }
foreign::build_coinbase( foreign::build_coinbase(
&mut **w, w,
(&self.keychain_mask).as_ref(), (&self.keychain_mask).as_ref(),
block_fees, block_fees,
self.doctest_mode, self.doctest_mode,
@@ -362,7 +362,7 @@ where
)?; )?;
} }
let ret_slate = foreign::receive_tx( let ret_slate = foreign::receive_tx(
&mut **w, w,
(&self.keychain_mask).as_ref(), (&self.keychain_mask).as_ref(),
slate, slate,
dest_acct_name, dest_acct_name,
@@ -380,8 +380,8 @@ where
self.doctest_mode, self.doctest_mode,
); );
match res { match res {
Ok(s) => return Ok(s.unwrap()), Ok(s) => Ok(s.unwrap()),
Err(_) => return Ok(ret_slate), Err(_) => Ok(ret_slate),
} }
} }
None => Ok(ret_slate), None => Ok(ret_slate),
@@ -443,12 +443,7 @@ where
true => false, true => false,
false => post_automatically, false => post_automatically,
}; };
foreign::finalize_tx( foreign::finalize_tx(w, (&self.keychain_mask).as_ref(), slate, post_automatically)
&mut **w,
(&self.keychain_mask).as_ref(),
slate,
post_automatically,
)
} }
} }
@@ -496,17 +491,16 @@ macro_rules! doctest_helper_setup_doc_env_foreign {
let node_client = let node_client =
HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap();
let mut wallet = Box::new( let mut wallet =
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(), Box::new(DefaultWalletImpl::<HTTPNodeClient>::new(node_client.clone()).unwrap())
) as Box<
as Box< dyn WalletInst<
dyn WalletInst< 'static,
'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient,
HTTPNodeClient, ExtKeychain,
ExtKeychain, >,
>, >;
>;
let lc = wallet.lc_provider().unwrap(); let lc = wallet.lc_provider().unwrap();
let _ = lc.set_top_level_directory(&wallet_config.data_file_dir); let _ = lc.set_top_level_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, pw, false, false); lc.open_wallet(None, pw, false, false);
+3 -6
View File
@@ -381,7 +381,6 @@ pub fn run_doctest_foreign(
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client1.clone()).unwrap()) Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client1.clone()).unwrap())
as Box< as Box<
dyn WalletInst< dyn WalletInst<
'static,
DefaultLCProvider<LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
@@ -416,7 +415,6 @@ pub fn run_doctest_foreign(
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client2.clone()).unwrap()) Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client2.clone()).unwrap())
as Box< as Box<
dyn WalletInst< dyn WalletInst<
'static,
DefaultLCProvider<LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
@@ -473,7 +471,7 @@ pub fn run_doctest_foreign(
amount, amount,
..Default::default() ..Default::default()
}; };
api_impl::owner::issue_invoice_tx(&mut **w, (&mask2).as_ref(), args, true).unwrap() api_impl::owner::issue_invoice_tx(w, (&mask2).as_ref(), args, true).unwrap()
}; };
slate = { slate = {
let mut w_lock = wallet1.lock(); let mut w_lock = wallet1.lock();
@@ -487,8 +485,7 @@ pub fn run_doctest_foreign(
selection_strategy_is_use_all: true, selection_strategy_is_use_all: true,
..Default::default() ..Default::default()
}; };
api_impl::owner::process_invoice_tx(&mut **w, (&mask1).as_ref(), &slate, args, true) api_impl::owner::process_invoice_tx(w, (&mask1).as_ref(), &slate, args, true).unwrap()
.unwrap()
}; };
println!("INIT INVOICE SLATE"); println!("INIT INVOICE SLATE");
// Spit out slate for input to finalize_tx // Spit out slate for input to finalize_tx
@@ -508,7 +505,7 @@ pub fn run_doctest_foreign(
selection_strategy_is_use_all: true, selection_strategy_is_use_all: true,
..Default::default() ..Default::default()
}; };
let slate = api_impl::owner::init_send_tx(&mut **w, (&mask1).as_ref(), args, true).unwrap(); let slate = api_impl::owner::init_send_tx(w, (&mask1).as_ref(), args, true).unwrap();
println!("INIT SLATE"); println!("INIT SLATE");
// Spit out slate for input to finalize_tx // Spit out slate for input to finalize_tx
println!("{}", serde_json::to_string_pretty(&slate).unwrap()); println!("{}", serde_json::to_string_pretty(&slate).unwrap());
+34 -36
View File
@@ -97,7 +97,7 @@ where
/// ///
/// Each method will call the [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html)'s /// Each method will call the [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html)'s
/// [`open_with_credentials`](../grin_wallet_libwallet/types/trait.WalletBackend.html#tymethod.open_with_credentials) /// [`open_with_credentials`](../grin_wallet_libwallet/types/trait.WalletBackend.html#tymethod.open_with_credentials)
/// (initialising a keychain with the master seed,) perform its operation, then close the keychain /// initializing a keychain with the master seed, perform its operation, then close the keychain
/// with a call to [`close`](../grin_wallet_libwallet/types/trait.WalletBackend.html#tymethod.close) /// with a call to [`close`](../grin_wallet_libwallet/types/trait.WalletBackend.html#tymethod.close)
/// ///
/// # Arguments /// # Arguments
@@ -147,11 +147,11 @@ where
/// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); /// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap();
/// ///
/// // impls::DefaultWalletImpl is provided for convenience in instantiating the wallet /// // impls::DefaultWalletImpl is provided for convenience in instantiating the wallet
/// // It contains the LMDBBackend, DefaultLCProvider (lifecycle) and ExtKeychain used /// // It contains the WalletBackend, DefaultLCProvider (lifecycle) and ExtKeychain used
/// // by the reference wallet implementation. /// // by the reference wallet implementation.
/// // These traits can be replaced with alternative implementations if desired /// // These traits can be replaced with alternative implementations if desired
/// ///
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap()) /// let mut wallet = Box::new(DefaultWalletImpl::<HTTPNodeClient>::new(node_client.clone()).unwrap())
/// as Box<dyn WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>; /// as Box<dyn WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
/// ///
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc... /// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
@@ -258,7 +258,7 @@ where
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent // Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?; let _ = w.keychain(keychain_mask)?;
owner::accounts(&mut **w) owner::accounts(w)
} }
/// Creates a new 'account', which is a mapping of a user-specified /// Creates a new 'account', which is a mapping of a user-specified
@@ -308,7 +308,7 @@ where
) -> Result<Identifier, Error> { ) -> Result<Identifier, Error> {
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::create_account_path(&mut **w, keychain_mask, label) owner::create_account_path(w, keychain_mask, label)
} }
/// Sets the wallet's currently active account. This sets the /// Sets the wallet's currently active account. This sets the
@@ -358,7 +358,7 @@ where
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent // Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?; let _ = w.keychain(keychain_mask)?;
owner::set_active_account(&mut **w, label) owner::set_active_account(w, label)
} }
/// Returns a list of outputs from the active account in the wallet. /// Returns a list of outputs from the active account in the wallet.
@@ -665,7 +665,7 @@ where
let slate = { let slate = {
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::init_send_tx(&mut **w, keychain_mask, args, self.doctest_mode)? owner::init_send_tx(w, keychain_mask, args, self.doctest_mode)?
}; };
// Helper functionality. If send arguments exist, attempt to send sync and // Helper functionality. If send arguments exist, attempt to send sync and
// finalize // finalize
@@ -701,17 +701,17 @@ where
match result { match result {
Ok(_) => { Ok(_) => {
info!("Tx sent ok",); info!("Tx sent ok",);
return Ok(ret_slate); Ok(ret_slate)
} }
Err(e) => { Err(e) => {
error!("Tx sent fail: {}", e); error!("Tx sent fail: {}", e);
return Err(e); Err(e)
} }
} }
} else { } else {
self.tx_lock_outputs(keychain_mask, &s)?; self.tx_lock_outputs(keychain_mask, &s)?;
let ret_slate = self.finalize_tx(keychain_mask, &s)?; let ret_slate = self.finalize_tx(keychain_mask, &s)?;
return Ok(ret_slate); Ok(ret_slate)
} }
} }
Ok(None) => Ok(slate), Ok(None) => Ok(slate),
@@ -724,7 +724,7 @@ where
/// Issues a new invoice transaction slate, essentially a `request for payment`. /// Issues a new invoice transaction slate, essentially a `request for payment`.
/// The slate created by this function will contain the amount, an output for the amount, /// The slate created by this function will contain the amount, an output for the amount,
/// as well as round 1 of singature creation complete. The slate should then be send /// as well as round 1 of signature creation complete. The slate should then be sent
/// to the payer, who should add their inputs and signature data and return the slate /// to the payer, who should add their inputs and signature data and return the slate
/// via the [Foreign API's `finalize_tx`](struct.Foreign.html#method.finalize_tx) method. /// via the [Foreign API's `finalize_tx`](struct.Foreign.html#method.finalize_tx) method.
/// ///
@@ -764,14 +764,14 @@ where
) -> Result<Slate, Error> { ) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::issue_invoice_tx(&mut **w, keychain_mask, args, self.doctest_mode) owner::issue_invoice_tx(w, keychain_mask, args, self.doctest_mode)
} }
/// Processes an invoice tranaction created by another party, essentially /// Processes an invoice transaction created by another party, essentially
/// a `request for payment`. The incoming slate should contain a requested /// a `request for payment`. The incoming slate should contain a requested
/// amount, an output created by the invoicer convering the amount, and /// amount, an output created by the invoicer converting the amount, and
/// part 1 of signature creation completed. This function will add inputs /// part 1 of signature creation completed. This function will add inputs
/// equalling the amount + fees, as well as perform round 1 and 2 of signature /// equaling the amount + fees, as well as perform round 1 and 2 of signature
/// creation. /// creation.
/// ///
/// Callers should note that no prompting of the user will be done by this function /// Callers should note that no prompting of the user will be done by this function
@@ -837,8 +837,7 @@ where
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
let send_args = args.send_args.clone(); let send_args = args.send_args.clone();
let slate = let slate = owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)?;
owner::process_invoice_tx(&mut **w, keychain_mask, slate, args, self.doctest_mode)?;
// Helper functionality. If send arguments exist, attempt to send // Helper functionality. If send arguments exist, attempt to send
match send_args { match send_args {
Some(sa) => { Some(sa) => {
@@ -871,7 +870,7 @@ where
/// Locks the outputs associated with the inputs to the transaction in the given /// Locks the outputs associated with the inputs to the transaction in the given
/// [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html), /// [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html),
/// making them unavailable for use in further transactions. This function is called /// making them unavailable for use in further transactions. This function is called
/// by the sender, (or more generally, all parties who have put inputs into the transaction,) /// by the sender, (or more generally, all parties who have put inputs into the transaction)
/// and must be called before the corresponding call to [`finalize_tx`](struct.Owner.html#method.finalize_tx) /// and must be called before the corresponding call to [`finalize_tx`](struct.Owner.html#method.finalize_tx)
/// that completes the transaction. /// that completes the transaction.
/// ///
@@ -929,7 +928,7 @@ where
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::tx_lock_outputs(&mut **w, keychain_mask, slate) owner::tx_lock_outputs(w, keychain_mask, slate)
} }
/// Finalizes a transaction, after all parties /// Finalizes a transaction, after all parties
@@ -995,7 +994,7 @@ where
) -> Result<Slate, Error> { ) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::finalize_tx(&mut **w, keychain_mask, slate) owner::finalize_tx(w, keychain_mask, slate)
} }
/// Posts a completed transaction to the listening node for validation and inclusion in a block /// Posts a completed transaction to the listening node for validation and inclusion in a block
@@ -1186,7 +1185,7 @@ where
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent // Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?; let _ = w.keychain(keychain_mask)?;
owner::get_stored_tx(&**w, tx_id, slate_id) owner::get_stored_tx(w, tx_id, slate_id)
} }
/// Return the rewind hash of the wallet. /// Return the rewind hash of the wallet.
@@ -2422,7 +2421,7 @@ where
) -> Result<BuiltOutput, Error> { ) -> Result<BuiltOutput, Error> {
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::build_output(&mut **w, keychain_mask, features, amount) owner::build_output(w, keychain_mask, features, amount)
} }
// MWIXNET // MWIXNET
@@ -2481,7 +2480,7 @@ where
let mut w_lock = self.wallet_inst.lock(); let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
owner::create_mwixnet_req( owner::create_mwixnet_req(
&mut **w, w,
keychain_mask, keychain_mask,
params, params,
commitment, commitment,
@@ -2499,7 +2498,7 @@ pub fn try_slatepack_sync_workflow(
tor_sender: Option<HttpSlateSender>, tor_sender: Option<HttpSlateSender>,
send_to_finalize: bool, send_to_finalize: bool,
test_mode: bool, test_mode: bool,
) -> Result<Option<Slate>, libwallet::Error> { ) -> Result<Option<Slate>, Error> {
if let Some(tc) = &tor_config { if let Some(tc) = &tor_config {
if tc.skip_send_attempt == Some(true) { if tc.skip_send_attempt == Some(true) {
return Ok(None); return Ok(None);
@@ -2604,7 +2603,7 @@ macro_rules! doctest_helper_setup_doc_env {
use uuid::Uuid; use uuid::Uuid;
// don't run on windows CI, which gives very inconsistent results // don't run on Windows CI, which gives very inconsistent results
if cfg!(windows) { if cfg!(windows) {
return; return;
} }
@@ -2624,17 +2623,16 @@ macro_rules! doctest_helper_setup_doc_env {
let node_client = let node_client =
HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap();
let mut wallet = Box::new( let mut wallet =
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(), Box::new(DefaultWalletImpl::<HTTPNodeClient>::new(node_client.clone()).unwrap())
) as Box<
as Box< dyn WalletInst<
dyn WalletInst< 'static,
'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient,
HTTPNodeClient, ExtKeychain,
ExtKeychain, >,
>, >;
>;
let lc = wallet.lc_provider().unwrap(); let lc = wallet.lc_provider().unwrap();
let _ = lc.set_top_level_directory(&wallet_config.data_file_dir); let _ = lc.set_top_level_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, pw, false, false); lc.open_wallet(None, pw, false, false);
+17 -18
View File
@@ -2137,6 +2137,14 @@ where
VersionedSlate::into_version(out_slate, version) VersionedSlate::into_version(out_slate, version)
} }
fn tx_lock_outputs(&self, token: Token, in_slate: VersionedSlate) -> Result<(), Error> {
Owner::tx_lock_outputs(
self,
(&token.keychain_mask).as_ref(),
&Slate::from(in_slate),
)
}
fn finalize_tx(&self, token: Token, in_slate: VersionedSlate) -> Result<VersionedSlate, Error> { fn finalize_tx(&self, token: Token, in_slate: VersionedSlate) -> Result<VersionedSlate, Error> {
let out_slate = Owner::finalize_tx( let out_slate = Owner::finalize_tx(
self, self,
@@ -2147,11 +2155,12 @@ where
VersionedSlate::into_version(out_slate, version) VersionedSlate::into_version(out_slate, version)
} }
fn tx_lock_outputs(&self, token: Token, in_slate: VersionedSlate) -> Result<(), Error> { fn post_tx(&self, token: Token, slate: VersionedSlate, fluff: bool) -> Result<(), Error> {
Owner::tx_lock_outputs( Owner::post_tx(
self, self,
(&token.keychain_mask).as_ref(), (&token.keychain_mask).as_ref(),
&Slate::from(in_slate), &Slate::from(slate),
fluff,
) )
} }
@@ -2185,15 +2194,6 @@ where
} }
} }
fn post_tx(&self, token: Token, slate: VersionedSlate, fluff: bool) -> Result<(), Error> {
Owner::post_tx(
self,
(&token.keychain_mask).as_ref(),
&Slate::from(slate),
fluff,
)
}
fn get_rewind_hash(&self, token: Token) -> Result<String, Error> { fn get_rewind_hash(&self, token: Token) -> Result<String, Error> {
Owner::get_rewind_hash(self, (&token.keychain_mask).as_ref()) Owner::get_rewind_hash(self, (&token.keychain_mask).as_ref())
} }
@@ -2625,26 +2625,25 @@ pub fn run_doctest_owner(
payment_proof_recipient_address: proof_address, payment_proof_recipient_address: proof_address,
..Default::default() ..Default::default()
}; };
let mut slate = let mut slate = api_impl::owner::init_send_tx(w, (&mask1).as_ref(), args, true).unwrap();
api_impl::owner::init_send_tx(&mut **w, (&mask1).as_ref(), args, true).unwrap();
println!("INITIAL SLATE"); println!("INITIAL SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap()); println!("{}", serde_json::to_string_pretty(&slate).unwrap());
{ {
let mut w_lock = wallet2.lock(); let mut w_lock = wallet2.lock();
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap(); let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
slate = api_impl::foreign::receive_tx(&mut **w2, (&mask2).as_ref(), &slate, None, true) slate =
.unwrap(); api_impl::foreign::receive_tx(w2, (&mask2).as_ref(), &slate, None, true).unwrap();
w2.close().unwrap(); w2.close().unwrap();
} }
// Spit out slate for input to finalize_tx // Spit out slate for input to finalize_tx
if lock_tx { if lock_tx {
println!("LOCKING TX"); println!("LOCKING TX");
api_impl::owner::tx_lock_outputs(&mut **w, (&mask1).as_ref(), &slate).unwrap(); api_impl::owner::tx_lock_outputs(w, (&mask1).as_ref(), &slate).unwrap();
} }
println!("RECEIPIENT SLATE"); println!("RECEIPIENT SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap()); println!("{}", serde_json::to_string_pretty(&slate).unwrap());
if finalize_tx { if finalize_tx {
slate = api_impl::owner::finalize_tx(&mut **w, (&mask1).as_ref(), &slate).unwrap(); slate = api_impl::owner::finalize_tx(w, (&mask1).as_ref(), &slate).unwrap();
error!("FINALIZED TX SLATE"); error!("FINALIZED TX SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap()); println!("{}", serde_json::to_string_pretty(&slate).unwrap());
} }
+4 -4
View File
@@ -21,8 +21,8 @@ grin_wallet_util = { path = "../util", version = "5.4.0-alpha.1" }
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_core = "5.4.0" #grin_core = "5.4.0"
grin_util = "5.4.0" #grin_util = "5.4.0"
# For beta release # For beta release
@@ -30,8 +30,8 @@ grin_util = "5.4.0"
#grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } #grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing
# grin_core = { path = "../../grin/core"} # grin_core = { path = "../../grin/core"}
+12 -26
View File
@@ -39,10 +39,11 @@ grin_wallet_config = { path = "../config", version = "5.4.0-alpha.1" }
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_core = "5.4.0" #grin_core = "5.4.0"
grin_keychain = "5.4.0" #grin_keychain = "5.4.0"
grin_util = "5.4.0" #grin_util = "5.4.0"
grin_api = "5.4.0" #grin_api = "5.4.0"
#grin_chain = "5.4.0"
# For beta release # For beta release
@@ -50,39 +51,24 @@ grin_api = "5.4.0"
# grin_keychain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_keychain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_api = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# grin_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_keychain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_api = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
grin_chain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing
# grin_core = { path = "../../grin/core"} # grin_core = { path = "../../grin/core"}
# grin_keychain = { path = "../../grin/keychain"} # grin_keychain = { path = "../../grin/keychain"}
# grin_util = { path = "../../grin/util"} # grin_util = { path = "../../grin/util"}
# grin_api = { path = "../../grin/api"} # grin_api = { path = "../../grin/api"}
# grin_chain = { path = "../../grin/chain"}
##### #####
[dev-dependencies] [dev-dependencies]
ed25519-dalek = "1.0.0-pre.4" ed25519-dalek = "1.0.0-pre.4"
remove_dir_all = "0.7" remove_dir_all = "0.7"
##### Grin Imports
# For Release
grin_chain = "5.4.0"
# For beta release
# grin_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge
# grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
# For local testing
# grin_chain = { path = "../../grin/chain"}
#####
+5 -5
View File
@@ -98,7 +98,7 @@ pub fn create_wallet_proxy(
test_dir: &str, test_dir: &str,
) -> WalletProxy< ) -> WalletProxy<
'_, '_,
DefaultLCProvider<'_, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
> { > {
@@ -117,7 +117,7 @@ pub fn create_local_wallet(
Box< Box<
dyn WalletInst< dyn WalletInst<
'static, 'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
@@ -129,7 +129,7 @@ pub fn create_local_wallet(
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap()) let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
as Box< as Box<
dyn WalletInst< dyn WalletInst<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
@@ -156,7 +156,7 @@ pub fn open_local_wallet(
Box< Box<
dyn WalletInst< dyn WalletInst<
'static, 'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
@@ -168,7 +168,7 @@ pub fn open_local_wallet(
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap()) let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
as Box< as Box<
dyn WalletInst< dyn WalletInst<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
+1 -1
View File
@@ -41,7 +41,7 @@ type Wallet = Arc<
Box< Box<
dyn WalletInst< dyn WalletInst<
'static, 'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
+1 -1
View File
@@ -45,7 +45,7 @@ fn test_wallet_tx_filtering(
Box< Box<
dyn WalletInst< dyn WalletInst<
'static, 'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
+3 -10
View File
@@ -55,24 +55,22 @@ the default client only supports single-user - single recipient, an arbitrary nu
### Wallet Traits ### Wallet Traits
In the current code, a Wallet implementation is just a combination of these three traits. The vast majority of functions within libwallet In the current code, a Wallet implementation is just a combination of these two traits. The vast majority of functions within libwallet
and libTX have a signature similar to the following: and libTX have a signature similar to the following:
```rust ```rust
pub fn retrieve_outputs<T: ?Sized, C, K>( pub fn retrieve_outputs<T: ?Sized, C, K>(
!·wallet: &mut T, !·wallet: &mut WalletBackend<C, K>,
!·show_spent: bool, !·show_spent: bool,
!·tx_id: Option<u32>, !·tx_id: Option<u32>,
) -> Result<Vec<OutputData>, Error> ) -> Result<Vec<OutputData>, Error>
where where
!·T: WalletBackend<C, K>,
!·C: NodeClient, !·C: NodeClient,
!·K: Keychain, !·K: Keychain,
{ {
``` ```
With `T` in this instance being a class that implements the `WalletBackend` trait, which is further parameterized with implementations of Provided `WalletBackend` parameterized with implementations of `NodeClient` and `Keychain`.
`NodeClient` and `Keychain`.
There is currently only a single implementation of the Keychain trait within the Grin code, in the `keychain` crate exported as `ExtKeyChain`. There is currently only a single implementation of the Keychain trait within the Grin code, in the `keychain` crate exported as `ExtKeyChain`.
The `Keychain` trait makes several assumptions about the underlying implementation, particularly that it will adhere to a The `Keychain` trait makes several assumptions about the underlying implementation, particularly that it will adhere to a
@@ -82,8 +80,3 @@ There are two implementations of `NodeClient` within the code, the main version
the seconds a test client that communicates with an in-process instance of a chain. The NodeClient isolates all network calls, so upgrading wallet the seconds a test client that communicates with an in-process instance of a chain. The NodeClient isolates all network calls, so upgrading wallet
communication from the current simple http interaction to a more secure protocol (or allowing for many options) should be a simple communication from the current simple http interaction to a more secure protocol (or allowing for many options) should be a simple
matter of dropping in different `NodeClient` implementations. matter of dropping in different `NodeClient` implementations.
There are also two implementations of `WalletBackend` within the code at the base of the `wallet` crate. `LMDBBackend` found within
`wallet/src/lmdb_wallet.rs` is the main implementation, and is now used by all grin wallet commands. The earlier `FileWallet` still exists
within the code, however it is not invoked, and given there are no real advantages to running it over a DB implementation, development on it
has been dropped in favour of the LMDB implementation.
+12 -12
View File
@@ -43,12 +43,12 @@ grin_wallet_libwallet = { path = "../libwallet", version = "5.4.0-alpha.1" }
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_core = "5.4.0" #grin_core = "5.4.0"
grin_keychain = "5.4.0" #grin_keychain = "5.4.0"
grin_chain = "5.4.0" #grin_chain = "5.4.0"
grin_util = "5.4.0" #grin_util = "5.4.0"
grin_api = "5.4.0" #grin_api = "5.4.0"
grin_store = "5.4.0" #grin_store = "5.4.0"
# For beta release # For beta release
@@ -60,12 +60,12 @@ grin_store = "5.4.0"
# grin_store = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_store = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_keychain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_chain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_api = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_store = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_store = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing
# grin_core = { path = "../../grin/core"} # grin_core = { path = "../../grin/core"}
-777
View File
@@ -1,777 +0,0 @@
// Copyright 2021 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cell::RefCell;
use std::{fs, path};
// for writing stored transaction files
use std::fs::File;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::path::Path;
use uuid::Uuid;
use crate::blake2::blake2b::{Blake2b, Blake2bResult};
use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain, SwitchCommitmentType};
use crate::store::{self, option_to_not_found, to_key, to_key_u64};
use crate::core::core::Transaction;
use crate::core::ser;
use crate::libwallet::{
AcctPathMapping, Context, Error, NodeClient, OutputData, ScannedBlockInfo, TxLogEntry,
WalletBackend, WalletInitStatus, WalletOutputBatch,
};
use crate::util::secp::constants::SECRET_KEY_SIZE;
use crate::util::secp::key::SecretKey;
use crate::util::{self, secp, ToHex};
use rand::rngs::mock::StepRng;
use rand::thread_rng;
pub const DB_DIR: &str = "db";
pub const TX_SAVE_DIR: &str = "saved_txs";
const OUTPUT_PREFIX: u8 = b'o';
const DERIV_PREFIX: u8 = b'd';
const CONFIRMED_HEIGHT_PREFIX: u8 = b'c';
const PRIVATE_TX_CONTEXT_PREFIX: u8 = b'p';
const TX_LOG_ENTRY_PREFIX: u8 = b't';
const TX_LOG_ID_PREFIX: u8 = b'i';
const ACCOUNT_PATH_MAPPING_PREFIX: u8 = b'a';
const LAST_SCANNED_BLOCK: u8 = b'l';
const LAST_SCANNED_KEY: &str = "LAST_SCANNED_KEY";
const WALLET_INIT_STATUS: u8 = b'w';
const WALLET_INIT_STATUS_KEY: &str = "WALLET_INIT_STATUS";
/// test to see if database files exist in the current directory. If so,
/// use a DB backend for all operations
pub fn wallet_db_exists(data_file_dir: &str) -> bool {
let db_path = path::Path::new(data_file_dir).join(DB_DIR);
db_path.exists()
}
/// Helper to derive XOR keys for storing private transaction keys in the DB
/// (blind_xor_key, nonce_xor_key)
fn private_ctx_xor_keys<K>(
keychain: &K,
slate_id: &[u8],
) -> Result<([u8; SECRET_KEY_SIZE], [u8; SECRET_KEY_SIZE]), Error>
where
K: Keychain,
{
let root_key = keychain.derive_key(0, &K::root_key_id(), SwitchCommitmentType::Regular)?;
// derive XOR values for storing secret values in DB
// h(root_key|slate_id|"blind")
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
hasher.update(&slate_id[..]);
hasher.update(&b"blind"[..]);
let blind_xor_key = hasher.finalize();
let mut ret_blind = [0; SECRET_KEY_SIZE];
ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
// h(root_key|slate_id|"nonce")
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
hasher.update(&slate_id[..]);
hasher.update(&b"nonce"[..]);
let nonce_xor_key = hasher.finalize();
let mut ret_nonce = [0; SECRET_KEY_SIZE];
ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
Ok((ret_blind, ret_nonce))
}
pub struct LMDBBackend<'ck, C, K>
where
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
db: store::Store,
data_file_dir: String,
/// Keychain
pub keychain: Option<K>,
/// Check value for XORed keychain seed
pub master_checksum: Box<Option<Blake2bResult>>,
/// Parent path to use by default for output operations
parent_key_id: Identifier,
/// wallet to node client
w2n_client: C,
///phantom
_phantom: &'ck PhantomData<C>,
}
impl<'ck, C, K> LMDBBackend<'ck, C, K>
where
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
pub fn new(data_file_dir: &str, n_client: C) -> Result<Self, Error> {
let db_path = path::Path::new(data_file_dir).join(DB_DIR);
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
let stored_tx_path = path::Path::new(data_file_dir).join(TX_SAVE_DIR);
fs::create_dir_all(&stored_tx_path)
.expect("Couldn't create wallet backend tx storage directory!");
let store = store::Store::new(db_path.to_str().unwrap(), None, Some(DB_DIR), None)?;
// Make sure default wallet derivation path always exists
// as well as path (so it can be retrieved by batches to know where to store
// completed transactions, for reference
let default_account = AcctPathMapping {
label: "default".to_owned(),
path: LMDBBackend::<C, K>::default_path(),
};
let acct_key = to_key(
ACCOUNT_PATH_MAPPING_PREFIX,
&mut default_account.label.as_bytes().to_vec(),
);
{
let batch = store.batch()?;
batch.put_ser(&acct_key, &default_account)?;
batch.commit()?;
}
let res = LMDBBackend {
db: store,
data_file_dir: data_file_dir.to_owned(),
keychain: None,
master_checksum: Box::new(None),
parent_key_id: LMDBBackend::<C, K>::default_path(),
w2n_client: n_client,
_phantom: &PhantomData,
};
Ok(res)
}
fn default_path() -> Identifier {
// return the default parent wallet path, corresponding to the default account
// in the BIP32 spec. Parent is account 0 at level 2, child output identifiers
// are all at level 3
ExtKeychain::derive_key_id(2, 0, 0, 0, 0)
}
/// Just test to see if database files exist in the current directory. If
/// so, use a DB backend for all operations
pub fn exists(data_file_dir: &str) -> bool {
let db_path = path::Path::new(data_file_dir).join(DB_DIR);
db_path.exists()
}
}
impl<'ck, C, K> WalletBackend<'ck, C, K> for LMDBBackend<'ck, C, K>
where
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
/// Set the keychain, which should already have been opened
fn set_keychain(
&mut self,
mut k: Box<K>,
mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error> {
// store hash of master key, so it can be verified later after unmasking
let root_key = k.derive_key(0, &K::root_key_id(), SwitchCommitmentType::Regular)?;
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
self.master_checksum = Box::new(Some(hasher.finalize()));
let mask_value = {
match mask {
true => {
// Random value that must be XORed against the stored wallet seed
// before it is used
let mask_value = match use_test_rng {
true => {
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
secp::key::SecretKey::new(&k.secp(), &mut test_rng)
}
false => secp::key::SecretKey::new(&k.secp(), &mut thread_rng()),
};
k.mask_master_key(&mask_value)?;
Some(mask_value)
}
false => None,
}
};
self.keychain = Some(*k);
Ok(mask_value)
}
/// Close wallet
fn close(&mut self) -> Result<(), Error> {
self.keychain = None;
Ok(())
}
/// Return the keychain being used, cloned with XORed token value
/// for temporary use
fn keychain(&self, mask: Option<&SecretKey>) -> Result<K, Error> {
match self.keychain.as_ref() {
Some(k) => {
let mut k_masked = k.clone();
if let Some(m) = mask {
k_masked.mask_master_key(m)?;
}
// Check if master seed is what is expected (especially if it's been xored)
let root_key =
k_masked.derive_key(0, &K::root_key_id(), SwitchCommitmentType::Regular)?;
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
if *self.master_checksum != Some(hasher.finalize()) {
error!("Supplied keychain mask is invalid");
return Err(Error::InvalidKeychainMask);
}
Ok(k_masked)
}
None => Err(Error::KeychainDoesntExist),
}
}
/// Return the node client being used
fn w2n_client(&mut self) -> &mut C {
&mut self.w2n_client
}
/// return the version of the commit for caching
fn calc_commit_for_cache(
&mut self,
keychain_mask: Option<&SecretKey>,
amount: u64,
id: &Identifier,
) -> Result<Option<String>, Error> {
//TODO: Check if this is really necessary, it's the only thing
//preventing removing the need for config in the wallet backend
/*if self.config.no_commit_cache == Some(true) {
Ok(None)
} else {*/
Ok(Some(
self.keychain(keychain_mask)?
.commit(amount, &id, SwitchCommitmentType::Regular)?
.0
.to_vec()
.to_hex(), // TODO: proper support for different switch commitment schemes
))
/*}*/
}
/// Set parent path by account name
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> {
let label = label.to_owned();
let res = self.acct_path_iter().find(|l| l.label == label);
if let Some(a) = res {
self.set_parent_key_id(a.path);
Ok(())
} else {
Err(Error::UnknownAccountLabel(label))
}
}
/// set parent path
fn set_parent_key_id(&mut self, id: Identifier) {
self.parent_key_id = id;
}
fn parent_key_id(&mut self) -> Identifier {
self.parent_key_id.clone()
}
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
let key = match mmr_index {
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
};
option_to_not_found(self.db.get_ser(&key, None), || format!("Key Id: {}", id))
.map_err(|e| e.into())
}
// TODO - fix this awkward conversion between PrefixIterator and our Box<dyn Iterator>
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(&[OUTPUT_PREFIX], move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter.expect("deserialize").into_iter();
Box::new(iter)
}
fn get_tx_log_entry(&self, u: &Uuid) -> Result<Option<TxLogEntry>, Error> {
let key = to_key(TX_LOG_ENTRY_PREFIX, &mut u.as_bytes().to_vec());
self.db.get_ser(&key, None).map_err(|e| e.into())
}
// TODO - fix this awkward conversion between PrefixIterator and our Box<dyn Iterator>
fn tx_log_iter<'a>(&'a self) -> Box<dyn Iterator<Item = TxLogEntry> + 'a> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(&[TX_LOG_ENTRY_PREFIX], move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter.expect("deserialize").into_iter();
Box::new(iter)
}
fn get_private_context(
&mut self,
keychain_mask: Option<&SecretKey>,
slate_id: &[u8],
) -> Result<Context, Error> {
let ctx_key = to_key_u64(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec(), 0);
let (blind_xor_key, nonce_xor_key) =
private_ctx_xor_keys(&self.keychain(keychain_mask)?, slate_id)?;
let mut ctx: Context = option_to_not_found(self.db.get_ser(&ctx_key, None), || {
format!("Slate id: {:x?}", slate_id.to_vec())
})?;
for i in 0..SECRET_KEY_SIZE {
ctx.sec_key.0[i] ^= blind_xor_key[i];
ctx.sec_nonce.0[i] ^= nonce_xor_key[i];
}
Ok(ctx)
}
// TODO - fix this awkward conversion between PrefixIterator and our Box<dyn Iterator>
fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self
.db
.iter(&[ACCOUNT_PATH_MAPPING_PREFIX], move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter.expect("deserialize").into_iter();
Box::new(iter)
}
fn get_acct_path(&self, label: String) -> Result<Option<AcctPathMapping>, Error> {
let acct_key = to_key(ACCOUNT_PATH_MAPPING_PREFIX, &mut label.as_bytes().to_vec());
self.db.get_ser(&acct_key, None).map_err(|e| e.into())
}
fn store_tx(&self, uuid: &str, tx: &Transaction) -> Result<(), Error> {
let filename = format!("{}.grintx", uuid);
let path = path::Path::new(&self.data_file_dir)
.join(TX_SAVE_DIR)
.join(filename);
let path_buf = Path::new(&path).to_path_buf();
let mut stored_tx = File::create(path_buf)?;
let tx_hex = ser::ser_vec(tx, ser::ProtocolVersion(1)).unwrap().to_hex();
stored_tx.write_all(&tx_hex.as_bytes())?;
stored_tx.sync_all()?;
Ok(())
}
fn get_stored_tx(&self, uuid: &str) -> Result<Option<Transaction>, Error> {
let filename = format!("{}.grintx", uuid);
let path = path::Path::new(&self.data_file_dir)
.join(TX_SAVE_DIR)
.join(filename);
let tx_file = Path::new(&path).to_path_buf();
let mut tx_f = File::open(tx_file)?;
let mut content = String::new();
tx_f.read_to_string(&mut content)?;
let tx_bin = util::from_hex(&content).unwrap();
Ok(Some(
ser::deserialize(
&mut &tx_bin[..],
ser::ProtocolVersion(1),
ser::DeserializationMode::default(),
)
.unwrap(),
))
}
fn batch<'a>(
&'a mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error> {
Ok(Box::new(Batch {
_store: self,
db: RefCell::new(Some(self.db.batch()?)),
keychain: Some(self.keychain(keychain_mask)?),
}))
}
fn batch_no_mask<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error> {
Ok(Box::new(Batch {
_store: self,
db: RefCell::new(Some(self.db.batch()?)),
keychain: None,
}))
}
fn current_child_index<'a>(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> {
let index = {
let batch = self.db.batch()?;
let deriv_key = to_key(DERIV_PREFIX, &mut parent_key_id.to_bytes().to_vec());
match batch.get_ser(&deriv_key, None)? {
Some(idx) => idx,
None => 0,
}
};
Ok(index)
}
fn next_child<'a>(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error> {
let parent_key_id = self.parent_key_id.clone();
let mut deriv_idx = {
let batch = self.db.batch()?;
let deriv_key = to_key(DERIV_PREFIX, &mut self.parent_key_id.to_bytes().to_vec());
match batch.get_ser(&deriv_key, None)? {
Some(idx) => idx,
None => 0,
}
};
let mut return_path = self.parent_key_id.to_path();
return_path.depth += 1;
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
deriv_idx += 1;
let mut batch = self.batch(keychain_mask)?;
batch.save_child_index(&parent_key_id, deriv_idx)?;
batch.commit()?;
Ok(Identifier::from_path(&return_path))
}
fn last_confirmed_height<'a>(&mut self) -> Result<u64, Error> {
let batch = self.db.batch()?;
let height_key = to_key(
CONFIRMED_HEIGHT_PREFIX,
&mut self.parent_key_id.to_bytes().to_vec(),
);
let last_confirmed_height = match batch.get_ser(&height_key, None)? {
Some(h) => h,
None => 0,
};
Ok(last_confirmed_height)
}
fn last_scanned_block<'a>(&mut self) -> Result<ScannedBlockInfo, Error> {
let batch = self.db.batch()?;
let scanned_block_key = to_key(
LAST_SCANNED_BLOCK,
&mut LAST_SCANNED_KEY.as_bytes().to_vec(),
);
let last_scanned_block = match batch.get_ser(&scanned_block_key, None)? {
Some(b) => b,
None => ScannedBlockInfo {
height: 0,
hash: "".to_owned(),
start_pmmr_index: 0,
last_pmmr_index: 0,
},
};
Ok(last_scanned_block)
}
fn init_status<'a>(&mut self) -> Result<WalletInitStatus, Error> {
let batch = self.db.batch()?;
let init_status_key = to_key(
WALLET_INIT_STATUS,
&mut WALLET_INIT_STATUS_KEY.as_bytes().to_vec(),
);
let status = match batch.get_ser(&init_status_key, None)? {
Some(s) => s,
None => WalletInitStatus::InitComplete,
};
Ok(status)
}
}
/// An atomic batch in which all changes can be committed all at once or
/// discarded on error.
pub struct Batch<'a, C, K>
where
C: NodeClient,
K: Keychain,
{
_store: &'a LMDBBackend<'a, C, K>,
db: RefCell<Option<store::Batch<'a>>>,
/// Keychain
keychain: Option<K>,
}
#[allow(missing_docs)]
impl<'a, C, K> WalletOutputBatch<K> for Batch<'a, C, K>
where
C: NodeClient,
K: Keychain,
{
fn keychain(&mut self) -> &mut K {
self.keychain.as_mut().unwrap()
}
fn save(&mut self, out: OutputData) -> Result<(), Error> {
// Save the output data to the db.
{
let key = match out.mmr_index {
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec(), i),
None => to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec()),
};
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
}
Ok(())
}
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
let key = match mmr_index {
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
};
option_to_not_found(
self.db.borrow().as_ref().unwrap().get_ser(&key, None),
|| format!("Key ID: {}", id),
)
.map_err(|e| e.into())
}
// TODO - fix this awkward conversion between PrefixIterator and our Box<dyn Iterator>
fn iter(&self) -> Box<dyn Iterator<Item = OutputData>> {
let db = self.db.borrow();
let db = db.as_ref().unwrap();
let protocol_version = db.protocol_version();
let prefix_iter = db.iter(&[OUTPUT_PREFIX], move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter.expect("deserialize").into_iter();
Box::new(iter)
}
fn delete(&mut self, id: &Identifier, mmr_index: &Option<u64>) -> Result<(), Error> {
// Delete the output data.
{
let key = match mmr_index {
Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i),
None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()),
};
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
}
Ok(())
}
fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> {
let tx_id_key = to_key(TX_LOG_ID_PREFIX, &mut parent_key_id.to_bytes().to_vec());
let last_tx_log_id = match self
.db
.borrow()
.as_ref()
.unwrap()
.get_ser(&tx_id_key, None)?
{
Some(t) => t,
None => 0,
};
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&tx_id_key, &(last_tx_log_id + 1))?;
Ok(last_tx_log_id)
}
// TODO - fix this awkward conversion between PrefixIterator and our Box<dyn Iterator>
fn tx_log_iter(&self) -> Box<dyn Iterator<Item = TxLogEntry>> {
let db = self.db.borrow();
let db = db.as_ref().unwrap();
let protocol_version = db.protocol_version();
let prefix_iter = db.iter(&[TX_LOG_ENTRY_PREFIX], move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter.expect("deserialize").into_iter();
Box::new(iter)
}
fn save_last_confirmed_height(
&mut self,
parent_key_id: &Identifier,
height: u64,
) -> Result<(), Error> {
let height_key = to_key(
CONFIRMED_HEIGHT_PREFIX,
&mut parent_key_id.to_bytes().to_vec(),
);
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&height_key, &height)?;
Ok(())
}
fn save_last_scanned_block(&mut self, block_info: ScannedBlockInfo) -> Result<(), Error> {
let pmmr_index_key = to_key(
LAST_SCANNED_BLOCK,
&mut LAST_SCANNED_KEY.as_bytes().to_vec(),
);
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&pmmr_index_key, &block_info)?;
Ok(())
}
fn save_init_status(&mut self, value: WalletInitStatus) -> Result<(), Error> {
let init_status_key = to_key(
WALLET_INIT_STATUS,
&mut WALLET_INIT_STATUS_KEY.as_bytes().to_vec(),
);
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&init_status_key, &value)?;
Ok(())
}
fn save_child_index(&mut self, parent_id: &Identifier, child_n: u32) -> Result<(), Error> {
let deriv_key = to_key(DERIV_PREFIX, &mut parent_id.to_bytes().to_vec());
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&deriv_key, &child_n)?;
Ok(())
}
fn save_tx_log_entry(
&mut self,
tx_in: TxLogEntry,
parent_id: &Identifier,
) -> Result<(), Error> {
let tx_log_key = to_key_u64(
TX_LOG_ENTRY_PREFIX,
&mut parent_id.to_bytes().to_vec(),
tx_in.id as u64,
);
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&tx_log_key, &tx_in)?;
Ok(())
}
fn delete_tx_log_entry(&mut self, tx_id: u32, parent_id: &Identifier) -> Result<(), Error> {
let tx_log_key = to_key_u64(
TX_LOG_ENTRY_PREFIX,
&mut parent_id.to_bytes().to_vec(),
tx_id as u64,
);
self.db.borrow().as_ref().unwrap().delete(&tx_log_key)?;
Ok(())
}
fn save_acct_path(&mut self, mapping: AcctPathMapping) -> Result<(), Error> {
let acct_key = to_key(
ACCOUNT_PATH_MAPPING_PREFIX,
&mut mapping.label.as_bytes().to_vec(),
);
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&acct_key, &mapping)?;
Ok(())
}
// TODO - fix this awkward conversion between PrefixIterator and our Box<dyn Iterator>
fn acct_path_iter(&self) -> Box<dyn Iterator<Item = AcctPathMapping>> {
let db = self.db.borrow();
let db = db.as_ref().unwrap();
let protocol_version = db.protocol_version();
let prefix_iter = db.iter(&[ACCOUNT_PATH_MAPPING_PREFIX], move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter.expect("deserialize").into_iter();
Box::new(iter)
}
fn lock_output(&mut self, out: &mut OutputData) -> Result<(), Error> {
out.lock();
self.save(out.clone())
}
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
let ctx_key = to_key_u64(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec(), 0);
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
let mut s_ctx = ctx.clone();
for i in 0..SECRET_KEY_SIZE {
s_ctx.sec_key.0[i] ^= blind_xor_key[i];
s_ctx.sec_nonce.0[i] ^= nonce_xor_key[i];
}
self.db
.borrow()
.as_ref()
.unwrap()
.put_ser(&ctx_key, &s_ctx)?;
Ok(())
}
fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error> {
let ctx_key = to_key_u64(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec(), 0);
self.db
.borrow()
.as_ref()
.unwrap()
.delete(&ctx_key)
.map_err(|e| e.into())
}
fn commit(&self) -> Result<(), Error> {
let db = self.db.replace(None);
db.unwrap().commit()?;
Ok(())
}
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright 2021 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
mod lmdb;
pub use self::lmdb::{wallet_db_exists, LMDBBackend};
+9 -16
View File
@@ -28,14 +28,12 @@ use grin_api as api;
use grin_chain as chain; use grin_chain as chain;
use grin_core as core; use grin_core as core;
use grin_keychain as keychain; use grin_keychain as keychain;
use grin_store as store;
use grin_util as util; use grin_util as util;
use grin_wallet_libwallet as libwallet; use grin_wallet_libwallet as libwallet;
use grin_wallet_config as config; use grin_wallet_config as config;
mod adapters; mod adapters;
mod backends;
mod client_utils; mod client_utils;
mod error; mod error;
mod lifecycle; mod lifecycle;
@@ -47,7 +45,6 @@ pub use crate::adapters::{
HttpSlateSender, PathToSlate, PathToSlatepack, SlateGetter, SlatePutter, SlateReceiver, HttpSlateSender, PathToSlate, PathToSlatepack, SlateGetter, SlatePutter, SlateReceiver,
SlateSender, SlateSender,
}; };
pub use crate::backends::{wallet_db_exists, LMDBBackend};
pub use crate::error::Error; pub use crate::error::Error;
pub use crate::lifecycle::DefaultLCProvider; pub use crate::lifecycle::DefaultLCProvider;
pub use crate::node_clients::HTTPNodeClient; pub use crate::node_clients::HTTPNodeClient;
@@ -57,35 +54,31 @@ use crate::keychain::{ExtKeychain, Keychain};
use libwallet::{NodeClient, WalletInst, WalletLCProvider}; use libwallet::{NodeClient, WalletInst, WalletLCProvider};
/// Main wallet instance /// Main wallet instance
pub struct DefaultWalletImpl<'a, C> pub struct DefaultWalletImpl<C>
where where
C: NodeClient + 'a, C: NodeClient,
{ {
lc_provider: DefaultLCProvider<'a, C, ExtKeychain>, lc_provider: DefaultLCProvider<C, ExtKeychain>,
} }
impl<'a, C> DefaultWalletImpl<'a, C> impl<C> DefaultWalletImpl<C>
where where
C: NodeClient + 'a, C: NodeClient,
{ {
pub fn new(node_client: C) -> Result<Self, Error> { pub fn new(node_client: C) -> Result<Self, Error> {
let lc_provider = DefaultLCProvider::new(node_client); let lc_provider = DefaultLCProvider::new(node_client);
Ok(DefaultWalletImpl { Ok(DefaultWalletImpl { lc_provider })
lc_provider: lc_provider,
})
} }
} }
impl<'a, L, C, K> WalletInst<'a, L, C, K> for DefaultWalletImpl<'a, C> impl<'a, L, C, K> WalletInst<'a, L, C, K> for DefaultWalletImpl<C>
where where
DefaultLCProvider<'a, C, ExtKeychain>: WalletLCProvider<'a, C, K>, DefaultLCProvider<C, ExtKeychain>: WalletLCProvider<'a, C, K>,
L: WalletLCProvider<'a, C, K>, L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a, C: NodeClient + 'a,
K: Keychain + 'a, K: Keychain + 'a,
{ {
fn lc_provider( fn lc_provider(&mut self) -> Result<&mut dyn WalletLCProvider<'a, C, K>, libwallet::Error> {
&mut self,
) -> Result<&mut (dyn WalletLCProvider<'a, C, K> + 'a), libwallet::Error> {
Ok(&mut self.lc_provider) Ok(&mut self.lc_provider)
} }
} }
+19 -21
View File
@@ -23,13 +23,12 @@ use crate::libwallet::{Error, NodeClient, WalletBackend, WalletInitStatus, Walle
use crate::lifecycle::seed::WalletSeed; use crate::lifecycle::seed::WalletSeed;
use crate::util::secp::key::SecretKey; use crate::util::secp::key::SecretKey;
use crate::util::ZeroingString; use crate::util::ZeroingString;
use crate::LMDBBackend;
use grin_util::logger::LoggingConfig; use grin_util::logger::LoggingConfig;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::path::MAIN_SEPARATOR; use std::path::MAIN_SEPARATOR;
// Helper fuction to format paths according to OS, avoids bugs on Linux /// Helper function to format paths according to OS, avoids bugs on Linux
pub fn fmt_path(path: String) -> String { pub fn fmt_path(path: String) -> String {
let sep = &MAIN_SEPARATOR.to_string(); let sep = &MAIN_SEPARATOR.to_string();
let path = path.replace("/", &sep); let path = path.replace("/", &sep);
@@ -37,20 +36,20 @@ pub fn fmt_path(path: String) -> String {
path path
} }
pub struct DefaultLCProvider<'a, C, K> pub struct DefaultLCProvider<C, K>
where where
C: NodeClient + 'a, C: NodeClient,
K: Keychain + 'a, K: Keychain,
{ {
data_dir: String, data_dir: String,
node_client: C, node_client: C,
backend: Option<Box<dyn WalletBackend<'a, C, K> + 'a>>, backend: Option<WalletBackend<C, K>>,
} }
impl<'a, C, K> DefaultLCProvider<'a, C, K> impl<C, K> DefaultLCProvider<C, K>
where where
C: NodeClient + 'a, C: NodeClient,
K: Keychain + 'a, K: Keychain,
{ {
/// Create new provider /// Create new provider
pub fn new(node_client: C) -> Self { pub fn new(node_client: C) -> Self {
@@ -62,7 +61,7 @@ where
} }
} }
impl<'a, C, K> WalletLCProvider<'a, C, K> for DefaultLCProvider<'a, C, K> impl<'a, C, K> WalletLCProvider<'a, C, K> for DefaultLCProvider<C, K>
where where
C: NodeClient + 'a, C: NodeClient + 'a,
K: Keychain + 'a, K: Keychain + 'a,
@@ -217,8 +216,8 @@ where
Error::Lifecycle("Error creating wallet seed (is mnemonic valid?)".to_owned()) Error::Lifecycle("Error creating wallet seed (is mnemonic valid?)".to_owned())
})?; })?;
info!("Wallet seed file created"); info!("Wallet seed file created");
let mut wallet: LMDBBackend<'a, C, K> = let mut wallet: WalletBackend<C, K> =
match LMDBBackend::new(&data_dir_name, self.node_client.clone()) { match WalletBackend::new(&data_dir_name, self.node_client.clone()) {
Err(e) => { Err(e) => {
let msg = format!("Error creating wallet: {}, Data Dir: {}", e, &data_dir_name); let msg = format!("Error creating wallet: {}, Data Dir: {}", e, &data_dir_name);
error!("{}", msg); error!("{}", msg);
@@ -247,8 +246,8 @@ where
let mut data_dir_name = PathBuf::from(self.data_dir.clone()); let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR); data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = fmt_path(data_dir_name.to_str().unwrap().to_string()); let data_dir_name = fmt_path(data_dir_name.to_str().unwrap().to_string());
let mut wallet: LMDBBackend<'a, C, K> = let mut wallet: WalletBackend<C, K> =
match LMDBBackend::new(&data_dir_name, self.node_client.clone()) { match WalletBackend::new(&data_dir_name, self.node_client.clone()) {
Err(e) => { Err(e) => {
let msg = format!("Error opening wallet: {}, Data Dir: {}", e, &data_dir_name); let msg = format!("Error opening wallet: {}, Data Dir: {}", e, &data_dir_name);
return Err(Error::Lifecycle(msg)); return Err(Error::Lifecycle(msg));
@@ -262,8 +261,8 @@ where
.derive_keychain(global::is_testnet()) .derive_keychain(global::is_testnet())
.map_err(|_| Error::Lifecycle("Error deriving keychain".to_owned()))?; .map_err(|_| Error::Lifecycle("Error deriving keychain".to_owned()))?;
let mask = wallet.set_keychain(Box::new(keychain), create_mask, use_test_rng)?; let mask = wallet.set_keychain(keychain, create_mask, use_test_rng)?;
self.backend = Some(Box::new(wallet)); self.backend = Some(wallet);
Ok(mask) Ok(mask)
} }
@@ -329,8 +328,7 @@ where
let mut data_dir_name = PathBuf::from(self.data_dir.clone()); let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR); data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap(); let data_dir_name = data_dir_name.to_str().unwrap();
// get seed for later check // Get seed for later check
let orig_wallet_seed = WalletSeed::from_file(&data_dir_name, old) let orig_wallet_seed = WalletSeed::from_file(&data_dir_name, old)
.map_err(|_| Error::Lifecycle("Error opening wallet seed file".into()))?; .map_err(|_| Error::Lifecycle("Error opening wallet seed file".into()))?;
let orig_mnemonic = orig_wallet_seed let orig_mnemonic = orig_wallet_seed
@@ -366,7 +364,7 @@ where
.to_string(); .to_string();
return Err(Error::Lifecycle(msg)); return Err(Error::Lifecycle(msg));
} }
// Removin // Removing old file
info!("Password change confirmed, removing old seed file."); info!("Password change confirmed, removing old seed file.");
fs::remove_file(backup_name).map_err(|e| Error::IO(e.to_string()))?; fs::remove_file(backup_name).map_err(|e| Error::IO(e.to_string()))?;
@@ -383,13 +381,13 @@ where
Ok(()) Ok(())
} }
fn wallet_inst(&mut self) -> Result<&mut Box<dyn WalletBackend<'a, C, K> + 'a>, Error> { fn wallet_inst(&mut self) -> Result<&mut WalletBackend<C, K>, Error> {
match self.backend.as_mut() { match self.backend.as_mut() {
None => { None => {
let msg = "Wallet has not been opened".into(); let msg = "Wallet has not been opened".into();
Err(Error::Lifecycle(msg)) Err(Error::Lifecycle(msg))
} }
Some(_) => Ok(&mut *self.backend.as_mut().unwrap()), Some(b) => Ok(b),
} }
} }
} }
+7 -13
View File
@@ -25,6 +25,7 @@ use ring::aead;
use ring::pbkdf2; use ring::pbkdf2;
use crate::keychain::{mnemonic, Keychain}; use crate::keychain::{mnemonic, Keychain};
use crate::lifecycle::default::fmt_path;
use crate::util::{self, ToHex}; use crate::util::{self, ToHex};
use crate::Error; use crate::Error;
@@ -56,13 +57,6 @@ impl WalletSeed {
self.0.to_vec().to_hex() self.0.to_vec().to_hex()
} }
// Helper fuction to format paths according to OS, avoids bugs on Linux
pub fn fmt_path(path: String) -> String {
let sep = &MAIN_SEPARATOR.to_string();
let path = path.replace("/", &sep);
let path = path.replace("\\", &sep);
path
}
pub fn to_mnemonic(&self) -> Result<String, Error> { pub fn to_mnemonic(&self) -> Result<String, Error> {
let result = mnemonic::from_entropy(&self.0); let result = mnemonic::from_entropy(&self.0);
match result { match result {
@@ -105,7 +99,7 @@ impl WalletSeed {
pub fn seed_file_exists(data_file_dir: &str) -> Result<bool, Error> { pub fn seed_file_exists(data_file_dir: &str) -> Result<bool, Error> {
let seed_file_path = &format!( let seed_file_path = &format!(
"{}{}{}", "{}{}{}",
Self::fmt_path(data_file_dir.to_string()), fmt_path(data_file_dir.to_string()),
MAIN_SEPARATOR, MAIN_SEPARATOR,
SEED_FILE, SEED_FILE,
); );
@@ -145,7 +139,7 @@ impl WalletSeed {
) -> Result<(), Error> { ) -> Result<(), Error> {
let seed_file_path = &format!( let seed_file_path = &format!(
"{}{}{}", "{}{}{}",
Self::fmt_path(data_file_dir.to_string()), fmt_path(data_file_dir.to_string()),
MAIN_SEPARATOR, MAIN_SEPARATOR,
SEED_FILE, SEED_FILE,
); );
@@ -184,11 +178,11 @@ impl WalletSeed {
let seed_file_path = &format!( let seed_file_path = &format!(
"{}{}{}", "{}{}{}",
Self::fmt_path(data_file_dir.to_string()), fmt_path(data_file_dir.to_string()),
MAIN_SEPARATOR, MAIN_SEPARATOR,
SEED_FILE, SEED_FILE,
); );
let data_file_dir = Self::fmt_path(data_file_dir.to_string()); let data_file_dir = fmt_path(data_file_dir.to_string());
warn!("Generating wallet seed file at: {}", seed_file_path); warn!("Generating wallet seed file at: {}", seed_file_path);
let exists = WalletSeed::seed_file_exists(&data_file_dir)?; let exists = WalletSeed::seed_file_exists(&data_file_dir)?;
if exists && !test_mode { if exists && !test_mode {
@@ -219,7 +213,7 @@ impl WalletSeed {
let seed_file_path = &format!( let seed_file_path = &format!(
"{}{}{}", "{}{}{}",
Self::fmt_path(data_file_dir.to_string()), fmt_path(data_file_dir.to_string()),
MAIN_SEPARATOR, MAIN_SEPARATOR,
SEED_FILE, SEED_FILE,
); );
@@ -247,7 +241,7 @@ impl WalletSeed {
pub fn delete_seed_file(data_file_dir: &str) -> Result<(), Error> { pub fn delete_seed_file(data_file_dir: &str) -> Result<(), Error> {
let seed_file_path = &format!( let seed_file_path = &format!(
"{}{}{}", "{}{}{}",
Self::fmt_path(data_file_dir.to_string()), fmt_path(data_file_dir.to_string()),
MAIN_SEPARATOR, MAIN_SEPARATOR,
SEED_FILE, SEED_FILE,
); );
+17 -17
View File
@@ -18,7 +18,6 @@ use crate::chain::Chain;
use crate::core; use crate::core;
use crate::core::core::{Output, Transaction, TxKernel}; use crate::core::core::{Output, Transaction, TxKernel};
use crate::core::{consensus, global, pow}; use crate::core::{consensus, global, pow};
use crate::keychain;
use crate::libwallet; use crate::libwallet;
use crate::libwallet::api_impl::{foreign, owner}; use crate::libwallet::api_impl::{foreign, owner};
use crate::libwallet::{ use crate::libwallet::{
@@ -28,6 +27,7 @@ use crate::util::secp::key::SecretKey;
use crate::util::secp::pedersen; use crate::util::secp::pedersen;
use crate::util::Mutex; use crate::util::Mutex;
use chrono::Duration; use chrono::Duration;
use grin_keychain::Keychain;
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::thread;
@@ -36,7 +36,7 @@ mod testclient;
pub use self::{testclient::LocalWalletClient, testclient::WalletProxy}; pub use self::{testclient::LocalWalletClient, testclient::WalletProxy};
/// Get an output from the chain locally and present it back as an API output /// Get an output from the chain locally and present it back as an API output
fn get_output_local(chain: &chain::Chain, commit: pedersen::Commitment) -> Option<api::Output> { fn get_output_local(chain: &Chain, commit: pedersen::Commitment) -> Option<api::Output> {
if chain.get_unspent(commit).unwrap().is_some() { if chain.get_unspent(commit).unwrap().is_some() {
let block_height = chain.get_header_for_output(commit).unwrap().height; let block_height = chain.get_header_for_output(commit).unwrap().height;
let output_pos = chain.get_output_pos(&commit).unwrap_or(0); let output_pos = chain.get_output_pos(&commit).unwrap_or(0);
@@ -48,7 +48,7 @@ fn get_output_local(chain: &chain::Chain, commit: pedersen::Commitment) -> Optio
/// Get a kernel from the chain locally /// Get a kernel from the chain locally
fn get_kernel_local( fn get_kernel_local(
chain: Arc<chain::Chain>, chain: Arc<Chain>,
excess: &pedersen::Commitment, excess: &pedersen::Commitment,
min_height: Option<u64>, min_height: Option<u64>,
max_height: Option<u64>, max_height: Option<u64>,
@@ -65,7 +65,7 @@ fn get_kernel_local(
/// get output listing traversing pmmr from local /// get output listing traversing pmmr from local
fn get_outputs_by_pmmr_index_local( fn get_outputs_by_pmmr_index_local(
chain: Arc<chain::Chain>, chain: Arc<Chain>,
start_index: u64, start_index: u64,
end_index: Option<u64>, end_index: Option<u64>,
max: u64, max: u64,
@@ -86,7 +86,7 @@ fn get_outputs_by_pmmr_index_local(
/// get output listing in a given block range /// get output listing in a given block range
fn height_range_to_pmmr_indices_local( fn height_range_to_pmmr_indices_local(
chain: Arc<chain::Chain>, chain: Arc<Chain>,
start_index: u64, start_index: u64,
end_index: Option<u64>, end_index: Option<u64>,
) -> api::OutputListing { ) -> api::OutputListing {
@@ -147,13 +147,13 @@ pub fn create_block_for_wallet<'a, L, C, K>(
chain: &Chain, chain: &Chain,
prev: core::core::BlockHeader, prev: core::core::BlockHeader,
txs: &[Transaction], txs: &[Transaction],
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>, wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
) -> Result<core::core::Block, libwallet::Error> ) -> Result<core::core::Block, libwallet::Error>
where where
L: WalletLCProvider<'a, C, K>, L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a, C: NodeClient + 'a,
K: keychain::Keychain + 'a, K: Keychain + 'a,
{ {
// build block fees // build block fees
let fee_amt = txs.iter().map(|tx| tx.fee()).sum(); let fee_amt = txs.iter().map(|tx| tx.fee()).sum();
@@ -166,7 +166,7 @@ where
let coinbase_tx = { let coinbase_tx = {
let mut w_lock = wallet.lock(); let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
foreign::build_coinbase(&mut **w, keychain_mask, &block_fees, false)? foreign::build_coinbase(w, keychain_mask, &block_fees, false)?
}; };
let block = create_block_with_reward(chain, prev, txs, coinbase_tx.output, coinbase_tx.kernel); let block = create_block_with_reward(chain, prev, txs, coinbase_tx.output, coinbase_tx.kernel);
Ok(block) Ok(block)
@@ -178,13 +178,13 @@ where
pub fn award_block_to_wallet<'a, L, C, K>( pub fn award_block_to_wallet<'a, L, C, K>(
chain: &Chain, chain: &Chain,
txs: &[Transaction], txs: &[Transaction],
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>, wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
) -> Result<(), libwallet::Error> ) -> Result<(), libwallet::Error>
where where
L: WalletLCProvider<'a, C, K>, L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a, C: NodeClient + 'a,
K: keychain::Keychain + 'a, K: Keychain + 'a,
{ {
let prev = chain.head_header().unwrap(); let prev = chain.head_header().unwrap();
let block = create_block_for_wallet(chain, prev, txs, wallet, keychain_mask)?; let block = create_block_for_wallet(chain, prev, txs, wallet, keychain_mask)?;
@@ -200,7 +200,7 @@ pub fn process_block(chain: &Chain, block: core::core::Block) {
/// Award a blocks to a wallet directly /// Award a blocks to a wallet directly
pub fn award_blocks_to_wallet<'a, L, C, K>( pub fn award_blocks_to_wallet<'a, L, C, K>(
chain: &Chain, chain: &Chain,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>, wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
number: usize, number: usize,
pause_between: bool, pause_between: bool,
@@ -208,7 +208,7 @@ pub fn award_blocks_to_wallet<'a, L, C, K>(
where where
L: WalletLCProvider<'a, C, K>, L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a, C: NodeClient + 'a,
K: keychain::Keychain + 'a, K: Keychain + 'a,
{ {
for _ in 0..number { for _ in 0..number {
award_block_to_wallet(chain, &[], wallet.clone(), keychain_mask)?; award_block_to_wallet(chain, &[], wallet.clone(), keychain_mask)?;
@@ -231,7 +231,7 @@ pub fn send_to_dest<'a, L, C, K>(
where where
L: WalletLCProvider<'a, C, K>, L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a, C: NodeClient + 'a,
K: keychain::Keychain + 'a, K: Keychain + 'a,
{ {
let slate = { let slate = {
let mut w_lock = wallet.lock(); let mut w_lock = wallet.lock();
@@ -245,10 +245,10 @@ where
selection_strategy_is_use_all: true, selection_strategy_is_use_all: true,
..Default::default() ..Default::default()
}; };
let slate_i = owner::init_send_tx(&mut **w, keychain_mask, args, test_mode)?; let slate_i = owner::init_send_tx(w, keychain_mask, args, test_mode)?;
let slate = client.send_tx_slate_direct(dest, &slate_i)?; let slate = client.send_tx_slate_direct(dest, &slate_i)?;
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate)?; owner::tx_lock_outputs(w, keychain_mask, &slate)?;
owner::finalize_tx(&mut **w, keychain_mask, &slate)? owner::finalize_tx(w, keychain_mask, &slate)?
}; };
let client = { let client = {
let mut w_lock = wallet.lock(); let mut w_lock = wallet.lock();
@@ -267,7 +267,7 @@ pub fn wallet_info<'a, L, C, K>(
where where
L: WalletLCProvider<'a, C, K>, L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a, C: NodeClient + 'a,
K: keychain::Keychain + 'a, K: Keychain + 'a,
{ {
let (wallet_refreshed, wallet_info) = let (wallet_refreshed, wallet_info) =
owner::retrieve_summary_info(wallet, keychain_mask, &None, true, 1)?; owner::retrieve_summary_info(wallet, keychain_mask, &None, true, 1)?;
+6 -6
View File
@@ -70,7 +70,7 @@ where
String, String,
( (
Sender<WalletProxyMessage>, Sender<WalletProxyMessage>,
Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>, Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
Option<SecretKey>, Option<SecretKey>,
), ),
>, >,
@@ -100,14 +100,15 @@ where
genesis_block, genesis_block,
pow::verify_size, pow::verify_size,
false, false,
None,
) )
.unwrap(); .unwrap();
let (tx, rx) = channel(); let (tx, rx) = channel();
WalletProxy { WalletProxy {
chain_dir: chain_dir.to_owned(), chain_dir: chain_dir.to_owned(),
chain: Arc::new(c), chain: Arc::new(c),
tx: tx, tx,
rx: rx, rx,
wallets: HashMap::new(), wallets: HashMap::new(),
running: Arc::new(AtomicBool::new(false)), running: Arc::new(AtomicBool::new(false)),
} }
@@ -118,7 +119,7 @@ where
&mut self, &mut self,
addr: &str, addr: &str,
tx: Sender<WalletProxyMessage>, tx: Sender<WalletProxyMessage>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>, wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<SecretKey>, keychain_mask: Option<SecretKey>,
) { ) {
self.wallets self.wallets
@@ -218,8 +219,7 @@ where
let w = w_lock.lc_provider()?.wallet_inst()?; let w = w_lock.lc_provider()?.wallet_inst()?;
let mask = wallet.2.clone(); let mask = wallet.2.clone();
// receive tx // receive tx
match foreign::receive_tx(&mut **w, (&mask).as_ref(), &Slate::from(slate), None, false) match foreign::receive_tx(w, (&mask).as_ref(), &Slate::from(slate), None, false) {
{
Err(e) => { Err(e) => {
return Ok(WalletProxyMessage { return Ok(WalletProxyMessage {
sender_id: m.dest, sender_id: m.dest,
+8 -8
View File
@@ -46,10 +46,10 @@ grin_wallet_config = { path = "../config", version = "5.4.0-alpha.1" }
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_core = "5.4.0" #grin_core = "5.4.0"
grin_keychain = "5.4.0" #grin_keychain = "5.4.0"
grin_util = "5.4.0" #grin_util = "5.4.0"
grin_store = "5.4.0" #grin_store = "5.4.0"
# For beta release # For beta release
@@ -59,10 +59,10 @@ grin_store = "5.4.0"
# grin_store = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_store = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_keychain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# grin_store = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_store = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing
# grin_core = { path = "../../grin/core"} # grin_core = { path = "../../grin/core"}
+14 -18
View File
@@ -15,7 +15,9 @@
//! Generic implementation of owner API functions //! Generic implementation of owner API functions
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use super::owner::tx_lock_outputs;
use crate::api_impl::owner::{check_ttl, post_tx}; use crate::api_impl::owner::{check_ttl, post_tx};
use crate::backend::WalletBackend;
use crate::grin_core::core::FeeFields; use crate::grin_core::core::FeeFields;
use crate::grin_keychain::Keychain; use crate::grin_keychain::Keychain;
use crate::grin_util::secp::key::SecretKey; use crate::grin_util::secp::key::SecretKey;
@@ -23,11 +25,8 @@ use crate::internal::{selection, tx, updater};
use crate::slate_versions::SlateVersion; use crate::slate_versions::SlateVersion;
use crate::{ use crate::{
address, BlockFees, CbData, Error, NodeClient, Slate, SlateState, TxLogEntryType, VersionInfo, address, BlockFees, CbData, Error, NodeClient, Slate, SlateState, TxLogEntryType, VersionInfo,
WalletBackend,
}; };
use super::owner::tx_lock_outputs;
const FOREIGN_API_VERSION: u16 = 2; const FOREIGN_API_VERSION: u16 = 2;
/// Return the version info /// Return the version info
@@ -39,32 +38,30 @@ pub fn check_version() -> VersionInfo {
} }
/// Build a coinbase transaction /// Build a coinbase transaction
pub fn build_coinbase<'a, T: ?Sized, C, K>( pub fn build_coinbase<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
block_fees: &BlockFees, block_fees: &BlockFees,
test_mode: bool, test_mode: bool,
) -> Result<CbData, Error> ) -> Result<CbData, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
updater::build_coinbase(&mut *w, keychain_mask, block_fees, test_mode) updater::build_coinbase(&mut *w, keychain_mask, block_fees, test_mode)
} }
/// Receive a tx as recipient /// Receive a tx as recipient
pub fn receive_tx<'a, T: ?Sized, C, K>( pub fn receive_tx<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &Slate, slate: &Slate,
dest_acct_name: Option<&str>, dest_acct_name: Option<&str>,
use_test_rng: bool, use_test_rng: bool,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut ret_slate = slate.clone(); let mut ret_slate = slate.clone();
check_ttl(w, &ret_slate)?; check_ttl(w, &ret_slate)?;
@@ -133,16 +130,15 @@ where
} }
/// Receive a tx that this wallet has issued /// Receive a tx that this wallet has issued
pub fn finalize_tx<'a, T: ?Sized, C, K>( pub fn finalize_tx<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &Slate, slate: &Slate,
post_automatically: bool, post_automatically: bool,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut sl = slate.clone(); let mut sl = slate.clone();
let mut context = w.get_private_context(keychain_mask, sl.id.as_bytes())?; let mut context = w.get_private_context(keychain_mask, sl.id.as_bytes())?;
+70 -99
View File
@@ -30,15 +30,14 @@ use crate::api_impl::owner_updater::StatusMessage;
use crate::grin_keychain::{BlindingFactor, Identifier, Keychain, SwitchCommitmentType}; use crate::grin_keychain::{BlindingFactor, Identifier, Keychain, SwitchCommitmentType};
use crate::internal::{keys, scan, selection, tx, updater}; use crate::internal::{keys, scan, selection, tx, updater};
use crate::slate::{PaymentInfo, Slate, SlateState}; use crate::slate::{PaymentInfo, Slate, SlateState};
use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletBackend, WalletInfo}; use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletInfo};
use crate::Error;
use crate::{ use crate::{
address, address,
mwixnet::{create_onion, ComSignature, Hop, MixnetReqCreationParams, SwapReq}, mwixnet::{create_onion, ComSignature, Hop, MixnetReqCreationParams, SwapReq},
wallet_lock, BuiltOutput, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult, wallet_lock, BuiltOutput, Error, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult,
OutputCommitMapping, PaymentProof, RetrieveTxQueryArgs, ScannedBlockInfo, Slatepack, OutputCommitMapping, PaymentProof, RetrieveTxQueryArgs, ScannedBlockInfo, Slatepack,
SlatepackAddress, Slatepacker, SlatepackerArgs, TxLogEntryType, ViewWallet, WalletInitStatus, SlatepackAddress, Slatepacker, SlatepackerArgs, TxLogEntryType, ViewWallet, WalletBackend,
WalletInst, WalletLCProvider, WalletInitStatus, WalletInst, WalletLCProvider,
}; };
use ed25519_dalek::PublicKey as DalekPublicKey; use ed25519_dalek::PublicKey as DalekPublicKey;
@@ -51,35 +50,32 @@ use std::sync::mpsc::Sender;
use std::sync::Arc; use std::sync::Arc;
/// List of accounts /// List of accounts
pub fn accounts<'a, T: ?Sized, C, K>(w: &mut T) -> Result<Vec<AcctPathMapping>, Error> pub fn accounts<C, K>(w: &mut WalletBackend<C, K>) -> Result<Vec<AcctPathMapping>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
keys::accounts(&mut *w) keys::accounts(&mut *w)
} }
/// new account path /// new account path
pub fn create_account_path<'a, T: ?Sized, C, K>( pub fn create_account_path<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
label: &str, label: &str,
) -> Result<Identifier, Error> ) -> Result<Identifier, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
keys::new_acct_path(&mut *w, keychain_mask, label) keys::new_acct_path(&mut *w, keychain_mask, label)
} }
/// set active account /// set active account
pub fn set_active_account<'a, T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<(), Error> pub fn set_active_account<C, K>(w: &mut WalletBackend<C, K>, label: &str) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
w.set_parent_key_id_by_name(label) w.set_parent_key_id_by_name(label)
} }
@@ -195,7 +191,7 @@ where
dec_key: None, dec_key: None,
}); });
let slatepack = packer.deser_slatepack(slatepack.as_bytes(), true)?; let slatepack = packer.deser_slatepack(slatepack.as_bytes(), true)?;
return packer.get_slate(&slatepack); packer.get_slate(&slatepack)
} else { } else {
for index in secret_indices { for index in secret_indices {
let dec_key = Some(get_slatepack_secret_key( let dec_key = Some(get_slatepack_secret_key(
@@ -217,11 +213,11 @@ where
}; };
return packer.get_slate(&slatepack); return packer.get_slate(&slatepack);
} }
return Err(Error::SlatepackDecryption( Err(Error::SlatepackDecryption(
"Could not decrypt slatepack with any provided index on the address derivation path" "Could not decrypt slatepack with any provided index on the address derivation path"
.to_owned(), .to_owned(),
) )
.into()); .into())
} }
} }
@@ -301,13 +297,7 @@ where
Ok(( Ok((
validated, validated,
updater::retrieve_outputs( updater::retrieve_outputs(w, keychain_mask, include_spent, tx_id, Some(&parent_key_id))?,
&mut **w,
keychain_mask,
include_spent,
tx_id,
Some(&parent_key_id),
)?,
)) ))
} }
@@ -340,7 +330,7 @@ where
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
let parent_key_id = w.parent_key_id(); let parent_key_id = w.parent_key_id();
let txs = updater::retrieve_txs( let txs = updater::retrieve_txs(
&mut **w, w,
tx_id, tx_id,
tx_slate_id, tx_slate_id,
query_args, query_args,
@@ -377,7 +367,7 @@ where
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
let parent_key_id = w.parent_key_id(); let parent_key_id = w.parent_key_id();
let wallet_info = updater::retrieve_info(&mut **w, &parent_key_id, minimum_confirmations)?; let wallet_info = updater::retrieve_info(w, &parent_key_id, minimum_confirmations)?;
Ok((validated, wallet_info)) Ok((validated, wallet_info))
} }
@@ -468,8 +458,8 @@ where
} }
}; };
Ok(PaymentProof { Ok(PaymentProof {
amount: amount, amount,
excess: excess, excess,
recipient_address: SlatepackAddress::new(&proof.receiver_address), recipient_address: SlatepackAddress::new(&proof.receiver_address),
recipient_sig: r_sig, recipient_sig: r_sig,
sender_address: SlatepackAddress::new(&proof.sender_address), sender_address: SlatepackAddress::new(&proof.sender_address),
@@ -478,16 +468,15 @@ where
} }
/// Initiate tx as sender /// Initiate tx as sender
pub fn init_send_tx<'a, T: ?Sized, C, K>( pub fn init_send_tx<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
args: InitTxArgs, args: InitTxArgs,
use_test_rng: bool, use_test_rng: bool,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let parent_key_id = match &args.src_acct_name { let parent_key_id = match &args.src_acct_name {
Some(d) => { Some(d) => {
@@ -594,16 +583,15 @@ where
} }
/// Initiate a transaction as the recipient (invoicing) /// Initiate a transaction as the recipient (invoicing)
pub fn issue_invoice_tx<'a, T: ?Sized, C, K>( pub fn issue_invoice_tx<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
args: IssueInvoiceTxArgs, args: IssueInvoiceTxArgs,
use_test_rng: bool, use_test_rng: bool,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let parent_key_id = match args.dest_acct_name { let parent_key_id = match args.dest_acct_name {
Some(d) => { Some(d) => {
@@ -647,17 +635,16 @@ where
/// Receive an invoice tx, essentially adding inputs to whatever /// Receive an invoice tx, essentially adding inputs to whatever
/// output was specified /// output was specified
pub fn process_invoice_tx<'a, T: ?Sized, C, K>( pub fn process_invoice_tx<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &Slate, slate: &Slate,
args: InitTxArgs, args: InitTxArgs,
use_test_rng: bool, use_test_rng: bool,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut ret_slate = slate.clone(); let mut ret_slate = slate.clone();
check_ttl(w, &ret_slate)?; check_ttl(w, &ret_slate)?;
@@ -768,15 +755,14 @@ where
} }
/// Lock sender outputs /// Lock sender outputs
pub fn tx_lock_outputs<'a, T: ?Sized, C, K>( pub fn tx_lock_outputs<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &Slate, slate: &Slate,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let context = w.get_private_context(keychain_mask, slate.id.as_bytes())?; let context = w.get_private_context(keychain_mask, slate.id.as_bytes())?;
let mut excess_override = None; let mut excess_override = None;
@@ -805,15 +791,14 @@ where
} }
/// Finalize slate /// Finalize slate
pub fn finalize_tx<'a, T: ?Sized, C, K>( pub fn finalize_tx<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &Slate, slate: &Slate,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
foreign_finalize(w, keychain_mask, slate, false) foreign_finalize(w, keychain_mask, slate, false)
} }
@@ -843,20 +828,19 @@ where
} }
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
let parent_key_id = w.parent_key_id(); let parent_key_id = w.parent_key_id();
tx::cancel_tx(&mut **w, keychain_mask, &parent_key_id, tx_id, tx_slate_id) tx::cancel_tx(w, keychain_mask, &parent_key_id, tx_id, tx_slate_id)
} }
/// get stored tx /// get stored tx
/// crashes if stored tx has total fees exceeding 2^40 nanogrin /// crashes if stored tx has total fees exceeding 2^40 nanogrin
pub fn get_stored_tx<'a, T: ?Sized, C, K>( pub fn get_stored_tx<C, K>(
w: &T, w: &WalletBackend<C, K>,
tx_id: Option<u32>, tx_id: Option<u32>,
slate_id: Option<&Uuid>, slate_id: Option<&Uuid>,
) -> Result<Option<Slate>, Error> ) -> Result<Option<Slate>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut uuid = None; let mut uuid = None;
if let Some(i) = tx_id { if let Some(i) = tx_id {
@@ -895,9 +879,9 @@ where
/// Posts a transaction to the chain /// Posts a transaction to the chain
/// take a client impl instead of wallet so as not to have to lock the wallet /// take a client impl instead of wallet so as not to have to lock the wallet
pub fn post_tx<'a, C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error> pub fn post_tx<C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error>
where where
C: NodeClient + 'a, C: NodeClient,
{ {
let res = client.post_tx(tx, fluff); let res = client.post_tx(tx, fluff);
if let Err(e) = res { if let Err(e) = res {
@@ -929,8 +913,7 @@ where
let is_hex = rewind_hash.chars().all(|c| c.is_ascii_hexdigit()); let is_hex = rewind_hash.chars().all(|c| c.is_ascii_hexdigit());
let rewind_hash = rewind_hash.to_lowercase(); let rewind_hash = rewind_hash.to_lowercase();
if !(is_hex && rewind_hash.len() == 64) { if !(is_hex && rewind_hash.len() == 64) {
let msg = format!("Invalid Rewind Hash"); return Err(Error::RewindHash("Invalid Rewind Hash".to_string()));
return Err(Error::RewindHash(msg));
} }
let tip = { let tip = {
@@ -938,10 +921,7 @@ where
w.w2n_client().get_chain_tip()? w.w2n_client().get_chain_tip()?
}; };
let start_height = match start_height { let start_height = start_height.unwrap_or_else(|| 1);
Some(h) => h,
None => 1,
};
let info = scan::scan_rewind_hash( let info = scan::scan_rewind_hash(
wallet_inst, wallet_inst,
@@ -974,10 +954,7 @@ where
w.w2n_client().get_chain_tip()? w.w2n_client().get_chain_tip()?
}; };
let start_height = match start_height { let start_height = start_height.unwrap_or_else(|| 1);
Some(h) => h,
None => 1,
};
let mut info = scan::scan( let mut info = scan::scan(
wallet_inst.clone(), wallet_inst.clone(),
@@ -1019,10 +996,7 @@ where
}), }),
Err(_) => { Err(_) => {
let outputs = retrieve_outputs(wallet_inst, keychain_mask, &None, true, false, None)?; let outputs = retrieve_outputs(wallet_inst, keychain_mask, &None, true, false, None)?;
let height = match outputs.1.iter().map(|m| m.output.height).max() { let height = outputs.1.iter().map(|m| m.output.height).max().unwrap_or_else(|| 0);
Some(height) => height,
None => 0,
};
Ok(NodeHeightResult { Ok(NodeHeightResult {
height, height,
header_hash: "".to_owned(), header_hash: "".to_owned(),
@@ -1079,7 +1053,7 @@ where
// Step 2: Update outstanding transactions with no change outputs by kernel // Step 2: Update outstanding transactions with no change outputs by kernel
let mut txs = { let mut txs = {
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
updater::retrieve_txs(&mut **w, None, None, None, Some(&parent_key_id), true)? updater::retrieve_txs(w, None, None, None, Some(&parent_key_id), true)?
}; };
result = update_txs_via_kernel(wallet_inst.clone(), keychain_mask, &mut txs)?; result = update_txs_via_kernel(wallet_inst.clone(), keychain_mask, &mut txs)?;
if !result { if !result {
@@ -1161,7 +1135,7 @@ where
if tip.0 >= e { if tip.0 >= e {
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
let parent_key_id = w.parent_key_id(); let parent_key_id = w.parent_key_id();
tx::cancel_tx(&mut **w, keychain_mask, &parent_key_id, Some(tx.id), None)?; tx::cancel_tx(w, keychain_mask, &parent_key_id, Some(tx.id), None)?;
} }
} }
} }
@@ -1170,11 +1144,10 @@ where
} }
/// Check TTL /// Check TTL
pub fn check_ttl<'a, T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<(), Error> pub fn check_ttl<C, K>(w: &mut WalletBackend<C, K>, slate: &Slate) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// Refuse if TTL is expired // Refuse if TTL is expired
let last_confirmed_height = w.last_confirmed_height()?; let last_confirmed_height = w.last_confirmed_height()?;
@@ -1240,7 +1213,7 @@ where
return Err(Error::PaymentProof("Invalid sender signature".to_owned())); return Err(Error::PaymentProof("Invalid sender signature".to_owned()));
}; };
// for now, simple test as to whether one of the addresses belongs to this wallet // for now, simple test whether one of the addresses belongs to this wallet
let sec_key = address::address_from_derivation_path(&keychain, &parent_key_id, 0)?; let sec_key = address::address_from_derivation_path(&keychain, &parent_key_id, 0)?;
let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) { let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) {
Ok(k) => k, Ok(k) => k,
@@ -1269,7 +1242,7 @@ where
{ {
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
let parent_key_id = w.parent_key_id(); let parent_key_id = w.parent_key_id();
match updater::refresh_outputs(&mut **w, keychain_mask, &parent_key_id, update_all) { match updater::refresh_outputs(w, keychain_mask, &parent_key_id, update_all) {
Ok(_) => Ok(true), Ok(_) => Ok(true),
Err(e) => { Err(e) => {
if let Error::InvalidKeychainMask = e { if let Error::InvalidKeychainMask = e {
@@ -1336,16 +1309,15 @@ where
} }
/// Builds an output for the wallet's next available key /// Builds an output for the wallet's next available key
pub fn build_output<'a, T: ?Sized, C, K>( pub fn build_output<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
features: OutputFeatures, features: OutputFeatures,
amount: u64, amount: u64,
) -> Result<BuiltOutput, Error> ) -> Result<BuiltOutput, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let k = w.keychain(keychain_mask)?; let k = w.keychain(keychain_mask)?;
@@ -1369,14 +1341,14 @@ where
Ok(BuiltOutput { Ok(BuiltOutput {
blind: BlindingFactor::from_secret_key(blind), blind: BlindingFactor::from_secret_key(blind),
key_id: key_id, key_id,
output: output, output,
}) })
} }
/// Create MXMixnet request /// Create MXMixnet request
pub fn create_mwixnet_req<'a, T: ?Sized, C, K>( pub fn create_mwixnet_req<C, K>(
w: &mut T, w: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
params: &MixnetReqCreationParams, params: &MixnetReqCreationParams,
commitment: &Commitment, commitment: &Commitment,
@@ -1384,9 +1356,8 @@ pub fn create_mwixnet_req<'a, T: ?Sized, C, K>(
use_test_rng: bool, use_test_rng: bool,
) -> Result<SwapReq, Error> ) -> Result<SwapReq, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let parent_key_id = w.parent_key_id(); let parent_key_id = w.parent_key_id();
let keychain = w.keychain(keychain_mask)?; let keychain = w.keychain(keychain_mask)?;
+779
View File
@@ -0,0 +1,779 @@
// Copyright 2021 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use byteorder::{BigEndian, WriteBytesExt};
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use uuid::Uuid;
use crate::blake2::blake2b::{Blake2b, Blake2bResult};
use crate::{
AcctPathMapping, Context, Error, NodeClient, OutputData, ScannedBlockInfo, TxLogEntry,
WalletInitStatus,
};
use grin_core::core::Transaction;
use grin_core::ser;
use grin_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain, SwitchCommitmentType};
use grin_store::{option_to_not_found, Store};
use grin_util::secp::constants::SECRET_KEY_SIZE;
use grin_util::secp::SecretKey;
use grin_util::ToHex;
use rand::rngs::mock::StepRng;
use rand::thread_rng;
pub const DB_DIR: &str = "db";
pub const TX_SAVE_DIR: &str = "saved_txs";
const OUTPUT_PREFIX: u8 = b'o';
const DERIV_PREFIX: u8 = b'd';
const CONFIRMED_HEIGHT_PREFIX: u8 = b'c';
const PRIVATE_TX_CONTEXT_PREFIX: u8 = b'p';
const TX_LOG_ENTRY_PREFIX: u8 = b't';
const TX_LOG_ID_PREFIX: u8 = b'i';
const ACCOUNT_PATH_MAPPING_PREFIX: u8 = b'a';
const LAST_SCANNED_BLOCK: u8 = b'l';
const LAST_SCANNED_KEY: &str = "LAST_SCANNED_KEY";
const WALLET_INIT_STATUS: u8 = b'w';
const WALLET_INIT_STATUS_KEY: &str = "WALLET_INIT_STATUS";
const DB_PREFIXES: [u8; 9] = [
OUTPUT_PREFIX,
DERIV_PREFIX,
CONFIRMED_HEIGHT_PREFIX,
PRIVATE_TX_CONTEXT_PREFIX,
TX_LOG_ENTRY_PREFIX,
TX_LOG_ID_PREFIX,
ACCOUNT_PATH_MAPPING_PREFIX,
LAST_SCANNED_BLOCK,
WALLET_INIT_STATUS,
];
/// Helper to derive XOR keys for storing private transaction keys in the DB
/// (blind_xor_key, nonce_xor_key)
fn private_ctx_xor_keys<K>(
keychain: &K,
slate_id: &[u8],
) -> Result<([u8; SECRET_KEY_SIZE], [u8; SECRET_KEY_SIZE]), Error>
where
K: Keychain,
{
let root_key = keychain.derive_key(0, &K::root_key_id(), SwitchCommitmentType::Regular)?;
// derive XOR values for storing secret values in DB
// h(root_key|slate_id|"blind")
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
hasher.update(&slate_id[..]);
hasher.update(&b"blind"[..]);
let blind_xor_key = hasher.finalize();
let mut ret_blind = [0; SECRET_KEY_SIZE];
ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
// h(root_key|slate_id|"nonce")
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
hasher.update(&slate_id[..]);
hasher.update(&b"nonce"[..]);
let nonce_xor_key = hasher.finalize();
let mut ret_nonce = [0; SECRET_KEY_SIZE];
ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
Ok((ret_blind, ret_nonce))
}
/// Wallet backend. All functions here expect that the wallet instance
/// has instantiated itself or stored whatever credentials it needs.
pub struct WalletBackend<C, K>
where
C: NodeClient,
K: Keychain,
{
db: Store,
data_file_dir: String,
/// Keychain
pub keychain: Option<K>,
/// Check value for XORed keychain seed
pub master_checksum: Box<Option<Blake2bResult>>,
/// Parent path to use by default for output operations
parent_key_id: Identifier,
/// wallet to node client
w2n_client: C,
}
impl<C, K> WalletBackend<C, K>
where
C: NodeClient,
K: Keychain,
{
/// Create new wallet backend.
pub fn new(data_file_dir: &str, n_client: C) -> Result<Self, Error> {
let db_path = Path::new(data_file_dir).join(DB_DIR);
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
let stored_tx_path = Path::new(data_file_dir).join(TX_SAVE_DIR);
fs::create_dir_all(&stored_tx_path)
.expect("Couldn't create wallet backend tx storage directory!");
let store = Store::new(
db_path.to_str().unwrap(),
None,
Some(DB_DIR),
DB_PREFIXES.to_vec(),
None,
None,
)?;
// Make sure default wallet derivation path always exists
// as well as path (so it can be retrieved by batches to know where to store
// completed transactions, for reference
let default_account = AcctPathMapping {
label: "default".to_owned(),
path: WalletBackend::<C, K>::default_path(),
};
{
let mut batch = store.batch()?;
batch.put_ser(
Some(ACCOUNT_PATH_MAPPING_PREFIX),
default_account.label.as_bytes(),
&default_account,
)?;
batch.commit()?;
}
let res = WalletBackend {
db: store,
data_file_dir: data_file_dir.to_owned(),
keychain: None,
master_checksum: Box::new(None),
parent_key_id: WalletBackend::<C, K>::default_path(),
w2n_client: n_client,
};
Ok(res)
}
/// Return the default parent wallet path, corresponding to the default account
/// in the BIP32 spec. Parent is account 0 at level 2, child output identifiers
/// are all at level 3.
pub fn default_path() -> Identifier {
ExtKeychain::derive_key_id(2, 0, 0, 0, 0)
}
/// Just test to see if database files exist in the current directory. If
/// so, use a DB backend for all operations.
pub fn exists(data_file_dir: &str) -> bool {
let db_path = Path::new(data_file_dir).join(DB_DIR);
db_path.exists()
}
/// Set the keychain, which should already be initialized
/// Optionally return a token value used to XOR the stored
/// key value
pub fn set_keychain(
&mut self,
mut k: K,
mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error> {
// store hash of master key, so it can be verified later after unmasking
let root_key = k.derive_key(0, &K::root_key_id(), SwitchCommitmentType::Regular)?;
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
self.master_checksum = Box::new(Some(hasher.finalize()));
let mask_value = {
match mask {
true => {
// Random value that must be XORed against the stored wallet seed
// before it is used
let mask_value = match use_test_rng {
true => {
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
SecretKey::new(&k.secp(), &mut test_rng)
}
false => SecretKey::new(&k.secp(), &mut thread_rng()),
};
k.mask_master_key(&mask_value)?;
Some(mask_value)
}
false => None,
}
};
self.keychain = Some(k);
Ok(mask_value)
}
/// Close wallet by removing stored keychain.
pub fn close(&mut self) -> Result<(), Error> {
self.keychain = None;
Ok(())
}
/// Return the keychain being used, cloned with XORed token value
/// for temporary use
/// Can optionally take a mask value
pub fn keychain(&self, mask: Option<&SecretKey>) -> Result<K, Error> {
match self.keychain.as_ref() {
Some(k) => {
let mut k_masked = k.clone();
if let Some(m) = mask {
k_masked.mask_master_key(m)?;
}
// Check if master seed is what is expected (especially if it's been xored)
let root_key =
k_masked.derive_key(0, &K::root_key_id(), SwitchCommitmentType::Regular)?;
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
if *self.master_checksum != Some(hasher.finalize()) {
error!("Supplied keychain mask is invalid");
return Err(Error::InvalidKeychainMask);
}
Ok(k_masked)
}
None => Err(Error::KeychainDoesntExist),
}
}
/// Return the client being used to communicate with the node.
pub fn w2n_client(&mut self) -> &mut C {
&mut self.w2n_client
}
/// Return the version of the commit for caching if allowed.
pub fn calc_commit_for_cache(
&mut self,
keychain_mask: Option<&SecretKey>,
amount: u64,
id: &Identifier,
) -> Result<Option<String>, Error> {
//TODO: Check if this is really necessary, it's the only thing
//preventing removing the need for config in the wallet backend
/*if self.config.no_commit_cache == Some(true) {
Ok(None)
} else {*/
Ok(Some(
self.keychain(keychain_mask)?
.commit(amount, &id, SwitchCommitmentType::Regular)?
.0
.to_vec()
.to_hex(), // TODO: proper support for different switch commitment schemes
))
/*}*/
}
/// Set parent key id by stored account name.
pub fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> {
let label = label.to_owned();
let res = self.acct_path_iter().find(|l| l.label == label);
if let Some(a) = res {
self.set_parent_key_id(a.path);
Ok(())
} else {
Err(Error::UnknownAccountLabel(label))
}
}
/// The BIP32 path of the parent path to use for all output-related
/// functions, essentially 'accounts' within a wallet.
pub fn set_parent_key_id(&mut self, id: Identifier) {
self.parent_key_id = id;
}
/// Get the parent path.
pub fn parent_key_id(&mut self) -> Identifier {
self.parent_key_id.clone()
}
/// Get output data by id.
pub fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
let key = match mmr_index {
Some(i) => to_key_u64(id.to_bytes(), *i),
None => id.to_bytes().to_vec(),
};
option_to_not_found(self.db.get_ser(Some(OUTPUT_PREFIX), &key, None), || {
format!("Key Id: {}", id)
})
.map_err(|e| e.into())
}
/// Iterate over all output data stored by the backend.
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(OUTPUT_PREFIX), move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
Box::new(iter)
}
/// Get an (Optional) tx log entry by uuid.
pub fn get_tx_log_entry(&self, u: &Uuid) -> Result<Option<TxLogEntry>, Error> {
self.db
.get_ser(Some(TX_LOG_ENTRY_PREFIX), u.as_bytes(), None)
.map_err(|e| e.into())
}
/// Iterate over all tx log data stored by the backend.
pub fn tx_log_iter<'a>(&'a self) -> Box<dyn Iterator<Item = TxLogEntry> + 'a> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(TX_LOG_ENTRY_PREFIX), move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
Box::new(iter)
}
/// Retrieve the private context associated with a given slate id.
pub fn get_private_context(
&mut self,
keychain_mask: Option<&SecretKey>,
slate_id: &[u8],
) -> Result<Context, Error> {
let ctx_key = to_key_u64(slate_id, 0);
let (blind_xor_key, nonce_xor_key) =
private_ctx_xor_keys(&self.keychain(keychain_mask)?, slate_id)?;
let mut ctx: Context = option_to_not_found(
self.db
.get_ser(Some(PRIVATE_TX_CONTEXT_PREFIX), &ctx_key, None),
|| format!("Slate id: {:x?}", slate_id.to_vec()),
)?;
for i in 0..SECRET_KEY_SIZE {
ctx.sec_key.0[i] ^= blind_xor_key[i];
ctx.sec_nonce.0[i] ^= nonce_xor_key[i];
}
Ok(ctx)
}
/// Iterate over all stored account paths.
pub fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self
.db
.iter(Some(ACCOUNT_PATH_MAPPING_PREFIX), move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
Box::new(iter)
}
/// Gets an account path for a given label.
pub fn get_acct_path(&self, label: String) -> Result<Option<AcctPathMapping>, Error> {
self.db
.get_ser(Some(ACCOUNT_PATH_MAPPING_PREFIX), label.as_bytes(), None)
.map_err(|e| e.into())
}
/// Stores a transaction.
pub fn store_tx(&self, uuid: &str, tx: &Transaction) -> Result<(), Error> {
let filename = format!("{}.grintx", uuid);
let path = Path::new(&self.data_file_dir)
.join(TX_SAVE_DIR)
.join(filename);
let path_buf = Path::new(&path).to_path_buf();
let mut stored_tx = File::create(path_buf)?;
let tx_hex = ser::ser_vec(tx, ser::ProtocolVersion(1)).unwrap().to_hex();
stored_tx.write_all(&tx_hex.as_bytes())?;
stored_tx.sync_all()?;
Ok(())
}
/// Retrieves a stored transaction.
//TODO: Store content of .grintx file at TxLogEntry?
pub fn get_stored_tx(&self, uuid: &str) -> Result<Option<Transaction>, Error> {
let filename = format!("{}.grintx", uuid);
let path = Path::new(&self.data_file_dir)
.join(TX_SAVE_DIR)
.join(filename);
let tx_file = Path::new(&path).to_path_buf();
let mut tx_f = File::open(tx_file)?;
let mut content = String::new();
tx_f.read_to_string(&mut content)?;
let tx_bin = grin_util::from_hex(&content).unwrap();
Ok(Some(
ser::deserialize(
&mut &tx_bin[..],
ser::ProtocolVersion(1),
ser::DeserializationMode::default(),
)
.unwrap(),
))
}
/// Create a new write batch to update or remove output data.
pub fn batch(
&mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<WalletBatch<'_, K>, Error> {
Ok(WalletBatch {
db: self.db.batch()?,
keychain: Some(self.keychain(keychain_mask)?),
})
}
/// Batch for use when keychain isn't available or required.
pub fn batch_no_mask(&mut self) -> Result<WalletBatch<'_, K>, Error> {
Ok(WalletBatch {
db: self.db.batch()?,
keychain: None,
})
}
/// Return the current child index.
pub fn current_child_index(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> {
let index = {
let batch = self.db.batch()?;
batch
.get_ser(Some(DERIV_PREFIX), &parent_key_id.to_bytes(), None)?
.unwrap_or_else(|| 0)
};
Ok(index)
}
/// Next child ID when we want to create a new output, based on current parent.
pub fn next_child(
&mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<Identifier, Error> {
let parent_key_id = self.parent_key_id.clone();
let mut deriv_idx = {
let batch = self.db.batch()?;
batch
.get_ser(Some(DERIV_PREFIX), &self.parent_key_id.to_bytes(), None)?
.unwrap_or_else(|| 0)
};
let mut return_path = self.parent_key_id.to_path();
return_path.depth += 1;
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
deriv_idx += 1;
let mut batch = self.batch(keychain_mask)?;
batch.save_child_index(&parent_key_id, deriv_idx)?;
batch.commit()?;
Ok(Identifier::from_path(&return_path))
}
/// Last verified height of outputs directly descending from the given parent key.
pub fn last_confirmed_height(&mut self) -> Result<u64, Error> {
let batch = self.db.batch()?;
let last_confirmed_height = batch
.get_ser(
Some(CONFIRMED_HEIGHT_PREFIX),
&self.parent_key_id.to_bytes(),
None,
)?
.unwrap_or_else(|| 0);
Ok(last_confirmed_height)
}
/// Last block scanned during scan or restore.
pub fn last_scanned_block(&mut self) -> Result<ScannedBlockInfo, Error> {
let batch = self.db.batch()?;
let last_scanned_block = batch
.get_ser(Some(LAST_SCANNED_BLOCK), LAST_SCANNED_KEY.as_bytes(), None)?
.unwrap_or_else(|| ScannedBlockInfo {
height: 0,
hash: "".to_owned(),
start_pmmr_index: 0,
last_pmmr_index: 0,
});
Ok(last_scanned_block)
}
/// Flag whether the wallet needs a full UTXO scan on next update attempt.
pub fn init_status(&mut self) -> Result<WalletInitStatus, Error> {
let batch = self.db.batch()?;
let status = batch
.get_ser(
Some(WALLET_INIT_STATUS),
WALLET_INIT_STATUS_KEY.as_bytes(),
None,
)?
.unwrap_or_else(|| WalletInitStatus::InitComplete);
Ok(status)
}
}
/// An atomic batch in which all changes can be committed all at once or
/// discarded on error.
pub struct WalletBatch<'a, K>
where
K: Keychain,
{
db: grin_store::Batch<'a>,
/// Keychain
keychain: Option<K>,
}
#[allow(missing_docs)]
impl<'a, K> WalletBatch<'a, K>
where
K: Keychain,
{
/// Return the keychain being used.
pub fn keychain(&mut self) -> &mut K {
self.keychain.as_mut().unwrap()
}
/// Add or update data about an output to the backend.
pub fn save(&mut self, out: OutputData) -> Result<(), Error> {
let key = match out.mmr_index {
Some(i) => to_key_u64(out.key_id.to_bytes(), i),
None => out.key_id.to_bytes().to_vec(),
};
self.db.put_ser(Some(OUTPUT_PREFIX), &key, &out)?;
Ok(())
}
/// Gets output data by id
pub fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error> {
let key = match mmr_index {
Some(i) => to_key_u64(id.to_bytes(), *i),
None => id.to_bytes().to_vec(),
};
option_to_not_found(self.db.get_ser(Some(OUTPUT_PREFIX), &key, None), || {
format!("Key ID: {}", id)
})
.map_err(|e| e.into())
}
/// Iterate over all output data stored by the backend.
pub fn iter(&'a self) -> impl Iterator<Item = OutputData> + 'a {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(OUTPUT_PREFIX), move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
iter
}
/// Delete data about an output from the backend.
pub fn delete(&mut self, id: &Identifier, mmr_index: &Option<u64>) -> Result<(), Error> {
// Delete the output data.
let key = match mmr_index {
Some(i) => to_key_u64(id.to_bytes(), *i),
None => id.to_bytes().to_vec(),
};
self.db.delete(Some(OUTPUT_PREFIX), &key)?;
Ok(())
}
/// Save last stored child index of a given parent.
pub fn save_child_index(&mut self, parent_id: &Identifier, child_n: u32) -> Result<(), Error> {
self.db
.put_ser(Some(DERIV_PREFIX), &parent_id.to_bytes(), &child_n)?;
Ok(())
}
/// Save last confirmed height of outputs for a given parent.
pub fn save_last_confirmed_height(
&mut self,
parent_key_id: &Identifier,
height: u64,
) -> Result<(), Error> {
self.db.put_ser(
Some(CONFIRMED_HEIGHT_PREFIX),
&parent_key_id.to_bytes(),
&height,
)?;
Ok(())
}
/// Save the last PMMR index that was scanned via a scan operation.
pub fn save_last_scanned_block(&mut self, block_info: ScannedBlockInfo) -> Result<(), Error> {
self.db.put_ser(
Some(LAST_SCANNED_BLOCK),
LAST_SCANNED_KEY.as_bytes(),
&block_info,
)?;
Ok(())
}
/// Save flag indicating whether wallet needs a full UTXO scan.
pub fn save_init_status(&mut self, value: WalletInitStatus) -> Result<(), Error> {
self.db.put_ser(
Some(WALLET_INIT_STATUS),
WALLET_INIT_STATUS_KEY.as_bytes(),
&value,
)?;
Ok(())
}
/// Get next transaction log entry for the parent.
pub fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result<u32, Error> {
let last_tx_log_id = self
.db
.get_ser(Some(TX_LOG_ID_PREFIX), &parent_key_id.to_bytes(), None)?
.unwrap_or_else(|| 0);
self.db.put_ser(
Some(TX_LOG_ID_PREFIX),
&parent_key_id.to_bytes(),
&(last_tx_log_id + 1),
)?;
Ok(last_tx_log_id)
}
/// Iterate over transactions data stored by the backend.
pub fn tx_log_iter(&'a self) -> impl Iterator<Item = TxLogEntry> + 'a {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(TX_LOG_ENTRY_PREFIX), move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
iter
}
/// Save a transaction log entry.
pub fn save_tx_log_entry(
&mut self,
tx_in: TxLogEntry,
parent_id: &Identifier,
) -> Result<(), Error> {
let tx_log_key = to_key_u64(parent_id.to_bytes(), tx_in.id as u64);
self.db
.put_ser(Some(TX_LOG_ENTRY_PREFIX), &tx_log_key, &tx_in)?;
Ok(())
}
/// Delete a transaction log entry.
pub fn delete_tx_log_entry(&mut self, tx_id: u32, parent_id: &Identifier) -> Result<(), Error> {
let tx_log_key = to_key_u64(parent_id.to_bytes(), tx_id as u64);
self.db.delete(Some(TX_LOG_ENTRY_PREFIX), &tx_log_key)?;
Ok(())
}
/// Save an account label -> path mapping.
pub fn save_acct_path(&mut self, mapping: AcctPathMapping) -> Result<(), Error> {
self.db.put_ser(
Some(ACCOUNT_PATH_MAPPING_PREFIX),
mapping.label.as_bytes(),
&mapping,
)?;
Ok(())
}
/// Iterate over account names stored in backend.
pub fn acct_path_iter(&'a self) -> impl Iterator<Item = AcctPathMapping> + 'a {
let protocol_version = self.db.protocol_version();
let prefix_iter = self
.db
.iter(Some(ACCOUNT_PATH_MAPPING_PREFIX), move |_, mut v| {
ser::deserialize(
&mut v,
protocol_version,
ser::DeserializationMode::default(),
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
iter
}
/// Save an output as locked in the backend.
pub fn lock_output(&mut self, out: &mut OutputData) -> Result<(), Error> {
out.lock();
self.save(out.clone())
}
/// Save the private context associated with a slate id.
pub fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
let ctx_key = to_key_u64(slate_id, 0);
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
let mut s_ctx = ctx.clone();
for i in 0..SECRET_KEY_SIZE {
s_ctx.sec_key.0[i] ^= blind_xor_key[i];
s_ctx.sec_nonce.0[i] ^= nonce_xor_key[i];
}
self.db
.put_ser(Some(PRIVATE_TX_CONTEXT_PREFIX), &ctx_key, &s_ctx)?;
Ok(())
}
/// Delete the private context associated with the slate id.
pub fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error> {
let ctx_key = to_key_u64(slate_id, 0);
self.db
.delete(Some(PRIVATE_TX_CONTEXT_PREFIX), &ctx_key)
.map_err(|e| e.into())
}
/// Write the wallet data to backend file.
pub fn commit(self) -> Result<(), Error> {
self.db.commit()?;
Ok(())
}
}
/// Build a db key from a byte vector identifier and numeric identifier
fn to_key_u64<K: AsRef<[u8]>>(k: K, val: u64) -> Vec<u8> {
let mut res = k.as_ref().to_vec();
res.write_u64::<BigEndian>(val).unwrap();
res
}
+24 -28
View File
@@ -16,32 +16,31 @@
use crate::error::Error; use crate::error::Error;
use crate::grin_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain}; use crate::grin_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
use crate::grin_util::secp::key::SecretKey; use crate::grin_util::secp::key::SecretKey;
use crate::types::{AcctPathMapping, NodeClient, WalletBackend}; use crate::types::{AcctPathMapping, NodeClient};
use crate::WalletBackend;
/// Get next available key in the wallet for a given parent /// Get next available key in the wallet for a given parent
pub fn next_available_key<'a, T: ?Sized, C, K>( pub fn next_available_key<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
) -> Result<Identifier, Error> ) -> Result<Identifier, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let child = wallet.next_child(keychain_mask)?; let child = wallet.next_child(keychain_mask)?;
Ok(child) Ok(child)
} }
/// Retrieve an existing key from a wallet /// Retrieve an existing key from a wallet
pub fn retrieve_existing_key<'a, T: ?Sized, C, K>( pub fn retrieve_existing_key<C, K>(
wallet: &T, wallet: &WalletBackend<C, K>,
key_id: Identifier, key_id: Identifier,
mmr_index: Option<u64>, mmr_index: Option<u64>,
) -> Result<(Identifier, u32), Error> ) -> Result<(Identifier, u32), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let existing = wallet.get(&key_id, &mmr_index)?; let existing = wallet.get(&key_id, &mmr_index)?;
let key_id = existing.key_id.clone(); let key_id = existing.key_id.clone();
@@ -50,25 +49,23 @@ where
} }
/// Returns a list of account to BIP32 path mappings /// Returns a list of account to BIP32 path mappings
pub fn accounts<'a, T: ?Sized, C, K>(wallet: &mut T) -> Result<Vec<AcctPathMapping>, Error> pub fn accounts<C, K>(wallet: &WalletBackend<C, K>) -> Result<Vec<AcctPathMapping>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
Ok(wallet.acct_path_iter().collect()) Ok(wallet.acct_path_iter().collect())
} }
/// Adds an new parent account path with a given label /// Adds a new parent account path with a given label
pub fn new_acct_path<'a, T: ?Sized, C, K>( pub fn new_acct_path<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
label: &str, label: &str,
) -> Result<Identifier, Error> ) -> Result<Identifier, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let label = label.to_owned(); let label = label.to_owned();
if wallet.acct_path_iter().any(|l| l.label == label) { if wallet.acct_path_iter().any(|l| l.label == label) {
@@ -94,7 +91,7 @@ where
}; };
let save_path = AcctPathMapping { let save_path = AcctPathMapping {
label: label, label,
path: return_id.clone(), path: return_id.clone(),
}; };
@@ -105,20 +102,19 @@ where
} }
/// Adds/sets a particular account path with a given label /// Adds/sets a particular account path with a given label
pub fn set_acct_path<'a, T: ?Sized, C, K>( pub fn set_acct_path<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
label: &str, label: &str,
path: &Identifier, path: &Identifier,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let label = label.to_owned(); let label = label.to_owned();
let save_path = AcctPathMapping { let save_path = AcctPathMapping {
label: label, label,
path: path.clone(), path: path.clone(),
}; };
+12 -13
View File
@@ -66,14 +66,14 @@ struct RestoredTxStats {
pub num_outputs: usize, pub num_outputs: usize,
} }
fn identify_utxo_outputs<'a, K>( fn identify_utxo_outputs<K>(
keychain: &K, keychain: &K,
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>, outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
status_send_channel: &Option<Sender<StatusMessage>>, status_send_channel: &Option<Sender<StatusMessage>>,
percentage_complete: u8, percentage_complete: u8,
) -> Result<Vec<OutputResult>, Error> ) -> Result<Vec<OutputResult>, Error>
where where
K: Keychain + 'a, K: Keychain,
{ {
let mut wallet_outputs: Vec<OutputResult> = Vec::new(); let mut wallet_outputs: Vec<OutputResult> = Vec::new();
@@ -137,7 +137,7 @@ where
n_child: key_id.to_path().last_path_index(), n_child: key_id.to_path().last_path_index(),
value: amount, value: amount,
height: *height, height: *height,
lock_height: lock_height, lock_height,
is_coinbase: *is_coinbase, is_coinbase: *is_coinbase,
mmr_index: *mmr_index, mmr_index: *mmr_index,
}); });
@@ -145,7 +145,7 @@ where
Ok(wallet_outputs) Ok(wallet_outputs)
} }
fn collect_chain_outputs_rewind_hash<'a, C>( fn collect_chain_outputs_rewind_hash<C>(
client: C, client: C,
rewind_hash: String, rewind_hash: String,
start_index: u64, start_index: u64,
@@ -153,13 +153,13 @@ fn collect_chain_outputs_rewind_hash<'a, C>(
status_send_channel: &Option<Sender<StatusMessage>>, status_send_channel: &Option<Sender<StatusMessage>>,
) -> Result<ViewWallet, Error> ) -> Result<ViewWallet, Error>
where where
C: NodeClient + 'a, C: NodeClient,
{ {
let batch_size = 1000; let batch_size = 1000;
let start_index_stat = start_index; let start_index_stat = start_index;
let mut start_index = start_index; let mut start_index = start_index;
let mut vw = ViewWallet { let mut vw = ViewWallet {
rewind_hash: rewind_hash, rewind_hash,
output_result: vec![], output_result: vec![],
total_balance: 0, total_balance: 0,
last_pmmr_index: 0, last_pmmr_index: 0,
@@ -198,7 +198,7 @@ where
continue; continue;
} }
let info = info.unwrap(); let info = info?;
vw.total_balance += info.value; vw.total_balance += info.value;
let lock_height = if *is_coinbase { let lock_height = if *is_coinbase {
*height + global::coinbase_maturity() *height + global::coinbase_maturity()
@@ -212,7 +212,7 @@ where
height: *height, height: *height,
mmr_index: *mmr_index, mmr_index: *mmr_index,
is_coinbase: *is_coinbase, is_coinbase: *is_coinbase,
lock_height: lock_height, lock_height,
}; };
vw.output_result.push(output_info); vw.output_result.push(output_info);
@@ -264,7 +264,7 @@ where
keychain, keychain,
outputs.clone(), outputs.clone(),
status_send_channel, status_send_channel,
perc_complete as u8, perc_complete,
)?); )?);
if highest_index <= last_retrieved_index { if highest_index <= last_retrieved_index {
@@ -276,7 +276,6 @@ where
Ok((result_vec, last_retrieved_return_index)) Ok((result_vec, last_retrieved_return_index))
} }
///
fn restore_missing_output<'a, L, C, K>( fn restore_missing_output<'a, L, C, K>(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>, wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
@@ -375,7 +374,7 @@ where
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
let updated_tx_entry = if output.tx_log_entry.is_some() { let updated_tx_entry = if output.tx_log_entry.is_some() {
let entries = updater::retrieve_txs( let entries = updater::retrieve_txs(
&mut **w, w,
output.tx_log_entry, output.tx_log_entry,
None, None,
None, None,
@@ -498,7 +497,7 @@ where
// Now, get all outputs owned by this wallet (regardless of account) // Now, get all outputs owned by this wallet (regardless of account)
let wallet_outputs = { let wallet_outputs = {
wallet_lock!(wallet_inst, w); wallet_lock!(wallet_inst, w);
updater::retrieve_outputs(&mut **w, keychain_mask, true, None, None)? updater::retrieve_outputs(w, keychain_mask, true, None, None)?
}; };
let mut missing_outs = vec![]; let mut missing_outs = vec![];
@@ -618,7 +617,7 @@ where
if let Some(ref s) = status_send_channel { if let Some(ref s) = status_send_channel {
let _ = s.send(StatusMessage::Scanning(msg, 99)); let _ = s.send(StatusMessage::Scanning(msg, 99));
} }
keys::set_acct_path(&mut **w, keychain_mask, &label, path)?; keys::set_acct_path(w, keychain_mask, &label, path)?;
acct_index += 1; acct_index += 1;
} }
let current_child_index = w.current_child_index(&path)?; let current_child_index = w.current_child_index(&path)?;
+50 -60
View File
@@ -14,7 +14,6 @@
//! Selection of inputs for building transactions //! Selection of inputs for building transactions
use crate::address;
use crate::error::Error; use crate::error::Error;
use crate::grin_core::core::amount_to_hr_string; use crate::grin_core::core::amount_to_hr_string;
use crate::grin_core::libtx::{ use crate::grin_core::libtx::{
@@ -29,6 +28,7 @@ use crate::internal::keys;
use crate::slate::Slate; use crate::slate::Slate;
use crate::types::*; use crate::types::*;
use crate::util::OnionV3Address; use crate::util::OnionV3Address;
use crate::{address, WalletBackend};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
@@ -37,8 +37,8 @@ use std::convert::TryInto;
/// and saves the private wallet identifiers of our selected outputs /// and saves the private wallet identifiers of our selected outputs
/// into our transaction context /// into our transaction context
pub fn build_send_tx<'a, T: ?Sized, C, K>( pub fn build_send_tx<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain: &K, keychain: &K,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
@@ -54,9 +54,8 @@ pub fn build_send_tx<'a, T: ?Sized, C, K>(
amount_includes_fee: bool, amount_includes_fee: bool,
) -> Result<Context, Error> ) -> Result<Context, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let (elems, inputs, change_amounts_derivations, fee) = select_send_tx( let (elems, inputs, change_amounts_derivations, fee) = select_send_tx(
wallet, wallet,
@@ -73,7 +72,7 @@ where
)?; )?;
if amount_includes_fee { if amount_includes_fee {
slate.amount = slate.amount.checked_sub(fee).ok_or(Error::GenericError( slate.amount = slate.amount.checked_sub(fee).ok_or(Error::GenericError(
format!("Transaction amount is too small to include fee").into(), "Transaction amount is too small to include fee".to_string(),
))?; ))?;
}; };
@@ -119,8 +118,8 @@ where
/// Locks all corresponding outputs in the context, creates /// Locks all corresponding outputs in the context, creates
/// change outputs and tx log entry /// change outputs and tx log entry
pub fn lock_tx_context<'a, T: ?Sized, C, K>( pub fn lock_tx_context<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &Slate, slate: &Slate,
current_height: u64, current_height: u64,
@@ -128,9 +127,8 @@ pub fn lock_tx_context<'a, T: ?Sized, C, K>(
excess_override: Option<pedersen::Commitment>, excess_override: Option<pedersen::Commitment>,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut output_commits: HashMap<Identifier, (Option<String>, u64)> = HashMap::new(); let mut output_commits: HashMap<Identifier, (Option<String>, u64)> = HashMap::new();
// Store cached commits before locking wallet // Store cached commits before locking wallet
@@ -178,7 +176,7 @@ where
let mut amount_debited = 0; let mut amount_debited = 0;
t.num_inputs = lock_inputs.len(); t.num_inputs = lock_inputs.len();
for id in lock_inputs { for id in lock_inputs {
let mut coin = batch.get(&id.0, &id.1).unwrap(); let mut coin = batch.get(&id.0, &id.1)?;
coin.tx_log_entry = Some(log_id); coin.tx_log_entry = Some(log_id);
amount_debited += coin.value; amount_debited += coin.value;
batch.lock_output(&mut coin)?; batch.lock_output(&mut coin)?;
@@ -221,11 +219,11 @@ where
root_key_id: parent_key_id.clone(), root_key_id: parent_key_id.clone(),
key_id: id.clone(), key_id: id.clone(),
n_child: id.to_path().last_path_index(), n_child: id.to_path().last_path_index(),
commit: commit, commit,
mmr_index: None, mmr_index: None,
value: change_amount, value: change_amount,
status: OutputStatus::Unconfirmed, status: OutputStatus::Unconfirmed,
height: height, height,
lock_height: 0, lock_height: 0,
is_coinbase: false, is_coinbase: false,
tx_log_entry: Some(log_id), tx_log_entry: Some(log_id),
@@ -245,8 +243,8 @@ where
/// Creates a new output in the wallet for the recipient, /// Creates a new output in the wallet for the recipient,
/// returning the key of the fresh output /// returning the key of the fresh output
/// Also creates a new transaction containing the output /// Also creates a new transaction containing the output
pub fn build_recipient_output<'a, T: ?Sized, C, K>( pub fn build_recipient_output<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
current_height: u64, current_height: u64,
@@ -255,9 +253,8 @@ pub fn build_recipient_output<'a, T: ?Sized, C, K>(
is_initiator: bool, is_initiator: bool,
) -> Result<(Identifier, Context, TxLogEntry), Error> ) -> Result<(Identifier, Context, TxLogEntry), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// Create a potential output for this transaction // Create a potential output for this transaction
let key_id = keys::next_available_key(wallet, keychain_mask).unwrap(); let key_id = keys::next_available_key(wallet, keychain_mask).unwrap();
@@ -300,10 +297,10 @@ where
key_id: key_id_inner.clone(), key_id: key_id_inner.clone(),
mmr_index: None, mmr_index: None,
n_child: key_id_inner.to_path().last_path_index(), n_child: key_id_inner.to_path().last_path_index(),
commit: commit, commit,
value: amount, value: amount,
status: OutputStatus::Unconfirmed, status: OutputStatus::Unconfirmed,
height: height, height,
lock_height: 0, lock_height: 0,
is_coinbase: false, is_coinbase: false,
tx_log_entry: Some(log_id), tx_log_entry: Some(log_id),
@@ -317,8 +314,8 @@ where
/// Builds a transaction to send to someone from the HD seed associated with the /// Builds a transaction to send to someone from the HD seed associated with the
/// wallet and the amount to send. Handles reading through the wallet data file, /// wallet and the amount to send. Handles reading through the wallet data file,
/// selecting outputs to spend and building the change. /// selecting outputs to spend and building the change.
pub fn select_send_tx<'a, T: ?Sized, C, K, B>( pub fn select_send_tx<C, K, B>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
amount: u64, amount: u64,
amount_includes_fee: bool, amount_includes_fee: bool,
@@ -339,9 +336,8 @@ pub fn select_send_tx<'a, T: ?Sized, C, K, B>(
Error, Error,
> >
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
B: ProofBuild, B: ProofBuild,
{ {
let (coins, _total, amount, fee) = select_coins_and_fee( let (coins, _total, amount, fee) = select_coins_and_fee(
@@ -371,8 +367,8 @@ where
} }
/// Select outputs and calculating fee. /// Select outputs and calculating fee.
pub fn select_coins_and_fee<'a, T: ?Sized, C, K>( pub fn select_coins_and_fee<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
amount: u64, amount: u64,
amount_includes_fee: bool, amount_includes_fee: bool,
current_height: u64, current_height: u64,
@@ -391,9 +387,8 @@ pub fn select_coins_and_fee<'a, T: ?Sized, C, K>(
Error, Error,
> >
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// select some spendable coins from the wallet // select some spendable coins from the wallet
let (max_outputs, mut coins) = select_coins( let (max_outputs, mut coins) = select_coins(
@@ -407,7 +402,7 @@ where
); );
// sender is responsible for setting the fee on the partial tx // sender is responsible for setting the fee on the partial tx
// recipient should double check the fee calculation and not blindly trust the // recipient should double-check the fee calculation and not blindly trust the
// sender // sender
// First attempt to spend without change // First attempt to spend without change
@@ -422,8 +417,8 @@ where
return Err(Error::NotEnoughFunds { return Err(Error::NotEnoughFunds {
available: 0, available: 0,
available_disp: amount_to_hr_string(0, false), available_disp: amount_to_hr_string(0, false),
needed: amount_with_fee as u64, needed: amount_with_fee,
needed_disp: amount_to_hr_string(amount_with_fee as u64, false), needed_disp: amount_to_hr_string(amount_with_fee, false),
}); });
} }
@@ -432,8 +427,8 @@ where
return Err(Error::NotEnoughFunds { return Err(Error::NotEnoughFunds {
available: total, available: total,
available_disp: amount_to_hr_string(total, false), available_disp: amount_to_hr_string(total, false),
needed: amount_with_fee as u64, needed: amount_with_fee,
needed_disp: amount_to_hr_string(amount_with_fee as u64, false), needed_disp: amount_to_hr_string(amount_with_fee, false),
}); });
} }
@@ -453,10 +448,10 @@ where
// End the loop if we have selected all the outputs and still not enough funds // End the loop if we have selected all the outputs and still not enough funds
if coins.len() == max_outputs { if coins.len() == max_outputs {
return Err(Error::NotEnoughFunds { return Err(Error::NotEnoughFunds {
available: total as u64, available: total,
available_disp: amount_to_hr_string(total, false), available_disp: amount_to_hr_string(total, false),
needed: amount_with_fee as u64, needed: amount_with_fee,
needed_disp: amount_to_hr_string(amount_with_fee as u64, false), needed_disp: amount_to_hr_string(amount_with_fee, false),
}); });
} }
@@ -483,7 +478,7 @@ where
// be reduced, to accommodate the fee. // be reduced, to accommodate the fee.
let new_amount = match amount_includes_fee { let new_amount = match amount_includes_fee {
true => amount.checked_sub(fee).ok_or(Error::GenericError( true => amount.checked_sub(fee).ok_or(Error::GenericError(
format!("Transaction amount is too small to include fee").into(), "Transaction amount is too small to include fee".to_string(),
))?, ))?,
false => amount, false => amount,
}; };
@@ -491,9 +486,9 @@ where
} }
/// Selects inputs and change for a transaction /// Selects inputs and change for a transaction
pub fn inputs_and_change<'a, T: ?Sized, C, K, B>( pub fn inputs_and_change<C, K, B>(
coins: &[OutputData], coins: &[OutputData],
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
amount: u64, amount: u64,
fee: u64, fee: u64,
@@ -507,9 +502,8 @@ pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
Error, Error,
> >
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
B: ProofBuild, B: ProofBuild,
{ {
let mut parts = vec![]; let mut parts = vec![];
@@ -554,7 +548,7 @@ where
part_change part_change
}; };
let change_key = wallet.next_child(keychain_mask).unwrap(); let change_key = wallet.next_child(keychain_mask)?;
change_amounts_derivations.push((change_amount, change_key.clone(), None)); change_amounts_derivations.push((change_amount, change_key.clone(), None));
parts.push(build::output(change_amount, change_key)); parts.push(build::output(change_amount, change_key));
@@ -569,10 +563,8 @@ where
/// max_outputs). Alternative strategy is to spend smallest outputs first /// max_outputs). Alternative strategy is to spend smallest outputs first
/// but only as many as necessary. When we introduce additional strategies /// but only as many as necessary. When we introduce additional strategies
/// we should pass something other than a bool in. /// we should pass something other than a bool in.
/// TODO: Possibly move this into another trait to be owned by a wallet? pub fn select_coins<C, K>(
wallet: &WalletBackend<C, K>,
pub fn select_coins<'a, T: ?Sized, C, K>(
wallet: &mut T,
amount: u64, amount: u64,
current_height: u64, current_height: u64,
minimum_confirmations: u64, minimum_confirmations: u64,
@@ -582,9 +574,8 @@ pub fn select_coins<'a, T: ?Sized, C, K>(
) -> (usize, Vec<OutputData>) ) -> (usize, Vec<OutputData>)
// max_outputs_available, Outputs // max_outputs_available, Outputs
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// first find all eligible outputs based on number of confirmations // first find all eligible outputs based on number of confirmations
let mut eligible = wallet let mut eligible = wallet
@@ -663,21 +654,20 @@ fn select_from(amount: u64, select_all: bool, outputs: Vec<OutputData>) -> Optio
} }
} }
/// Repopulates output in the slate's tranacstion /// Repopulates output in the slate's transaction
/// with outputs from the stored context /// with outputs from the stored context
/// change outputs and tx log entry /// change outputs and tx log entry
/// Remove the explicitly stored excess /// Remove the explicitly stored excess
pub fn repopulate_tx<'a, T: ?Sized, C, K>( pub fn repopulate_tx<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
context: &Context, context: &Context,
update_fee: bool, update_fee: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// restore the original amount, fee // restore the original amount, fee
slate.amount = context.amount; slate.amount = context.amount;
+39 -51
View File
@@ -26,10 +26,10 @@ use crate::grin_util::secp::pedersen;
use crate::grin_util::Mutex; use crate::grin_util::Mutex;
use crate::internal::{selection, updater}; use crate::internal::{selection, updater};
use crate::slate::Slate; use crate::slate::Slate;
use crate::types::{Context, NodeClient, StoredProofInfo, TxLogEntryType, WalletBackend}; use crate::types::{Context, NodeClient, StoredProofInfo, TxLogEntryType};
use crate::util::OnionV3Address; use crate::util::OnionV3Address;
use crate::InitTxArgs;
use crate::{address, Error}; use crate::{address, Error};
use crate::{InitTxArgs, WalletBackend};
use ed25519_dalek::Keypair as DalekKeypair; use ed25519_dalek::Keypair as DalekKeypair;
use ed25519_dalek::PublicKey as DalekPublicKey; use ed25519_dalek::PublicKey as DalekPublicKey;
use ed25519_dalek::SecretKey as DalekSecretKey; use ed25519_dalek::SecretKey as DalekSecretKey;
@@ -44,8 +44,8 @@ lazy_static! {
/// Creates a new slate for a transaction, can be called by anyone involved in /// Creates a new slate for a transaction, can be called by anyone involved in
/// the transaction (sender(s), receiver(s)) /// the transaction (sender(s), receiver(s))
pub fn new_tx_slate<'a, T: ?Sized, C, K>( pub fn new_tx_slate<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
amount: u64, amount: u64,
is_invoice: bool, is_invoice: bool,
num_participants: u8, num_participants: u8,
@@ -53,9 +53,8 @@ pub fn new_tx_slate<'a, T: ?Sized, C, K>(
ttl_blocks: Option<u64>, ttl_blocks: Option<u64>,
) -> Result<Slate, Error> ) -> Result<Slate, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let current_height = wallet.w2n_client().get_chain_tip()?.0; let current_height = wallet.w2n_client().get_chain_tip()?.0;
let mut slate = Slate::blank(num_participants, is_invoice); let mut slate = Slate::blank(num_participants, is_invoice);
@@ -92,8 +91,8 @@ where
} }
/// Estimates locked amount and fee for the transaction without creating one /// Estimates locked amount and fee for the transaction without creating one
pub fn estimate_send_tx<'a, T: ?Sized, C, K>( pub fn estimate_send_tx<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
amount: u64, amount: u64,
amount_includes_fee: bool, amount_includes_fee: bool,
@@ -110,9 +109,8 @@ pub fn estimate_send_tx<'a, T: ?Sized, C, K>(
Error, Error,
> >
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// Get lock height // Get lock height
let current_height = wallet.w2n_client().get_chain_tip()?.0; let current_height = wallet.w2n_client().get_chain_tip()?.0;
@@ -141,8 +139,8 @@ where
} }
/// Add inputs to the slate (effectively becoming the sender) /// Add inputs to the slate (effectively becoming the sender)
pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>( pub fn add_inputs_to_slate<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
current_height: u64, current_height: u64,
@@ -156,9 +154,8 @@ pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>(
amount_includes_fee: bool, amount_includes_fee: bool,
) -> Result<Context, Error> ) -> Result<Context, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// sender should always refresh outputs // sender should always refresh outputs
updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?; updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?;
@@ -207,8 +204,8 @@ where
} }
/// Add receiver output to the slate /// Add receiver output to the slate
pub fn add_output_to_slate<'a, T: ?Sized, C, K>( pub fn add_output_to_slate<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
current_height: u64, current_height: u64,
@@ -217,9 +214,8 @@ pub fn add_output_to_slate<'a, T: ?Sized, C, K>(
use_test_rng: bool, use_test_rng: bool,
) -> Result<Context, Error> ) -> Result<Context, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let keychain = wallet.keychain(keychain_mask)?; let keychain = wallet.keychain(keychain_mask)?;
// create an output using the amount in the slate // create an output using the amount in the slate
@@ -252,8 +248,8 @@ where
} }
/// Create context, without adding inputs to slate /// Create context, without adding inputs to slate
pub fn create_late_lock_context<'a, T: ?Sized, C, K>( pub fn create_late_lock_context<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
current_height: u64, current_height: u64,
@@ -262,9 +258,8 @@ pub fn create_late_lock_context<'a, T: ?Sized, C, K>(
use_test_rng: bool, use_test_rng: bool,
) -> Result<Context, Error> ) -> Result<Context, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// sender should always refresh outputs // sender should always refresh outputs
updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?; updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?;
@@ -300,16 +295,15 @@ where
} }
/// Complete a transaction /// Complete a transaction
pub fn complete_tx<'a, T: ?Sized, C, K>( pub fn complete_tx<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
slate: &mut Slate, slate: &mut Slate,
context: &Context, context: &Context,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// when self sending invoice tx, use initiator nonce to finalize // when self sending invoice tx, use initiator nonce to finalize
let (sec_key, sec_nonce) = { let (sec_key, sec_nonce) = {
@@ -333,17 +327,16 @@ where
} }
/// Rollback outputs associated with a transaction in the wallet /// Rollback outputs associated with a transaction in the wallet
pub fn cancel_tx<'a, T: ?Sized, C, K>( pub fn cancel_tx<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
tx_id: Option<u32>, tx_id: Option<u32>,
tx_slate_id: Option<Uuid>, tx_slate_id: Option<Uuid>,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut tx_id_string = String::new(); let mut tx_id_string = String::new();
if let Some(tx_id) = tx_id { if let Some(tx_id) = tx_id {
@@ -384,17 +377,16 @@ where
} }
/// Update the stored transaction (this update needs to happen when the TX is finalised) /// Update the stored transaction (this update needs to happen when the TX is finalised)
pub fn update_stored_tx<'a, T: ?Sized, C, K>( pub fn update_stored_tx<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
context: &Context, context: &Context,
slate: &Slate, slate: &Slate,
is_invoiced: bool, is_invoiced: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// finalize command // finalize command
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, None, false)?; let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, None, false)?;
@@ -421,10 +413,7 @@ where
} }
if let Some(ref p) = slate.clone().payment_proof { if let Some(ref p) = slate.clone().payment_proof {
let derivation_index = match context.payment_proof_derivation_index { let derivation_index = context.payment_proof_derivation_index.unwrap_or_else(|| 0);
Some(i) => i,
None => 0,
};
let keychain = wallet.keychain(keychain_mask)?; let keychain = wallet.keychain(keychain_mask)?;
let parent_key_id = wallet.parent_key_id(); let parent_key_id = wallet.parent_key_id();
let excess = slate.calc_excess(keychain.secp())?; let excess = slate.calc_excess(keychain.secp())?;
@@ -506,17 +495,16 @@ pub fn create_payment_proof_signature(
} }
/// Verify all aspects of a completed payment proof on the current slate /// Verify all aspects of a completed payment proof on the current slate
pub fn verify_slate_payment_proof<'a, T: ?Sized, C, K>( pub fn verify_slate_payment_proof<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
context: &Context, context: &Context,
slate: &Slate, slate: &Slate,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let tx_vec = updater::retrieve_txs( let tx_vec = updater::retrieve_txs(
wallet, wallet,
+64 -75
View File
@@ -30,28 +30,25 @@ use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::secp::pedersen; use crate::grin_util::secp::pedersen;
use crate::grin_util::static_secp_instance; use crate::grin_util::static_secp_instance;
use crate::internal::keys; use crate::internal::keys;
use crate::types::{ use crate::types::{NodeClient, OutputData, OutputStatus, TxLogEntry, TxLogEntryType, WalletInfo};
NodeClient, OutputData, OutputStatus, TxLogEntry, TxLogEntryType, WalletBackend, WalletInfo,
};
use crate::{ use crate::{
BlockFees, CbData, OutputCommitMapping, RetrieveTxQueryArgs, RetrieveTxQuerySortField, BlockFees, CbData, OutputCommitMapping, RetrieveTxQueryArgs, RetrieveTxQuerySortField,
RetrieveTxQuerySortOrder, RetrieveTxQuerySortOrder, WalletBackend,
}; };
use num_bigint::BigInt; use num_bigint::BigInt;
/// Retrieve all of the outputs (doesn't attempt to update from node) /// Retrieve all the outputs (don't attempt to update from node)
pub fn retrieve_outputs<'a, T: ?Sized, C, K>( pub fn retrieve_outputs<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
show_spent: bool, show_spent: bool,
tx_id: Option<u32>, tx_id: Option<u32>,
parent_key_id: Option<&Identifier>, parent_key_id: Option<&Identifier>,
) -> Result<Vec<OutputCommitMapping>, Error> ) -> Result<Vec<OutputCommitMapping>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// just read the wallet here, no need for a write lock // just read the wallet here, no need for a write lock
let mut outputs = wallet let mut outputs = wallet
@@ -94,15 +91,14 @@ where
} }
/// Apply advanced filtering to resultset from retrieve_txs below /// Apply advanced filtering to resultset from retrieve_txs below
pub fn apply_advanced_tx_list_filtering<'a, T: ?Sized, C, K>( pub fn apply_advanced_tx_list_filtering<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
parent_key_id: Option<&Identifier>, parent_key_id: Option<&Identifier>,
query_args: &RetrieveTxQueryArgs, query_args: &RetrieveTxQueryArgs,
) -> Vec<TxLogEntry> ) -> Vec<TxLogEntry>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// Apply simple bool, GTE or LTE fields // Apply simple bool, GTE or LTE fields
let txs_iter: Box<dyn Iterator<Item = TxLogEntry>> = Box::new( let txs_iter: Box<dyn Iterator<Item = TxLogEntry>> = Box::new(
@@ -329,10 +325,10 @@ where
return_txs return_txs
} }
/// Retrieve all of the transaction entries, or a particular entry /// Retrieve all the transaction entries, or a particular entry
/// if `parent_key_id` is set, only return entries from that key /// if `parent_key_id` is set, only return entries from that key
pub fn retrieve_txs<'a, T: ?Sized, C, K>( pub fn retrieve_txs<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
tx_id: Option<u32>, tx_id: Option<u32>,
tx_slate_id: Option<Uuid>, tx_slate_id: Option<Uuid>,
query_args: Option<RetrieveTxQueryArgs>, query_args: Option<RetrieveTxQueryArgs>,
@@ -340,9 +336,8 @@ pub fn retrieve_txs<'a, T: ?Sized, C, K>(
outstanding_only: bool, outstanding_only: bool,
) -> Result<Vec<TxLogEntry>, Error> ) -> Result<Vec<TxLogEntry>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut txs; let mut txs;
// Adding in new transaction list query logic. If `tx_id` or `tx_slate_id` // Adding in new transaction list query logic. If `tx_id` or `tx_slate_id`
@@ -384,16 +379,15 @@ where
/// Refreshes the outputs in a wallet with the latest information /// Refreshes the outputs in a wallet with the latest information
/// from a node /// from a node
pub fn refresh_outputs<'a, T: ?Sized, C, K>( pub fn refresh_outputs<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
update_all: bool, update_all: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let height = wallet.w2n_client().get_chain_tip()?.0; let height = wallet.w2n_client().get_chain_tip()?.0;
refresh_output_state(wallet, keychain_mask, height, parent_key_id, update_all)?; refresh_output_state(wallet, keychain_mask, height, parent_key_id, update_all)?;
@@ -402,16 +396,15 @@ where
/// build a local map of wallet outputs keyed by commit /// build a local map of wallet outputs keyed by commit
/// and a list of outputs we want to query the node for /// and a list of outputs we want to query the node for
pub fn map_wallet_outputs<'a, T: ?Sized, C, K>( pub fn map_wallet_outputs<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
update_all: bool, update_all: bool,
) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>, Error> ) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut wallet_outputs = HashMap::new(); let mut wallet_outputs = HashMap::new();
let keychain = wallet.keychain(keychain_mask)?; let keychain = wallet.keychain(keychain_mask)?;
@@ -453,17 +446,16 @@ where
} }
/// Cancel transaction and associated outputs /// Cancel transaction and associated outputs
pub fn cancel_tx_and_outputs<'a, T: ?Sized, C, K>( pub fn cancel_tx_and_outputs<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
mut tx: TxLogEntry, mut tx: TxLogEntry,
outputs: Vec<OutputData>, outputs: Vec<OutputData>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut batch = wallet.batch(keychain_mask)?; let mut batch = wallet.batch(keychain_mask)?;
@@ -490,8 +482,8 @@ where
} }
/// Apply refreshed API output data to the wallet /// Apply refreshed API output data to the wallet
pub fn apply_api_outputs<'a, T: ?Sized, C, K>( pub fn apply_api_outputs<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>, wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>,
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>, api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
@@ -500,9 +492,8 @@ pub fn apply_api_outputs<'a, T: ?Sized, C, K>(
parent_key_id: &Identifier, parent_key_id: &Identifier,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
// now for each commit, find the output in the wallet and the corresponding // now for each commit, find the output in the wallet and the corresponding
// api output (if it exists) and refresh it in-place in the wallet. // api output (if it exists) and refresh it in-place in the wallet.
@@ -590,6 +581,7 @@ where
} }
} }
let mut txs_to_save = vec![];
for mut tx in batch.tx_log_iter() { for mut tx in batch.tx_log_iter() {
if reverted_kernels.contains(&tx.id) && tx.parent_key_id == *parent_key_id { if reverted_kernels.contains(&tx.id) && tx.parent_key_id == *parent_key_id {
tx.tx_type = TxLogEntryType::TxReverted; tx.tx_type = TxLogEntryType::TxReverted;
@@ -598,9 +590,12 @@ where
(now - t).to_std().ok() (now - t).to_std().ok()
}); });
tx.confirmed = false; tx.confirmed = false;
batch.save_tx_log_entry(tx, &parent_key_id)?; txs_to_save.push(tx);
} }
} }
for tx in txs_to_save {
batch.save_tx_log_entry(tx, &parent_key_id)?;
}
{ {
batch.save_last_confirmed_height(parent_key_id, height)?; batch.save_last_confirmed_height(parent_key_id, height)?;
@@ -612,17 +607,16 @@ where
/// Builds a single api query to retrieve the latest output data from the node. /// Builds a single api query to retrieve the latest output data from the node.
/// So we can refresh the local wallet outputs. /// So we can refresh the local wallet outputs.
fn refresh_output_state<'a, T: ?Sized, C, K>( fn refresh_output_state<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
height: u64, height: u64,
parent_key_id: &Identifier, parent_key_id: &Identifier,
update_all: bool, update_all: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
debug!("Refreshing wallet outputs"); debug!("Refreshing wallet outputs");
@@ -654,16 +648,15 @@ where
Ok(()) Ok(())
} }
fn find_reverted_kernels<'a, T: ?Sized, C, K>( fn find_reverted_kernels<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>, wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>,
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>, api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
) -> Result<HashSet<u32>, Error> ) -> Result<HashSet<u32>, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let mut client = wallet.w2n_client().clone(); let mut client = wallet.w2n_client().clone();
let mut ids = HashSet::new(); let mut ids = HashSet::new();
@@ -701,15 +694,14 @@ where
Ok(reverted) Ok(reverted)
} }
fn clean_old_unconfirmed<'a, T: ?Sized, C, K>( fn clean_old_unconfirmed<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
height: u64, height: u64,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
if height < 50 { if height < 50 {
return Ok(()); return Ok(());
@@ -734,15 +726,14 @@ where
/// Retrieve summary info about the wallet /// Retrieve summary info about the wallet
/// caller should refresh first if desired /// caller should refresh first if desired
pub fn retrieve_info<'a, T: ?Sized, C, K>( pub fn retrieve_info<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
parent_key_id: &Identifier, parent_key_id: &Identifier,
minimum_confirmations: u64, minimum_confirmations: u64,
) -> Result<WalletInfo, Error> ) -> Result<WalletInfo, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let current_height = wallet.last_confirmed_height()?; let current_height = wallet.last_confirmed_height()?;
let outputs = wallet let outputs = wallet
@@ -800,16 +791,15 @@ where
} }
/// Build a coinbase output and insert into wallet /// Build a coinbase output and insert into wallet
pub fn build_coinbase<'a, T: ?Sized, C, K>( pub fn build_coinbase<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
block_fees: &BlockFees, block_fees: &BlockFees,
test_mode: bool, test_mode: bool,
) -> Result<CbData, Error> ) -> Result<CbData, Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let (out, kern, block_fees) = receive_coinbase(wallet, keychain_mask, block_fees, test_mode)?; let (out, kern, block_fees) = receive_coinbase(wallet, keychain_mask, block_fees, test_mode)?;
@@ -822,16 +812,15 @@ where
//TODO: Split up the output creation and the wallet insertion //TODO: Split up the output creation and the wallet insertion
/// Build a coinbase output and the corresponding kernel /// Build a coinbase output and the corresponding kernel
pub fn receive_coinbase<'a, T: ?Sized, C, K>( pub fn receive_coinbase<C, K>(
wallet: &mut T, wallet: &mut WalletBackend<C, K>,
keychain_mask: Option<&SecretKey>, keychain_mask: Option<&SecretKey>,
block_fees: &BlockFees, block_fees: &BlockFees,
test_mode: bool, test_mode: bool,
) -> Result<(Output, TxKernel, BlockFees), Error> ) -> Result<(Output, TxKernel, BlockFees), Error>
where where
T: WalletBackend<'a, C, K>, C: NodeClient,
C: NodeClient + 'a, K: Keychain,
K: Keychain + 'a,
{ {
let height = block_fees.height; let height = block_fees.height;
let lock_height = height + global::coinbase_maturity(); let lock_height = height + global::coinbase_maturity();
@@ -856,11 +845,11 @@ where
key_id: key_id.clone(), key_id: key_id.clone(),
n_child: key_id.to_path().last_path_index(), n_child: key_id.to_path().last_path_index(),
mmr_index: None, mmr_index: None,
commit: commit, commit,
value: amount, value: amount,
status: OutputStatus::Unconfirmed, status: OutputStatus::Unconfirmed,
height: height, height,
lock_height: lock_height, lock_height,
is_coinbase: true, is_coinbase: true,
tx_log_entry: None, tx_log_entry: None,
})?; })?;
+3 -2
View File
@@ -45,6 +45,7 @@ extern crate strum_macros;
pub mod address; pub mod address;
pub mod api_impl; pub mod api_impl;
mod backend;
mod error; mod error;
mod internal; mod internal;
pub mod mwixnet; pub mod mwixnet;
@@ -69,13 +70,13 @@ pub use api_impl::types::{
NodeHeightResult, OutputCommitMapping, PaymentProof, RetrieveTxQueryArgs, NodeHeightResult, OutputCommitMapping, PaymentProof, RetrieveTxQueryArgs,
RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, VersionInfo, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, VersionInfo,
}; };
pub use backend::{WalletBackend, WalletBatch};
pub use internal::scan::scan; pub use internal::scan::scan;
pub use slate_versions::ser as dalek_ser; pub use slate_versions::ser as dalek_ser;
pub use types::{ pub use types::{
AcctPathMapping, BlockIdentifier, CbData, Context, NodeClient, NodeVersionInfo, OutputData, AcctPathMapping, BlockIdentifier, CbData, Context, NodeClient, NodeVersionInfo, OutputData,
OutputStatus, ScannedBlockInfo, StoredProofInfo, TxLogEntry, TxLogEntryType, TxWrapper, OutputStatus, ScannedBlockInfo, StoredProofInfo, TxLogEntry, TxLogEntryType, TxWrapper,
ViewWallet, WalletBackend, WalletInfo, WalletInitStatus, WalletInst, WalletLCProvider, ViewWallet, WalletInfo, WalletInitStatus, WalletInst, WalletLCProvider,
WalletOutputBatch,
}; };
/// Helper for taking a lock on the wallet instance /// Helper for taking a lock on the wallet instance
+3 -178
View File
@@ -28,7 +28,7 @@ use crate::grin_util::secp::key::{PublicKey, SecretKey};
use crate::grin_util::secp::{self, pedersen, Secp256k1}; use crate::grin_util::secp::{self, pedersen, Secp256k1};
use crate::grin_util::{ToHex, ZeroingString}; use crate::grin_util::{ToHex, ZeroingString};
use crate::slate_versions::ser as dalek_ser; use crate::slate_versions::ser as dalek_ser;
use crate::InitTxArgs; use crate::{InitTxArgs, WalletBackend};
use chrono::prelude::*; use chrono::prelude::*;
use ed25519_dalek::PublicKey as DalekPublicKey; use ed25519_dalek::PublicKey as DalekPublicKey;
use ed25519_dalek::Signature as DalekSignature; use ed25519_dalek::Signature as DalekSignature;
@@ -49,7 +49,7 @@ where
K: Keychain + 'a, K: Keychain + 'a,
{ {
/// Return the stored instance /// Return the stored instance
fn lc_provider(&mut self) -> Result<&mut (dyn WalletLCProvider<'a, C, K> + 'a), Error>; fn lc_provider(&mut self) -> Result<&mut dyn WalletLCProvider<'a, C, K>, Error>;
} }
/// Trait for a provider of wallet lifecycle methods /// Trait for a provider of wallet lifecycle methods
@@ -131,182 +131,7 @@ where
fn delete_wallet(&self, name: Option<&str>) -> Result<(), Error>; fn delete_wallet(&self, name: Option<&str>) -> Result<(), Error>;
/// return wallet instance /// return wallet instance
fn wallet_inst(&mut self) -> Result<&mut Box<dyn WalletBackend<'a, C, K> + 'a>, Error>; fn wallet_inst(&mut self) -> Result<&mut WalletBackend<C, K>, Error>;
}
/// TODO:
/// Wallets should implement this backend for their storage. All functions
/// here expect that the wallet instance has instantiated itself or stored
/// whatever credentials it needs
pub trait WalletBackend<'ck, C, K>: Send + Sync
where
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
/// Set the keychain, which should already be initialized
/// Optionally return a token value used to XOR the stored
/// key value
fn set_keychain(
&mut self,
k: Box<K>,
mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error>;
/// Close wallet and remove any stored credentials (TBD)
fn close(&mut self) -> Result<(), Error>;
/// Return the keychain being used. Ensure a cloned copy so it will be dropped
/// and zeroized by the caller
/// Can optionally take a mask value
fn keychain(&self, mask: Option<&SecretKey>) -> Result<K, Error>;
/// Return the client being used to communicate with the node
fn w2n_client(&mut self) -> &mut C;
/// return the commit for caching if allowed, none otherwise
fn calc_commit_for_cache(
&mut self,
keychain_mask: Option<&SecretKey>,
amount: u64,
id: &Identifier,
) -> Result<Option<String>, Error>;
/// Set parent key id by stored account name
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error>;
/// The BIP32 path of the parent path to use for all output-related
/// functions, (essentially 'accounts' within a wallet.
fn set_parent_key_id(&mut self, _: Identifier);
/// return the parent path
fn parent_key_id(&mut self) -> Identifier;
/// Iterate over all output data stored by the backend
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a>;
/// Get output data by id
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error>;
/// Get an (Optional) tx log entry by uuid
fn get_tx_log_entry(&self, uuid: &Uuid) -> Result<Option<TxLogEntry>, Error>;
/// Retrieves the private context associated with a given slate id
fn get_private_context(
&mut self,
keychain_mask: Option<&SecretKey>,
slate_id: &[u8],
) -> Result<Context, Error>;
/// Iterate over all output data stored by the backend
fn tx_log_iter<'a>(&'a self) -> Box<dyn Iterator<Item = TxLogEntry> + 'a>;
/// Iterate over all stored account paths
fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a>;
/// Gets an account path for a given label
fn get_acct_path(&self, label: String) -> Result<Option<AcctPathMapping>, Error>;
/// Stores a transaction
fn store_tx(&self, uuid: &str, tx: &Transaction) -> Result<(), Error>;
/// Retrieves a stored transaction from a TxLogEntry
fn get_stored_tx(&self, uuid: &str) -> Result<Option<Transaction>, Error>;
/// Create a new write batch to update or remove output data
fn batch<'a>(
&'a mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error>;
/// Batch for use when keychain isn't available or required
fn batch_no_mask<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error>;
/// Return the current child Index
fn current_child_index(&mut self, parent_key_id: &Identifier) -> Result<u32, Error>;
/// Next child ID when we want to create a new output, based on current parent
fn next_child(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error>;
/// last verified height of outputs directly descending from the given parent key
fn last_confirmed_height(&mut self) -> Result<u64, Error>;
/// last block scanned during scan or restore
fn last_scanned_block(&mut self) -> Result<ScannedBlockInfo, Error>;
/// Flag whether the wallet needs a full UTXO scan on next update attempt
fn init_status(&mut self) -> Result<WalletInitStatus, Error>;
}
/// Batch trait to update the output data backend atomically. Trying to use a
/// batch after commit MAY result in a panic. Due to this being a trait, the
/// commit method can't take ownership.
/// TODO: Should these be split into separate batch objects, for outputs,
/// tx_log entries and meta/details?
pub trait WalletOutputBatch<K>
where
K: Keychain,
{
/// Return the keychain being used
fn keychain(&mut self) -> &mut K;
/// Add or update data about an output to the backend
fn save(&mut self, out: OutputData) -> Result<(), Error>;
/// Gets output data by id
fn get(&self, id: &Identifier, mmr_index: &Option<u64>) -> Result<OutputData, Error>;
/// Iterate over all output data stored by the backend
fn iter(&self) -> Box<dyn Iterator<Item = OutputData>>;
/// Delete data about an output from the backend
fn delete(&mut self, id: &Identifier, mmr_index: &Option<u64>) -> Result<(), Error>;
/// Save last stored child index of a given parent
fn save_child_index(&mut self, parent_key_id: &Identifier, child_n: u32) -> Result<(), Error>;
/// Save last confirmed height of outputs for a given parent
fn save_last_confirmed_height(
&mut self,
parent_key_id: &Identifier,
height: u64,
) -> Result<(), Error>;
/// Save the last PMMR index that was scanned via a scan operation
fn save_last_scanned_block(&mut self, block: ScannedBlockInfo) -> Result<(), Error>;
/// Save flag indicating whether wallet needs a full UTXO scan
fn save_init_status(&mut self, value: WalletInitStatus) -> Result<(), Error>;
/// get next tx log entry for the parent
fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result<u32, Error>;
/// Iterate over tx log data stored by the backend
fn tx_log_iter(&self) -> Box<dyn Iterator<Item = TxLogEntry>>;
/// save a tx log entry
fn save_tx_log_entry(&mut self, t: TxLogEntry, parent_id: &Identifier) -> Result<(), Error>;
/// delete a tx log entry
fn delete_tx_log_entry(&mut self, tx_id: u32, parent_id: &Identifier) -> Result<(), Error>;
/// save an account label -> path mapping
fn save_acct_path(&mut self, mapping: AcctPathMapping) -> Result<(), Error>;
/// Iterate over account names stored in backend
fn acct_path_iter(&self) -> Box<dyn Iterator<Item = AcctPathMapping>>;
/// Save an output as locked in the backend
fn lock_output(&mut self, out: &mut OutputData) -> Result<(), Error>;
/// Saves the private context associated with a slate id
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error>;
/// Delete the private context associated with the slate id
fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error>;
/// Write the wallet data to backend file
fn commit(&self) -> Result<(), Error>;
} }
/// Encapsulate all wallet-node communication functions. No functions within libwallet /// Encapsulate all wallet-node communication functions. No functions within libwallet
+1 -1
View File
@@ -115,7 +115,7 @@ pub fn command_loop<L, C, K>(
test_mode: bool, test_mode: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K> + 'static, L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static, C: NodeClient + 'static,
K: keychain::Keychain + 'static, K: keychain::Keychain + 'static,
+14 -16
View File
@@ -92,7 +92,7 @@ fn prompt_recovery_phrase<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>, wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
) -> Result<ZeroingString, ParseError> ) -> Result<ZeroingString, ParseError>
where where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>, L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static, C: NodeClient + 'static,
K: keychain::Keychain + 'static, K: keychain::Keychain + 'static,
@@ -224,12 +224,12 @@ pub fn inst_wallet<L, C, K>(
node_client: C, node_client: C,
) -> Result<Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>, ParseError> ) -> Result<Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>, ParseError>
where where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>, L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static, C: NodeClient + 'static,
K: keychain::Keychain + 'static, K: keychain::Keychain + 'static,
{ {
let mut wallet = Box::new(DefaultWalletImpl::<'static, C>::new(node_client.clone()).unwrap()) let mut wallet = Box::new(DefaultWalletImpl::<C>::new(node_client.clone()).unwrap())
as Box<dyn WalletInst<'static, L, C, K>>; as Box<dyn WalletInst<'static, L, C, K>>;
let lc = wallet.lc_provider().unwrap(); let lc = wallet.lc_provider().unwrap();
let _ = lc.set_top_level_directory(&config.data_file_dir); let _ = lc.set_top_level_directory(&config.data_file_dir);
@@ -335,7 +335,7 @@ pub fn parse_init_args<L, C, K>(
_test_mode: bool, _test_mode: bool,
) -> Result<command::InitArgs, ParseError> ) -> Result<command::InitArgs, ParseError>
where where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>, L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static, C: NodeClient + 'static,
K: keychain::Keychain + 'static, K: keychain::Keychain + 'static,
@@ -355,16 +355,16 @@ where
println!("Please enter a password for your new wallet"); println!("Please enter a password for your new wallet");
} }
let password = match g_args.password.clone() { let password = g_args
Some(p) => p, .password
None => prompt_password_confirm(), .clone()
}; .unwrap_or_else(|| prompt_password_confirm());
Ok(command::InitArgs { Ok(command::InitArgs {
list_length: list_length, list_length,
password: password, password,
config: config.clone(), config: config.clone(),
recovery_phrase: recovery_phrase, recovery_phrase,
restore: false, restore: false,
}) })
} }
@@ -375,9 +375,7 @@ pub fn parse_recover_args(
where where
{ {
let passphrase = prompt_password(&g_args.password); let passphrase = prompt_password(&g_args.password);
Ok(command::RecoverArgs { Ok(command::RecoverArgs { passphrase })
passphrase: passphrase,
})
} }
pub fn parse_listen_args( pub fn parse_listen_args(
@@ -977,7 +975,7 @@ where
Box< Box<
dyn WalletInst< dyn WalletInst<
'static, 'static,
DefaultLCProvider<'static, C, keychain::ExtKeychain>, DefaultLCProvider<C, keychain::ExtKeychain>,
C, C,
keychain::ExtKeychain, keychain::ExtKeychain,
>, >,
@@ -1112,7 +1110,7 @@ pub fn parse_and_execute<L, C, K>(
cli_mode: bool, cli_mode: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K> + 'static, L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static, C: NodeClient + 'static,
K: keychain::Keychain + 'static, K: keychain::Keychain + 'static,
+3 -14
View File
@@ -226,7 +226,7 @@ pub fn instantiate_wallet(
Box< Box<
dyn WalletInst< dyn WalletInst<
'static, 'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
@@ -241,7 +241,7 @@ pub fn instantiate_wallet(
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(node_client).unwrap()) let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(node_client).unwrap())
as Box< as Box<
dyn WalletInst< dyn WalletInst<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
@@ -302,18 +302,7 @@ pub fn execute_command_no_setup<C, F>(
where where
C: NodeClient + 'static + Clone, C: NodeClient + 'static + Clone,
F: FnOnce( F: FnOnce(
Arc< Arc<Mutex<Box<dyn WalletInst<'static, DefaultLCProvider<C, ExtKeychain>, C, ExtKeychain>>>>,
Mutex<
Box<
dyn WalletInst<
'static,
DefaultLCProvider<'static, C, ExtKeychain>,
C,
ExtKeychain,
>,
>,
>,
>,
), ),
{ {
let args = app.clone().get_matches_from(arg_vec); let args = app.clone().get_matches_from(arg_vec);
+1 -1
View File
@@ -57,7 +57,7 @@ fn owner_v3_lifecycle() -> Result<(), grin_wallet_controller::Error> {
let wallet_proxy_a: Arc< let wallet_proxy_a: Arc<
Mutex< Mutex<
WalletProxy< WalletProxy<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient, LocalWalletClient,
ExtKeychain, ExtKeychain,
>, >,
+2 -2
View File
@@ -21,14 +21,14 @@ thiserror = "1"
##### Grin Imports ##### Grin Imports
# For Release # For Release
grin_util = "5.4.0" #grin_util = "5.4.0"
# For beta release # For beta release
# grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" } # grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
# For bleeding edge # For bleeding edge
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
# For local testing # For local testing