Merge branch 'migrate_lmdb' into update_lmdb
This commit is contained in:
Generated
+1124
-671
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -41,10 +41,10 @@ grin_wallet_util = { path = "./util", version = "5.4.0-alpha.1" }
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.4.0"
|
||||
grin_keychain = "5.4.0"
|
||||
grin_util = "5.4.0"
|
||||
grin_api = "5.4.0"
|
||||
#grin_core = "5.4.0"
|
||||
#grin_keychain = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
#grin_api = "5.4.0"
|
||||
|
||||
# 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" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_api = { 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", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_api = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../grin/core"}
|
||||
|
||||
+6
-6
@@ -30,9 +30,9 @@ grin_wallet_util = { path = "../util", version = "5.4.0-alpha.1" }
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.4.0"
|
||||
grin_keychain = "5.4.0"
|
||||
grin_util = "5.4.0"
|
||||
#grin_core = "5.4.0"
|
||||
#grin_keychain = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
|
||||
# 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" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { 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", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
|
||||
+16
-22
@@ -146,7 +146,7 @@ where
|
||||
/// // by the reference wallet implementation.
|
||||
/// // 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>>;
|
||||
///
|
||||
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
|
||||
@@ -286,7 +286,7 @@ where
|
||||
)?;
|
||||
}
|
||||
foreign::build_coinbase(
|
||||
&mut **w,
|
||||
w,
|
||||
(&self.keychain_mask).as_ref(),
|
||||
block_fees,
|
||||
self.doctest_mode,
|
||||
@@ -362,7 +362,7 @@ where
|
||||
)?;
|
||||
}
|
||||
let ret_slate = foreign::receive_tx(
|
||||
&mut **w,
|
||||
w,
|
||||
(&self.keychain_mask).as_ref(),
|
||||
slate,
|
||||
dest_acct_name,
|
||||
@@ -380,8 +380,8 @@ where
|
||||
self.doctest_mode,
|
||||
);
|
||||
match res {
|
||||
Ok(s) => return Ok(s.unwrap()),
|
||||
Err(_) => return Ok(ret_slate),
|
||||
Ok(s) => Ok(s.unwrap()),
|
||||
Err(_) => Ok(ret_slate),
|
||||
}
|
||||
}
|
||||
None => Ok(ret_slate),
|
||||
@@ -443,12 +443,7 @@ where
|
||||
true => false,
|
||||
false => post_automatically,
|
||||
};
|
||||
foreign::finalize_tx(
|
||||
&mut **w,
|
||||
(&self.keychain_mask).as_ref(),
|
||||
slate,
|
||||
post_automatically,
|
||||
)
|
||||
foreign::finalize_tx(w, (&self.keychain_mask).as_ref(), slate, post_automatically)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,17 +491,16 @@ macro_rules! doctest_helper_setup_doc_env_foreign {
|
||||
|
||||
let node_client =
|
||||
HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap();
|
||||
let mut wallet = Box::new(
|
||||
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(),
|
||||
)
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
|
||||
HTTPNodeClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let mut wallet =
|
||||
Box::new(DefaultWalletImpl::<HTTPNodeClient>::new(node_client.clone()).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
|
||||
HTTPNodeClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
let _ = lc.set_top_level_directory(&wallet_config.data_file_dir);
|
||||
lc.open_wallet(None, pw, false, false);
|
||||
|
||||
@@ -381,7 +381,6 @@ pub fn run_doctest_foreign(
|
||||
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client1.clone()).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
@@ -416,7 +415,6 @@ pub fn run_doctest_foreign(
|
||||
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client2.clone()).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
@@ -473,7 +471,7 @@ pub fn run_doctest_foreign(
|
||||
amount,
|
||||
..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 = {
|
||||
let mut w_lock = wallet1.lock();
|
||||
@@ -487,8 +485,7 @@ pub fn run_doctest_foreign(
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
api_impl::owner::process_invoice_tx(&mut **w, (&mask1).as_ref(), &slate, args, true)
|
||||
.unwrap()
|
||||
api_impl::owner::process_invoice_tx(w, (&mask1).as_ref(), &slate, args, true).unwrap()
|
||||
};
|
||||
println!("INIT INVOICE SLATE");
|
||||
// Spit out slate for input to finalize_tx
|
||||
@@ -508,7 +505,7 @@ pub fn run_doctest_foreign(
|
||||
selection_strategy_is_use_all: true,
|
||||
..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");
|
||||
// Spit out slate for input to finalize_tx
|
||||
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
|
||||
|
||||
+34
-36
@@ -97,7 +97,7 @@ where
|
||||
///
|
||||
/// 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)
|
||||
/// (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)
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -147,11 +147,11 @@ where
|
||||
/// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap();
|
||||
///
|
||||
/// // 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.
|
||||
/// // 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>>;
|
||||
///
|
||||
/// // 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()?;
|
||||
// Test keychain mask, to keep API consistent
|
||||
let _ = w.keychain(keychain_mask)?;
|
||||
owner::accounts(&mut **w)
|
||||
owner::accounts(w)
|
||||
}
|
||||
|
||||
/// Creates a new 'account', which is a mapping of a user-specified
|
||||
@@ -308,7 +308,7 @@ where
|
||||
) -> Result<Identifier, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
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
|
||||
@@ -358,7 +358,7 @@ where
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
// Test keychain mask, to keep API consistent
|
||||
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.
|
||||
@@ -665,7 +665,7 @@ where
|
||||
let slate = {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
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
|
||||
// finalize
|
||||
@@ -701,17 +701,17 @@ where
|
||||
match result {
|
||||
Ok(_) => {
|
||||
info!("Tx sent ok",);
|
||||
return Ok(ret_slate);
|
||||
Ok(ret_slate)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Tx sent fail: {}", e);
|
||||
return Err(e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tx_lock_outputs(keychain_mask, &s)?;
|
||||
let ret_slate = self.finalize_tx(keychain_mask, &s)?;
|
||||
return Ok(ret_slate);
|
||||
Ok(ret_slate)
|
||||
}
|
||||
}
|
||||
Ok(None) => Ok(slate),
|
||||
@@ -724,7 +724,7 @@ where
|
||||
|
||||
/// 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,
|
||||
/// 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
|
||||
/// via the [Foreign API's `finalize_tx`](struct.Foreign.html#method.finalize_tx) method.
|
||||
///
|
||||
@@ -764,14 +764,14 @@ where
|
||||
) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
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
|
||||
/// 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
|
||||
/// 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.
|
||||
///
|
||||
/// 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 w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
let send_args = args.send_args.clone();
|
||||
let slate =
|
||||
owner::process_invoice_tx(&mut **w, keychain_mask, slate, args, self.doctest_mode)?;
|
||||
let slate = owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)?;
|
||||
// Helper functionality. If send arguments exist, attempt to send
|
||||
match send_args {
|
||||
Some(sa) => {
|
||||
@@ -871,7 +870,7 @@ where
|
||||
/// Locks the outputs associated with the inputs to the transaction in the given
|
||||
/// [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html),
|
||||
/// 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)
|
||||
/// that completes the transaction.
|
||||
///
|
||||
@@ -929,7 +928,7 @@ where
|
||||
) -> Result<(), Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
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
|
||||
@@ -995,7 +994,7 @@ where
|
||||
) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
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
|
||||
@@ -1186,7 +1185,7 @@ where
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
// Test keychain mask, to keep API consistent
|
||||
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.
|
||||
@@ -2422,7 +2421,7 @@ where
|
||||
) -> Result<BuiltOutput, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
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
|
||||
@@ -2481,7 +2480,7 @@ where
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
owner::create_mwixnet_req(
|
||||
&mut **w,
|
||||
w,
|
||||
keychain_mask,
|
||||
params,
|
||||
commitment,
|
||||
@@ -2499,7 +2498,7 @@ pub fn try_slatepack_sync_workflow(
|
||||
tor_sender: Option<HttpSlateSender>,
|
||||
send_to_finalize: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<Option<Slate>, libwallet::Error> {
|
||||
) -> Result<Option<Slate>, Error> {
|
||||
if let Some(tc) = &tor_config {
|
||||
if tc.skip_send_attempt == Some(true) {
|
||||
return Ok(None);
|
||||
@@ -2604,7 +2603,7 @@ macro_rules! doctest_helper_setup_doc_env {
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -2624,17 +2623,16 @@ macro_rules! doctest_helper_setup_doc_env {
|
||||
|
||||
let node_client =
|
||||
HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap();
|
||||
let mut wallet = Box::new(
|
||||
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(),
|
||||
)
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
|
||||
HTTPNodeClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let mut wallet =
|
||||
Box::new(DefaultWalletImpl::<HTTPNodeClient>::new(node_client.clone()).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
|
||||
HTTPNodeClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
let _ = lc.set_top_level_directory(&wallet_config.data_file_dir);
|
||||
lc.open_wallet(None, pw, false, false);
|
||||
|
||||
+17
-18
@@ -2137,6 +2137,14 @@ where
|
||||
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> {
|
||||
let out_slate = Owner::finalize_tx(
|
||||
self,
|
||||
@@ -2147,11 +2155,12 @@ where
|
||||
VersionedSlate::into_version(out_slate, version)
|
||||
}
|
||||
|
||||
fn tx_lock_outputs(&self, token: Token, in_slate: VersionedSlate) -> Result<(), Error> {
|
||||
Owner::tx_lock_outputs(
|
||||
fn post_tx(&self, token: Token, slate: VersionedSlate, fluff: bool) -> Result<(), Error> {
|
||||
Owner::post_tx(
|
||||
self,
|
||||
(&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> {
|
||||
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,
|
||||
..Default::default()
|
||||
};
|
||||
let mut slate =
|
||||
api_impl::owner::init_send_tx(&mut **w, (&mask1).as_ref(), args, true).unwrap();
|
||||
let mut slate = api_impl::owner::init_send_tx(w, (&mask1).as_ref(), args, true).unwrap();
|
||||
println!("INITIAL SLATE");
|
||||
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
|
||||
{
|
||||
let mut w_lock = wallet2.lock();
|
||||
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
|
||||
slate = api_impl::foreign::receive_tx(&mut **w2, (&mask2).as_ref(), &slate, None, true)
|
||||
.unwrap();
|
||||
slate =
|
||||
api_impl::foreign::receive_tx(w2, (&mask2).as_ref(), &slate, None, true).unwrap();
|
||||
w2.close().unwrap();
|
||||
}
|
||||
// Spit out slate for input to finalize_tx
|
||||
if lock_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!("{}", serde_json::to_string_pretty(&slate).unwrap());
|
||||
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");
|
||||
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
|
||||
}
|
||||
|
||||
+4
-4
@@ -21,8 +21,8 @@ grin_wallet_util = { path = "../util", version = "5.4.0-alpha.1" }
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.4.0"
|
||||
grin_util = "5.4.0"
|
||||
#grin_core = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
|
||||
# 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" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { 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", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
|
||||
+12
-26
@@ -39,10 +39,11 @@ grin_wallet_config = { path = "../config", version = "5.4.0-alpha.1" }
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.4.0"
|
||||
grin_keychain = "5.4.0"
|
||||
grin_util = "5.4.0"
|
||||
grin_api = "5.4.0"
|
||||
#grin_core = "5.4.0"
|
||||
#grin_keychain = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
#grin_api = "5.4.0"
|
||||
#grin_chain = "5.4.0"
|
||||
|
||||
# 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_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_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_api = { 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", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_api = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_chain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
# grin_keychain = { path = "../../grin/keychain"}
|
||||
# grin_util = { path = "../../grin/util"}
|
||||
# grin_api = { path = "../../grin/api"}
|
||||
# grin_chain = { path = "../../grin/chain"}
|
||||
|
||||
#####
|
||||
|
||||
[dev-dependencies]
|
||||
ed25519-dalek = "1.0.0-pre.4"
|
||||
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"}
|
||||
|
||||
#####
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ pub fn create_wallet_proxy(
|
||||
test_dir: &str,
|
||||
) -> WalletProxy<
|
||||
'_,
|
||||
DefaultLCProvider<'_, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
> {
|
||||
@@ -117,7 +117,7 @@ pub fn create_local_wallet(
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
@@ -129,7 +129,7 @@ pub fn create_local_wallet(
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
@@ -156,7 +156,7 @@ pub fn open_local_wallet(
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
@@ -168,7 +168,7 @@ pub fn open_local_wallet(
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
|
||||
@@ -41,7 +41,7 @@ type Wallet = Arc<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
|
||||
@@ -45,7 +45,7 @@ fn test_wallet_tx_filtering(
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
|
||||
+4
-11
@@ -55,24 +55,22 @@ the default client only supports single-user - single recipient, an arbitrary nu
|
||||
|
||||
### 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:
|
||||
|
||||
```rust
|
||||
pub fn retrieve_outputs<T: ?Sized, C, K>(
|
||||
!·wallet: &mut T,
|
||||
!·wallet: &mut WalletBackend<C, K>,
|
||||
!·show_spent: bool,
|
||||
!·tx_id: Option<u32>,
|
||||
) -> Result<Vec<OutputData>, Error>
|
||||
where
|
||||
!·T: WalletBackend<C, K>,
|
||||
!·C: NodeClient,
|
||||
!·K: Keychain,
|
||||
{
|
||||
```
|
||||
|
||||
With `T` in this instance being a class that implements the `WalletBackend` trait, which is further parameterized with implementations of
|
||||
`NodeClient` and `Keychain`.
|
||||
Provided `WalletBackend` parameterized with implementations of `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`.
|
||||
The `Keychain` trait makes several assumptions about the underlying implementation, particularly that it will adhere to a
|
||||
@@ -81,9 +79,4 @@ The `Keychain` trait makes several assumptions about the underlying implementati
|
||||
There are two implementations of `NodeClient` within the code, the main version being the `HTTPNodeClient` found within `wallet/src/client.rs` and
|
||||
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
|
||||
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.
|
||||
matter of dropping in different `NodeClient` implementations.
|
||||
+12
-12
@@ -43,12 +43,12 @@ grin_wallet_libwallet = { path = "../libwallet", version = "5.4.0-alpha.1" }
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.4.0"
|
||||
grin_keychain = "5.4.0"
|
||||
grin_chain = "5.4.0"
|
||||
grin_util = "5.4.0"
|
||||
grin_api = "5.4.0"
|
||||
grin_store = "5.4.0"
|
||||
#grin_core = "5.4.0"
|
||||
#grin_keychain = "5.4.0"
|
||||
#grin_chain = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
#grin_api = "5.4.0"
|
||||
#grin_store = "5.4.0"
|
||||
|
||||
# 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" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_store = { 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", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_chain = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_api = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_store = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -28,14 +28,12 @@ use grin_api as api;
|
||||
use grin_chain as chain;
|
||||
use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_store as store;
|
||||
use grin_util as util;
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
|
||||
use grin_wallet_config as config;
|
||||
|
||||
mod adapters;
|
||||
mod backends;
|
||||
mod client_utils;
|
||||
mod error;
|
||||
mod lifecycle;
|
||||
@@ -47,7 +45,6 @@ pub use crate::adapters::{
|
||||
HttpSlateSender, PathToSlate, PathToSlatepack, SlateGetter, SlatePutter, SlateReceiver,
|
||||
SlateSender,
|
||||
};
|
||||
pub use crate::backends::{wallet_db_exists, LMDBBackend};
|
||||
pub use crate::error::Error;
|
||||
pub use crate::lifecycle::DefaultLCProvider;
|
||||
pub use crate::node_clients::HTTPNodeClient;
|
||||
@@ -57,35 +54,31 @@ use crate::keychain::{ExtKeychain, Keychain};
|
||||
use libwallet::{NodeClient, WalletInst, WalletLCProvider};
|
||||
|
||||
/// Main wallet instance
|
||||
pub struct DefaultWalletImpl<'a, C>
|
||||
pub struct DefaultWalletImpl<C>
|
||||
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
|
||||
C: NodeClient + 'a,
|
||||
C: NodeClient,
|
||||
{
|
||||
pub fn new(node_client: C) -> Result<Self, Error> {
|
||||
let lc_provider = DefaultLCProvider::new(node_client);
|
||||
Ok(DefaultWalletImpl {
|
||||
lc_provider: lc_provider,
|
||||
})
|
||||
Ok(DefaultWalletImpl { 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
|
||||
DefaultLCProvider<'a, C, ExtKeychain>: WalletLCProvider<'a, C, K>,
|
||||
DefaultLCProvider<C, ExtKeychain>: WalletLCProvider<'a, C, K>,
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
fn lc_provider(
|
||||
&mut self,
|
||||
) -> Result<&mut (dyn WalletLCProvider<'a, C, K> + 'a), libwallet::Error> {
|
||||
fn lc_provider(&mut self) -> Result<&mut dyn WalletLCProvider<'a, C, K>, libwallet::Error> {
|
||||
Ok(&mut self.lc_provider)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,12 @@ use crate::libwallet::{Error, NodeClient, WalletBackend, WalletInitStatus, Walle
|
||||
use crate::lifecycle::seed::WalletSeed;
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::ZeroingString;
|
||||
use crate::LMDBBackend;
|
||||
use grin_util::logger::LoggingConfig;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
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 {
|
||||
let sep = &MAIN_SEPARATOR.to_string();
|
||||
let path = path.replace("/", &sep);
|
||||
@@ -37,20 +36,20 @@ pub fn fmt_path(path: String) -> String {
|
||||
path
|
||||
}
|
||||
|
||||
pub struct DefaultLCProvider<'a, C, K>
|
||||
pub struct DefaultLCProvider<C, K>
|
||||
where
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
data_dir: String,
|
||||
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
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Create new provider
|
||||
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
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
@@ -217,8 +216,8 @@ where
|
||||
Error::Lifecycle("Error creating wallet seed (is mnemonic valid?)".to_owned())
|
||||
})?;
|
||||
info!("Wallet seed file created");
|
||||
let mut wallet: LMDBBackend<'a, C, K> =
|
||||
match LMDBBackend::new(&data_dir_name, self.node_client.clone()) {
|
||||
let mut wallet: WalletBackend<C, K> =
|
||||
match WalletBackend::new(&data_dir_name, self.node_client.clone()) {
|
||||
Err(e) => {
|
||||
let msg = format!("Error creating wallet: {}, Data Dir: {}", e, &data_dir_name);
|
||||
error!("{}", msg);
|
||||
@@ -247,8 +246,8 @@ where
|
||||
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
|
||||
data_dir_name.push(GRIN_WALLET_DIR);
|
||||
let data_dir_name = fmt_path(data_dir_name.to_str().unwrap().to_string());
|
||||
let mut wallet: LMDBBackend<'a, C, K> =
|
||||
match LMDBBackend::new(&data_dir_name, self.node_client.clone()) {
|
||||
let mut wallet: WalletBackend<C, K> =
|
||||
match WalletBackend::new(&data_dir_name, self.node_client.clone()) {
|
||||
Err(e) => {
|
||||
let msg = format!("Error opening wallet: {}, Data Dir: {}", e, &data_dir_name);
|
||||
return Err(Error::Lifecycle(msg));
|
||||
@@ -262,8 +261,8 @@ where
|
||||
.derive_keychain(global::is_testnet())
|
||||
.map_err(|_| Error::Lifecycle("Error deriving keychain".to_owned()))?;
|
||||
|
||||
let mask = wallet.set_keychain(Box::new(keychain), create_mask, use_test_rng)?;
|
||||
self.backend = Some(Box::new(wallet));
|
||||
let mask = wallet.set_keychain(keychain, create_mask, use_test_rng)?;
|
||||
self.backend = Some(wallet);
|
||||
Ok(mask)
|
||||
}
|
||||
|
||||
@@ -329,8 +328,7 @@ where
|
||||
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
|
||||
data_dir_name.push(GRIN_WALLET_DIR);
|
||||
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)
|
||||
.map_err(|_| Error::Lifecycle("Error opening wallet seed file".into()))?;
|
||||
let orig_mnemonic = orig_wallet_seed
|
||||
@@ -366,7 +364,7 @@ where
|
||||
.to_string();
|
||||
return Err(Error::Lifecycle(msg));
|
||||
}
|
||||
// Removin
|
||||
// Removing old file
|
||||
info!("Password change confirmed, removing old seed file.");
|
||||
fs::remove_file(backup_name).map_err(|e| Error::IO(e.to_string()))?;
|
||||
|
||||
@@ -383,13 +381,13 @@ where
|
||||
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() {
|
||||
None => {
|
||||
let msg = "Wallet has not been opened".into();
|
||||
Err(Error::Lifecycle(msg))
|
||||
}
|
||||
Some(_) => Ok(&mut *self.backend.as_mut().unwrap()),
|
||||
Some(b) => Ok(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ use ring::aead;
|
||||
use ring::pbkdf2;
|
||||
|
||||
use crate::keychain::{mnemonic, Keychain};
|
||||
use crate::lifecycle::default::fmt_path;
|
||||
use crate::util::{self, ToHex};
|
||||
use crate::Error;
|
||||
|
||||
@@ -56,13 +57,6 @@ impl WalletSeed {
|
||||
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> {
|
||||
let result = mnemonic::from_entropy(&self.0);
|
||||
match result {
|
||||
@@ -105,7 +99,7 @@ impl WalletSeed {
|
||||
pub fn seed_file_exists(data_file_dir: &str) -> Result<bool, Error> {
|
||||
let seed_file_path = &format!(
|
||||
"{}{}{}",
|
||||
Self::fmt_path(data_file_dir.to_string()),
|
||||
fmt_path(data_file_dir.to_string()),
|
||||
MAIN_SEPARATOR,
|
||||
SEED_FILE,
|
||||
);
|
||||
@@ -145,7 +139,7 @@ impl WalletSeed {
|
||||
) -> Result<(), Error> {
|
||||
let seed_file_path = &format!(
|
||||
"{}{}{}",
|
||||
Self::fmt_path(data_file_dir.to_string()),
|
||||
fmt_path(data_file_dir.to_string()),
|
||||
MAIN_SEPARATOR,
|
||||
SEED_FILE,
|
||||
);
|
||||
@@ -184,11 +178,11 @@ impl WalletSeed {
|
||||
|
||||
let seed_file_path = &format!(
|
||||
"{}{}{}",
|
||||
Self::fmt_path(data_file_dir.to_string()),
|
||||
fmt_path(data_file_dir.to_string()),
|
||||
MAIN_SEPARATOR,
|
||||
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);
|
||||
let exists = WalletSeed::seed_file_exists(&data_file_dir)?;
|
||||
if exists && !test_mode {
|
||||
@@ -219,7 +213,7 @@ impl WalletSeed {
|
||||
|
||||
let seed_file_path = &format!(
|
||||
"{}{}{}",
|
||||
Self::fmt_path(data_file_dir.to_string()),
|
||||
fmt_path(data_file_dir.to_string()),
|
||||
MAIN_SEPARATOR,
|
||||
SEED_FILE,
|
||||
);
|
||||
@@ -247,7 +241,7 @@ impl WalletSeed {
|
||||
pub fn delete_seed_file(data_file_dir: &str) -> Result<(), Error> {
|
||||
let seed_file_path = &format!(
|
||||
"{}{}{}",
|
||||
Self::fmt_path(data_file_dir.to_string()),
|
||||
fmt_path(data_file_dir.to_string()),
|
||||
MAIN_SEPARATOR,
|
||||
SEED_FILE,
|
||||
);
|
||||
|
||||
@@ -18,7 +18,6 @@ use crate::chain::Chain;
|
||||
use crate::core;
|
||||
use crate::core::core::{Output, Transaction, TxKernel};
|
||||
use crate::core::{consensus, global, pow};
|
||||
use crate::keychain;
|
||||
use crate::libwallet;
|
||||
use crate::libwallet::api_impl::{foreign, owner};
|
||||
use crate::libwallet::{
|
||||
@@ -28,6 +27,7 @@ use crate::util::secp::key::SecretKey;
|
||||
use crate::util::secp::pedersen;
|
||||
use crate::util::Mutex;
|
||||
use chrono::Duration;
|
||||
use grin_keychain::Keychain;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
@@ -36,7 +36,7 @@ mod testclient;
|
||||
pub use self::{testclient::LocalWalletClient, testclient::WalletProxy};
|
||||
|
||||
/// 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() {
|
||||
let block_height = chain.get_header_for_output(commit).unwrap().height;
|
||||
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
|
||||
fn get_kernel_local(
|
||||
chain: Arc<chain::Chain>,
|
||||
chain: Arc<Chain>,
|
||||
excess: &pedersen::Commitment,
|
||||
min_height: Option<u64>,
|
||||
max_height: Option<u64>,
|
||||
@@ -65,7 +65,7 @@ fn get_kernel_local(
|
||||
|
||||
/// get output listing traversing pmmr from local
|
||||
fn get_outputs_by_pmmr_index_local(
|
||||
chain: Arc<chain::Chain>,
|
||||
chain: Arc<Chain>,
|
||||
start_index: u64,
|
||||
end_index: Option<u64>,
|
||||
max: u64,
|
||||
@@ -86,7 +86,7 @@ fn get_outputs_by_pmmr_index_local(
|
||||
|
||||
/// get output listing in a given block range
|
||||
fn height_range_to_pmmr_indices_local(
|
||||
chain: Arc<chain::Chain>,
|
||||
chain: Arc<Chain>,
|
||||
start_index: u64,
|
||||
end_index: Option<u64>,
|
||||
) -> api::OutputListing {
|
||||
@@ -147,13 +147,13 @@ pub fn create_block_for_wallet<'a, L, C, K>(
|
||||
chain: &Chain,
|
||||
prev: core::core::BlockHeader,
|
||||
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>,
|
||||
) -> Result<core::core::Block, libwallet::Error>
|
||||
where
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: keychain::Keychain + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
// build block fees
|
||||
let fee_amt = txs.iter().map(|tx| tx.fee()).sum();
|
||||
@@ -166,7 +166,7 @@ where
|
||||
let coinbase_tx = {
|
||||
let mut w_lock = wallet.lock();
|
||||
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);
|
||||
Ok(block)
|
||||
@@ -178,13 +178,13 @@ where
|
||||
pub fn award_block_to_wallet<'a, L, C, K>(
|
||||
chain: &Chain,
|
||||
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>,
|
||||
) -> Result<(), libwallet::Error>
|
||||
where
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: keychain::Keychain + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let prev = chain.head_header().unwrap();
|
||||
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
|
||||
pub fn award_blocks_to_wallet<'a, L, C, K>(
|
||||
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>,
|
||||
number: usize,
|
||||
pause_between: bool,
|
||||
@@ -208,7 +208,7 @@ pub fn award_blocks_to_wallet<'a, L, C, K>(
|
||||
where
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: keychain::Keychain + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
for _ in 0..number {
|
||||
award_block_to_wallet(chain, &[], wallet.clone(), keychain_mask)?;
|
||||
@@ -231,7 +231,7 @@ pub fn send_to_dest<'a, L, C, K>(
|
||||
where
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: keychain::Keychain + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let slate = {
|
||||
let mut w_lock = wallet.lock();
|
||||
@@ -245,10 +245,10 @@ where
|
||||
selection_strategy_is_use_all: true,
|
||||
..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)?;
|
||||
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate)?;
|
||||
owner::finalize_tx(&mut **w, keychain_mask, &slate)?
|
||||
owner::tx_lock_outputs(w, keychain_mask, &slate)?;
|
||||
owner::finalize_tx(w, keychain_mask, &slate)?
|
||||
};
|
||||
let client = {
|
||||
let mut w_lock = wallet.lock();
|
||||
@@ -267,7 +267,7 @@ pub fn wallet_info<'a, L, C, K>(
|
||||
where
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: keychain::Keychain + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let (wallet_refreshed, wallet_info) =
|
||||
owner::retrieve_summary_info(wallet, keychain_mask, &None, true, 1)?;
|
||||
|
||||
@@ -70,7 +70,7 @@ where
|
||||
String,
|
||||
(
|
||||
Sender<WalletProxyMessage>,
|
||||
Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
|
||||
Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
|
||||
Option<SecretKey>,
|
||||
),
|
||||
>,
|
||||
@@ -100,14 +100,15 @@ where
|
||||
genesis_block,
|
||||
pow::verify_size,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let (tx, rx) = channel();
|
||||
WalletProxy {
|
||||
chain_dir: chain_dir.to_owned(),
|
||||
chain: Arc::new(c),
|
||||
tx: tx,
|
||||
rx: rx,
|
||||
tx,
|
||||
rx,
|
||||
wallets: HashMap::new(),
|
||||
running: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
@@ -118,7 +119,7 @@ where
|
||||
&mut self,
|
||||
addr: &str,
|
||||
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>,
|
||||
) {
|
||||
self.wallets
|
||||
@@ -218,8 +219,7 @@ where
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
let mask = wallet.2.clone();
|
||||
// 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) => {
|
||||
return Ok(WalletProxyMessage {
|
||||
sender_id: m.dest,
|
||||
|
||||
@@ -46,10 +46,10 @@ grin_wallet_config = { path = "../config", version = "5.4.0-alpha.1" }
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_core = "5.4.0"
|
||||
grin_keychain = "5.4.0"
|
||||
grin_util = "5.4.0"
|
||||
grin_store = "5.4.0"
|
||||
#grin_core = "5.4.0"
|
||||
#grin_keychain = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
#grin_store = "5.4.0"
|
||||
|
||||
# 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" }
|
||||
|
||||
# For bleeding edge
|
||||
# grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
# grin_store = { 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", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_util = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
grin_store = { git = "https://github.com/mimblewimble/grin", rev = "110e0e143fdf188b69ac56c2f378c4121859703b" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
@@ -70,4 +70,4 @@ grin_store = "5.4.0"
|
||||
# grin_util = { path = "../../grin/util"}
|
||||
# grin_store = { path = "../../grin/store"}
|
||||
|
||||
#####
|
||||
#####
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
//! Generic implementation of owner API functions
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use super::owner::tx_lock_outputs;
|
||||
use crate::api_impl::owner::{check_ttl, post_tx};
|
||||
use crate::backend::WalletBackend;
|
||||
use crate::grin_core::core::FeeFields;
|
||||
use crate::grin_keychain::Keychain;
|
||||
use crate::grin_util::secp::key::SecretKey;
|
||||
@@ -23,11 +25,8 @@ use crate::internal::{selection, tx, updater};
|
||||
use crate::slate_versions::SlateVersion;
|
||||
use crate::{
|
||||
address, BlockFees, CbData, Error, NodeClient, Slate, SlateState, TxLogEntryType, VersionInfo,
|
||||
WalletBackend,
|
||||
};
|
||||
|
||||
use super::owner::tx_lock_outputs;
|
||||
|
||||
const FOREIGN_API_VERSION: u16 = 2;
|
||||
|
||||
/// Return the version info
|
||||
@@ -39,32 +38,30 @@ pub fn check_version() -> VersionInfo {
|
||||
}
|
||||
|
||||
/// Build a coinbase transaction
|
||||
pub fn build_coinbase<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn build_coinbase<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
block_fees: &BlockFees,
|
||||
test_mode: bool,
|
||||
) -> Result<CbData, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
updater::build_coinbase(&mut *w, keychain_mask, block_fees, test_mode)
|
||||
}
|
||||
|
||||
/// Receive a tx as recipient
|
||||
pub fn receive_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn receive_tx<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
dest_acct_name: Option<&str>,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut ret_slate = slate.clone();
|
||||
check_ttl(w, &ret_slate)?;
|
||||
@@ -133,16 +130,15 @@ where
|
||||
}
|
||||
|
||||
/// Receive a tx that this wallet has issued
|
||||
pub fn finalize_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn finalize_tx<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
post_automatically: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut sl = slate.clone();
|
||||
let mut context = w.get_private_context(keychain_mask, sl.id.as_bytes())?;
|
||||
|
||||
@@ -31,15 +31,14 @@ use crate::api_impl::owner_updater::StatusMessage;
|
||||
use crate::grin_keychain::{BlindingFactor, Identifier, Keychain, SwitchCommitmentType};
|
||||
use crate::internal::{keys, scan, selection, tx, updater};
|
||||
use crate::slate::{PaymentInfo, Slate, SlateState};
|
||||
use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletBackend, WalletInfo};
|
||||
use crate::Error;
|
||||
use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletInfo};
|
||||
use crate::{
|
||||
address,
|
||||
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,
|
||||
SlatepackAddress, Slatepacker, SlatepackerArgs, TxLogEntryType, ViewWallet, WalletInitStatus,
|
||||
WalletInst, WalletLCProvider,
|
||||
SlatepackAddress, Slatepacker, SlatepackerArgs, TxLogEntryType, ViewWallet, WalletBackend,
|
||||
WalletInitStatus, WalletInst, WalletLCProvider,
|
||||
};
|
||||
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
@@ -52,35 +51,32 @@ use std::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
keys::accounts(&mut *w)
|
||||
}
|
||||
|
||||
/// new account path
|
||||
pub fn create_account_path<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn create_account_path<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
label: &str,
|
||||
) -> Result<Identifier, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
keys::new_acct_path(&mut *w, keychain_mask, label)
|
||||
}
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
w.set_parent_key_id_by_name(label)
|
||||
}
|
||||
@@ -196,7 +192,7 @@ where
|
||||
dec_key: None,
|
||||
});
|
||||
let slatepack = packer.deser_slatepack(slatepack.as_bytes(), true)?;
|
||||
return packer.get_slate(&slatepack);
|
||||
packer.get_slate(&slatepack)
|
||||
} else {
|
||||
for index in secret_indices {
|
||||
let dec_key = Some(get_slatepack_secret_key(
|
||||
@@ -218,11 +214,11 @@ where
|
||||
};
|
||||
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"
|
||||
.to_owned(),
|
||||
)
|
||||
.into());
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,13 +298,7 @@ where
|
||||
|
||||
Ok((
|
||||
validated,
|
||||
updater::retrieve_outputs(
|
||||
&mut **w,
|
||||
keychain_mask,
|
||||
include_spent,
|
||||
tx_id,
|
||||
Some(&parent_key_id),
|
||||
)?,
|
||||
updater::retrieve_outputs(w, keychain_mask, include_spent, tx_id, Some(&parent_key_id))?,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -341,7 +331,7 @@ where
|
||||
wallet_lock!(wallet_inst, w);
|
||||
let parent_key_id = w.parent_key_id();
|
||||
let txs = updater::retrieve_txs(
|
||||
&mut **w,
|
||||
w,
|
||||
tx_id,
|
||||
tx_slate_id,
|
||||
query_args,
|
||||
@@ -378,7 +368,7 @@ where
|
||||
|
||||
wallet_lock!(wallet_inst, w);
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -469,8 +459,8 @@ where
|
||||
}
|
||||
};
|
||||
Ok(PaymentProof {
|
||||
amount: amount,
|
||||
excess: excess,
|
||||
amount,
|
||||
excess,
|
||||
recipient_address: SlatepackAddress::new(&proof.receiver_address),
|
||||
recipient_sig: r_sig,
|
||||
sender_address: SlatepackAddress::new(&proof.sender_address),
|
||||
@@ -479,16 +469,15 @@ where
|
||||
}
|
||||
|
||||
/// Initiate tx as sender
|
||||
pub fn init_send_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn init_send_tx<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
args: InitTxArgs,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = match &args.src_acct_name {
|
||||
Some(d) => {
|
||||
@@ -595,16 +584,15 @@ where
|
||||
}
|
||||
|
||||
/// Initiate a transaction as the recipient (invoicing)
|
||||
pub fn issue_invoice_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn issue_invoice_tx<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
args: IssueInvoiceTxArgs,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = match args.dest_acct_name {
|
||||
Some(d) => {
|
||||
@@ -648,17 +636,16 @@ where
|
||||
|
||||
/// Receive an invoice tx, essentially adding inputs to whatever
|
||||
/// output was specified
|
||||
pub fn process_invoice_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn process_invoice_tx<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
args: InitTxArgs,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut ret_slate = slate.clone();
|
||||
check_ttl(w, &ret_slate)?;
|
||||
@@ -769,15 +756,14 @@ where
|
||||
}
|
||||
|
||||
/// Lock sender outputs
|
||||
pub fn tx_lock_outputs<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn tx_lock_outputs<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let context = w.get_private_context(keychain_mask, slate.id.as_bytes())?;
|
||||
let mut excess_override = None;
|
||||
@@ -806,15 +792,14 @@ where
|
||||
}
|
||||
|
||||
/// Finalize slate
|
||||
pub fn finalize_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn finalize_tx<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
foreign_finalize(w, keychain_mask, slate, false)
|
||||
}
|
||||
@@ -844,20 +829,19 @@ where
|
||||
}
|
||||
wallet_lock!(wallet_inst, w);
|
||||
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
|
||||
/// crashes if stored tx has total fees exceeding 2^40 nanogrin
|
||||
pub fn get_stored_tx<'a, T: ?Sized, C, K>(
|
||||
w: &T,
|
||||
pub fn get_stored_tx<C, K>(
|
||||
w: &WalletBackend<C, K>,
|
||||
tx_id: Option<u32>,
|
||||
slate_id: Option<&Uuid>,
|
||||
) -> Result<Option<Slate>, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut uuid = None;
|
||||
if let Some(i) = tx_id {
|
||||
@@ -896,9 +880,9 @@ where
|
||||
|
||||
/// Posts a transaction to the chain
|
||||
/// 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
|
||||
C: NodeClient + 'a,
|
||||
C: NodeClient,
|
||||
{
|
||||
let res = client.post_tx(tx, fluff);
|
||||
if let Err(e) = res {
|
||||
@@ -930,8 +914,7 @@ where
|
||||
let is_hex = rewind_hash.chars().all(|c| c.is_ascii_hexdigit());
|
||||
let rewind_hash = rewind_hash.to_lowercase();
|
||||
if !(is_hex && rewind_hash.len() == 64) {
|
||||
let msg = format!("Invalid Rewind Hash");
|
||||
return Err(Error::RewindHash(msg));
|
||||
return Err(Error::RewindHash("Invalid Rewind Hash".to_string()));
|
||||
}
|
||||
|
||||
let tip = {
|
||||
@@ -939,10 +922,7 @@ where
|
||||
w.w2n_client().get_chain_tip()?
|
||||
};
|
||||
|
||||
let start_height = match start_height {
|
||||
Some(h) => h,
|
||||
None => 1,
|
||||
};
|
||||
let start_height = start_height.unwrap_or_else(|| 1);
|
||||
|
||||
let info = scan::scan_rewind_hash(
|
||||
wallet_inst,
|
||||
@@ -1030,10 +1010,7 @@ where
|
||||
}),
|
||||
Err(_) => {
|
||||
let outputs = retrieve_outputs(wallet_inst, keychain_mask, &None, true, false, None)?;
|
||||
let height = match outputs.1.iter().map(|m| m.output.height).max() {
|
||||
Some(height) => height,
|
||||
None => 0,
|
||||
};
|
||||
let height = outputs.1.iter().map(|m| m.output.height).max().unwrap_or_else(|| 0);
|
||||
Ok(NodeHeightResult {
|
||||
height,
|
||||
header_hash: "".to_owned(),
|
||||
@@ -1093,7 +1070,7 @@ where
|
||||
// Step 2: Update outstanding transactions with no change outputs by kernel
|
||||
let mut txs = {
|
||||
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)?;
|
||||
if !result {
|
||||
@@ -1186,7 +1163,7 @@ where
|
||||
if tip.0 >= e {
|
||||
wallet_lock!(wallet_inst, w);
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1195,11 +1172,10 @@ where
|
||||
}
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// Refuse if TTL is expired
|
||||
let last_confirmed_height = w.last_confirmed_height()?;
|
||||
@@ -1265,7 +1241,7 @@ where
|
||||
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 d_skey = match DalekSecretKey::from_bytes(&sec_key.0) {
|
||||
Ok(k) => k,
|
||||
@@ -1294,7 +1270,7 @@ where
|
||||
{
|
||||
wallet_lock!(wallet_inst, w);
|
||||
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),
|
||||
Err(e) => {
|
||||
if let Error::InvalidKeychainMask = e {
|
||||
@@ -1361,16 +1337,15 @@ where
|
||||
}
|
||||
|
||||
/// Builds an output for the wallet's next available key
|
||||
pub fn build_output<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn build_output<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
features: OutputFeatures,
|
||||
amount: u64,
|
||||
) -> Result<BuiltOutput, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let k = w.keychain(keychain_mask)?;
|
||||
|
||||
@@ -1394,14 +1369,14 @@ where
|
||||
|
||||
Ok(BuiltOutput {
|
||||
blind: BlindingFactor::from_secret_key(blind),
|
||||
key_id: key_id,
|
||||
output: output,
|
||||
key_id,
|
||||
output,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create MXMixnet request
|
||||
pub fn create_mwixnet_req<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
pub fn create_mwixnet_req<C, K>(
|
||||
w: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
params: &MixnetReqCreationParams,
|
||||
commitment: &Commitment,
|
||||
@@ -1409,9 +1384,8 @@ pub fn create_mwixnet_req<'a, T: ?Sized, C, K>(
|
||||
use_test_rng: bool,
|
||||
) -> Result<SwapReq, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
let keychain = w.keychain(keychain_mask)?;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -16,32 +16,31 @@
|
||||
use crate::error::Error;
|
||||
use crate::grin_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
|
||||
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
|
||||
pub fn next_available_key<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn next_available_key<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
) -> Result<Identifier, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let child = wallet.next_child(keychain_mask)?;
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
/// Retrieve an existing key from a wallet
|
||||
pub fn retrieve_existing_key<'a, T: ?Sized, C, K>(
|
||||
wallet: &T,
|
||||
pub fn retrieve_existing_key<C, K>(
|
||||
wallet: &WalletBackend<C, K>,
|
||||
key_id: Identifier,
|
||||
mmr_index: Option<u64>,
|
||||
) -> Result<(Identifier, u32), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let existing = wallet.get(&key_id, &mmr_index)?;
|
||||
let key_id = existing.key_id.clone();
|
||||
@@ -50,25 +49,23 @@ where
|
||||
}
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
Ok(wallet.acct_path_iter().collect())
|
||||
}
|
||||
|
||||
/// Adds an new parent account path with a given label
|
||||
pub fn new_acct_path<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
/// Adds a new parent account path with a given label
|
||||
pub fn new_acct_path<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
label: &str,
|
||||
) -> Result<Identifier, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let label = label.to_owned();
|
||||
if wallet.acct_path_iter().any(|l| l.label == label) {
|
||||
@@ -94,7 +91,7 @@ where
|
||||
};
|
||||
|
||||
let save_path = AcctPathMapping {
|
||||
label: label,
|
||||
label,
|
||||
path: return_id.clone(),
|
||||
};
|
||||
|
||||
@@ -105,20 +102,19 @@ where
|
||||
}
|
||||
|
||||
/// Adds/sets a particular account path with a given label
|
||||
pub fn set_acct_path<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn set_acct_path<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
label: &str,
|
||||
path: &Identifier,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let label = label.to_owned();
|
||||
let save_path = AcctPathMapping {
|
||||
label: label,
|
||||
label,
|
||||
path: path.clone(),
|
||||
};
|
||||
|
||||
|
||||
@@ -66,14 +66,14 @@ struct RestoredTxStats {
|
||||
pub num_outputs: usize,
|
||||
}
|
||||
|
||||
fn identify_utxo_outputs<'a, K>(
|
||||
fn identify_utxo_outputs<K>(
|
||||
keychain: &K,
|
||||
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
|
||||
status_send_channel: &Option<Sender<StatusMessage>>,
|
||||
percentage_complete: u8,
|
||||
) -> Result<Vec<OutputResult>, Error>
|
||||
where
|
||||
K: Keychain + 'a,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut wallet_outputs: Vec<OutputResult> = Vec::new();
|
||||
|
||||
@@ -137,7 +137,7 @@ where
|
||||
n_child: key_id.to_path().last_path_index(),
|
||||
value: amount,
|
||||
height: *height,
|
||||
lock_height: lock_height,
|
||||
lock_height,
|
||||
is_coinbase: *is_coinbase,
|
||||
mmr_index: *mmr_index,
|
||||
});
|
||||
@@ -145,7 +145,7 @@ where
|
||||
Ok(wallet_outputs)
|
||||
}
|
||||
|
||||
fn collect_chain_outputs_rewind_hash<'a, C>(
|
||||
fn collect_chain_outputs_rewind_hash<C>(
|
||||
client: C,
|
||||
rewind_hash: String,
|
||||
start_index: u64,
|
||||
@@ -153,13 +153,13 @@ fn collect_chain_outputs_rewind_hash<'a, C>(
|
||||
status_send_channel: &Option<Sender<StatusMessage>>,
|
||||
) -> Result<ViewWallet, Error>
|
||||
where
|
||||
C: NodeClient + 'a,
|
||||
C: NodeClient,
|
||||
{
|
||||
let batch_size = 1000;
|
||||
let start_index_stat = start_index;
|
||||
let mut start_index = start_index;
|
||||
let mut vw = ViewWallet {
|
||||
rewind_hash: rewind_hash,
|
||||
rewind_hash,
|
||||
output_result: vec![],
|
||||
total_balance: 0,
|
||||
last_pmmr_index: 0,
|
||||
@@ -198,7 +198,7 @@ where
|
||||
continue;
|
||||
}
|
||||
|
||||
let info = info.unwrap();
|
||||
let info = info?;
|
||||
vw.total_balance += info.value;
|
||||
let lock_height = if *is_coinbase {
|
||||
*height + global::coinbase_maturity()
|
||||
@@ -212,7 +212,7 @@ where
|
||||
height: *height,
|
||||
mmr_index: *mmr_index,
|
||||
is_coinbase: *is_coinbase,
|
||||
lock_height: lock_height,
|
||||
lock_height,
|
||||
};
|
||||
|
||||
vw.output_result.push(output_info);
|
||||
@@ -285,7 +285,6 @@ where
|
||||
Ok((result_vec, last_retrieved_return_index, perc_complete))
|
||||
}
|
||||
|
||||
///
|
||||
fn restore_missing_output<'a, L, C, K>(
|
||||
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
@@ -384,7 +383,7 @@ where
|
||||
wallet_lock!(wallet_inst, w);
|
||||
let updated_tx_entry = if output.tx_log_entry.is_some() {
|
||||
let entries = updater::retrieve_txs(
|
||||
&mut **w,
|
||||
w,
|
||||
output.tx_log_entry,
|
||||
None,
|
||||
None,
|
||||
@@ -536,7 +535,7 @@ where
|
||||
// Now, get all outputs owned by this wallet (regardless of account)
|
||||
let wallet_outputs = {
|
||||
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![];
|
||||
@@ -656,7 +655,7 @@ where
|
||||
if let Some(ref s) = status_send_channel {
|
||||
let _ = s.send(StatusMessage::Scanning(msg, perc_complete));
|
||||
}
|
||||
keys::set_acct_path(&mut **w, keychain_mask, &label, path)?;
|
||||
keys::set_acct_path(w, keychain_mask, &label, path)?;
|
||||
acct_index += 1;
|
||||
}
|
||||
let current_child_index = w.current_child_index(&path)?;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
//! Selection of inputs for building transactions
|
||||
|
||||
use crate::address;
|
||||
use crate::error::Error;
|
||||
use crate::grin_core::core::amount_to_hr_string;
|
||||
use crate::grin_core::libtx::{
|
||||
@@ -29,6 +28,7 @@ use crate::internal::keys;
|
||||
use crate::slate::Slate;
|
||||
use crate::types::*;
|
||||
use crate::util::OnionV3Address;
|
||||
use crate::{address, WalletBackend};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
|
||||
@@ -37,8 +37,8 @@ use std::convert::TryInto;
|
||||
/// and saves the private wallet identifiers of our selected outputs
|
||||
/// into our transaction context
|
||||
|
||||
pub fn build_send_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn build_send_tx<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain: &K,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
@@ -54,9 +54,8 @@ pub fn build_send_tx<'a, T: ?Sized, C, K>(
|
||||
amount_includes_fee: bool,
|
||||
) -> Result<Context, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let (elems, inputs, change_amounts_derivations, fee) = select_send_tx(
|
||||
wallet,
|
||||
@@ -73,7 +72,7 @@ where
|
||||
)?;
|
||||
if amount_includes_fee {
|
||||
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
|
||||
/// change outputs and tx log entry
|
||||
pub fn lock_tx_context<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn lock_tx_context<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
current_height: u64,
|
||||
@@ -128,9 +127,8 @@ pub fn lock_tx_context<'a, T: ?Sized, C, K>(
|
||||
excess_override: Option<pedersen::Commitment>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut output_commits: HashMap<Identifier, (Option<String>, u64)> = HashMap::new();
|
||||
// Store cached commits before locking wallet
|
||||
@@ -178,7 +176,7 @@ where
|
||||
let mut amount_debited = 0;
|
||||
t.num_inputs = lock_inputs.len();
|
||||
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);
|
||||
amount_debited += coin.value;
|
||||
batch.lock_output(&mut coin)?;
|
||||
@@ -221,11 +219,11 @@ where
|
||||
root_key_id: parent_key_id.clone(),
|
||||
key_id: id.clone(),
|
||||
n_child: id.to_path().last_path_index(),
|
||||
commit: commit,
|
||||
commit,
|
||||
mmr_index: None,
|
||||
value: change_amount,
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: height,
|
||||
height,
|
||||
lock_height: 0,
|
||||
is_coinbase: false,
|
||||
tx_log_entry: Some(log_id),
|
||||
@@ -245,8 +243,8 @@ where
|
||||
/// Creates a new output in the wallet for the recipient,
|
||||
/// returning the key of the fresh output
|
||||
/// Also creates a new transaction containing the output
|
||||
pub fn build_recipient_output<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn build_recipient_output<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
@@ -255,9 +253,8 @@ pub fn build_recipient_output<'a, T: ?Sized, C, K>(
|
||||
is_initiator: bool,
|
||||
) -> Result<(Identifier, Context, TxLogEntry), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// Create a potential output for this transaction
|
||||
let key_id = keys::next_available_key(wallet, keychain_mask).unwrap();
|
||||
@@ -300,10 +297,10 @@ where
|
||||
key_id: key_id_inner.clone(),
|
||||
mmr_index: None,
|
||||
n_child: key_id_inner.to_path().last_path_index(),
|
||||
commit: commit,
|
||||
commit,
|
||||
value: amount,
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: height,
|
||||
height,
|
||||
lock_height: 0,
|
||||
is_coinbase: false,
|
||||
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
|
||||
/// wallet and the amount to send. Handles reading through the wallet data file,
|
||||
/// selecting outputs to spend and building the change.
|
||||
pub fn select_send_tx<'a, T: ?Sized, C, K, B>(
|
||||
wallet: &mut T,
|
||||
pub fn select_send_tx<C, K, B>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
amount: u64,
|
||||
amount_includes_fee: bool,
|
||||
@@ -339,9 +336,8 @@ pub fn select_send_tx<'a, T: ?Sized, C, K, B>(
|
||||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let (coins, _total, amount, fee) = select_coins_and_fee(
|
||||
@@ -371,8 +367,8 @@ where
|
||||
}
|
||||
|
||||
/// Select outputs and calculating fee.
|
||||
pub fn select_coins_and_fee<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn select_coins_and_fee<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
amount: u64,
|
||||
amount_includes_fee: bool,
|
||||
current_height: u64,
|
||||
@@ -391,9 +387,8 @@ pub fn select_coins_and_fee<'a, T: ?Sized, C, K>(
|
||||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// select some spendable coins from the wallet
|
||||
let (max_outputs, mut coins) = select_coins(
|
||||
@@ -407,7 +402,7 @@ where
|
||||
);
|
||||
|
||||
// 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
|
||||
|
||||
// First attempt to spend without change
|
||||
@@ -422,8 +417,8 @@ where
|
||||
return Err(Error::NotEnoughFunds {
|
||||
available: 0,
|
||||
available_disp: amount_to_hr_string(0, false),
|
||||
needed: amount_with_fee as u64,
|
||||
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
||||
needed: amount_with_fee,
|
||||
needed_disp: amount_to_hr_string(amount_with_fee, false),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -432,8 +427,8 @@ where
|
||||
return Err(Error::NotEnoughFunds {
|
||||
available: total,
|
||||
available_disp: amount_to_hr_string(total, false),
|
||||
needed: amount_with_fee as u64,
|
||||
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
||||
needed: amount_with_fee,
|
||||
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
|
||||
if coins.len() == max_outputs {
|
||||
return Err(Error::NotEnoughFunds {
|
||||
available: total as u64,
|
||||
available: total,
|
||||
available_disp: amount_to_hr_string(total, false),
|
||||
needed: amount_with_fee as u64,
|
||||
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
||||
needed: amount_with_fee,
|
||||
needed_disp: amount_to_hr_string(amount_with_fee, false),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -483,7 +478,7 @@ where
|
||||
// be reduced, to accommodate the fee.
|
||||
let new_amount = match amount_includes_fee {
|
||||
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,
|
||||
};
|
||||
@@ -491,9 +486,9 @@ where
|
||||
}
|
||||
|
||||
/// 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],
|
||||
wallet: &mut T,
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
amount: u64,
|
||||
fee: u64,
|
||||
@@ -507,9 +502,8 @@ pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
|
||||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
let mut parts = vec![];
|
||||
@@ -554,7 +548,7 @@ where
|
||||
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));
|
||||
parts.push(build::output(change_amount, change_key));
|
||||
@@ -569,10 +563,8 @@ where
|
||||
/// max_outputs). Alternative strategy is to spend smallest outputs first
|
||||
/// but only as many as necessary. When we introduce additional strategies
|
||||
/// 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<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn select_coins<C, K>(
|
||||
wallet: &WalletBackend<C, K>,
|
||||
amount: u64,
|
||||
current_height: u64,
|
||||
minimum_confirmations: u64,
|
||||
@@ -582,9 +574,8 @@ pub fn select_coins<'a, T: ?Sized, C, K>(
|
||||
) -> (usize, Vec<OutputData>)
|
||||
// max_outputs_available, Outputs
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// first find all eligible outputs based on number of confirmations
|
||||
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
|
||||
/// change outputs and tx log entry
|
||||
/// Remove the explicitly stored excess
|
||||
pub fn repopulate_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn repopulate_tx<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
context: &Context,
|
||||
update_fee: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// restore the original amount, fee
|
||||
slate.amount = context.amount;
|
||||
|
||||
@@ -26,10 +26,10 @@ use crate::grin_util::secp::pedersen;
|
||||
use crate::grin_util::Mutex;
|
||||
use crate::internal::{selection, updater};
|
||||
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::InitTxArgs;
|
||||
use crate::{address, Error};
|
||||
use crate::{InitTxArgs, WalletBackend};
|
||||
use ed25519_dalek::Keypair as DalekKeypair;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
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
|
||||
/// the transaction (sender(s), receiver(s))
|
||||
pub fn new_tx_slate<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn new_tx_slate<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
amount: u64,
|
||||
is_invoice: bool,
|
||||
num_participants: u8,
|
||||
@@ -53,9 +53,8 @@ pub fn new_tx_slate<'a, T: ?Sized, C, K>(
|
||||
ttl_blocks: Option<u64>,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let current_height = wallet.w2n_client().get_chain_tip()?.0;
|
||||
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
|
||||
pub fn estimate_send_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn estimate_send_tx<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
amount: u64,
|
||||
amount_includes_fee: bool,
|
||||
@@ -110,9 +109,8 @@ pub fn estimate_send_tx<'a, T: ?Sized, C, K>(
|
||||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// Get lock height
|
||||
let current_height = wallet.w2n_client().get_chain_tip()?.0;
|
||||
@@ -141,8 +139,8 @@ where
|
||||
}
|
||||
|
||||
/// Add inputs to the slate (effectively becoming the sender)
|
||||
pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn add_inputs_to_slate<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
@@ -156,9 +154,8 @@ pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>(
|
||||
amount_includes_fee: bool,
|
||||
) -> Result<Context, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// sender should always refresh outputs
|
||||
updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?;
|
||||
@@ -207,8 +204,8 @@ where
|
||||
}
|
||||
|
||||
/// Add receiver output to the slate
|
||||
pub fn add_output_to_slate<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn add_output_to_slate<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
@@ -217,9 +214,8 @@ pub fn add_output_to_slate<'a, T: ?Sized, C, K>(
|
||||
use_test_rng: bool,
|
||||
) -> Result<Context, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
// create an output using the amount in the slate
|
||||
@@ -252,8 +248,8 @@ where
|
||||
}
|
||||
|
||||
/// Create context, without adding inputs to slate
|
||||
pub fn create_late_lock_context<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn create_late_lock_context<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
@@ -262,9 +258,8 @@ pub fn create_late_lock_context<'a, T: ?Sized, C, K>(
|
||||
use_test_rng: bool,
|
||||
) -> Result<Context, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// sender should always refresh outputs
|
||||
updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?;
|
||||
@@ -300,16 +295,15 @@ where
|
||||
}
|
||||
|
||||
/// Complete a transaction
|
||||
pub fn complete_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn complete_tx<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
context: &Context,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// when self sending invoice tx, use initiator nonce to finalize
|
||||
let (sec_key, sec_nonce) = {
|
||||
@@ -333,17 +327,16 @@ where
|
||||
}
|
||||
|
||||
/// Rollback outputs associated with a transaction in the wallet
|
||||
pub fn cancel_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn cancel_tx<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
parent_key_id: &Identifier,
|
||||
tx_id: Option<u32>,
|
||||
tx_slate_id: Option<Uuid>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut tx_id_string = String::new();
|
||||
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)
|
||||
pub fn update_stored_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn update_stored_tx<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
context: &Context,
|
||||
slate: &Slate,
|
||||
is_invoiced: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// finalize command
|
||||
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 {
|
||||
let derivation_index = match context.payment_proof_derivation_index {
|
||||
Some(i) => i,
|
||||
None => 0,
|
||||
};
|
||||
let derivation_index = context.payment_proof_derivation_index.unwrap_or_else(|| 0);
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
let parent_key_id = wallet.parent_key_id();
|
||||
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
|
||||
pub fn verify_slate_payment_proof<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn verify_slate_payment_proof<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
parent_key_id: &Identifier,
|
||||
context: &Context,
|
||||
slate: &Slate,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let tx_vec = updater::retrieve_txs(
|
||||
wallet,
|
||||
|
||||
@@ -30,28 +30,25 @@ use crate::grin_util::secp::key::SecretKey;
|
||||
use crate::grin_util::secp::pedersen;
|
||||
use crate::grin_util::static_secp_instance;
|
||||
use crate::internal::keys;
|
||||
use crate::types::{
|
||||
NodeClient, OutputData, OutputStatus, TxLogEntry, TxLogEntryType, WalletBackend, WalletInfo,
|
||||
};
|
||||
use crate::types::{NodeClient, OutputData, OutputStatus, TxLogEntry, TxLogEntryType, WalletInfo};
|
||||
use crate::{
|
||||
BlockFees, CbData, OutputCommitMapping, RetrieveTxQueryArgs, RetrieveTxQuerySortField,
|
||||
RetrieveTxQuerySortOrder,
|
||||
RetrieveTxQuerySortOrder, WalletBackend,
|
||||
};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
|
||||
/// Retrieve all of the outputs (doesn't attempt to update from node)
|
||||
pub fn retrieve_outputs<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
/// Retrieve all the outputs (don't attempt to update from node)
|
||||
pub fn retrieve_outputs<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
show_spent: bool,
|
||||
tx_id: Option<u32>,
|
||||
parent_key_id: Option<&Identifier>,
|
||||
) -> Result<Vec<OutputCommitMapping>, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// just read the wallet here, no need for a write lock
|
||||
let mut outputs = wallet
|
||||
@@ -94,15 +91,14 @@ where
|
||||
}
|
||||
|
||||
/// Apply advanced filtering to resultset from retrieve_txs below
|
||||
pub fn apply_advanced_tx_list_filtering<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn apply_advanced_tx_list_filtering<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
parent_key_id: Option<&Identifier>,
|
||||
query_args: &RetrieveTxQueryArgs,
|
||||
) -> Vec<TxLogEntry>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// Apply simple bool, GTE or LTE fields
|
||||
let txs_iter: Box<dyn Iterator<Item = TxLogEntry>> = Box::new(
|
||||
@@ -329,10 +325,10 @@ where
|
||||
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
|
||||
pub fn retrieve_txs<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn retrieve_txs<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
tx_id: Option<u32>,
|
||||
tx_slate_id: Option<Uuid>,
|
||||
query_args: Option<RetrieveTxQueryArgs>,
|
||||
@@ -340,9 +336,8 @@ pub fn retrieve_txs<'a, T: ?Sized, C, K>(
|
||||
outstanding_only: bool,
|
||||
) -> Result<Vec<TxLogEntry>, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut txs;
|
||||
// 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
|
||||
/// from a node
|
||||
pub fn refresh_outputs<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn refresh_outputs<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
parent_key_id: &Identifier,
|
||||
update_all: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let height = wallet.w2n_client().get_chain_tip()?.0;
|
||||
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
|
||||
/// and a list of outputs we want to query the node for
|
||||
pub fn map_wallet_outputs<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn map_wallet_outputs<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
parent_key_id: &Identifier,
|
||||
update_all: bool,
|
||||
) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut wallet_outputs = HashMap::new();
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
@@ -453,17 +446,16 @@ where
|
||||
}
|
||||
|
||||
/// Cancel transaction and associated outputs
|
||||
pub fn cancel_tx_and_outputs<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn cancel_tx_and_outputs<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
mut tx: TxLogEntry,
|
||||
outputs: Vec<OutputData>,
|
||||
parent_key_id: &Identifier,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut batch = wallet.batch(keychain_mask)?;
|
||||
|
||||
@@ -490,8 +482,8 @@ where
|
||||
}
|
||||
|
||||
/// Apply refreshed API output data to the wallet
|
||||
pub fn apply_api_outputs<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn apply_api_outputs<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>,
|
||||
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,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// 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.
|
||||
@@ -590,6 +581,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let mut txs_to_save = vec![];
|
||||
for mut tx in batch.tx_log_iter() {
|
||||
if reverted_kernels.contains(&tx.id) && tx.parent_key_id == *parent_key_id {
|
||||
tx.tx_type = TxLogEntryType::TxReverted;
|
||||
@@ -598,9 +590,12 @@ where
|
||||
(now - t).to_std().ok()
|
||||
});
|
||||
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)?;
|
||||
@@ -612,17 +607,16 @@ where
|
||||
|
||||
/// Builds a single api query to retrieve the latest output data from the node.
|
||||
/// So we can refresh the local wallet outputs.
|
||||
fn refresh_output_state<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
fn refresh_output_state<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
height: u64,
|
||||
parent_key_id: &Identifier,
|
||||
update_all: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
debug!("Refreshing wallet outputs");
|
||||
|
||||
@@ -654,16 +648,15 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_reverted_kernels<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
fn find_reverted_kernels<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>, Option<u32>, bool)>,
|
||||
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
|
||||
parent_key_id: &Identifier,
|
||||
) -> Result<HashSet<u32>, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut client = wallet.w2n_client().clone();
|
||||
let mut ids = HashSet::new();
|
||||
@@ -701,15 +694,14 @@ where
|
||||
Ok(reverted)
|
||||
}
|
||||
|
||||
fn clean_old_unconfirmed<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
fn clean_old_unconfirmed<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
height: u64,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
if height < 50 {
|
||||
return Ok(());
|
||||
@@ -734,15 +726,14 @@ where
|
||||
|
||||
/// Retrieve summary info about the wallet
|
||||
/// caller should refresh first if desired
|
||||
pub fn retrieve_info<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn retrieve_info<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
parent_key_id: &Identifier,
|
||||
minimum_confirmations: u64,
|
||||
) -> Result<WalletInfo, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let current_height = wallet.last_confirmed_height()?;
|
||||
let outputs = wallet
|
||||
@@ -800,16 +791,15 @@ where
|
||||
}
|
||||
|
||||
/// Build a coinbase output and insert into wallet
|
||||
pub fn build_coinbase<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn build_coinbase<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
block_fees: &BlockFees,
|
||||
test_mode: bool,
|
||||
) -> Result<CbData, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
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
|
||||
/// Build a coinbase output and the corresponding kernel
|
||||
pub fn receive_coinbase<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
pub fn receive_coinbase<C, K>(
|
||||
wallet: &mut WalletBackend<C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
block_fees: &BlockFees,
|
||||
test_mode: bool,
|
||||
) -> Result<(Output, TxKernel, BlockFees), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let height = block_fees.height;
|
||||
let lock_height = height + global::coinbase_maturity();
|
||||
@@ -856,11 +845,11 @@ where
|
||||
key_id: key_id.clone(),
|
||||
n_child: key_id.to_path().last_path_index(),
|
||||
mmr_index: None,
|
||||
commit: commit,
|
||||
commit,
|
||||
value: amount,
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: height,
|
||||
lock_height: lock_height,
|
||||
height,
|
||||
lock_height,
|
||||
is_coinbase: true,
|
||||
tx_log_entry: None,
|
||||
})?;
|
||||
|
||||
@@ -45,6 +45,7 @@ extern crate strum_macros;
|
||||
|
||||
pub mod address;
|
||||
pub mod api_impl;
|
||||
mod backend;
|
||||
mod error;
|
||||
mod internal;
|
||||
pub mod mwixnet;
|
||||
@@ -69,13 +70,13 @@ pub use api_impl::types::{
|
||||
NodeHeightResult, OutputCommitMapping, PaymentProof, RetrieveTxQueryArgs,
|
||||
RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, VersionInfo,
|
||||
};
|
||||
pub use backend::{WalletBackend, WalletBatch};
|
||||
pub use internal::scan::scan;
|
||||
pub use slate_versions::ser as dalek_ser;
|
||||
pub use types::{
|
||||
AcctPathMapping, BlockIdentifier, CbData, Context, NodeClient, NodeVersionInfo, OutputData,
|
||||
OutputStatus, ScannedBlockInfo, StoredProofInfo, TxLogEntry, TxLogEntryType, TxWrapper,
|
||||
ViewWallet, WalletBackend, WalletInfo, WalletInitStatus, WalletInst, WalletLCProvider,
|
||||
WalletOutputBatch,
|
||||
ViewWallet, WalletInfo, WalletInitStatus, WalletInst, WalletLCProvider,
|
||||
};
|
||||
|
||||
/// Helper for taking a lock on the wallet instance
|
||||
|
||||
+3
-178
@@ -28,7 +28,7 @@ use crate::grin_util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::grin_util::secp::{self, pedersen, Secp256k1};
|
||||
use crate::grin_util::{ToHex, ZeroingString};
|
||||
use crate::slate_versions::ser as dalek_ser;
|
||||
use crate::InitTxArgs;
|
||||
use crate::{InitTxArgs, WalletBackend};
|
||||
use chrono::prelude::*;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
use ed25519_dalek::Signature as DalekSignature;
|
||||
@@ -49,7 +49,7 @@ where
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
/// 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
|
||||
@@ -131,182 +131,7 @@ where
|
||||
fn delete_wallet(&self, name: Option<&str>) -> Result<(), Error>;
|
||||
|
||||
/// return wallet instance
|
||||
fn wallet_inst(&mut self) -> Result<&mut Box<dyn WalletBackend<'a, C, K> + 'a>, 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>;
|
||||
fn wallet_inst(&mut self) -> Result<&mut WalletBackend<C, K>, Error>;
|
||||
}
|
||||
|
||||
/// Encapsulate all wallet-node communication functions. No functions within libwallet
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ pub fn command_loop<L, C, K>(
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
|
||||
DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
|
||||
+14
-16
@@ -92,7 +92,7 @@ fn prompt_recovery_phrase<L, C, K>(
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
|
||||
) -> Result<ZeroingString, ParseError>
|
||||
where
|
||||
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
|
||||
DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
@@ -224,12 +224,12 @@ pub fn inst_wallet<L, C, K>(
|
||||
node_client: C,
|
||||
) -> Result<Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>, ParseError>
|
||||
where
|
||||
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
|
||||
DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + '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>>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
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,
|
||||
) -> Result<command::InitArgs, ParseError>
|
||||
where
|
||||
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
|
||||
DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
@@ -355,16 +355,16 @@ where
|
||||
println!("Please enter a password for your new wallet");
|
||||
}
|
||||
|
||||
let password = match g_args.password.clone() {
|
||||
Some(p) => p,
|
||||
None => prompt_password_confirm(),
|
||||
};
|
||||
let password = g_args
|
||||
.password
|
||||
.clone()
|
||||
.unwrap_or_else(|| prompt_password_confirm());
|
||||
|
||||
Ok(command::InitArgs {
|
||||
list_length: list_length,
|
||||
password: password,
|
||||
list_length,
|
||||
password,
|
||||
config: config.clone(),
|
||||
recovery_phrase: recovery_phrase,
|
||||
recovery_phrase,
|
||||
restore: false,
|
||||
})
|
||||
}
|
||||
@@ -375,9 +375,7 @@ pub fn parse_recover_args(
|
||||
where
|
||||
{
|
||||
let passphrase = prompt_password(&g_args.password);
|
||||
Ok(command::RecoverArgs {
|
||||
passphrase: passphrase,
|
||||
})
|
||||
Ok(command::RecoverArgs { passphrase })
|
||||
}
|
||||
|
||||
pub fn parse_listen_args(
|
||||
@@ -977,7 +975,7 @@ where
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, C, keychain::ExtKeychain>,
|
||||
DefaultLCProvider<C, keychain::ExtKeychain>,
|
||||
C,
|
||||
keychain::ExtKeychain,
|
||||
>,
|
||||
@@ -1112,7 +1110,7 @@ pub fn parse_and_execute<L, C, K>(
|
||||
cli_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
|
||||
DefaultWalletImpl<C>: WalletInst<'static, L, C, K>,
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
|
||||
+3
-14
@@ -226,7 +226,7 @@ pub fn instantiate_wallet(
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
@@ -241,7 +241,7 @@ pub fn instantiate_wallet(
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(node_client).unwrap())
|
||||
as Box<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
@@ -302,18 +302,7 @@ pub fn execute_command_no_setup<C, F>(
|
||||
where
|
||||
C: NodeClient + 'static + Clone,
|
||||
F: FnOnce(
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, C, ExtKeychain>,
|
||||
C,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Arc<Mutex<Box<dyn WalletInst<'static, DefaultLCProvider<C, ExtKeychain>, C, ExtKeychain>>>>,
|
||||
),
|
||||
{
|
||||
let args = app.clone().get_matches_from(arg_vec);
|
||||
|
||||
@@ -57,7 +57,7 @@ fn owner_v3_lifecycle() -> Result<(), grin_wallet_controller::Error> {
|
||||
let wallet_proxy_a: Arc<
|
||||
Mutex<
|
||||
WalletProxy<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
|
||||
+2
-2
@@ -21,14 +21,14 @@ thiserror = "1"
|
||||
##### Grin Imports
|
||||
|
||||
# For Release
|
||||
grin_util = "5.4.0"
|
||||
#grin_util = "5.4.0"
|
||||
|
||||
# For beta release
|
||||
|
||||
# grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.2.0-beta.3" }
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user