mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 19:11:09 +03:00
Less cloning and additional pattern simplifications (#326)
* API cleanup * Config cleanup * Impl cleanup * Libwallet cleanup * Additionnal simplification
This commit is contained in:
parent
2d264db91a
commit
1116bc5545
35 changed files with 554 additions and 616 deletions
|
@ -121,10 +121,10 @@ where
|
||||||
/// let mut wallet_config = WalletConfig::default();
|
/// let mut wallet_config = WalletConfig::default();
|
||||||
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
||||||
/// # let dir = dir
|
/// # let dir = dir
|
||||||
/// # .path()
|
/// # .path()
|
||||||
/// # .to_str()
|
/// # .to_str()
|
||||||
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
||||||
/// # .unwrap();
|
/// # .unwrap();
|
||||||
/// # wallet_config.data_file_dir = dir.to_owned();
|
/// # wallet_config.data_file_dir = dir.to_owned();
|
||||||
///
|
///
|
||||||
/// // A NodeClient must first be created to handle communication between
|
/// // A NodeClient must first be created to handle communication between
|
||||||
|
@ -137,7 +137,7 @@ where
|
||||||
/// // These traits can be replaced with alternative implementations if desired
|
/// // These traits can be replaced with alternative implementations if desired
|
||||||
///
|
///
|
||||||
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap())
|
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap())
|
||||||
/// as Box<WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
|
/// as Box<WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
|
||||||
///
|
///
|
||||||
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
|
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
|
||||||
/// let lc = wallet.lc_provider().unwrap();
|
/// let lc = wallet.lc_provider().unwrap();
|
||||||
|
@ -236,17 +236,17 @@ where
|
||||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||||
///
|
///
|
||||||
/// let block_fees = BlockFees {
|
/// let block_fees = BlockFees {
|
||||||
/// fees: 800000,
|
/// fees: 800000,
|
||||||
/// height: 234323,
|
/// height: 234323,
|
||||||
/// key_id: None,
|
/// key_id: None,
|
||||||
/// };
|
/// };
|
||||||
/// // Build a new coinbase output
|
/// // Build a new coinbase output
|
||||||
///
|
///
|
||||||
/// let res = api_foreign.build_coinbase(&block_fees);
|
/// let res = api_foreign.build_coinbase(&block_fees);
|
||||||
///
|
///
|
||||||
/// if let Ok(cb_data) = res {
|
/// if let Ok(cb_data) = res {
|
||||||
/// // cb_data is populated with coinbase output info
|
/// // cb_data is populated with coinbase output info
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -294,13 +294,13 @@ where
|
||||||
/// # let slate = Slate::blank(2);
|
/// # let slate = Slate::blank(2);
|
||||||
/// // Receive a slate via some means
|
/// // Receive a slate via some means
|
||||||
///
|
///
|
||||||
/// let res = api_foreign.verify_slate_messages(&slate);
|
/// let res = api_foreign.verify_slate_messages(&slate);
|
||||||
///
|
///
|
||||||
/// if let Err(e) = res {
|
/// if let Err(e) = res {
|
||||||
/// // Messages don't validate, likely return an error
|
/// // Messages don't validate, likely return an error
|
||||||
/// // ...
|
/// // ...
|
||||||
/// } else {
|
/// } else {
|
||||||
/// // Slate messages are fine
|
/// // Slate messages are fine
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
@ -370,8 +370,8 @@ where
|
||||||
/// let result = api_foreign.receive_tx(&slate, None, None);
|
/// let result = api_foreign.receive_tx(&slate, None, None);
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send back to recipient somehow
|
/// // Send back to recipient somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -433,15 +433,15 @@ where
|
||||||
/// // . . .
|
/// // . . .
|
||||||
/// // Issue the invoice tx via the owner API
|
/// // Issue the invoice tx via the owner API
|
||||||
/// let args = IssueInvoiceTxArgs {
|
/// let args = IssueInvoiceTxArgs {
|
||||||
/// amount: 10_000_000_000,
|
/// amount: 10_000_000_000,
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.issue_invoice_tx(None, args);
|
/// let result = api_owner.issue_invoice_tx(None, args);
|
||||||
///
|
///
|
||||||
/// // If result okay, send to payer, who will apply the transaction via their
|
/// // If result okay, send to payer, who will apply the transaction via their
|
||||||
/// // owner API, then send back the slate
|
/// // owner API, then send back the slate
|
||||||
/// // ...
|
/// // ...
|
||||||
/// # let slate = Slate::blank(2);
|
/// # let slate = Slate::blank(2);
|
||||||
///
|
///
|
||||||
/// let slate = api_foreign.finalize_invoice_tx(&slate);
|
/// let slate = api_foreign.finalize_invoice_tx(&slate);
|
||||||
/// // if okay, then post via the owner API
|
/// // if okay, then post via the owner API
|
||||||
|
|
|
@ -674,9 +674,7 @@ pub fn run_doctest_foreign(
|
||||||
let _ = lc.set_top_level_directory(&format!("{}/wallet2", test_dir));
|
let _ = lc.set_top_level_directory(&format!("{}/wallet2", test_dir));
|
||||||
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone(), false)
|
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone(), false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mask2 = lc
|
let mask2 = lc.open_wallet(None, empty_string, use_token, true).unwrap();
|
||||||
.open_wallet(None, empty_string.clone(), use_token, true)
|
|
||||||
.unwrap();
|
|
||||||
let wallet2 = Arc::new(Mutex::new(wallet2));
|
let wallet2 = Arc::new(Mutex::new(wallet2));
|
||||||
|
|
||||||
wallet_proxy.add_wallet(
|
wallet_proxy.add_wallet(
|
||||||
|
|
372
api/src/owner.rs
372
api/src/owner.rs
|
@ -127,10 +127,10 @@ where
|
||||||
/// let mut wallet_config = WalletConfig::default();
|
/// let mut wallet_config = WalletConfig::default();
|
||||||
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
||||||
/// # let dir = dir
|
/// # let dir = dir
|
||||||
/// # .path()
|
/// # .path()
|
||||||
/// # .to_str()
|
/// # .to_str()
|
||||||
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
||||||
/// # .unwrap();
|
/// # .unwrap();
|
||||||
/// # wallet_config.data_file_dir = dir.to_owned();
|
/// # wallet_config.data_file_dir = dir.to_owned();
|
||||||
///
|
///
|
||||||
/// // A NodeClient must first be created to handle communication between
|
/// // A NodeClient must first be created to handle communication between
|
||||||
|
@ -143,7 +143,7 @@ where
|
||||||
/// // These traits can be replaced with alternative implementations if desired
|
/// // These traits can be replaced with alternative implementations if desired
|
||||||
///
|
///
|
||||||
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap())
|
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap())
|
||||||
/// as Box<WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
|
/// as Box<WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
|
||||||
///
|
///
|
||||||
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
|
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
|
||||||
/// let lc = wallet.lc_provider().unwrap();
|
/// let lc = wallet.lc_provider().unwrap();
|
||||||
|
@ -236,7 +236,7 @@ where
|
||||||
/// let result = api_owner.accounts(None);
|
/// let result = api_owner.accounts(None);
|
||||||
///
|
///
|
||||||
/// if let Ok(accts) = result {
|
/// if let Ok(accts) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ where
|
||||||
/// let result = api_owner.create_account_path(None, "account1");
|
/// let result = api_owner.create_account_path(None, "account1");
|
||||||
///
|
///
|
||||||
/// if let Ok(identifier) = result {
|
/// if let Ok(identifier) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -334,8 +334,8 @@ where
|
||||||
/// let result = api_owner.create_account_path(None, "account1");
|
/// let result = api_owner.create_account_path(None, "account1");
|
||||||
///
|
///
|
||||||
/// if let Ok(identifier) = result {
|
/// if let Ok(identifier) = result {
|
||||||
/// // set the account active
|
/// // set the account active
|
||||||
/// let result2 = api_owner.set_active_account(None, "account1");
|
/// let result2 = api_owner.set_active_account(None, "account1");
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ where
|
||||||
/// let result = api_owner.retrieve_outputs(None, show_spent, update_from_node, tx_id);
|
/// let result = api_owner.retrieve_outputs(None, show_spent, update_from_node, tx_id);
|
||||||
///
|
///
|
||||||
/// if let Ok((was_updated, output_mappings)) = result {
|
/// if let Ok((was_updated, output_mappings)) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ where
|
||||||
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
|
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
|
||||||
///
|
///
|
||||||
/// if let Ok((was_updated, tx_log_entries)) = result {
|
/// if let Ok((was_updated, tx_log_entries)) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -538,7 +538,7 @@ where
|
||||||
/// let result = api_owner.retrieve_summary_info(None, update_from_node, minimum_confirmations);
|
/// let result = api_owner.retrieve_summary_info(None, update_from_node, minimum_confirmations);
|
||||||
///
|
///
|
||||||
/// if let Ok((was_updated, summary_info)) = result {
|
/// if let Ok((was_updated, summary_info)) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -619,25 +619,25 @@ where
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// // Attempt to create a transaction using the 'default' account
|
/// // Attempt to create a transaction using the 'default' account
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: 2_000_000_000,
|
/// amount: 2_000_000_000,
|
||||||
/// minimum_confirmations: 2,
|
/// minimum_confirmations: 2,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// message: Some("Have some Grins. Love, Yeastplume".to_owned()),
|
/// message: Some("Have some Grins. Love, Yeastplume".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.init_send_tx(
|
/// let result = api_owner.init_send_tx(
|
||||||
/// None,
|
/// None,
|
||||||
/// args,
|
/// args,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send slate somehow
|
/// // Send slate somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||||
/// api_owner.tx_lock_outputs(None, &slate, 0);
|
/// api_owner.tx_lock_outputs(None, &slate, 0);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -710,14 +710,14 @@ where
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
///
|
///
|
||||||
/// let args = IssueInvoiceTxArgs {
|
/// let args = IssueInvoiceTxArgs {
|
||||||
/// amount: 60_000_000_000,
|
/// amount: 60_000_000_000,
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.issue_invoice_tx(None, args);
|
/// let result = api_owner.issue_invoice_tx(None, args);
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // if okay, send to the payer to add their inputs
|
/// // if okay, send to the payer to add their inputs
|
||||||
/// // . . .
|
/// // . . .
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn issue_invoice_tx(
|
pub fn issue_invoice_tx(
|
||||||
|
@ -768,21 +768,21 @@ where
|
||||||
/// // The slate has been recieved from the invoicer, somehow
|
/// // The slate has been recieved from the invoicer, somehow
|
||||||
/// # let slate = Slate::blank(2);
|
/// # let slate = Slate::blank(2);
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: slate.amount,
|
/// amount: slate.amount,
|
||||||
/// minimum_confirmations: 2,
|
/// minimum_confirmations: 2,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let result = api_owner.process_invoice_tx(None, &slate, args);
|
/// let result = api_owner.process_invoice_tx(None, &slate, args);
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // If result okay, send back to the invoicer
|
/// // If result okay, send back to the invoicer
|
||||||
/// // . . .
|
/// // . . .
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
pub fn process_invoice_tx(
|
pub fn process_invoice_tx(
|
||||||
|
@ -829,25 +829,25 @@ where
|
||||||
///
|
///
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: 2_000_000_000,
|
/// amount: 2_000_000_000,
|
||||||
/// minimum_confirmations: 10,
|
/// minimum_confirmations: 10,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// message: Some("Remember to lock this when we're happy this is sent".to_owned()),
|
/// message: Some("Remember to lock this when we're happy this is sent".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.init_send_tx(
|
/// let result = api_owner.init_send_tx(
|
||||||
/// None,
|
/// None,
|
||||||
/// args,
|
/// args,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send slate somehow
|
/// // Send slate somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||||
/// api_owner.tx_lock_outputs(None, &slate, 0);
|
/// api_owner.tx_lock_outputs(None, &slate, 0);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -893,29 +893,29 @@ where
|
||||||
///
|
///
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: 2_000_000_000,
|
/// amount: 2_000_000_000,
|
||||||
/// minimum_confirmations: 10,
|
/// minimum_confirmations: 10,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// message: Some("Finalize this tx now".to_owned()),
|
/// message: Some("Finalize this tx now".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.init_send_tx(
|
/// let result = api_owner.init_send_tx(
|
||||||
/// None,
|
/// None,
|
||||||
/// args,
|
/// args,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send slate somehow
|
/// // Send slate somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||||
/// //
|
/// //
|
||||||
/// // Retrieve slate back from recipient
|
/// // Retrieve slate back from recipient
|
||||||
/// //
|
/// //
|
||||||
/// let res = api_owner.finalize_tx(None, &slate);
|
/// let res = api_owner.finalize_tx(None, &slate);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -953,30 +953,30 @@ where
|
||||||
///
|
///
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: 2_000_000_000,
|
/// amount: 2_000_000_000,
|
||||||
/// minimum_confirmations: 10,
|
/// minimum_confirmations: 10,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// message: Some("Post this tx".to_owned()),
|
/// message: Some("Post this tx".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.init_send_tx(
|
/// let result = api_owner.init_send_tx(
|
||||||
/// None,
|
/// None,
|
||||||
/// args,
|
/// args,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send slate somehow
|
/// // Send slate somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||||
/// //
|
/// //
|
||||||
/// // Retrieve slate back from recipient
|
/// // Retrieve slate back from recipient
|
||||||
/// //
|
/// //
|
||||||
/// let res = api_owner.finalize_tx(None, &slate);
|
/// let res = api_owner.finalize_tx(None, &slate);
|
||||||
/// let res = api_owner.post_tx(None, &slate.tx, true);
|
/// let res = api_owner.post_tx(None, &slate.tx, true);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1025,29 +1025,29 @@ where
|
||||||
///
|
///
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: 2_000_000_000,
|
/// amount: 2_000_000_000,
|
||||||
/// minimum_confirmations: 10,
|
/// minimum_confirmations: 10,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// message: Some("Cancel this tx".to_owned()),
|
/// message: Some("Cancel this tx".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.init_send_tx(
|
/// let result = api_owner.init_send_tx(
|
||||||
/// None,
|
/// None,
|
||||||
/// args,
|
/// args,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send slate somehow
|
/// // Send slate somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||||
/// //
|
/// //
|
||||||
/// // We didn't get the slate back, or something else went wrong
|
/// // We didn't get the slate back, or something else went wrong
|
||||||
/// //
|
/// //
|
||||||
/// let res = api_owner.cancel_tx(None, None, Some(slate.id.clone()));
|
/// let res = api_owner.cancel_tx(None, None, Some(slate.id.clone()));
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1098,8 +1098,8 @@ where
|
||||||
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
|
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
|
||||||
///
|
///
|
||||||
/// if let Ok((was_updated, tx_log_entries)) = result {
|
/// if let Ok((was_updated, tx_log_entries)) = result {
|
||||||
/// let stored_tx = api_owner.get_stored_tx(None, &tx_log_entries[0]).unwrap();
|
/// let stored_tx = api_owner.get_stored_tx(None, &tx_log_entries[0]).unwrap();
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1141,29 +1141,29 @@ where
|
||||||
///
|
///
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let args = InitTxArgs {
|
/// let args = InitTxArgs {
|
||||||
/// src_acct_name: None,
|
/// src_acct_name: None,
|
||||||
/// amount: 2_000_000_000,
|
/// amount: 2_000_000_000,
|
||||||
/// minimum_confirmations: 10,
|
/// minimum_confirmations: 10,
|
||||||
/// max_outputs: 500,
|
/// max_outputs: 500,
|
||||||
/// num_change_outputs: 1,
|
/// num_change_outputs: 1,
|
||||||
/// selection_strategy_is_use_all: false,
|
/// selection_strategy_is_use_all: false,
|
||||||
/// message: Some("Just verify messages".to_owned()),
|
/// message: Some("Just verify messages".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// let result = api_owner.init_send_tx(
|
/// let result = api_owner.init_send_tx(
|
||||||
/// None,
|
/// None,
|
||||||
/// args,
|
/// args,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(slate) = result {
|
/// if let Ok(slate) = result {
|
||||||
/// // Send slate somehow
|
/// // Send slate somehow
|
||||||
/// // ...
|
/// // ...
|
||||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||||
/// //
|
/// //
|
||||||
/// // Retrieve slate back from recipient
|
/// // Retrieve slate back from recipient
|
||||||
/// //
|
/// //
|
||||||
/// let res = api_owner.verify_slate_messages(None, &slate);
|
/// let res = api_owner.verify_slate_messages(None, &slate);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn verify_slate_messages(
|
pub fn verify_slate_messages(
|
||||||
|
@ -1221,14 +1221,14 @@ where
|
||||||
///
|
///
|
||||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let result = api_owner.scan(
|
/// let result = api_owner.scan(
|
||||||
/// None,
|
/// None,
|
||||||
/// Some(20000),
|
/// Some(20000),
|
||||||
/// false,
|
/// false,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// if let Ok(_) = result {
|
/// if let Ok(_) = result {
|
||||||
/// // Wallet outputs should be consistent with what's on chain
|
/// // Wallet outputs should be consistent with what's on chain
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1281,11 +1281,11 @@ where
|
||||||
/// let result = api_owner.node_height(None);
|
/// let result = api_owner.node_height(None);
|
||||||
///
|
///
|
||||||
/// if let Ok(node_height_result) = result {
|
/// if let Ok(node_height_result) = result {
|
||||||
/// if node_height_result.updated_from_node {
|
/// if node_height_result.updated_from_node {
|
||||||
/// //we can assume node_height_result.height is relatively safe to use
|
/// //we can assume node_height_result.height is relatively safe to use
|
||||||
///
|
///
|
||||||
/// }
|
/// }
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1338,8 +1338,8 @@ where
|
||||||
/// let result = api_owner.get_top_level_directory();
|
/// let result = api_owner.get_top_level_directory();
|
||||||
///
|
///
|
||||||
/// if let Ok(dir) = result {
|
/// if let Ok(dir) = result {
|
||||||
/// println!("Top level directory is: {}", dir);
|
/// println!("Top level directory is: {}", dir);
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1377,16 +1377,16 @@ where
|
||||||
///
|
///
|
||||||
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
||||||
/// # let dir = dir
|
/// # let dir = dir
|
||||||
/// # .path()
|
/// # .path()
|
||||||
/// # .to_str()
|
/// # .to_str()
|
||||||
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
||||||
/// # .unwrap();
|
/// # .unwrap();
|
||||||
///
|
///
|
||||||
/// let api_owner = Owner::new(wallet.clone(), None);
|
/// let api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let result = api_owner.set_top_level_directory(dir);
|
/// let result = api_owner.set_top_level_directory(dir);
|
||||||
///
|
///
|
||||||
/// if let Ok(dir) = result {
|
/// if let Ok(dir) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1427,10 +1427,10 @@ where
|
||||||
///
|
///
|
||||||
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
||||||
/// # let dir = dir
|
/// # let dir = dir
|
||||||
/// # .path()
|
/// # .path()
|
||||||
/// # .to_str()
|
/// # .to_str()
|
||||||
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
||||||
/// # .unwrap();
|
/// # .unwrap();
|
||||||
///
|
///
|
||||||
/// let api_owner = Owner::new(wallet.clone(), None);
|
/// let api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let _ = api_owner.set_top_level_directory(dir);
|
/// let _ = api_owner.set_top_level_directory(dir);
|
||||||
|
@ -1438,7 +1438,7 @@ where
|
||||||
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
|
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
|
||||||
///
|
///
|
||||||
/// if let Ok(_) = result {
|
/// if let Ok(_) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1490,29 +1490,29 @@ where
|
||||||
///
|
///
|
||||||
/// use grin_core::global::ChainTypes;
|
/// use grin_core::global::ChainTypes;
|
||||||
///
|
///
|
||||||
/// // note that the WalletInst struct does not necessarily need to contain an
|
/// // note that the WalletInst struct does not necessarily need to contain an
|
||||||
/// // instantiated wallet
|
/// // instantiated wallet
|
||||||
///
|
///
|
||||||
/// let dir = "path/to/wallet/dir";
|
/// let dir = "path/to/wallet/dir";
|
||||||
///
|
///
|
||||||
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
||||||
/// # let dir = dir
|
/// # let dir = dir
|
||||||
/// # .path()
|
/// # .path()
|
||||||
/// # .to_str()
|
/// # .to_str()
|
||||||
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
||||||
/// # .unwrap();
|
/// # .unwrap();
|
||||||
/// let api_owner = Owner::new(wallet.clone(), None);
|
/// let api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let _ = api_owner.set_top_level_directory(dir);
|
/// let _ = api_owner.set_top_level_directory(dir);
|
||||||
///
|
///
|
||||||
/// // Create configuration
|
/// // Create configuration
|
||||||
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
|
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
|
||||||
///
|
///
|
||||||
/// // create new wallet wirh random seed
|
/// // create new wallet wirh random seed
|
||||||
/// let pw = ZeroingString::from("my_password");
|
/// let pw = ZeroingString::from("my_password");
|
||||||
/// let result = api_owner.create_wallet(None, None, 0, pw);
|
/// let result = api_owner.create_wallet(None, None, 0, pw);
|
||||||
///
|
///
|
||||||
/// if let Ok(r) = result {
|
/// if let Ok(r) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1558,31 +1558,31 @@ where
|
||||||
///
|
///
|
||||||
/// use grin_core::global::ChainTypes;
|
/// use grin_core::global::ChainTypes;
|
||||||
///
|
///
|
||||||
/// // note that the WalletInst struct does not necessarily need to contain an
|
/// // note that the WalletInst struct does not necessarily need to contain an
|
||||||
/// // instantiated wallet
|
/// // instantiated wallet
|
||||||
/// let dir = "path/to/wallet/dir";
|
/// let dir = "path/to/wallet/dir";
|
||||||
///
|
///
|
||||||
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
|
||||||
/// # let dir = dir
|
/// # let dir = dir
|
||||||
/// # .path()
|
/// # .path()
|
||||||
/// # .to_str()
|
/// # .to_str()
|
||||||
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
|
||||||
/// # .unwrap();
|
/// # .unwrap();
|
||||||
/// let api_owner = Owner::new(wallet.clone(), None);
|
/// let api_owner = Owner::new(wallet.clone(), None);
|
||||||
/// let _ = api_owner.set_top_level_directory(dir);
|
/// let _ = api_owner.set_top_level_directory(dir);
|
||||||
///
|
///
|
||||||
/// // Create configuration
|
/// // Create configuration
|
||||||
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
|
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
|
||||||
///
|
///
|
||||||
/// // create new wallet wirh random seed
|
/// // create new wallet wirh random seed
|
||||||
/// let pw = ZeroingString::from("my_password");
|
/// let pw = ZeroingString::from("my_password");
|
||||||
/// let _ = api_owner.create_wallet(None, None, 0, pw.clone());
|
/// let _ = api_owner.create_wallet(None, None, 0, pw.clone());
|
||||||
///
|
///
|
||||||
/// let result = api_owner.open_wallet(None, pw, true);
|
/// let result = api_owner.open_wallet(None, pw, true);
|
||||||
///
|
///
|
||||||
/// if let Ok(m) = result {
|
/// if let Ok(m) = result {
|
||||||
/// // use this mask in all subsequent calls
|
/// // use this mask in all subsequent calls
|
||||||
/// let mask = m;
|
/// let mask = m;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1626,13 +1626,13 @@ where
|
||||||
///
|
///
|
||||||
/// use grin_core::global::ChainTypes;
|
/// use grin_core::global::ChainTypes;
|
||||||
///
|
///
|
||||||
/// // Set up as above
|
/// // Set up as above
|
||||||
/// # let api_owner = Owner::new(wallet.clone(), None);
|
/// # let api_owner = Owner::new(wallet.clone(), None);
|
||||||
///
|
///
|
||||||
/// let res = api_owner.close_wallet(None);
|
/// let res = api_owner.close_wallet(None);
|
||||||
///
|
///
|
||||||
/// if let Ok(_) = res {
|
/// if let Ok(_) = res {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -1662,14 +1662,14 @@ where
|
||||||
///
|
///
|
||||||
/// use grin_core::global::ChainTypes;
|
/// use grin_core::global::ChainTypes;
|
||||||
///
|
///
|
||||||
/// // Set up as above
|
/// // Set up as above
|
||||||
/// # let api_owner = Owner::new(wallet.clone(), None);
|
/// # let api_owner = Owner::new(wallet.clone(), None);
|
||||||
///
|
///
|
||||||
/// let pw = ZeroingString::from("my_password");
|
/// let pw = ZeroingString::from("my_password");
|
||||||
/// let res = api_owner.get_mnemonic(None, pw);
|
/// let res = api_owner.get_mnemonic(None, pw);
|
||||||
///
|
///
|
||||||
/// if let Ok(mne) = res {
|
/// if let Ok(mne) = res {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_mnemonic(
|
pub fn get_mnemonic(
|
||||||
|
@ -1707,15 +1707,15 @@ where
|
||||||
///
|
///
|
||||||
/// use grin_core::global::ChainTypes;
|
/// use grin_core::global::ChainTypes;
|
||||||
///
|
///
|
||||||
/// // Set up as above
|
/// // Set up as above
|
||||||
/// # let api_owner = Owner::new(wallet.clone(), None);
|
/// # let api_owner = Owner::new(wallet.clone(), None);
|
||||||
///
|
///
|
||||||
/// let old = ZeroingString::from("my_password");
|
/// let old = ZeroingString::from("my_password");
|
||||||
/// let new = ZeroingString::from("new_password");
|
/// let new = ZeroingString::from("new_password");
|
||||||
/// let res = api_owner.change_password(None, old, new);
|
/// let res = api_owner.change_password(None, old, new);
|
||||||
///
|
///
|
||||||
/// if let Ok(mne) = res {
|
/// if let Ok(mne) = res {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn change_password(
|
pub fn change_password(
|
||||||
|
@ -1750,13 +1750,13 @@ where
|
||||||
///
|
///
|
||||||
/// use grin_core::global::ChainTypes;
|
/// use grin_core::global::ChainTypes;
|
||||||
///
|
///
|
||||||
/// // Set up as above
|
/// // Set up as above
|
||||||
/// # let api_owner = Owner::new(wallet.clone(), None);
|
/// # let api_owner = Owner::new(wallet.clone(), None);
|
||||||
///
|
///
|
||||||
/// let res = api_owner.delete_wallet(None);
|
/// let res = api_owner.delete_wallet(None);
|
||||||
///
|
///
|
||||||
/// if let Ok(_) = res {
|
/// if let Ok(_) = res {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -2066,7 +2066,7 @@ where
|
||||||
/// let result = api_owner.retrieve_payment_proof(None, update_from_node, tx_id, tx_slate_id);
|
/// let result = api_owner.retrieve_payment_proof(None, update_from_node, tx_id, tx_slate_id);
|
||||||
///
|
///
|
||||||
/// if let Ok(p) = result {
|
/// if let Ok(p) = result {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
@ -2137,10 +2137,10 @@ where
|
||||||
/// // The proof will likely be exported as JSON to be provided to another party
|
/// // The proof will likely be exported as JSON to be provided to another party
|
||||||
///
|
///
|
||||||
/// if let Ok(p) = result {
|
/// if let Ok(p) = result {
|
||||||
/// let valid = api_owner.verify_payment_proof(None, &p);
|
/// let valid = api_owner.verify_payment_proof(None, &p);
|
||||||
/// if let Ok(_) = valid {
|
/// if let Ok(_) = valid {
|
||||||
/// //...
|
/// //...
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
|
|
@ -1344,7 +1344,7 @@ where
|
||||||
|
|
||||||
fn get_stored_tx(&self, tx: &TxLogEntry) -> Result<Option<TransactionV3>, ErrorKind> {
|
fn get_stored_tx(&self, tx: &TxLogEntry) -> Result<Option<TransactionV3>, ErrorKind> {
|
||||||
Owner::get_stored_tx(self, None, tx)
|
Owner::get_stored_tx(self, None, tx)
|
||||||
.map(|x| x.map(|y| TransactionV3::from(y)))
|
.map(|x| x.map(TransactionV3::from))
|
||||||
.map_err(|e| e.kind())
|
.map_err(|e| e.kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1458,9 +1458,7 @@ pub fn run_doctest_owner(
|
||||||
let _ = lc.set_top_level_directory(&format!("{}/wallet2", test_dir));
|
let _ = lc.set_top_level_directory(&format!("{}/wallet2", test_dir));
|
||||||
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone(), false)
|
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone(), false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mask2 = lc
|
let mask2 = lc.open_wallet(None, empty_string, use_token, true).unwrap();
|
||||||
.open_wallet(None, empty_string.clone(), use_token, true)
|
|
||||||
.unwrap();
|
|
||||||
let wallet2 = Arc::new(Mutex::new(wallet2));
|
let wallet2 = Arc::new(Mutex::new(wallet2));
|
||||||
|
|
||||||
if mask2.is_some() {
|
if mask2.is_some() {
|
||||||
|
@ -1558,7 +1556,7 @@ pub fn run_doctest_owner(
|
||||||
}
|
}
|
||||||
|
|
||||||
if payment_proof {
|
if payment_proof {
|
||||||
let _ = api_impl::owner::post_tx(&client1, &slate_outer.tx, true).unwrap();
|
api_impl::owner::post_tx(&client1, &slate_outer.tx, true).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if perform_tx && lock_tx && finalize_tx {
|
if perform_tx && lock_tx && finalize_tx {
|
||||||
|
|
|
@ -2130,7 +2130,7 @@ where
|
||||||
tx: &TxLogEntry,
|
tx: &TxLogEntry,
|
||||||
) -> Result<Option<TransactionV3>, ErrorKind> {
|
) -> Result<Option<TransactionV3>, ErrorKind> {
|
||||||
Owner::get_stored_tx(self, (&token.keychain_mask).as_ref(), tx)
|
Owner::get_stored_tx(self, (&token.keychain_mask).as_ref(), tx)
|
||||||
.map(|x| x.map(|y| TransactionV3::from(y)))
|
.map(|x| x.map(TransactionV3::from))
|
||||||
.map_err(|e| e.kind())
|
.map_err(|e| e.kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2173,21 +2173,19 @@ where
|
||||||
let secp = secp_inst.lock();
|
let secp = secp_inst.lock();
|
||||||
let sec_key = SecretKey::new(&secp, &mut thread_rng());
|
let sec_key = SecretKey::new(&secp, &mut thread_rng());
|
||||||
|
|
||||||
let mut shared_pubkey = ecdh_pubkey.ecdh_pubkey.clone();
|
let mut shared_pubkey = ecdh_pubkey.ecdh_pubkey;
|
||||||
shared_pubkey
|
shared_pubkey
|
||||||
.mul_assign(&secp, &sec_key)
|
.mul_assign(&secp, &sec_key)
|
||||||
.map_err(|e| ErrorKind::Secp(e))?;
|
.map_err(ErrorKind::Secp)?;
|
||||||
|
|
||||||
let x_coord = shared_pubkey.serialize_vec(&secp, true);
|
let x_coord = shared_pubkey.serialize_vec(&secp, true);
|
||||||
let shared_key =
|
let shared_key = SecretKey::from_slice(&secp, &x_coord[1..]).map_err(ErrorKind::Secp)?;
|
||||||
SecretKey::from_slice(&secp, &x_coord[1..]).map_err(|e| ErrorKind::Secp(e))?;
|
|
||||||
{
|
{
|
||||||
let mut s = self.shared_key.lock();
|
let mut s = self.shared_key.lock();
|
||||||
*s = Some(shared_key);
|
*s = Some(shared_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pub_key =
|
let pub_key = PublicKey::from_secret_key(&secp, &sec_key).map_err(ErrorKind::Secp)?;
|
||||||
PublicKey::from_secret_key(&secp, &sec_key).map_err(|e| ErrorKind::Secp(e))?;
|
|
||||||
|
|
||||||
Ok(ECDHPubkey {
|
Ok(ECDHPubkey {
|
||||||
ecdh_pubkey: pub_key,
|
ecdh_pubkey: pub_key,
|
||||||
|
@ -2247,7 +2245,7 @@ where
|
||||||
let n = name.as_ref().map(|s| s.as_str());
|
let n = name.as_ref().map(|s| s.as_str());
|
||||||
let res =
|
let res =
|
||||||
Owner::get_mnemonic(self, n, ZeroingString::from(password)).map_err(|e| e.kind())?;
|
Owner::get_mnemonic(self, n, ZeroingString::from(password)).map_err(|e| e.kind())?;
|
||||||
Ok(format!("{}", &*res))
|
Ok((&*res).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_password(
|
fn change_password(
|
||||||
|
|
|
@ -237,11 +237,11 @@ fn comments() -> HashMap<String, String> {
|
||||||
|
|
||||||
fn get_key(line: &str) -> String {
|
fn get_key(line: &str) -> String {
|
||||||
if line.contains('[') && line.contains(']') {
|
if line.contains('[') && line.contains(']') {
|
||||||
return line.to_owned();
|
line.to_owned()
|
||||||
} else if line.contains('=') {
|
} else if line.contains('=') {
|
||||||
return line.split('=').collect::<Vec<&str>>()[0].trim().to_owned();
|
line.split('=').collect::<Vec<&str>>()[0].trim().to_owned()
|
||||||
} else {
|
} else {
|
||||||
return "NOT_FOUND".to_owned();
|
"NOT_FOUND".to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,9 +59,9 @@ fn get_grin_path(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !grin_path.exists() {
|
if !grin_path.exists() {
|
||||||
return Err(ConfigError::PathNotFoundError(String::from(
|
Err(ConfigError::PathNotFoundError(String::from(
|
||||||
grin_path.to_str().unwrap(),
|
grin_path.to_str().unwrap(),
|
||||||
)));
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(grin_path)
|
Ok(grin_path)
|
||||||
}
|
}
|
||||||
|
@ -253,21 +253,12 @@ impl GlobalWalletConfig {
|
||||||
match decoded {
|
match decoded {
|
||||||
Ok(gc) => {
|
Ok(gc) => {
|
||||||
self.members = Some(gc);
|
self.members = Some(gc);
|
||||||
return Ok(self);
|
Ok(self)
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ConfigError::ParseError(
|
|
||||||
String::from(
|
|
||||||
self.config_file_path
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.clone(),
|
|
||||||
),
|
|
||||||
format!("{}", e),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
Err(e) => Err(ConfigError::ParseError(
|
||||||
|
String::from(self.config_file_path.as_mut().unwrap().to_str().unwrap()),
|
||||||
|
format!("{}", e),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,10 +300,8 @@ impl GlobalWalletConfig {
|
||||||
let encoded: Result<String, toml::ser::Error> =
|
let encoded: Result<String, toml::ser::Error> =
|
||||||
toml::to_string(self.members.as_mut().unwrap());
|
toml::to_string(self.members.as_mut().unwrap());
|
||||||
match encoded {
|
match encoded {
|
||||||
Ok(enc) => return Ok(enc),
|
Ok(enc) => Ok(enc),
|
||||||
Err(e) => {
|
Err(e) => Err(ConfigError::SerializationError(format!("{}", e))),
|
||||||
return Err(ConfigError::SerializationError(format!("{}", e)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ use std::path::MAIN_SEPARATOR;
|
||||||
use crate::tor::config as tor_config;
|
use crate::tor::config as tor_config;
|
||||||
use crate::tor::process as tor_process;
|
use crate::tor::process as tor_process;
|
||||||
|
|
||||||
const TOR_CONFIG_PATH: &'static str = "tor/sender";
|
const TOR_CONFIG_PATH: &str = "tor/sender";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HttpSlateSender {
|
pub struct HttpSlateSender {
|
||||||
|
@ -78,10 +78,9 @@ impl HttpSlateSender {
|
||||||
let err_string = format!("{}", e);
|
let err_string = format!("{}", e);
|
||||||
if err_string.contains("404") {
|
if err_string.contains("404") {
|
||||||
// Report that the other version of the wallet is out of date
|
// Report that the other version of the wallet is out of date
|
||||||
report = format!(
|
report = "Other wallet is incompatible and requires an upgrade. \
|
||||||
"Other wallet is incompatible and requires an upgrade. \
|
|
||||||
Please urge the other wallet owner to upgrade and try the transaction again."
|
Please urge the other wallet owner to upgrade and try the transaction again."
|
||||||
);
|
.to_string();
|
||||||
}
|
}
|
||||||
error!("{}", report);
|
error!("{}", report);
|
||||||
ErrorKind::ClientCallback(report)
|
ErrorKind::ClientCallback(report)
|
||||||
|
@ -107,7 +106,7 @@ impl HttpSlateSender {
|
||||||
|
|
||||||
// trivial tests for now, but will be expanded later
|
// trivial tests for now, but will be expanded later
|
||||||
if foreign_api_version < 2 {
|
if foreign_api_version < 2 {
|
||||||
let report = format!("Other wallet reports unrecognized API format.");
|
let report = "Other wallet reports unrecognized API format.".to_string();
|
||||||
error!("{}", report);
|
error!("{}", report);
|
||||||
return Err(ErrorKind::ClientCallback(report).into());
|
return Err(ErrorKind::ClientCallback(report).into());
|
||||||
}
|
}
|
||||||
|
@ -119,7 +118,7 @@ impl HttpSlateSender {
|
||||||
return Ok(SlateVersion::V2);
|
return Ok(SlateVersion::V2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let report = format!("Unable to negotiate slate format with other wallet.");
|
let report = "Unable to negotiate slate format with other wallet.".to_string();
|
||||||
error!("{}", report);
|
error!("{}", report);
|
||||||
Err(ErrorKind::ClientCallback(report).into())
|
Err(ErrorKind::ClientCallback(report).into())
|
||||||
}
|
}
|
||||||
|
@ -136,7 +135,7 @@ impl HttpSlateSender {
|
||||||
let mut client = Client::new();
|
let mut client = Client::new();
|
||||||
if self.use_socks {
|
if self.use_socks {
|
||||||
client.use_socks = true;
|
client.use_socks = true;
|
||||||
client.socks_proxy_addr = self.socks_proxy_addr.clone();
|
client.socks_proxy_addr = self.socks_proxy_addr;
|
||||||
}
|
}
|
||||||
let req = client.create_post_request(url, api_secret, &input)?;
|
let req = client.create_post_request(url, api_secret, &input)?;
|
||||||
let res = client.send_request(req)?;
|
let res = client.send_request(req)?;
|
||||||
|
@ -167,24 +166,24 @@ impl SlateSender for HttpSlateSender {
|
||||||
&tor_dir,
|
&tor_dir,
|
||||||
&self.socks_proxy_addr.unwrap().to_string(),
|
&self.socks_proxy_addr.unwrap().to_string(),
|
||||||
)
|
)
|
||||||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e).into()))?;
|
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e)))?;
|
||||||
// Start TOR process
|
// Start TOR process
|
||||||
tor.torrc_path(&format!("{}/torrc", &tor_dir))
|
tor.torrc_path(&format!("{}/torrc", &tor_dir))
|
||||||
.working_dir(&tor_dir)
|
.working_dir(&tor_dir)
|
||||||
.timeout(20)
|
.timeout(20)
|
||||||
.completion_percent(100)
|
.completion_percent(100)
|
||||||
.launch()
|
.launch()
|
||||||
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e).into()))?;
|
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let slate_send = match self.check_other_version(&url_str)? {
|
let slate_send = match self.check_other_version(&url_str)? {
|
||||||
SlateVersion::V3 => VersionedSlate::into_version(slate.clone(), SlateVersion::V3),
|
SlateVersion::V3 => VersionedSlate::into_version(slate.clone(), SlateVersion::V3),
|
||||||
SlateVersion::V2 => {
|
SlateVersion::V2 => {
|
||||||
let mut slate = slate.clone();
|
let mut slate = slate.clone();
|
||||||
if let Some(_) = slate.payment_proof {
|
if slate.payment_proof.is_some() {
|
||||||
return Err(ErrorKind::ClientCallback("Payment proof requested, but other wallet does not support payment proofs. Please urge other user to upgrade, or re-send tx without a payment proof".into()).into());
|
return Err(ErrorKind::ClientCallback("Payment proof requested, but other wallet does not support payment proofs. Please urge other user to upgrade, or re-send tx without a payment proof".into()).into());
|
||||||
}
|
}
|
||||||
if let Some(_) = slate.ttl_cutoff_height {
|
if slate.ttl_cutoff_height.is_some() {
|
||||||
warn!("Slate TTL value will be ignored and removed by other wallet, as other wallet does not support this feature. Please urge other user to upgrade");
|
warn!("Slate TTL value will be ignored and removed by other wallet, as other wallet does not support this feature. Please urge other user to upgrade");
|
||||||
}
|
}
|
||||||
slate.version_info.version = 2;
|
slate.version_info.version = 2;
|
||||||
|
@ -236,7 +235,7 @@ pub struct SchemeNotHttp;
|
||||||
|
|
||||||
impl Into<Error> for SchemeNotHttp {
|
impl Into<Error> for SchemeNotHttp {
|
||||||
fn into(self) -> Error {
|
fn into(self) -> Error {
|
||||||
let err_str = format!("url scheme must be http",);
|
let err_str = "url scheme must be http".to_string();
|
||||||
ErrorKind::GenericError(err_str).into()
|
ErrorKind::GenericError(err_str).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl KeybaseChannel {
|
||||||
/// Check if keybase is installed and return an adapter object.
|
/// Check if keybase is installed and return an adapter object.
|
||||||
pub fn new(channel: String) -> Result<KeybaseChannel, Error> {
|
pub fn new(channel: String) -> Result<KeybaseChannel, Error> {
|
||||||
// Limit only one recipient
|
// Limit only one recipient
|
||||||
if channel.matches(",").count() > 0 {
|
if channel.matches(',').count() > 0 {
|
||||||
return Err(
|
return Err(
|
||||||
ErrorKind::GenericError("Only one recipient is supported!".to_owned()).into(),
|
ErrorKind::GenericError("Only one recipient is supported!".to_owned()).into(),
|
||||||
);
|
);
|
||||||
|
@ -83,12 +83,12 @@ fn api_send(payload: &str) -> Result<Value, Error> {
|
||||||
String::from_utf8_lossy(&output.stdout),
|
String::from_utf8_lossy(&output.stdout),
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
);
|
);
|
||||||
Err(ErrorKind::GenericError("keybase api fail".to_owned()))?
|
Err(ErrorKind::GenericError("keybase api fail".to_owned()).into())
|
||||||
} else {
|
} else {
|
||||||
let response: Value =
|
let response: Value =
|
||||||
from_str(from_utf8(&output.stdout).expect("Bad output")).expect("Bad output");
|
from_str(from_utf8(&output.stdout).expect("Bad output")).expect("Bad output");
|
||||||
let err_msg = format!("{}", response["error"]["message"]);
|
let err_msg = format!("{}", response["error"]["message"]);
|
||||||
if err_msg.len() > 0 && err_msg != "null" {
|
if !err_msg.is_empty() && err_msg != "null" {
|
||||||
error!("api_send got error: {}", err_msg);
|
error!("api_send got error: {}", err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,12 +107,12 @@ fn whoami() -> Result<String, Error> {
|
||||||
String::from_utf8_lossy(&output.stdout),
|
String::from_utf8_lossy(&output.stdout),
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
);
|
);
|
||||||
Err(ErrorKind::GenericError("keybase api fail".to_owned()))?
|
Err(ErrorKind::GenericError("keybase api fail".to_owned()).into())
|
||||||
} else {
|
} else {
|
||||||
let response: Value =
|
let response: Value =
|
||||||
from_str(from_utf8(&output.stdout).expect("Bad output")).expect("Bad output");
|
from_str(from_utf8(&output.stdout).expect("Bad output")).expect("Bad output");
|
||||||
let err_msg = format!("{}", response["error"]["message"]);
|
let err_msg = format!("{}", response["error"]["message"]);
|
||||||
if err_msg.len() > 0 && err_msg != "null" {
|
if !err_msg.is_empty() && err_msg != "null" {
|
||||||
error!("status query got error: {}", err_msg);
|
error!("status query got error: {}", err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +121,7 @@ fn whoami() -> Result<String, Error> {
|
||||||
Ok(s.to_string())
|
Ok(s.to_string())
|
||||||
} else {
|
} else {
|
||||||
error!("keybase username query fail");
|
error!("keybase username query fail");
|
||||||
Err(ErrorKind::GenericError(
|
Err(ErrorKind::GenericError("keybase username query fail".to_owned()).into())
|
||||||
"keybase username query fail".to_owned(),
|
|
||||||
))?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +157,7 @@ fn read_from_channel(channel: &str, topic: &str) -> Result<Vec<String>, Error> {
|
||||||
}
|
}
|
||||||
Ok(unread)
|
Ok(unread)
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorKind::GenericError("keybase api fail".to_owned()))?
|
Err(ErrorKind::GenericError("keybase api fail".to_owned()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +200,7 @@ fn get_unread(topic: &str) -> Result<HashMap<String, String>, Error> {
|
||||||
}
|
}
|
||||||
Ok(unread)
|
Ok(unread)
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorKind::GenericError("keybase api fail".to_owned()))?
|
Err(ErrorKind::GenericError("keybase api fail".to_owned()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,16 +274,11 @@ fn poll(nseconds: u64, channel: &str) -> Option<Slate> {
|
||||||
let unread = read_from_channel(channel, SLATE_SIGNED);
|
let unread = read_from_channel(channel, SLATE_SIGNED);
|
||||||
for msg in unread.unwrap().iter() {
|
for msg in unread.unwrap().iter() {
|
||||||
let blob = Slate::deserialize_upgrade(&msg);
|
let blob = Slate::deserialize_upgrade(&msg);
|
||||||
match blob {
|
if let Ok(slate) = blob {
|
||||||
Ok(slate) => {
|
info!(
|
||||||
let slate: Slate = slate.into();
|
"keybase response message received from @{}, tx uuid: {}",
|
||||||
info!(
|
channel, slate.id,
|
||||||
"keybase response message received from @{}, tx uuid: {}",
|
);
|
||||||
channel, slate.id,
|
|
||||||
);
|
|
||||||
return Some(slate);
|
|
||||||
}
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sleep(POLL_SLEEP_DURATION);
|
sleep(POLL_SLEEP_DURATION);
|
||||||
|
@ -306,19 +299,17 @@ impl SlateSender for KeybaseChannel {
|
||||||
match send(&slate, &self.0, SLATE_NEW, TTL) {
|
match send(&slate, &self.0, SLATE_NEW, TTL) {
|
||||||
true => (),
|
true => (),
|
||||||
false => {
|
false => {
|
||||||
return Err(ErrorKind::ClientCallback(
|
return Err(
|
||||||
"Posting transaction slate".to_owned(),
|
ErrorKind::ClientCallback("Posting transaction slate".to_owned()).into(),
|
||||||
))?;
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("tx request has been sent to @{}, tx uuid: {}", &self.0, id);
|
info!("tx request has been sent to @{}, tx uuid: {}", &self.0, id);
|
||||||
// Wait for response from recipient with SLATE_SIGNED topic
|
// Wait for response from recipient with SLATE_SIGNED topic
|
||||||
match poll(TTL as u64, &self.0) {
|
match poll(TTL as u64, &self.0) {
|
||||||
Some(slate) => return Ok(slate),
|
Some(slate) => Ok(slate),
|
||||||
None => {
|
None => {
|
||||||
return Err(ErrorKind::ClientCallback(
|
Err(ErrorKind::ClientCallback("Receiving reply from recipient".to_owned()).into())
|
||||||
"Receiving reply from recipient".to_owned(),
|
|
||||||
))?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,17 +346,16 @@ impl SlateReceiver for KeybaseAllChannels {
|
||||||
node_api_secret: Option<String>,
|
node_api_secret: Option<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let node_client = HTTPNodeClient::new(&config.check_node_api_http_addr, node_api_secret);
|
let node_client = HTTPNodeClient::new(&config.check_node_api_http_addr, node_api_secret);
|
||||||
let mut wallet = Box::new(
|
let mut wallet =
|
||||||
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(),
|
Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client).unwrap())
|
||||||
)
|
as Box<
|
||||||
as Box<
|
dyn WalletInst<
|
||||||
dyn WalletInst<
|
'static,
|
||||||
'static,
|
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
|
||||||
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
|
HTTPNodeClient,
|
||||||
HTTPNodeClient,
|
ExtKeychain,
|
||||||
ExtKeychain,
|
>,
|
||||||
>,
|
>;
|
||||||
>;
|
|
||||||
let lc = wallet.lc_provider().unwrap();
|
let lc = wallet.lc_provider().unwrap();
|
||||||
lc.set_top_level_directory(&config.data_file_dir)?;
|
lc.set_top_level_directory(&config.data_file_dir)?;
|
||||||
let mask = lc.open_wallet(None, passphrase, true, false)?;
|
let mask = lc.open_wallet(None, passphrase, true, false)?;
|
||||||
|
@ -383,17 +373,16 @@ impl SlateReceiver for KeybaseAllChannels {
|
||||||
for (msg, channel) in &unread.unwrap() {
|
for (msg, channel) in &unread.unwrap() {
|
||||||
let blob = Slate::deserialize_upgrade(&msg);
|
let blob = Slate::deserialize_upgrade(&msg);
|
||||||
match blob {
|
match blob {
|
||||||
Ok(message) => {
|
Ok(slate) => {
|
||||||
let slate: Slate = message.clone().into();
|
|
||||||
let tx_uuid = slate.id;
|
let tx_uuid = slate.id;
|
||||||
|
|
||||||
// Reject multiple recipients channel for safety
|
// Reject multiple recipients channel for safety
|
||||||
{
|
{
|
||||||
if channel.matches(",").count() > 1 {
|
if channel.matches(',').count() > 1 {
|
||||||
error!(
|
error!(
|
||||||
"Incoming tx initiated on channel \"{}\" is rejected, multiple recipients channel! amount: {}(g), tx uuid: {}",
|
"Incoming tx initiated on channel \"{}\" is rejected, multiple recipients channel! amount: {}(g), tx uuid: {}",
|
||||||
channel,
|
channel,
|
||||||
slate.amount as f64 / 1000000000.0,
|
slate.amount as f64 / 1_000_000_000.0,
|
||||||
tx_uuid,
|
tx_uuid,
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
|
@ -403,7 +392,7 @@ impl SlateReceiver for KeybaseAllChannels {
|
||||||
info!(
|
info!(
|
||||||
"tx initiated on channel \"{}\", to send you {}(g). tx uuid: {}",
|
"tx initiated on channel \"{}\", to send you {}(g). tx uuid: {}",
|
||||||
channel,
|
channel,
|
||||||
slate.amount as f64 / 1000000000.0,
|
slate.amount as f64 / 1_000_000_000.0,
|
||||||
tx_uuid,
|
tx_uuid,
|
||||||
);
|
);
|
||||||
if let Err(e) = slate.verify_messages() {
|
if let Err(e) = slate.verify_messages() {
|
||||||
|
@ -411,15 +400,14 @@ impl SlateReceiver for KeybaseAllChannels {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
let res = {
|
let res = {
|
||||||
let r = foreign::receive_tx(
|
foreign::receive_tx(
|
||||||
&mut **wallet_inst,
|
&mut **wallet_inst,
|
||||||
Some(mask.as_ref().unwrap()),
|
Some(mask.as_ref().unwrap()),
|
||||||
&slate,
|
&slate,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
);
|
)
|
||||||
r
|
|
||||||
};
|
};
|
||||||
match res {
|
match res {
|
||||||
// Reply to the same channel with topic SLATE_SIGNED
|
// Reply to the same channel with topic SLATE_SIGNED
|
||||||
|
@ -460,7 +448,7 @@ fn notify_on_receive(keybase_notify_ttl: u16, channel: String, tx_uuid: String)
|
||||||
if keybase_notify_ttl > 0 {
|
if keybase_notify_ttl > 0 {
|
||||||
let my_username = whoami();
|
let my_username = whoami();
|
||||||
if let Ok(username) = my_username {
|
if let Ok(username) = my_username {
|
||||||
let split = channel.split(",");
|
let split = channel.split(',');
|
||||||
let vec: Vec<&str> = split.collect();
|
let vec: Vec<&str> = split.collect();
|
||||||
if vec.len() > 1 {
|
if vec.len() > 1 {
|
||||||
let receiver = username;
|
let receiver = username;
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub fn create_sender(
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut method = method.into();
|
let mut method = method;
|
||||||
|
|
||||||
// will test if this is a tor address and fill out
|
// will test if this is a tor address and fill out
|
||||||
// the http://[].onion if missing
|
// the http://[].onion if missing
|
||||||
|
@ -95,7 +95,7 @@ pub fn create_sender(
|
||||||
.map_err(|_| invalid())?,
|
.map_err(|_| invalid())?,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"keybase" => Box::new(KeybaseChannel::new(dest.to_owned())?),
|
"keybase" => Box::new(KeybaseChannel::new(dest)?),
|
||||||
"self" => {
|
"self" => {
|
||||||
return Err(ErrorKind::WalletComms(
|
return Err(ErrorKind::WalletComms(
|
||||||
"No sender implementation for \"self\".".to_string(),
|
"No sender implementation for \"self\".".to_string(),
|
||||||
|
|
|
@ -41,19 +41,19 @@ use crate::util::{self, secp};
|
||||||
use rand::rngs::mock::StepRng;
|
use rand::rngs::mock::StepRng;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
|
||||||
pub const DB_DIR: &'static str = "db";
|
pub const DB_DIR: &str = "db";
|
||||||
pub const TX_SAVE_DIR: &'static str = "saved_txs";
|
pub const TX_SAVE_DIR: &str = "saved_txs";
|
||||||
|
|
||||||
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
const OUTPUT_PREFIX: u8 = b'o';
|
||||||
const DERIV_PREFIX: u8 = 'd' as u8;
|
const DERIV_PREFIX: u8 = b'd';
|
||||||
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
const CONFIRMED_HEIGHT_PREFIX: u8 = b'c';
|
||||||
const PRIVATE_TX_CONTEXT_PREFIX: u8 = 'p' as u8;
|
const PRIVATE_TX_CONTEXT_PREFIX: u8 = b'p';
|
||||||
const TX_LOG_ENTRY_PREFIX: u8 = 't' as u8;
|
const TX_LOG_ENTRY_PREFIX: u8 = b't';
|
||||||
const TX_LOG_ID_PREFIX: u8 = 'i' as u8;
|
const TX_LOG_ID_PREFIX: u8 = b'i';
|
||||||
const ACCOUNT_PATH_MAPPING_PREFIX: u8 = 'a' as u8;
|
const ACCOUNT_PATH_MAPPING_PREFIX: u8 = b'a';
|
||||||
const LAST_SCANNED_BLOCK: u8 = 'l' as u8;
|
const LAST_SCANNED_BLOCK: u8 = b'l';
|
||||||
const LAST_SCANNED_KEY: &str = "LAST_SCANNED_KEY";
|
const LAST_SCANNED_KEY: &str = "LAST_SCANNED_KEY";
|
||||||
const WALLET_INIT_STATUS: u8 = 'w' as u8;
|
const WALLET_INIT_STATUS: u8 = b'w';
|
||||||
const WALLET_INIT_STATUS_KEY: &str = "WALLET_INIT_STATUS";
|
const WALLET_INIT_STATUS_KEY: &str = "WALLET_INIT_STATUS";
|
||||||
|
|
||||||
/// test to see if database files exist in the current directory. If so,
|
/// test to see if database files exist in the current directory. If so,
|
||||||
|
@ -79,7 +79,7 @@ where
|
||||||
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
|
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
|
||||||
hasher.update(&root_key.0[..]);
|
hasher.update(&root_key.0[..]);
|
||||||
hasher.update(&slate_id[..]);
|
hasher.update(&slate_id[..]);
|
||||||
hasher.update(&"blind".as_bytes()[..]);
|
hasher.update(&b"blind"[..]);
|
||||||
let blind_xor_key = hasher.finalize();
|
let blind_xor_key = hasher.finalize();
|
||||||
let mut ret_blind = [0; SECRET_KEY_SIZE];
|
let mut ret_blind = [0; SECRET_KEY_SIZE];
|
||||||
ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
|
ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
|
||||||
|
@ -88,7 +88,7 @@ where
|
||||||
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
|
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
|
||||||
hasher.update(&root_key.0[..]);
|
hasher.update(&root_key.0[..]);
|
||||||
hasher.update(&slate_id[..]);
|
hasher.update(&slate_id[..]);
|
||||||
hasher.update(&"nonce".as_bytes()[..]);
|
hasher.update(&b"nonce"[..]);
|
||||||
let nonce_xor_key = hasher.finalize();
|
let nonce_xor_key = hasher.finalize();
|
||||||
let mut ret_nonce = [0; SECRET_KEY_SIZE];
|
let mut ret_nonce = [0; SECRET_KEY_SIZE];
|
||||||
ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
|
ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]);
|
||||||
|
@ -200,7 +200,7 @@ where
|
||||||
// before it is used
|
// before it is used
|
||||||
let mask_value = match use_test_rng {
|
let mask_value = match use_test_rng {
|
||||||
true => {
|
true => {
|
||||||
let mut test_rng = StepRng::new(1234567890u64, 1);
|
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
|
||||||
secp::key::SecretKey::new(&k.secp(), &mut test_rng)
|
secp::key::SecretKey::new(&k.secp(), &mut test_rng)
|
||||||
}
|
}
|
||||||
false => secp::key::SecretKey::new(&k.secp(), &mut thread_rng()),
|
false => secp::key::SecretKey::new(&k.secp(), &mut thread_rng()),
|
||||||
|
@ -280,7 +280,7 @@ where
|
||||||
self.set_parent_key_id(a.path);
|
self.set_parent_key_id(a.path);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Err(ErrorKind::UnknownAccountLabel(label.clone()).into());
|
Err(ErrorKind::UnknownAccountLabel(label).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,8 +334,8 @@ where
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
for i in 0..SECRET_KEY_SIZE {
|
for i in 0..SECRET_KEY_SIZE {
|
||||||
ctx.sec_key.0[i] = ctx.sec_key.0[i] ^ blind_xor_key[i];
|
ctx.sec_key.0[i] ^= blind_xor_key[i];
|
||||||
ctx.sec_nonce.0[i] = ctx.sec_nonce.0[i] ^ nonce_xor_key[i];
|
ctx.sec_nonce.0[i] ^= nonce_xor_key[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ctx)
|
Ok(ctx)
|
||||||
|
@ -428,9 +428,9 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut return_path = self.parent_key_id.to_path();
|
let mut return_path = self.parent_key_id.to_path();
|
||||||
return_path.depth = return_path.depth + 1;
|
return_path.depth += 1;
|
||||||
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
|
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
|
||||||
deriv_idx = deriv_idx + 1;
|
deriv_idx += 1;
|
||||||
let mut batch = self.batch(keychain_mask)?;
|
let mut batch = self.batch(keychain_mask)?;
|
||||||
batch.save_child_index(&parent_key_id, deriv_idx)?;
|
batch.save_child_index(&parent_key_id, deriv_idx)?;
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
|
@ -696,8 +696,8 @@ where
|
||||||
|
|
||||||
let mut s_ctx = ctx.clone();
|
let mut s_ctx = ctx.clone();
|
||||||
for i in 0..SECRET_KEY_SIZE {
|
for i in 0..SECRET_KEY_SIZE {
|
||||||
s_ctx.sec_key.0[i] = s_ctx.sec_key.0[i] ^ blind_xor_key[i];
|
s_ctx.sec_key.0[i] ^= blind_xor_key[i];
|
||||||
s_ctx.sec_nonce.0[i] = s_ctx.sec_nonce.0[i] ^ nonce_xor_key[i];
|
s_ctx.sec_nonce.0[i] ^= nonce_xor_key[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
self.db
|
self.db
|
||||||
|
|
|
@ -339,9 +339,9 @@ impl Client {
|
||||||
let addr = match self.socks_proxy_addr {
|
let addr = match self.socks_proxy_addr {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
return Box::new(result(Err(ErrorKind::RequestError(format!(
|
return Box::new(result(Err(ErrorKind::RequestError(
|
||||||
"Can't parse Socks proxy address"
|
"Can't parse Socks proxy address".to_string(),
|
||||||
))
|
)
|
||||||
.into())))
|
.into())))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -125,7 +125,7 @@ fn answer_hello(
|
||||||
if response[0] == 5 && response[1] == 0 {
|
if response[0] == 5 && response[1] == 0 {
|
||||||
Box::new(ok(socket))
|
Box::new(ok(socket))
|
||||||
} else if response[0] == 5 && response[1] == 2 && creds.is_some() {
|
} else if response[0] == 5 && response[1] == 2 && creds.is_some() {
|
||||||
Box::new(auth_negotiation(socket, creds).and_then(|socket| ok(socket)))
|
Box::new(auth_negotiation(socket, creds).and_then(ok))
|
||||||
} else {
|
} else {
|
||||||
Box::new(
|
Box::new(
|
||||||
Err(Error::new(
|
Err(Error::new(
|
||||||
|
@ -159,7 +159,7 @@ fn write_addr(socket: TcpStream, req: Destination) -> HandshakeFuture<TcpStream>
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut packet = Vec::new();
|
let mut packet = Vec::new();
|
||||||
packet.write_all(&vec![5, 1, 0]).unwrap();
|
packet.write_all(&[5, 1, 0]).unwrap();
|
||||||
|
|
||||||
packet.write_u8(3).unwrap();
|
packet.write_u8(3).unwrap();
|
||||||
packet.write_u8(host.as_bytes().len() as u8).unwrap();
|
packet.write_u8(host.as_bytes().len() as u8).unwrap();
|
||||||
|
|
|
@ -109,13 +109,7 @@ impl Fail for Error {
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let show_bt = match env::var("RUST_BACKTRACE") {
|
let show_bt = match env::var("RUST_BACKTRACE") {
|
||||||
Ok(r) => {
|
Ok(r) => r == "1",
|
||||||
if r == "1" {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
};
|
};
|
||||||
let backtrace = match self.backtrace() {
|
let backtrace = match self.backtrace() {
|
||||||
|
@ -124,7 +118,7 @@ impl Display for Error {
|
||||||
};
|
};
|
||||||
let inner_output = format!("{}", self.inner,);
|
let inner_output = format!("{}", self.inner,);
|
||||||
let backtrace_output = format!("\nBacktrace: {}", backtrace);
|
let backtrace_output = format!("\nBacktrace: {}", backtrace);
|
||||||
let mut output = inner_output.clone();
|
let mut output = inner_output;
|
||||||
if show_bt {
|
if show_bt {
|
||||||
output.push_str(&backtrace_output);
|
output.push_str(&backtrace_output);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,21 +82,21 @@ where
|
||||||
let logging = match logging_config {
|
let logging = match logging_config {
|
||||||
Some(l) => Some(l),
|
Some(l) => Some(l),
|
||||||
None => match default_config.members.as_ref() {
|
None => match default_config.members.as_ref() {
|
||||||
Some(m) => m.clone().logging.clone(),
|
Some(m) => m.clone().logging,
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let wallet = match wallet_config {
|
let wallet = match wallet_config {
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
None => match default_config.members.as_ref() {
|
None => match default_config.members.as_ref() {
|
||||||
Some(m) => m.clone().wallet.clone(),
|
Some(m) => m.clone().wallet,
|
||||||
None => WalletConfig::default(),
|
None => WalletConfig::default(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let tor = match tor_config {
|
let tor = match tor_config {
|
||||||
Some(t) => Some(t),
|
Some(t) => Some(t),
|
||||||
None => match default_config.members.as_ref() {
|
None => match default_config.members.as_ref() {
|
||||||
Some(m) => m.clone().tor.clone(),
|
Some(m) => m.clone().tor,
|
||||||
None => Some(TorConfig::default()),
|
None => Some(TorConfig::default()),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -138,7 +138,7 @@ where
|
||||||
let mut abs_path = std::env::current_dir()?;
|
let mut abs_path = std::env::current_dir()?;
|
||||||
abs_path.push(self.data_dir.clone());
|
abs_path.push(self.data_dir.clone());
|
||||||
|
|
||||||
default_config.update_paths(&PathBuf::from(abs_path));
|
default_config.update_paths(&abs_path);
|
||||||
let res = default_config.write_to_file(config_file_name.to_str().unwrap());
|
let res = default_config.write_to_file(config_file_name.to_str().unwrap());
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
|
@ -180,7 +180,7 @@ where
|
||||||
if !test_mode {
|
if !test_mode {
|
||||||
if let Ok(true) = exists {
|
if let Ok(true) = exists {
|
||||||
let msg = format!("Wallet seed already exists at: {}", data_dir_name);
|
let msg = format!("Wallet seed already exists at: {}", data_dir_name);
|
||||||
return Err(ErrorKind::WalletSeedExists(msg))?;
|
return Err(ErrorKind::WalletSeedExists(msg).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WalletSeed::init_file(
|
WalletSeed::init_file(
|
||||||
|
@ -245,10 +245,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_wallet(&mut self, _name: Option<&str>) -> Result<(), Error> {
|
fn close_wallet(&mut self, _name: Option<&str>) -> Result<(), Error> {
|
||||||
match self.backend.as_mut() {
|
if let Some(b) = self.backend.as_mut() {
|
||||||
Some(b) => b.close()?,
|
b.close()?
|
||||||
None => {}
|
}
|
||||||
};
|
|
||||||
self.backend = None;
|
self.backend = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -283,7 +282,7 @@ where
|
||||||
fn validate_mnemonic(&self, mnemonic: ZeroingString) -> Result<(), Error> {
|
fn validate_mnemonic(&self, mnemonic: ZeroingString) -> Result<(), Error> {
|
||||||
match WalletSeed::from_mnemonic(mnemonic) {
|
match WalletSeed::from_mnemonic(mnemonic) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(ErrorKind::GenericError("Validating mnemonic".into()))?,
|
Err(_) => Err(ErrorKind::GenericError("Validating mnemonic".into()).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,9 +344,9 @@ where
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if orig_wallet_seed != new_wallet_seed {
|
if orig_wallet_seed != new_wallet_seed {
|
||||||
let msg = format!(
|
let msg =
|
||||||
"New and Old wallet seeds are not equal on password change, not removing backups."
|
"New and Old wallet seeds are not equal on password change, not removing backups."
|
||||||
);
|
.to_string();
|
||||||
return Err(ErrorKind::Lifecycle(msg).into());
|
return Err(ErrorKind::Lifecycle(msg).into());
|
||||||
}
|
}
|
||||||
// Removin
|
// Removin
|
||||||
|
|
|
@ -29,7 +29,7 @@ use crate::util;
|
||||||
use crate::{Error, ErrorKind};
|
use crate::{Error, ErrorKind};
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
|
||||||
pub const SEED_FILE: &'static str = "wallet.seed";
|
pub const SEED_FILE: &str = "wallet.seed";
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct WalletSeed(Vec<u8>);
|
pub struct WalletSeed(Vec<u8>);
|
||||||
|
@ -108,10 +108,8 @@ impl WalletSeed {
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
path.push(backup_seed_file_name.clone());
|
path.push(backup_seed_file_name.clone());
|
||||||
if let Err(_) = fs::rename(seed_file_name, backup_seed_file_name.as_str()) {
|
if fs::rename(seed_file_name, backup_seed_file_name.as_str()).is_err() {
|
||||||
return Err(ErrorKind::GenericError(
|
return Err(ErrorKind::GenericError("Can't rename wallet seed file".to_owned()).into());
|
||||||
"Can't rename wallet seed file".to_owned(),
|
|
||||||
))?;
|
|
||||||
}
|
}
|
||||||
warn!("{} backed up as {}", seed_file_name, backup_seed_file_name);
|
warn!("{} backed up as {}", seed_file_name, backup_seed_file_name);
|
||||||
Ok(backup_seed_file_name)
|
Ok(backup_seed_file_name)
|
||||||
|
@ -133,7 +131,8 @@ impl WalletSeed {
|
||||||
data_file_dir.to_owned(),
|
data_file_dir.to_owned(),
|
||||||
"To create a new wallet from a recovery phrase, use 'grin-wallet init -r'"
|
"To create a new wallet from a recovery phrase, use 'grin-wallet init -r'"
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
let seed = WalletSeed::from_mnemonic(word_list)?;
|
let seed = WalletSeed::from_mnemonic(word_list)?;
|
||||||
let enc_seed = EncryptedWalletSeed::from_seed(&seed, password)?;
|
let enc_seed = EncryptedWalletSeed::from_seed(&seed, password)?;
|
||||||
|
@ -162,7 +161,7 @@ impl WalletSeed {
|
||||||
if exists && !test_mode {
|
if exists && !test_mode {
|
||||||
let msg = format!("Wallet seed already exists at: {}", data_file_dir);
|
let msg = format!("Wallet seed already exists at: {}", data_file_dir);
|
||||||
error!("{}", msg);
|
error!("{}", msg);
|
||||||
return Err(ErrorKind::WalletSeedExists(msg))?;
|
return Err(ErrorKind::WalletSeedExists(msg).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let seed = match recovery_phrase {
|
let seed = match recovery_phrase {
|
||||||
|
@ -203,7 +202,7 @@ impl WalletSeed {
|
||||||
Run \"grin-wallet init\" to initialize a new wallet.",
|
Run \"grin-wallet init\" to initialize a new wallet.",
|
||||||
seed_file_path
|
seed_file_path
|
||||||
);
|
);
|
||||||
Err(ErrorKind::WalletSeedDoesntExist)?
|
Err(ErrorKind::WalletSeedDoesntExist.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +241,7 @@ impl EncryptedWalletSeed {
|
||||||
let mut key = [0; 32];
|
let mut key = [0; 32];
|
||||||
pbkdf2::derive(&digest::SHA512, 100, &salt, password, &mut key);
|
pbkdf2::derive(&digest::SHA512, 100, &salt, password, &mut key);
|
||||||
let content = seed.0.to_vec();
|
let content = seed.0.to_vec();
|
||||||
let mut enc_bytes = content.clone();
|
let mut enc_bytes = content;
|
||||||
let suffix_len = aead::CHACHA20_POLY1305.tag_len();
|
let suffix_len = aead::CHACHA20_POLY1305.tag_len();
|
||||||
for _ in 0..suffix_len {
|
for _ in 0..suffix_len {
|
||||||
enc_bytes.push(0);
|
enc_bytes.push(0);
|
||||||
|
@ -262,15 +261,15 @@ impl EncryptedWalletSeed {
|
||||||
pub fn decrypt(&self, password: &str) -> Result<WalletSeed, Error> {
|
pub fn decrypt(&self, password: &str) -> Result<WalletSeed, Error> {
|
||||||
let mut encrypted_seed = match util::from_hex(self.encrypted_seed.clone()) {
|
let mut encrypted_seed = match util::from_hex(self.encrypted_seed.clone()) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(_) => return Err(ErrorKind::Encryption)?,
|
Err(_) => return Err(ErrorKind::Encryption.into()),
|
||||||
};
|
};
|
||||||
let salt = match util::from_hex(self.salt.clone()) {
|
let salt = match util::from_hex(self.salt.clone()) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(_) => return Err(ErrorKind::Encryption)?,
|
Err(_) => return Err(ErrorKind::Encryption.into()),
|
||||||
};
|
};
|
||||||
let nonce = match util::from_hex(self.nonce.clone()) {
|
let nonce = match util::from_hex(self.nonce.clone()) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(_) => return Err(ErrorKind::Encryption)?,
|
Err(_) => return Err(ErrorKind::Encryption.into()),
|
||||||
};
|
};
|
||||||
let password = password.as_bytes();
|
let password = password.as_bytes();
|
||||||
let mut key = [0; 32];
|
let mut key = [0; 32];
|
||||||
|
|
|
@ -142,9 +142,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
) -> Result<Option<(TxKernel, u64, u64)>, libwallet::Error> {
|
) -> Result<Option<(TxKernel, u64, u64)>, libwallet::Error> {
|
||||||
let version = self
|
let version = self
|
||||||
.get_version_info()
|
.get_version_info()
|
||||||
.ok_or(libwallet::ErrorKind::ClientCallback(
|
.ok_or_else(|| libwallet::ErrorKind::ClientCallback("Unable to get version".into()))?;
|
||||||
"Unable to get version".into(),
|
|
||||||
))?;
|
|
||||||
let version = Version::parse(&version.node_version)
|
let version = Version::parse(&version.node_version)
|
||||||
.map_err(|_| libwallet::ErrorKind::ClientCallback("Unable to parse version".into()))?;
|
.map_err(|_| libwallet::ErrorKind::ClientCallback("Unable to parse version".into()))?;
|
||||||
if version <= Version::new(2, 0, 0) {
|
if version <= Version::new(2, 0, 0) {
|
||||||
|
@ -159,12 +157,12 @@ impl NodeClient for HTTPNodeClient {
|
||||||
query += &format!("min_height={}", h);
|
query += &format!("min_height={}", h);
|
||||||
}
|
}
|
||||||
if let Some(h) = max_height {
|
if let Some(h) = max_height {
|
||||||
if query.len() > 0 {
|
if !query.is_empty() {
|
||||||
query += "&";
|
query += "&";
|
||||||
}
|
}
|
||||||
query += &format!("max_height={}", h);
|
query += &format!("max_height={}", h);
|
||||||
}
|
}
|
||||||
if query.len() > 0 {
|
if !query.is_empty() {
|
||||||
query.insert_str(0, "?");
|
query.insert_str(0, "?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +290,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
out,
|
out,
|
||||||
e);
|
e);
|
||||||
error!("{}", msg);
|
error!("{}", msg);
|
||||||
Err(libwallet::ErrorKind::ClientCallback(msg))?
|
return Err(libwallet::ErrorKind::ClientCallback(msg).into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let block_height = match out.block_height {
|
let block_height = match out.block_height {
|
||||||
|
@ -302,7 +300,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
out.commit,
|
out.commit,
|
||||||
out);
|
out);
|
||||||
error!("{}", msg);
|
error!("{}", msg);
|
||||||
Err(libwallet::ErrorKind::ClientCallback(msg))?
|
return Err(libwallet::ErrorKind::ClientCallback(msg).into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
api_outputs.push((
|
api_outputs.push((
|
||||||
|
@ -322,7 +320,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
addr, e
|
addr, e
|
||||||
);
|
);
|
||||||
let report = format!("outputs by pmmr index: {}", e);
|
let report = format!("outputs by pmmr index: {}", e);
|
||||||
Err(libwallet::ErrorKind::ClientCallback(report))?
|
Err(libwallet::ErrorKind::ClientCallback(report).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,7 +347,7 @@ impl NodeClient for HTTPNodeClient {
|
||||||
// if we got anything other than 200 back from server, bye
|
// if we got anything other than 200 back from server, bye
|
||||||
error!("heightstopmmr: error contacting {}. Error: {}", addr, e);
|
error!("heightstopmmr: error contacting {}. Error: {}", addr, e);
|
||||||
let report = format!(": {}", e);
|
let report = format!(": {}", e);
|
||||||
Err(libwallet::ErrorKind::ClientCallback(report))?
|
Err(libwallet::ErrorKind::ClientCallback(report).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ fn get_output_local(chain: &chain::Chain, commit: &pedersen::Commitment) -> Opti
|
||||||
];
|
];
|
||||||
|
|
||||||
for x in outputs.iter() {
|
for x in outputs.iter() {
|
||||||
if let Ok(_) = chain.is_unspent(&x) {
|
if chain.is_unspent(&x).is_ok() {
|
||||||
let block_height = chain.get_header_for_output(&x).unwrap().height;
|
let block_height = chain.get_header_for_output(&x).unwrap().height;
|
||||||
let output_pos = chain.get_output_pos(&x.commit).unwrap_or(0);
|
let output_pos = chain.get_output_pos(&x.commit).unwrap_or(0);
|
||||||
return Some(api::Output::new(&commit, block_height, output_pos));
|
return Some(api::Output::new(&commit, block_height, output_pos));
|
||||||
|
@ -221,8 +221,7 @@ where
|
||||||
let slate_i = owner::init_send_tx(&mut **w, keychain_mask, args, test_mode)?;
|
let slate_i = owner::init_send_tx(&mut **w, keychain_mask, args, test_mode)?;
|
||||||
let slate = client.send_tx_slate_direct(dest, &slate_i)?;
|
let slate = client.send_tx_slate_direct(dest, &slate_i)?;
|
||||||
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate, 0)?;
|
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate, 0)?;
|
||||||
let slate = owner::finalize_tx(&mut **w, keychain_mask, &slate)?;
|
owner::finalize_tx(&mut **w, keychain_mask, &slate)?
|
||||||
slate
|
|
||||||
};
|
};
|
||||||
let client = {
|
let client = {
|
||||||
let mut w_lock = wallet.lock();
|
let mut w_lock = wallet.lock();
|
||||||
|
|
|
@ -100,7 +100,7 @@ where
|
||||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||||
let dir_name = format!("{}/.grin", chain_dir);
|
let dir_name = format!("{}/.grin", chain_dir);
|
||||||
let c = Chain::init(
|
let c = Chain::init(
|
||||||
dir_name.to_string(),
|
dir_name,
|
||||||
Arc::new(NoopAdapter {}),
|
Arc::new(NoopAdapter {}),
|
||||||
genesis_block,
|
genesis_block,
|
||||||
pow::verify_size,
|
pow::verify_size,
|
||||||
|
@ -109,15 +109,14 @@ where
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
let retval = WalletProxy {
|
WalletProxy {
|
||||||
chain_dir: chain_dir.to_owned(),
|
chain_dir: chain_dir.to_owned(),
|
||||||
chain: Arc::new(c),
|
chain: Arc::new(c),
|
||||||
tx: tx,
|
tx: tx,
|
||||||
rx: rx,
|
rx: rx,
|
||||||
wallets: HashMap::new(),
|
wallets: HashMap::new(),
|
||||||
running: Arc::new(AtomicBool::new(false)),
|
running: Arc::new(AtomicBool::new(false)),
|
||||||
};
|
}
|
||||||
retval
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add wallet with a given "address"
|
/// Add wallet with a given "address"
|
||||||
|
@ -274,12 +273,12 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
m: WalletProxyMessage,
|
m: WalletProxyMessage,
|
||||||
) -> Result<WalletProxyMessage, libwallet::Error> {
|
) -> Result<WalletProxyMessage, libwallet::Error> {
|
||||||
let split = m.body.split(",");
|
let split = m.body.split(',');
|
||||||
//let mut api_outputs: HashMap<pedersen::Commitment, String> = HashMap::new();
|
//let mut api_outputs: HashMap<pedersen::Commitment, String> = HashMap::new();
|
||||||
let mut outputs: Vec<api::Output> = vec![];
|
let mut outputs: Vec<api::Output> = vec![];
|
||||||
for o in split {
|
for o in split {
|
||||||
let o_str = String::from(o);
|
let o_str = String::from(o);
|
||||||
if o_str.len() == 0 {
|
if o_str.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let c = util::from_hex(o_str).unwrap();
|
let c = util::from_hex(o_str).unwrap();
|
||||||
|
@ -302,7 +301,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
m: WalletProxyMessage,
|
m: WalletProxyMessage,
|
||||||
) -> Result<WalletProxyMessage, libwallet::Error> {
|
) -> Result<WalletProxyMessage, libwallet::Error> {
|
||||||
let split = m.body.split(",").collect::<Vec<&str>>();
|
let split = m.body.split(',').collect::<Vec<&str>>();
|
||||||
let start_index = split[0].parse::<u64>().unwrap();
|
let start_index = split[0].parse::<u64>().unwrap();
|
||||||
let max = split[1].parse::<u64>().unwrap();
|
let max = split[1].parse::<u64>().unwrap();
|
||||||
let end_index = split[2].parse::<u64>().unwrap();
|
let end_index = split[2].parse::<u64>().unwrap();
|
||||||
|
@ -325,7 +324,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
m: WalletProxyMessage,
|
m: WalletProxyMessage,
|
||||||
) -> Result<WalletProxyMessage, libwallet::Error> {
|
) -> Result<WalletProxyMessage, libwallet::Error> {
|
||||||
let split = m.body.split(",").collect::<Vec<&str>>();
|
let split = m.body.split(',').collect::<Vec<&str>>();
|
||||||
let start_index = split[0].parse::<u64>().unwrap();
|
let start_index = split[0].parse::<u64>().unwrap();
|
||||||
let end_index = split[1].parse::<u64>().unwrap();
|
let end_index = split[1].parse::<u64>().unwrap();
|
||||||
let end_index = match end_index {
|
let end_index = match end_index {
|
||||||
|
@ -347,7 +346,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
m: WalletProxyMessage,
|
m: WalletProxyMessage,
|
||||||
) -> Result<WalletProxyMessage, libwallet::Error> {
|
) -> Result<WalletProxyMessage, libwallet::Error> {
|
||||||
let split = m.body.split(",").collect::<Vec<&str>>();
|
let split = m.body.split(',').collect::<Vec<&str>>();
|
||||||
let excess = split[0].parse::<String>().unwrap();
|
let excess = split[0].parse::<String>().unwrap();
|
||||||
let min = split[1].parse::<u64>().unwrap();
|
let min = split[1].parse::<u64>().unwrap();
|
||||||
let max = split[2].parse::<u64>().unwrap();
|
let max = split[2].parse::<u64>().unwrap();
|
||||||
|
@ -457,7 +456,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
}
|
}
|
||||||
let r = self.rx.lock();
|
let r = self.rx.lock();
|
||||||
let m = r.recv().unwrap();
|
let m = r.recv().unwrap();
|
||||||
trace!("Received post_tx response: {:?}", m.clone());
|
trace!("Received post_tx response: {:?}", m);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +483,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
.context(libwallet::ErrorKind::ClientCallback(
|
.context(libwallet::ErrorKind::ClientCallback(
|
||||||
"Parsing get_height response".to_owned(),
|
"Parsing get_height response".to_owned(),
|
||||||
))?;
|
))?;
|
||||||
let split: Vec<&str> = res.split(",").collect();
|
let split: Vec<&str> = res.split(',').collect();
|
||||||
Ok((split[0].parse::<u64>().unwrap(), split[1].to_owned()))
|
Ok((split[0].parse::<u64>().unwrap(), split[1].to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,7 +494,7 @@ impl NodeClient for LocalWalletClient {
|
||||||
) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, libwallet::Error> {
|
) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, libwallet::Error> {
|
||||||
let query_params: Vec<String> = wallet_outputs
|
let query_params: Vec<String> = wallet_outputs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|commit| format!("{}", util::to_hex(commit.as_ref().to_vec())))
|
.map(|commit| util::to_hex(commit.as_ref().to_vec()))
|
||||||
.collect();
|
.collect();
|
||||||
let query_str = query_params.join(",");
|
let query_str = query_params.join(",");
|
||||||
let m = WalletProxyMessage {
|
let m = WalletProxyMessage {
|
||||||
|
|
|
@ -28,13 +28,13 @@ use std::path::{Path, MAIN_SEPARATOR};
|
||||||
|
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
|
||||||
const SEC_KEY_FILE: &'static str = "hs_ed25519_secret_key";
|
const SEC_KEY_FILE: &str = "hs_ed25519_secret_key";
|
||||||
const PUB_KEY_FILE: &'static str = "hs_ed25519_public_key";
|
const PUB_KEY_FILE: &str = "hs_ed25519_public_key";
|
||||||
const HOSTNAME_FILE: &'static str = "hostname";
|
const HOSTNAME_FILE: &str = "hostname";
|
||||||
const TORRC_FILE: &'static str = "torrc";
|
const TORRC_FILE: &str = "torrc";
|
||||||
const TOR_DATA_DIR: &'static str = "data";
|
const TOR_DATA_DIR: &str = "data";
|
||||||
const AUTH_CLIENTS_DIR: &'static str = "authorized_clients";
|
const AUTH_CLIENTS_DIR: &str = "authorized_clients";
|
||||||
const HIDDEN_SERVICES_DIR: &'static str = "onion_service_addresses";
|
const HIDDEN_SERVICES_DIR: &str = "onion_service_addresses";
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn set_permissions(file_path: &str) -> Result<(), Error> {
|
fn set_permissions(file_path: &str) -> Result<(), Error> {
|
||||||
|
@ -100,7 +100,7 @@ pub fn create_onion_service_sec_key_file(
|
||||||
let key_file_path = &format!("{}{}{}", os_directory, MAIN_SEPARATOR, SEC_KEY_FILE);
|
let key_file_path = &format!("{}{}{}", os_directory, MAIN_SEPARATOR, SEC_KEY_FILE);
|
||||||
let mut file = File::create(key_file_path).context(ErrorKind::IO)?;
|
let mut file = File::create(key_file_path).context(ErrorKind::IO)?;
|
||||||
// Tag is always 32 bytes, so pad with null zeroes
|
// Tag is always 32 bytes, so pad with null zeroes
|
||||||
file.write("== ed25519v1-secret: type0 ==\0\0\0".as_bytes())
|
file.write(b"== ed25519v1-secret: type0 ==\0\0\0")
|
||||||
.context(ErrorKind::IO)?;
|
.context(ErrorKind::IO)?;
|
||||||
let expanded_skey: ExpandedSecretKey = ExpandedSecretKey::from(sec_key);
|
let expanded_skey: ExpandedSecretKey = ExpandedSecretKey::from(sec_key);
|
||||||
file.write_all(&expanded_skey.to_bytes())
|
file.write_all(&expanded_skey.to_bytes())
|
||||||
|
@ -115,7 +115,7 @@ pub fn create_onion_service_pub_key_file(
|
||||||
let key_file_path = &format!("{}{}{}", os_directory, MAIN_SEPARATOR, PUB_KEY_FILE);
|
let key_file_path = &format!("{}{}{}", os_directory, MAIN_SEPARATOR, PUB_KEY_FILE);
|
||||||
let mut file = File::create(key_file_path).context(ErrorKind::IO)?;
|
let mut file = File::create(key_file_path).context(ErrorKind::IO)?;
|
||||||
// Tag is always 32 bytes, so pad with null zeroes
|
// Tag is always 32 bytes, so pad with null zeroes
|
||||||
file.write("== ed25519v1-public: type0 ==\0\0\0".as_bytes())
|
file.write(b"== ed25519v1-public: type0 ==\0\0\0")
|
||||||
.context(ErrorKind::IO)?;
|
.context(ErrorKind::IO)?;
|
||||||
file.write_all(pub_key.as_bytes()).context(ErrorKind::IO)?;
|
file.write_all(pub_key.as_bytes()).context(ErrorKind::IO)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -170,7 +170,7 @@ pub fn output_torrc(
|
||||||
tor_config_directory: &str,
|
tor_config_directory: &str,
|
||||||
wallet_listener_addr: &str,
|
wallet_listener_addr: &str,
|
||||||
socks_port: &str,
|
socks_port: &str,
|
||||||
service_dirs: &Vec<String>,
|
service_dirs: &[String],
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let torrc_file_path = format!("{}{}{}", tor_config_directory, MAIN_SEPARATOR, TORRC_FILE);
|
let torrc_file_path = format!("{}{}{}", tor_config_directory, MAIN_SEPARATOR, TORRC_FILE);
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ pub fn output_torrc(
|
||||||
pub fn output_tor_listener_config(
|
pub fn output_tor_listener_config(
|
||||||
tor_config_directory: &str,
|
tor_config_directory: &str,
|
||||||
wallet_listener_addr: &str,
|
wallet_listener_addr: &str,
|
||||||
listener_keys: &Vec<SecretKey>,
|
listener_keys: &[SecretKey],
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let tor_data_dir = format!("{}{}{}", tor_config_directory, MAIN_SEPARATOR, TOR_DATA_DIR);
|
let tor_data_dir = format!("{}{}{}", tor_config_directory, MAIN_SEPARATOR, TOR_DATA_DIR);
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ pub fn output_tor_sender_config(
|
||||||
// create data directory if it doesn't exist
|
// create data directory if it doesn't exist
|
||||||
fs::create_dir_all(&tor_config_dir).context(ErrorKind::IO)?;
|
fs::create_dir_all(&tor_config_dir).context(ErrorKind::IO)?;
|
||||||
|
|
||||||
output_torrc(tor_config_dir, "", socks_listener_addr, &vec![])?;
|
output_torrc(tor_config_dir, "", socks_listener_addr, &[])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -238,13 +238,13 @@ pub fn is_tor_address(input: &str) -> Result<(), Error> {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = format!("{:?}", e);
|
let msg = format!("{:?}", e);
|
||||||
Err(ErrorKind::NotOnion(msg).to_owned())?
|
Err(ErrorKind::NotOnion(msg).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete_tor_address(input: &str) -> Result<String, Error> {
|
pub fn complete_tor_address(input: &str) -> Result<String, Error> {
|
||||||
let _ = is_tor_address(input)?;
|
is_tor_address(input)?;
|
||||||
let mut input = input.to_uppercase();
|
let mut input = input.to_uppercase();
|
||||||
if !input.starts_with("HTTP://") && !input.starts_with("HTTPS://") {
|
if !input.starts_with("HTTP://") && !input.starts_with("HTTPS://") {
|
||||||
input = format!("HTTP://{}", input);
|
input = format!("HTTP://{}", input);
|
||||||
|
@ -278,7 +278,7 @@ mod tests {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
let secp_inst = static_secp_instance();
|
let secp_inst = static_secp_instance();
|
||||||
let secp = secp_inst.lock();
|
let secp = secp_inst.lock();
|
||||||
let mut test_rng = StepRng::new(1234567890u64, 1);
|
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
|
||||||
let sec_key = secp::key::SecretKey::new(&secp, &mut test_rng);
|
let sec_key = secp::key::SecretKey::new(&secp, &mut test_rng);
|
||||||
output_onion_service_config(test_dir, &sec_key)?;
|
output_onion_service_config(test_dir, &sec_key)?;
|
||||||
clean_output_dir(test_dir);
|
clean_output_dir(test_dir);
|
||||||
|
@ -291,9 +291,9 @@ mod tests {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
let secp_inst = static_secp_instance();
|
let secp_inst = static_secp_instance();
|
||||||
let secp = secp_inst.lock();
|
let secp = secp_inst.lock();
|
||||||
let mut test_rng = StepRng::new(1234567890u64, 1);
|
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
|
||||||
let sec_key = secp::key::SecretKey::new(&secp, &mut test_rng);
|
let sec_key = secp::key::SecretKey::new(&secp, &mut test_rng);
|
||||||
output_tor_listener_config(test_dir, "127.0.0.1:3415", &vec![sec_key])?;
|
output_tor_listener_config(test_dir, "127.0.0.1:3415", &[sec_key])?;
|
||||||
clean_output_dir(test_dir);
|
clean_output_dir(test_dir);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,9 +61,9 @@ use std::thread;
|
||||||
use sysinfo::{Process, ProcessExt, Signal};
|
use sysinfo::{Process, ProcessExt, Signal};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const TOR_EXE_NAME: &'static str = "tor.exe";
|
const TOR_EXE_NAME: &str = "tor.exe";
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
const TOR_EXE_NAME: &'static str = "tor";
|
const TOR_EXE_NAME: &str = "tor";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -160,7 +160,7 @@ impl TorProcess {
|
||||||
let pid_file_name = format!("{}{}pid", d, MAIN_SEPARATOR);
|
let pid_file_name = format!("{}{}pid", d, MAIN_SEPARATOR);
|
||||||
// kill off PID if its already running
|
// kill off PID if its already running
|
||||||
if Path::new(&pid_file_name).exists() {
|
if Path::new(&pid_file_name).exists() {
|
||||||
let pid = fs::read_to_string(&pid_file_name).map_err(|err| Error::IO(err))?;
|
let pid = fs::read_to_string(&pid_file_name).map_err(Error::IO)?;
|
||||||
let pid = pid
|
let pid = pid
|
||||||
.parse::<i32>()
|
.parse::<i32>()
|
||||||
.map_err(|err| Error::PID(format!("{:?}", err)))?;
|
.map_err(|err| Error::PID(format!("{:?}", err)))?;
|
||||||
|
@ -186,9 +186,9 @@ impl TorProcess {
|
||||||
// split out the process id, so if we don't exit cleanly
|
// split out the process id, so if we don't exit cleanly
|
||||||
// we can take it down on the next run
|
// we can take it down on the next run
|
||||||
let pid_file_name = format!("{}{}pid", d, MAIN_SEPARATOR);
|
let pid_file_name = format!("{}{}pid", d, MAIN_SEPARATOR);
|
||||||
let mut file = File::create(pid_file_name).map_err(|err| Error::IO(err))?;
|
let mut file = File::create(pid_file_name).map_err(Error::IO)?;
|
||||||
file.write_all(format!("{}", tor_process.id()).as_bytes())
|
file.write_all(format!("{}", tor_process.id()).as_bytes())
|
||||||
.map_err(|err| Error::IO(err))?;
|
.map_err(Error::IO)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = BufReader::new(tor_process.stdout.take().unwrap());
|
let stdout = BufReader::new(tor_process.stdout.take().unwrap());
|
||||||
|
@ -228,7 +228,7 @@ impl TorProcess {
|
||||||
completion_perc: u8,
|
completion_perc: u8,
|
||||||
) -> Result<BufReader<ChildStdout>, Error> {
|
) -> Result<BufReader<ChildStdout>, Error> {
|
||||||
let re_bootstrap = Regex::new(r"^\[notice\] Bootstrapped (?P<perc>[0-9]+)%(.*): ")
|
let re_bootstrap = Regex::new(r"^\[notice\] Bootstrapped (?P<perc>[0-9]+)%(.*): ")
|
||||||
.map_err(|err| Error::Regex(err))?;
|
.map_err(Error::Regex)?;
|
||||||
|
|
||||||
let timestamp_len = "May 16 02:50:08.792".len();
|
let timestamp_len = "May 16 02:50:08.792".len();
|
||||||
let mut warnings = Vec::new();
|
let mut warnings = Vec::new();
|
||||||
|
@ -253,7 +253,7 @@ impl TorProcess {
|
||||||
.captures(line)
|
.captures(line)
|
||||||
.and_then(|c| c.name("perc"))
|
.and_then(|c| c.name("perc"))
|
||||||
.and_then(|pc| pc.as_str().parse::<u8>().ok())
|
.and_then(|pc| pc.as_str().parse::<u8>().ok())
|
||||||
.ok_or(Error::InvalidBootstrapLine(line.to_string()))?;
|
.ok_or_else(|| Error::InvalidBootstrapLine(line.to_string()))?;
|
||||||
|
|
||||||
if perc >= completion_perc {
|
if perc >= completion_perc {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -37,7 +37,7 @@ where
|
||||||
// for m/0: m/0/1/0, m/0/1/1
|
// for m/0: m/0/1/0, m/0/1/1
|
||||||
// for m/1: m/1/1/0, m/1/1/1
|
// for m/1: m/1/1/0, m/1/1/1
|
||||||
key_path.path[1] = ChildNumber::from(1);
|
key_path.path[1] = ChildNumber::from(1);
|
||||||
key_path.depth = key_path.depth + 1;
|
key_path.depth += 1;
|
||||||
key_path.path[key_path.depth as usize - 1] = ChildNumber::from(index);
|
key_path.path[key_path.depth as usize - 1] = ChildNumber::from(index);
|
||||||
let key_id = Identifier::from_path(&key_path);
|
let key_id = Identifier::from_path(&key_path);
|
||||||
let sec_key = keychain.derive_key(0, &key_id, &SwitchCommitmentType::None)?;
|
let sec_key = keychain.derive_key(0, &key_id, &SwitchCommitmentType::None)?;
|
||||||
|
|
|
@ -114,7 +114,7 @@ where
|
||||||
false,
|
false,
|
||||||
use_test_rng,
|
use_test_rng,
|
||||||
)?;
|
)?;
|
||||||
tx::update_message(&mut *w, keychain_mask, &mut ret_slate)?;
|
tx::update_message(&mut *w, keychain_mask, &ret_slate)?;
|
||||||
|
|
||||||
let keychain = w.keychain(keychain_mask)?;
|
let keychain = w.keychain(keychain_mask)?;
|
||||||
let excess = ret_slate.calc_excess(&keychain)?;
|
let excess = ret_slate.calc_excess(&keychain)?;
|
||||||
|
@ -148,8 +148,8 @@ where
|
||||||
check_ttl(w, &sl)?;
|
check_ttl(w, &sl)?;
|
||||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 1)?;
|
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 1)?;
|
||||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 1, &context)?;
|
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 1, &context)?;
|
||||||
tx::update_stored_tx(&mut *w, keychain_mask, &context, &mut sl, true)?;
|
tx::update_stored_tx(&mut *w, keychain_mask, &context, &sl, true)?;
|
||||||
tx::update_message(&mut *w, keychain_mask, &mut sl)?;
|
tx::update_message(&mut *w, keychain_mask, &sl)?;
|
||||||
{
|
{
|
||||||
let mut batch = w.batch(keychain_mask)?;
|
let mut batch = w.batch(keychain_mask)?;
|
||||||
batch.delete_private_context(sl.id.as_bytes(), 1)?;
|
batch.delete_private_context(sl.id.as_bytes(), 1)?;
|
||||||
|
|
|
@ -111,15 +111,16 @@ where
|
||||||
C: NodeClient + 'a,
|
C: NodeClient + 'a,
|
||||||
K: Keychain + 'a,
|
K: Keychain + 'a,
|
||||||
{
|
{
|
||||||
let mut validated = false;
|
let validated = if refresh_from_node {
|
||||||
if refresh_from_node {
|
update_wallet_state(
|
||||||
validated = update_wallet_state(
|
|
||||||
wallet_inst.clone(),
|
wallet_inst.clone(),
|
||||||
keychain_mask,
|
keychain_mask,
|
||||||
status_send_channel,
|
status_send_channel,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?
|
||||||
}
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = w.parent_key_id();
|
||||||
|
@ -150,15 +151,16 @@ where
|
||||||
C: NodeClient + 'a,
|
C: NodeClient + 'a,
|
||||||
K: Keychain + 'a,
|
K: Keychain + 'a,
|
||||||
{
|
{
|
||||||
let mut validated = false;
|
let validated = if refresh_from_node {
|
||||||
if refresh_from_node {
|
update_wallet_state(
|
||||||
validated = update_wallet_state(
|
|
||||||
wallet_inst.clone(),
|
wallet_inst.clone(),
|
||||||
keychain_mask,
|
keychain_mask,
|
||||||
status_send_channel,
|
status_send_channel,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?
|
||||||
}
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = w.parent_key_id();
|
||||||
|
@ -180,15 +182,16 @@ where
|
||||||
C: NodeClient + 'a,
|
C: NodeClient + 'a,
|
||||||
K: Keychain + 'a,
|
K: Keychain + 'a,
|
||||||
{
|
{
|
||||||
let mut validated = false;
|
let validated = if refresh_from_node {
|
||||||
if refresh_from_node {
|
update_wallet_state(
|
||||||
validated = update_wallet_state(
|
|
||||||
wallet_inst.clone(),
|
wallet_inst.clone(),
|
||||||
keychain_mask,
|
keychain_mask,
|
||||||
status_send_channel,
|
status_send_channel,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?
|
||||||
}
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = w.parent_key_id();
|
||||||
|
@ -216,15 +219,16 @@ where
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
let mut _validated = false;
|
|
||||||
if refresh_from_node {
|
if refresh_from_node {
|
||||||
_validated = update_wallet_state(
|
update_wallet_state(
|
||||||
wallet_inst.clone(),
|
wallet_inst.clone(),
|
||||||
keychain_mask,
|
keychain_mask,
|
||||||
status_send_channel,
|
status_send_channel,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?
|
||||||
}
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
let txs = retrieve_txs(
|
let txs = retrieve_txs(
|
||||||
wallet_inst.clone(),
|
wallet_inst.clone(),
|
||||||
keychain_mask,
|
keychain_mask,
|
||||||
|
@ -469,7 +473,7 @@ where
|
||||||
check_ttl(w, &ret_slate)?;
|
check_ttl(w, &ret_slate)?;
|
||||||
let parent_key_id = match args.src_acct_name {
|
let parent_key_id = match args.src_acct_name {
|
||||||
Some(d) => {
|
Some(d) => {
|
||||||
let pm = w.get_acct_path(d.to_owned())?;
|
let pm = w.get_acct_path(d)?;
|
||||||
match pm {
|
match pm {
|
||||||
Some(p) => p.path,
|
Some(p) => p.path,
|
||||||
None => w.parent_key_id(),
|
None => w.parent_key_id(),
|
||||||
|
@ -570,8 +574,8 @@ where
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = w.parent_key_id();
|
||||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 0, &context)?;
|
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 0, &context)?;
|
||||||
tx::verify_slate_payment_proof(&mut *w, keychain_mask, &parent_key_id, &context, &sl)?;
|
tx::verify_slate_payment_proof(&mut *w, keychain_mask, &parent_key_id, &context, &sl)?;
|
||||||
tx::update_stored_tx(&mut *w, keychain_mask, &context, &mut sl, false)?;
|
tx::update_stored_tx(&mut *w, keychain_mask, &context, &sl, false)?;
|
||||||
tx::update_message(&mut *w, keychain_mask, &mut sl)?;
|
tx::update_message(&mut *w, keychain_mask, &sl)?;
|
||||||
{
|
{
|
||||||
let mut batch = w.batch(keychain_mask)?;
|
let mut batch = w.batch(keychain_mask)?;
|
||||||
batch.delete_private_context(sl.id.as_bytes(), 0)?;
|
batch.delete_private_context(sl.id.as_bytes(), 0)?;
|
||||||
|
@ -601,7 +605,8 @@ where
|
||||||
)? {
|
)? {
|
||||||
return Err(ErrorKind::TransactionCancellationError(
|
return Err(ErrorKind::TransactionCancellationError(
|
||||||
"Can't contact running Grin node. Not Cancelling.",
|
"Can't contact running Grin node. Not Cancelling.",
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = w.parent_key_id();
|
||||||
|
@ -739,7 +744,7 @@ where
|
||||||
{
|
{
|
||||||
let parent_key_id = {
|
let parent_key_id = {
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
w.parent_key_id().clone()
|
w.parent_key_id()
|
||||||
};
|
};
|
||||||
let client = {
|
let client = {
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
|
@ -822,7 +827,7 @@ where
|
||||||
let start_index = last_scanned_block.height.saturating_sub(100);
|
let start_index = last_scanned_block.height.saturating_sub(100);
|
||||||
|
|
||||||
if last_scanned_block.height == 0 {
|
if last_scanned_block.height == 0 {
|
||||||
let msg = format!("This wallet has not been scanned against the current chain. Beginning full scan... (this first scan may take a while, but subsequent scans will be much quicker)");
|
let msg = "This wallet has not been scanned against the current chain. Beginning full scan... (this first scan may take a while, but subsequent scans will be much quicker)".to_string();
|
||||||
if let Some(ref s) = status_send_channel {
|
if let Some(ref s) = status_send_channel {
|
||||||
let _ = s.send(StatusMessage::FullScanWarn(msg));
|
let _ = s.send(StatusMessage::FullScanWarn(msg));
|
||||||
}
|
}
|
||||||
|
@ -873,7 +878,7 @@ where
|
||||||
let last_confirmed_height = w.last_confirmed_height()?;
|
let last_confirmed_height = w.last_confirmed_height()?;
|
||||||
if let Some(e) = slate.ttl_cutoff_height {
|
if let Some(e) = slate.ttl_cutoff_height {
|
||||||
if last_confirmed_height >= e {
|
if last_confirmed_height >= e {
|
||||||
return Err(ErrorKind::TransactionExpired)?;
|
return Err(ErrorKind::TransactionExpired.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -906,35 +911,31 @@ where
|
||||||
// Check kernel exists
|
// Check kernel exists
|
||||||
match client.get_kernel(&proof.excess, None, None) {
|
match client.get_kernel(&proof.excess, None, None) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(format!(
|
||||||
format!("Error retrieving kernel from chain: {}", e).to_owned(),
|
"Error retrieving kernel from chain: {}",
|
||||||
))?;
|
e
|
||||||
|
))
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(format!(
|
||||||
format!(
|
"Transaction kernel with excess {:?} not found on chain",
|
||||||
"Transaction kernel with excess {:?} not found on chain",
|
proof.excess
|
||||||
proof.excess
|
))
|
||||||
)
|
.into());
|
||||||
.to_owned(),
|
|
||||||
))?;
|
|
||||||
}
|
}
|
||||||
Ok(Some(_)) => {}
|
Ok(Some(_)) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check Sigs
|
// Check Sigs
|
||||||
let recipient_pubkey = proof.recipient_address.to_ed25519()?;
|
let recipient_pubkey = proof.recipient_address.to_ed25519()?;
|
||||||
if let Err(_) = recipient_pubkey.verify(&msg, &proof.recipient_sig) {
|
if recipient_pubkey.verify(&msg, &proof.recipient_sig).is_err() {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof("Invalid recipient signature".to_owned()).into());
|
||||||
"Invalid recipient signature".to_owned(),
|
|
||||||
))?;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let sender_pubkey = proof.sender_address.to_ed25519()?;
|
let sender_pubkey = proof.sender_address.to_ed25519()?;
|
||||||
if let Err(_) = sender_pubkey.verify(&msg, &proof.sender_sig) {
|
if sender_pubkey.verify(&msg, &proof.sender_sig).is_err() {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof("Invalid sender signature".to_owned()).into());
|
||||||
"Invalid sender signature".to_owned(),
|
|
||||||
))?;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// for now, simple test as to whether one of the addresses belongs to this wallet
|
// for now, simple test as to whether one of the addresses belongs to this wallet
|
||||||
|
@ -942,7 +943,7 @@ where
|
||||||
let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) {
|
let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) {
|
||||||
Ok(k) => k,
|
Ok(k) => k,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ErrorKind::ED25519Key(format!("{}", e)).to_owned())?;
|
return Err(ErrorKind::ED25519Key(format!("{}", e)).into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let my_address_pubkey: DalekPublicKey = (&d_skey).into();
|
let my_address_pubkey: DalekPublicKey = (&d_skey).into();
|
||||||
|
@ -990,7 +991,7 @@ where
|
||||||
{
|
{
|
||||||
let parent_key_id = {
|
let parent_key_id = {
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
w.parent_key_id().clone()
|
w.parent_key_id()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut client = {
|
let mut client = {
|
||||||
|
|
|
@ -125,10 +125,7 @@ where
|
||||||
let wallet_opened = {
|
let wallet_opened = {
|
||||||
let mut w_lock = self.wallet_inst.lock();
|
let mut w_lock = self.wallet_inst.lock();
|
||||||
let w_provider = w_lock.lc_provider()?;
|
let w_provider = w_lock.lc_provider()?;
|
||||||
match w_provider.wallet_inst() {
|
w_provider.wallet_inst().is_ok()
|
||||||
Ok(_) => true,
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// Business goes here
|
// Business goes here
|
||||||
if wallet_opened {
|
if wallet_opened {
|
||||||
|
|
|
@ -258,13 +258,7 @@ pub enum ErrorKind {
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let show_bt = match env::var("RUST_BACKTRACE") {
|
let show_bt = match env::var("RUST_BACKTRACE") {
|
||||||
Ok(r) => {
|
Ok(r) => r == "1",
|
||||||
if r == "1" {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
};
|
};
|
||||||
let backtrace = match self.backtrace() {
|
let backtrace = match self.backtrace() {
|
||||||
|
@ -273,7 +267,7 @@ impl Display for Error {
|
||||||
};
|
};
|
||||||
let inner_output = format!("{}", self.inner,);
|
let inner_output = format!("{}", self.inner,);
|
||||||
let backtrace_output = format!("\n Backtrace: {}", backtrace);
|
let backtrace_output = format!("\n Backtrace: {}", backtrace);
|
||||||
let mut output = inner_output.clone();
|
let mut output = inner_output;
|
||||||
if show_bt {
|
if show_bt {
|
||||||
output.push_str(&backtrace_output);
|
output.push_str(&backtrace_output);
|
||||||
}
|
}
|
||||||
|
@ -290,7 +284,7 @@ impl Error {
|
||||||
pub fn cause_string(&self) -> String {
|
pub fn cause_string(&self) -> String {
|
||||||
match self.cause() {
|
match self.cause() {
|
||||||
Some(k) => format!("{}", k),
|
Some(k) => format!("{}", k),
|
||||||
None => format!("Unknown"),
|
None => "Unknown".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// get cause
|
/// get cause
|
||||||
|
|
|
@ -71,8 +71,8 @@ where
|
||||||
K: Keychain + 'a,
|
K: Keychain + 'a,
|
||||||
{
|
{
|
||||||
let label = label.to_owned();
|
let label = label.to_owned();
|
||||||
if let Some(_) = wallet.acct_path_iter().find(|l| l.label == label) {
|
if wallet.acct_path_iter().any(|l| l.label == label) {
|
||||||
return Err(ErrorKind::AccountLabelAlreadyExists(label.clone()).into());
|
return Err(ErrorKind::AccountLabelAlreadyExists(label).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're always using paths at m/k/0 for parent keys for output derivations
|
// We're always using paths at m/k/0 for parent keys for output derivations
|
||||||
|
@ -94,7 +94,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let save_path = AcctPathMapping {
|
let save_path = AcctPathMapping {
|
||||||
label: label.to_owned(),
|
label: label,
|
||||||
path: return_id.clone(),
|
path: return_id.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ where
|
||||||
{
|
{
|
||||||
let label = label.to_owned();
|
let label = label.to_owned();
|
||||||
let save_path = AcctPathMapping {
|
let save_path = AcctPathMapping {
|
||||||
label: label.to_owned(),
|
label: label,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -238,21 +238,19 @@ where
|
||||||
t.update_confirmation_ts();
|
t.update_confirmation_ts();
|
||||||
batch.save_tx_log_entry(t, &parent_key_id)?;
|
batch.save_tx_log_entry(t, &parent_key_id)?;
|
||||||
log_id
|
log_id
|
||||||
|
} else if let Some(ref mut s) = tx_stats {
|
||||||
|
let ts = s.get(&parent_key_id).unwrap().clone();
|
||||||
|
s.insert(
|
||||||
|
parent_key_id.clone(),
|
||||||
|
RestoredTxStats {
|
||||||
|
log_id: ts.log_id,
|
||||||
|
amount_credited: ts.amount_credited + output.value,
|
||||||
|
num_outputs: ts.num_outputs + 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ts.log_id
|
||||||
} else {
|
} else {
|
||||||
if let Some(ref mut s) = tx_stats {
|
0
|
||||||
let ts = s.get(&parent_key_id).unwrap().clone();
|
|
||||||
s.insert(
|
|
||||||
parent_key_id.clone(),
|
|
||||||
RestoredTxStats {
|
|
||||||
log_id: ts.log_id,
|
|
||||||
amount_credited: ts.amount_credited + output.value,
|
|
||||||
num_outputs: ts.num_outputs + 1,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
ts.log_id
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = batch.save(OutputData {
|
let _ = batch.save(OutputData {
|
||||||
|
@ -269,9 +267,9 @@ where
|
||||||
tx_log_entry: Some(log_id),
|
tx_log_entry: Some(log_id),
|
||||||
});
|
});
|
||||||
|
|
||||||
let max_child_index = found_parents.get(&parent_key_id).unwrap().clone();
|
let max_child_index = *found_parents.get(&parent_key_id).unwrap();
|
||||||
if output.n_child >= max_child_index {
|
if output.n_child >= max_child_index {
|
||||||
found_parents.insert(parent_key_id.clone(), output.n_child);
|
found_parents.insert(parent_key_id, output.n_child);
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
|
@ -294,12 +292,12 @@ where
|
||||||
let updated_tx_entry = if output.tx_log_entry.is_some() {
|
let updated_tx_entry = if output.tx_log_entry.is_some() {
|
||||||
let entries = updater::retrieve_txs(
|
let entries = updater::retrieve_txs(
|
||||||
&mut **w,
|
&mut **w,
|
||||||
output.tx_log_entry.clone(),
|
output.tx_log_entry,
|
||||||
None,
|
None,
|
||||||
Some(&parent_key_id),
|
Some(&parent_key_id),
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
if entries.len() > 0 {
|
if !entries.is_empty() {
|
||||||
let mut entry = entries[0].clone();
|
let mut entry = entries[0].clone();
|
||||||
match entry.tx_type {
|
match entry.tx_type {
|
||||||
TxLogEntryType::TxSent => entry.tx_type = TxLogEntryType::TxSentCancelled,
|
TxLogEntryType::TxSent => entry.tx_type = TxLogEntryType::TxSentCancelled,
|
||||||
|
@ -343,7 +341,7 @@ where
|
||||||
}
|
}
|
||||||
let (client, keychain) = {
|
let (client, keychain) = {
|
||||||
wallet_lock!(wallet_inst, w);
|
wallet_lock!(wallet_inst, w);
|
||||||
(w.w2n_client().clone(), w.keychain(keychain_mask)?.clone())
|
(w.w2n_client().clone(), w.keychain(keychain_mask)?)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Retrieve the actual PMMR index range we're looking for
|
// Retrieve the actual PMMR index range we're looking for
|
||||||
|
|
|
@ -131,7 +131,7 @@ where
|
||||||
let keychain = wallet.keychain(keychain_mask)?;
|
let keychain = wallet.keychain(keychain_mask)?;
|
||||||
|
|
||||||
let tx_entry = {
|
let tx_entry = {
|
||||||
let lock_inputs = context.get_inputs().clone();
|
let lock_inputs = context.get_inputs();
|
||||||
let messages = Some(slate.participant_messages());
|
let messages = Some(slate.participant_messages());
|
||||||
let slate_id = slate.id;
|
let slate_id = slate.id;
|
||||||
let height = slate.height;
|
let height = slate.height;
|
||||||
|
@ -139,15 +139,14 @@ where
|
||||||
let mut batch = wallet.batch(keychain_mask)?;
|
let mut batch = wallet.batch(keychain_mask)?;
|
||||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||||
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
|
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
|
||||||
t.tx_slate_id = Some(slate_id.clone());
|
t.tx_slate_id = Some(slate_id);
|
||||||
let filename = format!("{}.grintx", slate_id);
|
let filename = format!("{}.grintx", slate_id);
|
||||||
t.stored_tx = Some(filename);
|
t.stored_tx = Some(filename);
|
||||||
t.fee = Some(slate.fee);
|
t.fee = Some(slate.fee);
|
||||||
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
||||||
|
|
||||||
match slate.calc_excess(&keychain) {
|
if let Ok(e) = slate.calc_excess(&keychain) {
|
||||||
Ok(e) => t.kernel_excess = Some(e),
|
t.kernel_excess = Some(e)
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
t.kernel_lookup_min_height = Some(slate.height);
|
t.kernel_lookup_min_height = Some(slate.height);
|
||||||
|
|
||||||
|
@ -156,7 +155,7 @@ where
|
||||||
for id in lock_inputs {
|
for id in lock_inputs {
|
||||||
let mut coin = batch.get(&id.0, &id.1).unwrap();
|
let mut coin = batch.get(&id.0, &id.1).unwrap();
|
||||||
coin.tx_log_entry = Some(log_id);
|
coin.tx_log_entry = Some(log_id);
|
||||||
amount_debited = amount_debited + coin.value;
|
amount_debited += coin.value;
|
||||||
batch.lock_output(&mut coin)?;
|
batch.lock_output(&mut coin)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +169,8 @@ where
|
||||||
None => {
|
None => {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Payment proof derivation index required".to_owned(),
|
"Payment proof derivation index required".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let sender_key = address::address_from_derivation_path(
|
let sender_key = address::address_from_derivation_path(
|
||||||
|
@ -180,8 +180,8 @@ where
|
||||||
)?;
|
)?;
|
||||||
let sender_address = OnionV3Address::from_private(&sender_key.0)?;
|
let sender_address = OnionV3Address::from_private(&sender_key.0)?;
|
||||||
t.payment_proof = Some(StoredProofInfo {
|
t.payment_proof = Some(StoredProofInfo {
|
||||||
receiver_address: p.receiver_address.clone(),
|
receiver_address: p.receiver_address,
|
||||||
receiver_signature: p.receiver_signature.clone(),
|
receiver_signature: p.receiver_signature,
|
||||||
sender_address: sender_address.to_ed25519()?,
|
sender_address: sender_address.to_ed25519()?,
|
||||||
sender_address_path,
|
sender_address_path,
|
||||||
sender_signature: None,
|
sender_signature: None,
|
||||||
|
@ -199,7 +199,7 @@ where
|
||||||
n_child: id.to_path().last_path_index(),
|
n_child: id.to_path().last_path_index(),
|
||||||
commit: commit,
|
commit: commit,
|
||||||
mmr_index: None,
|
mmr_index: None,
|
||||||
value: change_amount.clone(),
|
value: change_amount,
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: height,
|
height: height,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
|
@ -237,7 +237,7 @@ where
|
||||||
let amount = slate.amount;
|
let amount = slate.amount;
|
||||||
let height = slate.height;
|
let height = slate.height;
|
||||||
|
|
||||||
let slate_id = slate.id.clone();
|
let slate_id = slate.id;
|
||||||
let blinding = slate.add_transaction_elements(
|
let blinding = slate.add_transaction_elements(
|
||||||
&keychain,
|
&keychain,
|
||||||
&ProofBuilder::new(&keychain),
|
&ProofBuilder::new(&keychain),
|
||||||
|
@ -267,9 +267,8 @@ where
|
||||||
t.messages = messages;
|
t.messages = messages;
|
||||||
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
||||||
// when invoicing, this will be invalid
|
// when invoicing, this will be invalid
|
||||||
match slate.calc_excess(&keychain) {
|
if let Ok(e) = slate.calc_excess(&keychain) {
|
||||||
Ok(e) => t.kernel_excess = Some(e),
|
t.kernel_excess = Some(e)
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
t.kernel_lookup_min_height = Some(slate.height);
|
t.kernel_lookup_min_height = Some(slate.height);
|
||||||
batch.save(OutputData {
|
batch.save(OutputData {
|
||||||
|
@ -390,7 +389,8 @@ where
|
||||||
available_disp: amount_to_hr_string(0, false),
|
available_disp: amount_to_hr_string(0, false),
|
||||||
needed: amount_with_fee as u64,
|
needed: amount_with_fee as u64,
|
||||||
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
||||||
})?;
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The amount with fee is more than the total values of our max outputs
|
// The amount with fee is more than the total values of our max outputs
|
||||||
|
@ -400,7 +400,8 @@ where
|
||||||
available_disp: amount_to_hr_string(total, false),
|
available_disp: amount_to_hr_string(total, false),
|
||||||
needed: amount_with_fee as u64,
|
needed: amount_with_fee as u64,
|
||||||
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
||||||
})?;
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_outputs = change_outputs + 1;
|
let num_outputs = change_outputs + 1;
|
||||||
|
@ -420,7 +421,8 @@ where
|
||||||
available_disp: amount_to_hr_string(total, false),
|
available_disp: amount_to_hr_string(total, false),
|
||||||
needed: amount_with_fee as u64,
|
needed: amount_with_fee as u64,
|
||||||
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
needed_disp: amount_to_hr_string(amount_with_fee as u64, false),
|
||||||
})?;
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// select some spendable coins from the wallet
|
// select some spendable coins from the wallet
|
||||||
|
@ -444,7 +446,7 @@ where
|
||||||
|
|
||||||
/// Selects inputs and change for a transaction
|
/// Selects inputs and change for a transaction
|
||||||
pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
|
pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
|
||||||
coins: &Vec<OutputData>,
|
coins: &[OutputData],
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
keychain_mask: Option<&SecretKey>,
|
keychain_mask: Option<&SecretKey>,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
|
@ -559,7 +561,7 @@ where
|
||||||
// wants to send. So the wallet considers max_outputs more of a soft limit.
|
// wants to send. So the wallet considers max_outputs more of a soft limit.
|
||||||
if eligible.len() > max_outputs {
|
if eligible.len() > max_outputs {
|
||||||
for window in eligible.windows(max_outputs) {
|
for window in eligible.windows(max_outputs) {
|
||||||
let windowed_eligibles = window.iter().cloned().collect::<Vec<_>>();
|
let windowed_eligibles = window.to_vec();
|
||||||
if let Some(outputs) = select_from(amount, select_all, windowed_eligibles) {
|
if let Some(outputs) = select_from(amount, select_all, windowed_eligibles) {
|
||||||
return (max_available, outputs);
|
return (max_available, outputs);
|
||||||
}
|
}
|
||||||
|
@ -574,10 +576,8 @@ where
|
||||||
);
|
);
|
||||||
return (max_available, outputs);
|
return (max_available, outputs);
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Some(outputs) = select_from(amount, select_all, eligible.clone()) {
|
||||||
if let Some(outputs) = select_from(amount, select_all, eligible.clone()) {
|
return (max_available, outputs);
|
||||||
return (max_available, outputs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we failed to find a suitable set of outputs to spend,
|
// we failed to find a suitable set of outputs to spend,
|
||||||
|
@ -594,10 +594,10 @@ fn select_from(amount: u64, select_all: bool, outputs: Vec<OutputData>) -> Optio
|
||||||
let total = outputs.iter().fold(0, |acc, x| acc + x.value);
|
let total = outputs.iter().fold(0, |acc, x| acc + x.value);
|
||||||
if total >= amount {
|
if total >= amount {
|
||||||
if select_all {
|
if select_all {
|
||||||
return Some(outputs.iter().cloned().collect());
|
Some(outputs.to_vec())
|
||||||
} else {
|
} else {
|
||||||
let mut selected_amount = 0;
|
let mut selected_amount = 0;
|
||||||
return Some(
|
Some(
|
||||||
outputs
|
outputs
|
||||||
.iter()
|
.iter()
|
||||||
.take_while(|out| {
|
.take_while(|out| {
|
||||||
|
@ -607,7 +607,7 @@ fn select_from(amount: u64, select_all: bool, outputs: Vec<OutputData>) -> Optio
|
||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -181,7 +181,7 @@ where
|
||||||
// Generate a kernel offset and subtract from our context's secret key. Store
|
// Generate a kernel offset and subtract from our context's secret key. Store
|
||||||
// the offset in the slate's transaction kernel, and adds our public key
|
// the offset in the slate's transaction kernel, and adds our public key
|
||||||
// information to the slate
|
// information to the slate
|
||||||
let _ = slate.fill_round_1(
|
slate.fill_round_1(
|
||||||
&wallet.keychain(keychain_mask)?,
|
&wallet.keychain(keychain_mask)?,
|
||||||
&mut context.sec_key,
|
&mut context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
|
@ -192,7 +192,7 @@ where
|
||||||
|
|
||||||
if !is_initator {
|
if !is_initator {
|
||||||
// perform partial sig
|
// perform partial sig
|
||||||
let _ = slate.fill_round_2(
|
slate.fill_round_2(
|
||||||
&wallet.keychain(keychain_mask)?,
|
&wallet.keychain(keychain_mask)?,
|
||||||
&context.sec_key,
|
&context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
|
@ -229,7 +229,7 @@ where
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// fill public keys
|
// fill public keys
|
||||||
let _ = slate.fill_round_1(
|
slate.fill_round_1(
|
||||||
&wallet.keychain(keychain_mask)?,
|
&wallet.keychain(keychain_mask)?,
|
||||||
&mut context.sec_key,
|
&mut context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
|
@ -240,7 +240,7 @@ where
|
||||||
|
|
||||||
if !is_initiator {
|
if !is_initiator {
|
||||||
// perform partial sig
|
// perform partial sig
|
||||||
let _ = slate.fill_round_2(
|
slate.fill_round_2(
|
||||||
&wallet.keychain(keychain_mask)?,
|
&wallet.keychain(keychain_mask)?,
|
||||||
&context.sec_key,
|
&context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
|
@ -264,7 +264,7 @@ where
|
||||||
C: NodeClient + 'a,
|
C: NodeClient + 'a,
|
||||||
K: Keychain + 'a,
|
K: Keychain + 'a,
|
||||||
{
|
{
|
||||||
let _ = slate.fill_round_2(
|
slate.fill_round_2(
|
||||||
&wallet.keychain(keychain_mask)?,
|
&wallet.keychain(keychain_mask)?,
|
||||||
&context.sec_key,
|
&context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
|
@ -297,14 +297,14 @@ where
|
||||||
}
|
}
|
||||||
let tx_vec = updater::retrieve_txs(wallet, tx_id, tx_slate_id, Some(&parent_key_id), false)?;
|
let tx_vec = updater::retrieve_txs(wallet, tx_id, tx_slate_id, Some(&parent_key_id), false)?;
|
||||||
if tx_vec.len() != 1 {
|
if tx_vec.len() != 1 {
|
||||||
return Err(ErrorKind::TransactionDoesntExist(tx_id_string))?;
|
return Err(ErrorKind::TransactionDoesntExist(tx_id_string).into());
|
||||||
}
|
}
|
||||||
let tx = tx_vec[0].clone();
|
let tx = tx_vec[0].clone();
|
||||||
if tx.tx_type != TxLogEntryType::TxSent && tx.tx_type != TxLogEntryType::TxReceived {
|
if tx.tx_type != TxLogEntryType::TxSent && tx.tx_type != TxLogEntryType::TxReceived {
|
||||||
return Err(ErrorKind::TransactionNotCancellable(tx_id_string))?;
|
return Err(ErrorKind::TransactionNotCancellable(tx_id_string).into());
|
||||||
}
|
}
|
||||||
if tx.confirmed == true {
|
if tx.confirmed {
|
||||||
return Err(ErrorKind::TransactionNotCancellable(tx_id_string))?;
|
return Err(ErrorKind::TransactionNotCancellable(tx_id_string).into());
|
||||||
}
|
}
|
||||||
// get outputs associated with tx
|
// get outputs associated with tx
|
||||||
let res = updater::retrieve_outputs(
|
let res = updater::retrieve_outputs(
|
||||||
|
@ -338,17 +338,17 @@ where
|
||||||
// don't want to assume this is the right tx, in case of self-sending
|
// don't want to assume this is the right tx, in case of self-sending
|
||||||
for t in tx_vec {
|
for t in tx_vec {
|
||||||
if t.tx_type == TxLogEntryType::TxSent && !is_invoiced {
|
if t.tx_type == TxLogEntryType::TxSent && !is_invoiced {
|
||||||
tx = Some(t.clone());
|
tx = Some(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if t.tx_type == TxLogEntryType::TxReceived && is_invoiced {
|
if t.tx_type == TxLogEntryType::TxReceived && is_invoiced {
|
||||||
tx = Some(t.clone());
|
tx = Some(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut tx = match tx {
|
let mut tx = match tx {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()))?,
|
None => return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()).into()),
|
||||||
};
|
};
|
||||||
wallet.store_tx(&format!("{}", tx.tx_slate_id.unwrap()), &slate.tx)?;
|
wallet.store_tx(&format!("{}", tx.tx_slate_id.unwrap()), &slate.tx)?;
|
||||||
let parent_key = tx.parent_key_id.clone();
|
let parent_key = tx.parent_key_id.clone();
|
||||||
|
@ -395,7 +395,7 @@ where
|
||||||
{
|
{
|
||||||
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?;
|
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?;
|
||||||
if tx_vec.is_empty() {
|
if tx_vec.is_empty() {
|
||||||
return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()))?;
|
return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()).into());
|
||||||
}
|
}
|
||||||
let mut batch = wallet.batch(keychain_mask)?;
|
let mut batch = wallet.batch(keychain_mask)?;
|
||||||
for mut tx in tx_vec.into_iter() {
|
for mut tx in tx_vec.into_iter() {
|
||||||
|
@ -420,7 +420,7 @@ pub fn payment_proof_message(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _decode_payment_proof_message(
|
pub fn _decode_payment_proof_message(
|
||||||
msg: &Vec<u8>,
|
msg: &[u8],
|
||||||
) -> Result<(u64, pedersen::Commitment, DalekPublicKey), Error> {
|
) -> Result<(u64, pedersen::Commitment, DalekPublicKey), Error> {
|
||||||
let mut rdr = Cursor::new(msg);
|
let mut rdr = Cursor::new(msg);
|
||||||
let amount = rdr.read_u64::<BigEndian>()?;
|
let amount = rdr.read_u64::<BigEndian>()?;
|
||||||
|
@ -451,7 +451,7 @@ pub fn create_payment_proof_signature(
|
||||||
let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) {
|
let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) {
|
||||||
Ok(k) => k,
|
Ok(k) => k,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ErrorKind::ED25519Key(format!("{}", e)).to_owned())?;
|
return Err(ErrorKind::ED25519Key(format!("{}", e)).into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let pub_key: DalekPublicKey = (&d_skey).into();
|
let pub_key: DalekPublicKey = (&d_skey).into();
|
||||||
|
@ -476,10 +476,11 @@ where
|
||||||
K: Keychain + 'a,
|
K: Keychain + 'a,
|
||||||
{
|
{
|
||||||
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), Some(parent_key_id), false)?;
|
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), Some(parent_key_id), false)?;
|
||||||
if tx_vec.len() == 0 {
|
if tx_vec.is_empty() {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"TxLogEntry with original proof info not found (is account correct?)".to_owned(),
|
"TxLogEntry with original proof info not found (is account correct?)".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_proof_info = tx_vec[0].clone().payment_proof;
|
let orig_proof_info = tx_vec[0].clone().payment_proof;
|
||||||
|
@ -487,7 +488,8 @@ where
|
||||||
if orig_proof_info.is_some() && slate.payment_proof.is_none() {
|
if orig_proof_info.is_some() && slate.payment_proof.is_none() {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Expected Payment Proof for this Transaction is not present".to_owned(),
|
"Expected Payment Proof for this Transaction is not present".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref p) = slate.payment_proof {
|
if let Some(ref p) = slate.payment_proof {
|
||||||
|
@ -496,7 +498,8 @@ where
|
||||||
None => {
|
None => {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Original proof info not stored in tx".to_owned(),
|
"Original proof info not stored in tx".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let keychain = wallet.keychain(keychain_mask)?;
|
let keychain = wallet.keychain(keychain_mask)?;
|
||||||
|
@ -505,7 +508,8 @@ where
|
||||||
None => {
|
None => {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Payment proof derivation index required".to_owned(),
|
"Payment proof derivation index required".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let orig_sender_sk =
|
let orig_sender_sk =
|
||||||
|
@ -514,13 +518,15 @@ where
|
||||||
if p.sender_address != orig_sender_address.to_ed25519()? {
|
if p.sender_address != orig_sender_address.to_ed25519()? {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Sender address on slate does not match original sender address".to_owned(),
|
"Sender address on slate does not match original sender address".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if orig_proof_info.receiver_address != p.receiver_address {
|
if orig_proof_info.receiver_address != p.receiver_address {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Recipient address on slate does not match original recipient address".to_owned(),
|
"Recipient address on slate does not match original recipient address".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
let msg = payment_proof_message(
|
let msg = payment_proof_message(
|
||||||
slate.amount,
|
slate.amount,
|
||||||
|
@ -532,14 +538,13 @@ where
|
||||||
None => {
|
None => {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof(
|
||||||
"Recipient did not provide requested proof signature".to_owned(),
|
"Recipient did not provide requested proof signature".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(_) = p.receiver_address.verify(&msg, &sig) {
|
if p.receiver_address.verify(&msg, &sig).is_err() {
|
||||||
return Err(ErrorKind::PaymentProof(
|
return Err(ErrorKind::PaymentProof("Invalid proof signature".to_owned()).into());
|
||||||
"Invalid proof signature".to_owned(),
|
|
||||||
))?;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -588,7 +593,7 @@ mod test {
|
||||||
fn payment_proof_construction() {
|
fn payment_proof_construction() {
|
||||||
let secp_inst = static_secp_instance();
|
let secp_inst = static_secp_instance();
|
||||||
let secp = secp_inst.lock();
|
let secp = secp_inst.lock();
|
||||||
let mut test_rng = StepRng::new(1234567890u64, 1);
|
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
|
||||||
let sec_key = secp::key::SecretKey::new(&secp, &mut test_rng);
|
let sec_key = secp::key::SecretKey::new(&secp, &mut test_rng);
|
||||||
let d_skey = DalekSecretKey::from_bytes(&sec_key.0).unwrap();
|
let d_skey = DalekSecretKey::from_bytes(&sec_key.0).unwrap();
|
||||||
|
|
||||||
|
@ -615,7 +620,7 @@ mod test {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let amount = 12345678u64;
|
let amount = 1_234_567_890_u64;
|
||||||
let msg = payment_proof_message(amount, &kernel_excess, address).unwrap();
|
let msg = payment_proof_message(amount, &kernel_excess, address).unwrap();
|
||||||
println!("payment proof message is (len {}): {:?}", msg.len(), msg);
|
println!("payment proof message is (len {}): {:?}", msg.len(), msg);
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,8 @@ where
|
||||||
outputs = outputs
|
outputs = outputs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|o| o.root_key_id == *k)
|
.filter(|o| o.root_key_id == *k)
|
||||||
.map(|o| o.clone())
|
.cloned()
|
||||||
.collect();
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs.sort_by_key(|out| out.n_child);
|
outputs.sort_by_key(|out| out.n_child);
|
||||||
|
@ -178,13 +178,7 @@ where
|
||||||
false => unspents
|
false => unspents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| match x.tx_log_entry.as_ref() {
|
.filter(|x| match x.tx_log_entry.as_ref() {
|
||||||
Some(t) => {
|
Some(t) => tx_entries.iter().any(|te| te.id == *t),
|
||||||
if let Some(_) = tx_entries.iter().find(|&te| te.id == *t) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => true,
|
None => true,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -228,7 +222,7 @@ where
|
||||||
batch.save(o)?;
|
batch.save(o)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut tx = tx.clone();
|
let mut tx = tx;
|
||||||
if tx.tx_type == TxLogEntryType::TxSent {
|
if tx.tx_type == TxLogEntryType::TxSent {
|
||||||
tx.tx_type = TxLogEntryType::TxSentCancelled;
|
tx.tx_type = TxLogEntryType::TxSentCancelled;
|
||||||
}
|
}
|
||||||
|
@ -291,8 +285,7 @@ where
|
||||||
let secp = static_secp_instance();
|
let secp = static_secp_instance();
|
||||||
let secp = secp.lock();
|
let secp = secp.lock();
|
||||||
let over_commit = secp.commit_value(output.value)?;
|
let over_commit = secp.commit_value(output.value)?;
|
||||||
let excess =
|
let excess = secp.commit_sum(vec![*commit], vec![over_commit])?;
|
||||||
secp.commit_sum(vec![commit.clone()], vec![over_commit])?;
|
|
||||||
t.kernel_excess = Some(excess);
|
t.kernel_excess = Some(excess);
|
||||||
t.kernel_lookup_min_height = Some(height);
|
t.kernel_lookup_min_height = Some(height);
|
||||||
}
|
}
|
||||||
|
@ -350,7 +343,7 @@ where
|
||||||
// and a list of outputs we want to query the node for
|
// and a list of outputs we want to query the node for
|
||||||
let wallet_outputs = map_wallet_outputs(wallet, keychain_mask, parent_key_id, update_all)?;
|
let wallet_outputs = map_wallet_outputs(wallet, keychain_mask, parent_key_id, update_all)?;
|
||||||
|
|
||||||
let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect();
|
let wallet_output_keys = wallet_outputs.keys().copied().collect();
|
||||||
|
|
||||||
let api_outputs = wallet
|
let api_outputs = wallet
|
||||||
.w2n_client()
|
.w2n_client()
|
||||||
|
|
|
@ -122,7 +122,7 @@ impl ParticipantMessageData {
|
||||||
id: p.id,
|
id: p.id,
|
||||||
public_key: p.public_blind_excess,
|
public_key: p.public_blind_excess,
|
||||||
message: p.message.clone(),
|
message: p.message.clone(),
|
||||||
message_sig: p.message_sig.clone(),
|
message_sig: p.message_sig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ impl fmt::Display for ParticipantMessageData {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
};
|
};
|
||||||
writeln!(f, "Message: {}", message)?;
|
writeln!(f, "Message: {}", message)?;
|
||||||
let message_sig = match self.message_sig.clone() {
|
let message_sig = match self.message_sig {
|
||||||
None => "None".to_owned(),
|
None => "None".to_owned(),
|
||||||
Some(m) => grin_util::to_hex(m.to_raw_data().to_vec()),
|
Some(m) => grin_util::to_hex(m.to_raw_data().to_vec()),
|
||||||
};
|
};
|
||||||
|
@ -401,7 +401,7 @@ impl Slate {
|
||||||
.collect();
|
.collect();
|
||||||
match PublicKey::from_combination(secp, pub_nonces) {
|
match PublicKey::from_combination(secp, pub_nonces) {
|
||||||
Ok(k) => Ok(k),
|
Ok(k) => Ok(k),
|
||||||
Err(e) => Err(ErrorKind::Secp(e))?,
|
Err(e) => Err(ErrorKind::Secp(e).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +414,7 @@ impl Slate {
|
||||||
.collect();
|
.collect();
|
||||||
match PublicKey::from_combination(secp, pub_blinds) {
|
match PublicKey::from_combination(secp, pub_blinds) {
|
||||||
Ok(k) => Ok(k),
|
Ok(k) => Ok(k),
|
||||||
Err(e) => Err(ErrorKind::Secp(e))?,
|
Err(e) => Err(ErrorKind::Secp(e).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +513,7 @@ impl Slate {
|
||||||
}
|
}
|
||||||
true => {
|
true => {
|
||||||
// allow for consistent test results
|
// allow for consistent test results
|
||||||
let mut test_rng = StepRng::new(1234567890u64, 1);
|
let mut test_rng = StepRng::new(1_234_567_890_u64, 1);
|
||||||
BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut test_rng))
|
BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut test_rng))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -540,9 +540,9 @@ impl Slate {
|
||||||
);
|
);
|
||||||
|
|
||||||
if fee > self.tx.fee() {
|
if fee > self.tx.fee() {
|
||||||
return Err(ErrorKind::Fee(
|
return Err(
|
||||||
format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(),
|
ErrorKind::Fee(format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,)).into(),
|
||||||
))?;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if fee > self.amount + self.fee {
|
if fee > self.amount + self.fee {
|
||||||
|
@ -552,7 +552,7 @@ impl Slate {
|
||||||
amount_to_hr_string(self.amount + self.fee, false)
|
amount_to_hr_string(self.amount + self.fee, false)
|
||||||
);
|
);
|
||||||
info!("{}", reason);
|
info!("{}", reason);
|
||||||
return Err(ErrorKind::Fee(reason.to_string()))?;
|
return Err(ErrorKind::Fee(reason).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -589,7 +589,8 @@ impl Slate {
|
||||||
String::from_utf8_lossy(&msg.as_bytes()[..]));
|
String::from_utf8_lossy(&msg.as_bytes()[..]));
|
||||||
return Err(ErrorKind::Signature(
|
return Err(ErrorKind::Signature(
|
||||||
"Optional participant messages doesn't have signature".to_owned(),
|
"Optional participant messages doesn't have signature".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
};
|
};
|
||||||
|
@ -606,7 +607,8 @@ impl Slate {
|
||||||
String::from_utf8_lossy(&msg.as_bytes()[..]));
|
String::from_utf8_lossy(&msg.as_bytes()[..]));
|
||||||
return Err(ErrorKind::Signature(
|
return Err(ErrorKind::Signature(
|
||||||
"Optional participant messages do not match signatures".to_owned(),
|
"Optional participant messages do not match signatures".to_owned(),
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
"verify_messages - signature verified ok. Participant message: \"{}\"",
|
"verify_messages - signature verified ok. Participant message: \"{}\"",
|
||||||
|
@ -709,7 +711,7 @@ impl Slate {
|
||||||
// confirm the overall transaction is valid (including the updated kernel)
|
// confirm the overall transaction is valid (including the updated kernel)
|
||||||
// accounting for tx weight limits
|
// accounting for tx weight limits
|
||||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||||
let _ = final_tx.validate(Weighting::AsTransaction, verifier_cache)?;
|
final_tx.validate(Weighting::AsTransaction, verifier_cache)?;
|
||||||
|
|
||||||
self.tx = final_tx;
|
self.tx = final_tx;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -82,10 +82,7 @@ impl VersionedSlate {
|
||||||
impl From<VersionedSlate> for Slate {
|
impl From<VersionedSlate> for Slate {
|
||||||
fn from(slate: VersionedSlate) -> Slate {
|
fn from(slate: VersionedSlate) -> Slate {
|
||||||
match slate {
|
match slate {
|
||||||
VersionedSlate::V3(s) => {
|
VersionedSlate::V3(s) => Slate::from(s),
|
||||||
let s = SlateV3::from(s);
|
|
||||||
Slate::from(s)
|
|
||||||
}
|
|
||||||
VersionedSlate::V2(s) => {
|
VersionedSlate::V2(s) => {
|
||||||
let s = SlateV3::from(s);
|
let s = SlateV3::from(s);
|
||||||
Slate::from(s)
|
Slate::from(s)
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub mod ov3_serde {
|
||||||
String::deserialize(deserializer).and_then(|s| {
|
String::deserialize(deserializer).and_then(|s| {
|
||||||
OnionV3Address::try_from(s.as_str())
|
OnionV3Address::try_from(s.as_str())
|
||||||
.map_err(|err: OnionV3AddressError| Error::custom(format!("{:?}", err)))
|
.map_err(|err: OnionV3AddressError| Error::custom(format!("{:?}", err)))
|
||||||
.and_then(|a| Ok(a))
|
.and_then(Ok)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,13 +128,13 @@ pub mod option_dalek_pubkey_serde {
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
||||||
Some(string) => from_hex(string.to_string())
|
Some(string) => from_hex(string)
|
||||||
.map_err(|err| Error::custom(err.to_string()))
|
.map_err(|err| Error::custom(err.to_string()))
|
||||||
.and_then(|bytes: Vec<u8>| {
|
.and_then(|bytes: Vec<u8>| {
|
||||||
let mut b = [0u8; 32];
|
let mut b = [0u8; 32];
|
||||||
b.copy_from_slice(&bytes[0..32]);
|
b.copy_from_slice(&bytes[0..32]);
|
||||||
DalekPublicKey::from_bytes(&b)
|
DalekPublicKey::from_bytes(&b)
|
||||||
.map(|val| Some(val))
|
.map(Some)
|
||||||
.map_err(|err| Error::custom(err.to_string()))
|
.map_err(|err| Error::custom(err.to_string()))
|
||||||
}),
|
}),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
@ -198,13 +198,13 @@ pub mod option_dalek_sig_serde {
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
||||||
Some(string) => from_hex(string.to_string())
|
Some(string) => from_hex(string)
|
||||||
.map_err(|err| Error::custom(err.to_string()))
|
.map_err(|err| Error::custom(err.to_string()))
|
||||||
.and_then(|bytes: Vec<u8>| {
|
.and_then(|bytes: Vec<u8>| {
|
||||||
let mut b = [0u8; 64];
|
let mut b = [0u8; 64];
|
||||||
b.copy_from_slice(&bytes[0..64]);
|
b.copy_from_slice(&bytes[0..64]);
|
||||||
DalekSignature::from_bytes(&b)
|
DalekSignature::from_bytes(&b)
|
||||||
.map(|val| Some(val))
|
.map(Some)
|
||||||
.map_err(|err| Error::custom(err.to_string()))
|
.map_err(|err| Error::custom(err.to_string()))
|
||||||
}),
|
}),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
|
|
@ -221,19 +221,19 @@ where
|
||||||
fn batch_no_mask<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error>;
|
fn batch_no_mask<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error>;
|
||||||
|
|
||||||
/// Return the current child Index
|
/// Return the current child Index
|
||||||
fn current_child_index<'a>(&mut self, parent_key_id: &Identifier) -> Result<u32, Error>;
|
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
|
/// Next child ID when we want to create a new output, based on current parent
|
||||||
fn next_child<'a>(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error>;
|
fn next_child(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error>;
|
||||||
|
|
||||||
/// last verified height of outputs directly descending from the given parent key
|
/// last verified height of outputs directly descending from the given parent key
|
||||||
fn last_confirmed_height<'a>(&mut self) -> Result<u64, Error>;
|
fn last_confirmed_height(&mut self) -> Result<u64, Error>;
|
||||||
|
|
||||||
/// last block scanned during scan or restore
|
/// last block scanned during scan or restore
|
||||||
fn last_scanned_block<'a>(&mut self) -> Result<ScannedBlockInfo, Error>;
|
fn last_scanned_block(&mut self) -> Result<ScannedBlockInfo, Error>;
|
||||||
|
|
||||||
/// Flag whether the wallet needs a full UTXO scan on next update attempt
|
/// Flag whether the wallet needs a full UTXO scan on next update attempt
|
||||||
fn init_status<'a>(&mut self) -> Result<WalletInitStatus, Error>;
|
fn init_status(&mut self) -> Result<WalletInitStatus, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Batch trait to update the output data backend atomically. Trying to use a
|
/// Batch trait to update the output data backend atomically. Trying to use a
|
||||||
|
@ -274,7 +274,7 @@ where
|
||||||
fn save_last_scanned_block(&mut self, block: ScannedBlockInfo) -> Result<(), Error>;
|
fn save_last_scanned_block(&mut self, block: ScannedBlockInfo) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Save flag indicating whether wallet needs a full UTXO scan
|
/// Save flag indicating whether wallet needs a full UTXO scan
|
||||||
fn save_init_status<'a>(&mut self, value: WalletInitStatus) -> Result<(), Error>;
|
fn save_init_status(&mut self, value: WalletInitStatus) -> Result<(), Error>;
|
||||||
|
|
||||||
/// get next tx log entry for the parent
|
/// get next tx log entry for the parent
|
||||||
fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result<u32, Error>;
|
fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result<u32, Error>;
|
||||||
|
@ -469,29 +469,23 @@ impl OutputData {
|
||||||
/// Check if output is eligible to spend based on state and height and
|
/// Check if output is eligible to spend based on state and height and
|
||||||
/// confirmations
|
/// confirmations
|
||||||
pub fn eligible_to_spend(&self, current_height: u64, minimum_confirmations: u64) -> bool {
|
pub fn eligible_to_spend(&self, current_height: u64, minimum_confirmations: u64) -> bool {
|
||||||
if [OutputStatus::Spent, OutputStatus::Locked].contains(&self.status) {
|
if [OutputStatus::Spent, OutputStatus::Locked].contains(&self.status)
|
||||||
return false;
|
|| self.status == OutputStatus::Unconfirmed && self.is_coinbase
|
||||||
} else if self.status == OutputStatus::Unconfirmed && self.is_coinbase {
|
|| self.lock_height > current_height
|
||||||
return false;
|
|
||||||
} else if self.lock_height > current_height {
|
|
||||||
return false;
|
|
||||||
} else if self.status == OutputStatus::Unspent
|
|
||||||
&& self.num_confirmations(current_height) >= minimum_confirmations
|
|
||||||
{
|
{
|
||||||
return true;
|
false
|
||||||
} else if self.status == OutputStatus::Unconfirmed && minimum_confirmations == 0 {
|
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
(self.status == OutputStatus::Unspent
|
||||||
|
&& self.num_confirmations(current_height) >= minimum_confirmations)
|
||||||
|
|| self.status == OutputStatus::Unconfirmed && minimum_confirmations == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks this output as unspent if it was previously unconfirmed
|
/// Marks this output as unspent if it was previously unconfirmed
|
||||||
pub fn mark_unspent(&mut self) {
|
pub fn mark_unspent(&mut self) {
|
||||||
match self.status {
|
if let OutputStatus::Unconfirmed = self.status {
|
||||||
OutputStatus::Unconfirmed => self.status = OutputStatus::Unspent,
|
self.status = OutputStatus::Unspent
|
||||||
_ => (),
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark an output as spent
|
/// Mark an output as spent
|
||||||
|
@ -585,7 +579,7 @@ impl Context {
|
||||||
/// be kept between invocations
|
/// be kept between invocations
|
||||||
pub fn add_output(&mut self, output_id: &Identifier, mmr_index: &Option<u64>, amount: u64) {
|
pub fn add_output(&mut self, output_id: &Identifier, mmr_index: &Option<u64>, amount: u64) {
|
||||||
self.output_ids
|
self.output_ids
|
||||||
.push((output_id.clone(), mmr_index.clone(), amount));
|
.push((output_id.clone(), *mmr_index, amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all stored outputs
|
/// Returns all stored outputs
|
||||||
|
@ -596,8 +590,7 @@ impl Context {
|
||||||
/// Tracks IDs of my inputs into the transaction
|
/// Tracks IDs of my inputs into the transaction
|
||||||
/// be kept between invocations
|
/// be kept between invocations
|
||||||
pub fn add_input(&mut self, input_id: &Identifier, mmr_index: &Option<u64>, amount: u64) {
|
pub fn add_input(&mut self, input_id: &Identifier, mmr_index: &Option<u64>, amount: u64) {
|
||||||
self.input_ids
|
self.input_ids.push((input_id.clone(), *mmr_index, amount));
|
||||||
.push((input_id.clone(), mmr_index.clone(), amount));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all stored input identifiers
|
/// Returns all stored input identifiers
|
||||||
|
@ -839,7 +832,7 @@ impl TxLogEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a vec of TX log entries, return credited + debited sums
|
/// Given a vec of TX log entries, return credited + debited sums
|
||||||
pub fn sum_confirmed(txs: &Vec<TxLogEntry>) -> (u64, u64) {
|
pub fn sum_confirmed(txs: &[TxLogEntry]) -> (u64, u64) {
|
||||||
txs.iter().fold((0, 0), |acc, tx| match tx.confirmed {
|
txs.iter().fold((0, 0), |acc, tx| match tx.confirmed {
|
||||||
true => (acc.0 + tx.amount_credited, acc.1 + tx.amount_debited),
|
true => (acc.0 + tx.amount_credited, acc.1 + tx.amount_debited),
|
||||||
false => acc,
|
false => acc,
|
||||||
|
|
Loading…
Reference in a new issue