Skip to content

Commit

Permalink
End-to-end test of multisig outputs
Browse files Browse the repository at this point in the history
Add test and changes for end-to-end multisig output used together with
atomic swap transactions
  • Loading branch information
GeneFerneau committed Jul 6, 2021
1 parent 0b5fd34 commit 3584a61
Show file tree
Hide file tree
Showing 10 changed files with 712 additions and 211 deletions.
2 changes: 1 addition & 1 deletion api/src/owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ where
) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::process_multisig_tx(&mut **w, keychain_mask, slate, self.doctest_mode)
owner::process_multisig_tx(&mut **w, keychain_mask, slate)
}

/// Initializes an atomic swap transaction. The transaction can either be
Expand Down
60 changes: 45 additions & 15 deletions api/src/owner_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,8 @@ pub trait OwnerRpc {
/**
;Networked version of [Owner::init_atomic_swap](struct.Owner.html#method.init_atomic_swap).
```
FIXME: re-enable after figuring out how to run doctests in a debugger
```no_run
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
# r#"
{
Expand All @@ -680,13 +681,14 @@ pub trait OwnerRpc {
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
"args": {
"src_acct_name": null,
"amount": "6000000000",
"amount": "5000000000",
"minimum_confirmations": 2,
"max_outputs": 500,
"num_change_outputs": 1,
"selection_strategy_is_use_all": true,
"target_slate_version": null,
"payment_proof_recipient_address": "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp",
"multisig_path": "m/860601635/303558731/534026549/1571534207",
"ttl_blocks": null,
"send_args": null
}
Expand All @@ -701,7 +703,7 @@ pub trait OwnerRpc {
"jsonrpc": "2.0",
"result": {
"Ok": {
"amt": "6000000000",
"amt": "5000000000",
"fee": "23000000",
"id": "0436430c-2b02-624c-2032-570501212b00",
"off": "456498224e2a6850e073ab8fb5c32fcccfe70272c61759ea4ade53ae7dc367e6",
Expand All @@ -717,7 +719,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 4, false, false, false, false, false, false);
# , 4, false, false, false, false, true, false);
```
*/
fn init_atomic_swap(&self, token: Token, args: InitTxArgs)
Expand Down Expand Up @@ -2394,7 +2396,7 @@ pub fn run_doctest_owner(
lock_tx: bool,
finalize_tx: bool,
payment_proof: bool,
countersign_atomic: bool,
is_atomic: bool,
is_multisig: bool,
) -> Result<Option<serde_json::Value>, String> {
use easy_jsonrpc_mw::Handler;
Expand Down Expand Up @@ -2520,7 +2522,11 @@ pub fn run_doctest_owner(
}

if perform_tx {
let amount = 60_000_000_000;
let amount = if is_atomic {
50_000_000_000
} else {
60_000_000_000
};
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let proof_address = match payment_proof {
Expand All @@ -2542,8 +2548,33 @@ pub fn run_doctest_owner(
is_multisig: Some(is_multisig),
..Default::default()
};
let mut slate = if countersign_atomic {
api_impl::owner::init_atomic_swap(&mut **w, (&mask1).as_ref(), args, true).unwrap()
// Calculate the multisig output for the atomic swap
let sl = if is_atomic {
let mut pre_args = args.clone();
pre_args.is_multisig = Some(true);
let mut sl =
api_impl::owner::init_send_tx(&mut **w, mask1.as_ref(), pre_args, false).unwrap();
{
let mut w_lock = wallet2.lock();
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
sl = api_impl::foreign::receive_tx(&mut **w2, mask2.as_ref(), &sl, None, false)
.unwrap();

sl = api_impl::owner::process_multisig_tx(&mut **w, mask1.as_ref(), &sl).unwrap();

let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
sl = api_impl::foreign::finalize_tx(&mut **w2, mask2.as_ref(), &sl, false).unwrap();
w2.close().unwrap();
}

api_impl::owner::finalize_tx(&mut **w, mask1.as_ref(), &sl).unwrap()
} else {
Slate::blank(2, TxFlow::Standard)
};
let mut slate = if is_atomic {
let mut atom_args = args.clone();
atom_args.multisig_path = Some(sl.create_multisig_id().to_bip_32_string());
api_impl::owner::init_atomic_swap(&mut **w, (&mask1).as_ref(), atom_args, true).unwrap()
} else {
api_impl::owner::init_send_tx(&mut **w, (&mask1).as_ref(), args, true).unwrap()
};
Expand All @@ -2552,7 +2583,7 @@ pub fn run_doctest_owner(
{
let mut w_lock = wallet2.lock();
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
slate = if countersign_atomic {
slate = if is_atomic {
api_impl::foreign::receive_atomic_tx(
&mut **w2,
(&mask2).as_ref(),
Expand All @@ -2575,7 +2606,7 @@ pub fn run_doctest_owner(
println!("RECEIPIENT SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
if finalize_tx {
slate = if countersign_atomic {
slate = if is_atomic {
let sl =
api_impl::owner::countersign_atomic_swap(&mut **w, &slate, (&mask1).as_ref())
.unwrap();
Expand All @@ -2586,9 +2617,8 @@ pub fn run_doctest_owner(
w2.close().unwrap();
sl
} else if is_multisig {
let sl =
api_impl::owner::process_multisig_tx(&mut **w, (&mask1).as_ref(), &slate, true)
.unwrap();
let sl = api_impl::owner::process_multisig_tx(&mut **w, (&mask1).as_ref(), &slate)
.unwrap();
let mut w_lock = wallet2.lock();
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let sl = api_impl::foreign::finalize_tx(&mut **w2, (&mask2).as_ref(), &sl, false)
Expand Down Expand Up @@ -2630,7 +2660,7 @@ pub fn run_doctest_owner(
#[doc(hidden)]
#[macro_export]
macro_rules! doctest_helper_json_rpc_owner_assert_response {
($request:expr, $expected_response:expr, $blocks_to_mine:expr, $perform_tx:expr, $lock_tx:expr, $finalize_tx:expr, $payment_proof:expr, $countersign_atomic:expr, $is_multisig:expr) => {
($request:expr, $expected_response:expr, $blocks_to_mine:expr, $perform_tx:expr, $lock_tx:expr, $finalize_tx:expr, $payment_proof:expr, $is_atomic:expr, $is_multisig:expr) => {
// create temporary wallet, run jsonrpc request on owner api of wallet, delete wallet, return
// json response.
// In order to prevent leaking tempdirs, This function should not panic.
Expand Down Expand Up @@ -2663,7 +2693,7 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response {
$lock_tx,
$finalize_tx,
$payment_proof,
$countersign_atomic,
$is_atomic,
$is_multisig,
)
.unwrap()
Expand Down
6 changes: 1 addition & 5 deletions controller/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,11 +929,7 @@ where

println!("Transaction finalized successfully");

if slate
.participant_data
.iter()
.fold(false, |t, d| t | d.tau_x.is_some())
{
if slate.is_multisig() {
info!(
"Transaction multisig identifier: {}",
slate.create_multisig_id().to_bip_32_string()
Expand Down
123 changes: 24 additions & 99 deletions libwallet/src/api_impl/foreign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@
// limitations under the License.

//! Generic implementation of owner API functions
use rand::thread_rng;
use strum::IntoEnumIterator;

use crate::api_impl::owner::{check_ttl, post_tx};
use crate::api_impl::owner::{finalize_atomic_swap, finalize_tx as owner_finalize};
use crate::grin_core::core::FeeFields;
use crate::grin_core::libtx::proof;
use crate::grin_keychain::{Keychain, SwitchCommitmentType};
use crate::grin_util::{from_hex, ToHex};
use crate::grin_util::secp::key::{PublicKey, SecretKey};
use crate::grin_util::secp::pedersen::Commitment;
use crate::grin_util::ToHex;
use crate::internal::{selection, tx, updater};
use crate::slate_versions::SlateVersion;
use crate::{
Expand Down Expand Up @@ -110,8 +107,12 @@ where
use_test_rng,
)?;

// Add our contribution to the offset
ret_slate.adjust_offset(&keychain, &context)?;
let is_multisig = slate.is_multisig();

if !is_multisig {
// Add our contribution to the offset
ret_slate.adjust_offset(&keychain, &context)?;
}

let excess = ret_slate.calc_excess(keychain.secp())?;

Expand All @@ -126,12 +127,12 @@ where
p.receiver_signature = Some(sig);
}

ret_slate.amount = 0;
ret_slate.fee_fields = FeeFields::zero();
ret_slate.remove_other_sigdata(&keychain, &context.sec_nonce, &context.sec_key)?;
if ret_slate.is_multisig() {
ret_slate.state = SlateState::Multisig2;
} else {
ret_slate.amount = 0;
ret_slate.fee_fields = FeeFields::zero();
ret_slate.remove_other_sigdata(&keychain, &context.sec_nonce, &context.sec_key)?;
ret_slate.state = SlateState::Standard2;
}

Expand Down Expand Up @@ -203,14 +204,15 @@ where
(atomic_id, Some(atomic))
};

let min_confirmations = if use_test_rng { 0 } else { 10 };
let (input_ids, output_ids) = if is_height_lock {
// add input(s) and change output to slate
let ctx = tx::add_inputs_to_atomic_slate(
w,
keychain_mask,
&mut ret_slate,
height,
10, // min_confirmations
min_confirmations,
500, // max_outputs
1, // num_change_outputs
true, // selection_strategy_is_use_all
Expand Down Expand Up @@ -252,7 +254,6 @@ where
batch.commit()?;
}

ret_slate.adjust_offset(&keychain, &context)?;
ret_slate.state = SlateState::Atomic2;

Ok(ret_slate)
Expand Down Expand Up @@ -293,74 +294,10 @@ where
sl.state = SlateState::Invoice3;
sl.amount = 0;
} else if sl.state == SlateState::Multisig3 {
let tau_x =
selection::finalize_multisig_bulletproof(w, keychain_mask, &mut sl, &mut context)?;
let k = w.keychain(keychain_mask)?;
let secp = k.secp();
let (_, pub_nonce) = context.get_public_keys(secp);

let tau_one = context.tau_one.ok_or(Error::from(ErrorKind::GenericError(
"missing tau one multisig key".into(),
)))?;
let tau_two = context.tau_two.ok_or(Error::from(ErrorKind::GenericError(
"missing tau two multisig key".into(),
)))?;

{
let oth_data = sl
.participant_data
.iter()
.find(|d| d.public_nonce != pub_nonce)
.ok_or(Error::from(ErrorKind::GenericError(
"missing other participant's data".into(),
)))?;
let oth_tau_one = oth_data.tau_one.ok_or(Error::from(ErrorKind::GenericError(
"missing other tau one multisig key".into(),
)))?;
let oth_tau_two = oth_data.tau_two.ok_or(Error::from(ErrorKind::GenericError(
"missing other tau two multisig key".into(),
)))?;

context.tau_one = Some(PublicKey::from_combination(
secp,
vec![&tau_one, &oth_tau_one],
)?);
context.tau_two = Some(PublicKey::from_combination(
secp,
vec![&tau_two, &oth_tau_two],
)?);
let common_nonce = context.create_common_nonce(secp, &oth_data.public_nonce)?;
let key_id = sl.create_multisig_id();
let out = w.iter().find(|o| o.key_id == key_id).ok_or(Error::from(
ErrorKind::GenericError("missing multisig output".into()),
))?;

let commit_str = out
.commit
.as_ref()
.ok_or(Error::from(ErrorKind::GenericError(
"missing multisig output commit".into(),
)))?;
let commit_hex = from_hex(&commit_str)
.map_err(|e| ErrorKind::GenericError(format!("invalid hex: {}", e)))?;
let commit = Commitment::from_vec(commit_hex);

// finish receiver's side of the multisig bulletproof
context.tau_x = Some(SecretKey::new(secp, &mut thread_rng()));
let _ = proof::create_multisig(
&k,
&proof::ProofBuilder::new(&k),
sl.amount,
&key_id,
SwitchCommitmentType::Regular,
&common_nonce,
context.tau_x.as_mut(),
context.tau_one.as_mut(),
context.tau_two.as_mut(),
&[commit],
2,
None,
)?;
}

let (_, pub_nonce) = context.get_public_keys(k.secp());
{
let mut part_data = sl
.participant_data
Expand All @@ -369,28 +306,16 @@ where
.ok_or(Error::from(ErrorKind::GenericError(
"missing local participant data".into(),
)))?;
part_data.tau_x = context.tau_x.clone();
part_data.tau_x = tau_x;
}
// FIXME: keep tau_one and tau_two in participant data to check on other end?

let oth_tau_x = sl
.participant_data
.iter()
.find(|d| d.public_nonce != pub_nonce)
.ok_or(Error::from(ErrorKind::GenericError(
"missing other participant's data".into(),
)))?
.tau_x
.as_ref()
.ok_or(Error::from(ErrorKind::GenericError(
"missing other tau x key".into(),
)))?;

// compute tau x sum to store in the backend DB
let tau_x = context.tau_x.as_mut().unwrap();
tau_x.add_assign(secp, oth_tau_x)?;

sl.state = SlateState::Multisig4;

sl.adjust_offset(&k, &context)?;

debug!(
"multisig ID path: {}",
slate.create_multisig_id().to_bip_32_string()
);
debug!("Use with commands spending this multisig output");

let mut batch = w.batch(keychain_mask)?;
batch.save_private_context(sl.id.as_bytes().as_ref(), &context)?;
Expand Down
Loading

0 comments on commit 3584a61

Please sign in to comment.