diff --git a/controller/tests/invoice.rs b/controller/tests/invoice.rs index 70511d324..e637b4cf9 100644 --- a/controller/tests/invoice.rs +++ b/controller/tests/invoice.rs @@ -207,6 +207,45 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false); //bh += 3; + // As above, but use owner API to finalize + let mut slate = Slate::blank(2, true); + + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { + // Wallet 2 inititates an invoice transaction, requesting payment + let args = IssueInvoiceTxArgs { + amount: reward * 2, + ..Default::default() + }; + slate = api.issue_invoice_tx(m, args)?; + Ok(()) + })?; + assert_eq!(slate.state, SlateState::Invoice1); + + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { + // Wallet 1 receives the invoice transaction + let args = InitTxArgs { + src_acct_name: None, + amount: slate.amount, + minimum_confirmations: 2, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all: true, + ..Default::default() + }; + slate = api.process_invoice_tx(m, &slate, args)?; + api.tx_lock_outputs(m, &slate)?; + Ok(()) + })?; + assert_eq!(slate.state, SlateState::Invoice2); + + // wallet 2 finalizes via owner API + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { + // Wallet 2 receives the invoice transaction + slate = api.finalize_tx(m, &slate)?; + Ok(()) + })?; + assert_eq!(slate.state, SlateState::Invoice3); + // let logging finish stopper.store(false, Ordering::Relaxed); thread::sleep(Duration::from_millis(200)); diff --git a/libwallet/src/api_impl/foreign.rs b/libwallet/src/api_impl/foreign.rs index be08950f4..315091cd4 100644 --- a/libwallet/src/api_impl/foreign.rs +++ b/libwallet/src/api_impl/foreign.rs @@ -15,7 +15,6 @@ //! Generic implementation of owner API functions use strum::IntoEnumIterator; -use crate::api_impl::owner::finalize_tx as owner_finalize; use crate::api_impl::owner::{check_ttl, post_tx}; use crate::grin_core::core::FeeFields; use crate::grin_keychain::Keychain; @@ -27,6 +26,8 @@ use crate::{ WalletBackend, }; +use super::owner::tx_lock_outputs; + const FOREIGN_API_VERSION: u16 = 2; /// Return the version info @@ -144,10 +145,9 @@ where K: Keychain + 'a, { let mut sl = slate.clone(); - let context = w.get_private_context(keychain_mask, sl.id.as_bytes())?; + let mut context = w.get_private_context(keychain_mask, sl.id.as_bytes())?; + check_ttl(w, &sl)?; if sl.state == SlateState::Invoice2 { - check_ttl(w, &sl)?; - // Add our contribution to the offset sl.adjust_offset(&w.keychain(keychain_mask)?, &context)?; @@ -165,8 +165,66 @@ where } sl.state = SlateState::Invoice3; sl.amount = 0; + } else if sl.state == SlateState::Standard2 { + let keychain = w.keychain(keychain_mask)?; + let parent_key_id = w.parent_key_id(); + + if let Some(args) = context.late_lock_args.take() { + // Transaction was late locked, select inputs+change now + // and insert into original context + + let current_height = w.w2n_client().get_chain_tip()?.0; + let mut temp_sl = + tx::new_tx_slate(&mut *w, context.amount, false, 2, false, args.ttl_blocks)?; + let temp_context = selection::build_send_tx( + w, + &keychain, + keychain_mask, + &mut temp_sl, + current_height, + args.minimum_confirmations, + args.max_outputs as usize, + args.num_change_outputs as usize, + args.selection_strategy_is_use_all, + Some(context.fee.map(|f| f.fee()).unwrap_or(0)), + parent_key_id.clone(), + false, + true, + false, + )?; + + // Add inputs and outputs to original context + context.input_ids = temp_context.input_ids; + context.output_ids = temp_context.output_ids; + + // Store the updated context + { + let mut batch = w.batch(keychain_mask)?; + batch.save_private_context(sl.id.as_bytes(), &context)?; + batch.commit()?; + } + + // Now do the actual locking + tx_lock_outputs(w, keychain_mask, &sl)?; + } + + // Add our contribution to the offset + sl.adjust_offset(&keychain, &context)?; + + selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &context, true)?; + + tx::complete_tx(&mut *w, keychain_mask, &mut sl, &context)?; + tx::verify_slate_payment_proof(&mut *w, keychain_mask, &parent_key_id, &context, &sl)?; + tx::update_stored_tx(&mut *w, keychain_mask, &context, &sl, false)?; + { + let mut batch = w.batch(keychain_mask)?; + batch.delete_private_context(sl.id.as_bytes())?; + batch.commit()?; + } + sl.state = SlateState::Standard3; + sl.amount = 0; } else { - sl = owner_finalize(w, keychain_mask, slate)?; + return Err(Error::SlateState); } if post_automatically { post_tx(w.w2n_client(), sl.tx_or_err()?, true)?; diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs index 0ab87e47d..9563a5a8f 100644 --- a/libwallet/src/api_impl/owner.rs +++ b/libwallet/src/api_impl/owner.rs @@ -16,6 +16,7 @@ use uuid::Uuid; +use crate::api_impl::foreign::finalize_tx as foreign_finalize; use crate::grin_core::core::hash::Hashed; use crate::grin_core::core::{Output, OutputFeatures, Transaction}; use crate::grin_core::libtx::proof; @@ -807,68 +808,7 @@ where C: NodeClient + 'a, K: Keychain + 'a, { - let mut sl = slate.clone(); - check_ttl(w, &sl)?; - let mut context = w.get_private_context(keychain_mask, sl.id.as_bytes())?; - let keychain = w.keychain(keychain_mask)?; - let parent_key_id = w.parent_key_id(); - - if let Some(args) = context.late_lock_args.take() { - // Transaction was late locked, select inputs+change now - // and insert into original context - - let current_height = w.w2n_client().get_chain_tip()?.0; - let mut temp_sl = - tx::new_tx_slate(&mut *w, context.amount, false, 2, false, args.ttl_blocks)?; - let temp_context = selection::build_send_tx( - w, - &keychain, - keychain_mask, - &mut temp_sl, - current_height, - args.minimum_confirmations, - args.max_outputs as usize, - args.num_change_outputs as usize, - args.selection_strategy_is_use_all, - Some(context.fee.map(|f| f.fee()).unwrap_or(0)), - parent_key_id.clone(), - false, - true, - false, - )?; - - // Add inputs and outputs to original context - context.input_ids = temp_context.input_ids; - context.output_ids = temp_context.output_ids; - - // Store the updated context - { - let mut batch = w.batch(keychain_mask)?; - batch.save_private_context(sl.id.as_bytes(), &context)?; - batch.commit()?; - } - - // Now do the actual locking - tx_lock_outputs(w, keychain_mask, &sl)?; - } - - // Add our contribution to the offset - sl.adjust_offset(&keychain, &context)?; - - selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &context, true)?; - - tx::complete_tx(&mut *w, keychain_mask, &mut sl, &context)?; - tx::verify_slate_payment_proof(&mut *w, keychain_mask, &parent_key_id, &context, &sl)?; - tx::update_stored_tx(&mut *w, keychain_mask, &context, &sl, false)?; - { - let mut batch = w.batch(keychain_mask)?; - batch.delete_private_context(sl.id.as_bytes())?; - batch.commit()?; - } - sl.state = SlateState::Standard3; - sl.amount = 0; - - Ok(sl) + foreign_finalize(w, keychain_mask, slate, false) } /// cancel tx diff --git a/libwallet/src/error.rs b/libwallet/src/error.rs index dc5735ae2..3ac0bc946 100644 --- a/libwallet/src/error.rs +++ b/libwallet/src/error.rs @@ -189,6 +189,10 @@ pub enum Error { #[error("Can't Deserialize slate")] SlateDeser, + /// Invalid slate state + #[error("Invalid slate state")] + SlateState, + /// Can't serialize slate pack #[error("Can't Serialize slatepack")] SlatepackSer,