mirror of
https://github.com/jwinterm/grin-wallet.git
synced 2026-10-17 07:11:36 +02:00
test contract foreign api
This commit is contained in:
+21
-8
@@ -44,6 +44,10 @@ pub enum ForeignCheckMiddlewareFn {
|
||||
VerifySlateMessages,
|
||||
/// receive_tx
|
||||
ReceiveTx,
|
||||
/// contract_new
|
||||
ContractNew,
|
||||
/// contract_sign
|
||||
ContractSign,
|
||||
/// finalize_tx
|
||||
FinalizeTx,
|
||||
}
|
||||
@@ -463,26 +467,35 @@ where
|
||||
// Below is a foreign wrapper around owner calls to 'new' and 'sign' which are only executed
|
||||
// if this is a receiving contract. This preserves the ability to receive on a foreign interface.
|
||||
/// Start a receiving contract through the foreign API.
|
||||
pub fn contract_new(
|
||||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
args: &ContractNewArgsAPI,
|
||||
) -> Result<Slate, Error> {
|
||||
pub fn contract_new(&self, args: &ContractNewArgsAPI) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
foreign::contract_new(w, keychain_mask, args)
|
||||
if let Some(m) = self.middleware.as_ref() {
|
||||
m(
|
||||
ForeignCheckMiddlewareFn::ContractNew,
|
||||
w.w2n_client().get_version_info(),
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
foreign::contract_new(w, (&self.keychain_mask).as_ref(), args)
|
||||
}
|
||||
|
||||
/// Sign the receiving side of a contract through the foreign API.
|
||||
pub fn contract_sign(
|
||||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
args: &ContractSetupArgsAPI,
|
||||
) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
foreign::contract_sign(w, keychain_mask, args, slate)
|
||||
if let Some(m) = self.middleware.as_ref() {
|
||||
m(
|
||||
ForeignCheckMiddlewareFn::ContractSign,
|
||||
w.w2n_client().get_version_info(),
|
||||
Some(slate),
|
||||
)?;
|
||||
}
|
||||
foreign::contract_sign(w, (&self.keychain_mask).as_ref(), args, slate)
|
||||
}
|
||||
|
||||
/// Verify an invoice payment proof against the chain.
|
||||
|
||||
@@ -22,6 +22,7 @@ use grin_core as core;
|
||||
use grin_keychain as keychain;
|
||||
use grin_util as util;
|
||||
|
||||
use self::core::core::{FeeFields, KernelFeatures};
|
||||
use self::core::global;
|
||||
use self::core::global::ChainTypes;
|
||||
use self::keychain::{ExtKeychain, Keychain};
|
||||
@@ -96,6 +97,81 @@ pub fn contract_selection_args() -> libwallet::contract::types::OutputSelectionA
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ExpectedContractSlate {
|
||||
pub amount: u64,
|
||||
pub fee: u64,
|
||||
pub inputs: usize,
|
||||
pub outputs: usize,
|
||||
pub kernels: usize,
|
||||
pub num_participants: u8,
|
||||
pub participant_data: usize,
|
||||
pub signatures: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn assert_basic_contract_slate(slate: &libwallet::Slate, expected: ExpectedContractSlate) {
|
||||
let tx = slate.tx_or_err().unwrap();
|
||||
assert_ne!(expected.fee, 0, "expected fee must be non-zero");
|
||||
let expected_fee = FeeFields::new(0, expected.fee).unwrap();
|
||||
assert_eq!(slate.amount, expected.amount);
|
||||
assert_eq!(slate.fee_fields, expected_fee);
|
||||
assert_eq!(slate.kernel_features, 0);
|
||||
assert!(slate.kernel_features_args.is_none());
|
||||
assert!(slate.payment_proof.is_none());
|
||||
assert_eq!(slate.num_participants, expected.num_participants);
|
||||
assert_eq!(tx.inputs().len(), expected.inputs);
|
||||
assert_eq!(tx.outputs().len(), expected.outputs);
|
||||
assert_eq!(tx.kernels().len(), expected.kernels);
|
||||
assert_eq!(slate.participant_data.len(), expected.participant_data);
|
||||
assert_eq!(
|
||||
slate
|
||||
.participant_data
|
||||
.iter()
|
||||
.filter(|participant| participant.part_sig.is_some())
|
||||
.count(),
|
||||
expected.signatures
|
||||
);
|
||||
if expected.kernels != 0 {
|
||||
assert_eq!(
|
||||
tx.kernels()[0].features,
|
||||
KernelFeatures::Plain { fee: expected_fee }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub type TestOwner = grin_wallet_api::Owner<
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct WalletProgress {
|
||||
child_index: u32,
|
||||
tx_count: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn wallet_progress(
|
||||
api: &TestOwner,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
) -> Result<WalletProgress, libwallet::Error> {
|
||||
let child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
};
|
||||
let (_, txs) = api.retrieve_txs(keychain_mask, false, None, None, None)?;
|
||||
Ok(WalletProgress {
|
||||
child_index,
|
||||
tx_count: txs.len(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setup(test_dir: &str) {
|
||||
util::init_test_logger();
|
||||
clean_output_dir(test_dir);
|
||||
|
||||
@@ -22,7 +22,7 @@ use grin_wallet_libwallet as libwallet;
|
||||
use impls::test_framework::{self};
|
||||
use libwallet::contract::my_fee_contribution;
|
||||
use libwallet::contract::types::{ContractNewArgsAPI, ContractSetupArgsAPI};
|
||||
use libwallet::{Slate, SlateState, TxLogEntryType};
|
||||
use libwallet::{NodeVersionInfo, Slate, SlateState, TxLogEntryType};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -32,6 +32,21 @@ mod common;
|
||||
use common::{clean_output_dir, create_wallets, setup};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn reject_contract_new(
|
||||
method: grin_wallet_api::ForeignCheckMiddlewareFn,
|
||||
_node_version: Option<NodeVersionInfo>,
|
||||
slate: Option<&Slate>,
|
||||
) -> Result<(), libwallet::Error> {
|
||||
assert!(matches!(
|
||||
method,
|
||||
grin_wallet_api::ForeignCheckMiddlewareFn::ContractNew
|
||||
));
|
||||
assert!(slate.is_none());
|
||||
Err(libwallet::Error::GenericError(
|
||||
"Contract rejected by middleware".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
/// contract RSR flow
|
||||
fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// create two wallets and mine 4 blocks in each (we want both to have balance to get a payjoin)
|
||||
@@ -41,28 +56,68 @@ fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
let send_mask = wallets[0].1.as_ref();
|
||||
let recv_wallet = wallets[1].0.clone();
|
||||
let recv_mask = wallets[1].1.as_ref();
|
||||
let participant_fee = my_fee_contribution(1, 1, 1, 2)?.fee();
|
||||
|
||||
let mut slate = Slate::blank(0, true); // this gets overriden below
|
||||
|
||||
wallet::controller::owner_single_use(
|
||||
// Receive wallet initiates an invoice transaction through the foreign API.
|
||||
let args = &ContractNewArgsAPI {
|
||||
setup_args: ContractSetupArgsAPI {
|
||||
selection_args: common::contract_selection_args(),
|
||||
net_change: Some(5_000_000_000),
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
{
|
||||
let api = grin_wallet_api::Foreign::new(
|
||||
recv_wallet.clone(),
|
||||
PathBuf::from(test_dir),
|
||||
recv_mask.cloned(),
|
||||
Some(reject_contract_new),
|
||||
false,
|
||||
);
|
||||
let err = api.contract_new(args).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
libwallet::Error::GenericError(ref msg)
|
||||
if msg == "Contract rejected by middleware"
|
||||
));
|
||||
}
|
||||
let mut slate = None;
|
||||
wallet::controller::foreign_single_use(
|
||||
recv_wallet.clone(),
|
||||
recv_mask,
|
||||
PathBuf::from(test_dir),
|
||||
|api, m| {
|
||||
// Receive wallet inititates an invoice transaction with --receive=5
|
||||
let args = &mut ContractNewArgsAPI {
|
||||
recv_mask.cloned(),
|
||||
|api| {
|
||||
let rejected = ContractNewArgsAPI {
|
||||
setup_args: ContractSetupArgsAPI {
|
||||
selection_args: common::contract_selection_args(),
|
||||
net_change: Some(5_000_000_000),
|
||||
..Default::default()
|
||||
net_change: Some(-5_000_000_000),
|
||||
..args.setup_args.clone()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.contract_new(m, args)?;
|
||||
let err = api.contract_new(&rejected).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
libwallet::Error::GenericError(ref msg)
|
||||
if msg == "Can't create a non-receiving contract from a foreign API."
|
||||
));
|
||||
slate = Some(api.contract_new(args)?);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
let mut slate = slate.expect("foreign API returned no slate");
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
common::assert_basic_contract_slate(
|
||||
&slate,
|
||||
common::ExpectedContractSlate {
|
||||
amount: 5_000_000_000,
|
||||
fee: participant_fee,
|
||||
inputs: 0,
|
||||
outputs: 0,
|
||||
kernels: 0,
|
||||
num_participants: 2,
|
||||
participant_data: 1,
|
||||
signatures: 0,
|
||||
},
|
||||
);
|
||||
|
||||
wallet::controller::owner_single_use(
|
||||
send_wallet.clone(),
|
||||
@@ -92,6 +147,19 @@ fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
},
|
||||
)?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
common::assert_basic_contract_slate(
|
||||
&slate,
|
||||
common::ExpectedContractSlate {
|
||||
amount: 5_000_000_000,
|
||||
fee: 2 * participant_fee,
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
kernels: 1,
|
||||
num_participants: 2,
|
||||
participant_data: 2,
|
||||
signatures: 1,
|
||||
},
|
||||
);
|
||||
|
||||
// Receive wallet finalizes and posts
|
||||
wallet::controller::owner_single_use(
|
||||
@@ -108,6 +176,19 @@ fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
},
|
||||
)?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
common::assert_basic_contract_slate(
|
||||
&slate,
|
||||
common::ExpectedContractSlate {
|
||||
amount: 5_000_000_000,
|
||||
fee: 2 * participant_fee,
|
||||
inputs: 2,
|
||||
outputs: 2,
|
||||
kernels: 1,
|
||||
num_participants: 2,
|
||||
participant_data: 2,
|
||||
signatures: 2,
|
||||
},
|
||||
);
|
||||
|
||||
// Send wallet posts so receive wallet doesn't get the mined amount
|
||||
wallet::controller::owner_single_use(
|
||||
|
||||
@@ -23,7 +23,7 @@ use grin_core::core::transaction::{CommitWrapper, Input, Inputs, OutputFeatures,
|
||||
use impls::test_framework::{self};
|
||||
use libwallet::contract::my_fee_contribution;
|
||||
use libwallet::contract::types::{ContractNewArgsAPI, ContractSetupArgsAPI, OwnCommitmentStatus};
|
||||
use libwallet::{Slate, SlateState, TxLogEntryType};
|
||||
use libwallet::{NodeVersionInfo, Slate, SlateState, TxLogEntryType};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -33,6 +33,21 @@ mod common;
|
||||
use common::{clean_output_dir, create_wallets, setup};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn reject_contract_slate(
|
||||
method: grin_wallet_api::ForeignCheckMiddlewareFn,
|
||||
_node_version: Option<NodeVersionInfo>,
|
||||
slate: Option<&Slate>,
|
||||
) -> Result<(), libwallet::Error> {
|
||||
assert!(matches!(
|
||||
method,
|
||||
grin_wallet_api::ForeignCheckMiddlewareFn::ContractSign
|
||||
));
|
||||
assert!(slate.is_some());
|
||||
Err(libwallet::Error::GenericError(
|
||||
"Contract rejected by middleware".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
/// contract SRS flow
|
||||
fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
// create two wallets and mine 4 blocks in each (we want both to have balance to get a payjoin)
|
||||
@@ -42,8 +57,9 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
let send_mask = wallets[0].1.as_ref();
|
||||
let recv_wallet = wallets[1].0.clone();
|
||||
let recv_mask = wallets[1].1.as_ref();
|
||||
let participant_fee = my_fee_contribution(1, 1, 1, 2)?.fee();
|
||||
|
||||
let mut slate = Slate::blank(0, true); // this gets overriden below
|
||||
let mut slate = Slate::blank(0, false); // this gets overriden below
|
||||
|
||||
wallet::controller::owner_single_use(
|
||||
send_wallet.clone(),
|
||||
@@ -64,19 +80,26 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
},
|
||||
)?;
|
||||
assert_eq!(slate.state, SlateState::Standard1);
|
||||
common::assert_basic_contract_slate(
|
||||
&slate,
|
||||
common::ExpectedContractSlate {
|
||||
amount: 5_000_000_000,
|
||||
fee: participant_fee,
|
||||
inputs: 0,
|
||||
outputs: 0,
|
||||
kernels: 0,
|
||||
num_participants: 2,
|
||||
participant_data: 1,
|
||||
signatures: 0,
|
||||
},
|
||||
);
|
||||
|
||||
wallet::controller::owner_single_use(
|
||||
recv_wallet.clone(),
|
||||
recv_mask,
|
||||
PathBuf::from(test_dir),
|
||||
|api, m| {
|
||||
let child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
};
|
||||
let (_, txs) = api.retrieve_txs(m, false, None, None, None)?;
|
||||
let progress = common::wallet_progress(api, m)?;
|
||||
let mut invalid = slate.clone();
|
||||
invalid.state = SlateState::Unknown;
|
||||
let err = api
|
||||
@@ -95,15 +118,7 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
libwallet::Error::GenericError(ref msg)
|
||||
if msg == "Cannot advance a contract slate in state UN"
|
||||
));
|
||||
let current_child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
};
|
||||
let (_, current_txs) = api.retrieve_txs(m, false, None, None, None)?;
|
||||
assert_eq!(current_child_index, child_index);
|
||||
assert_eq!(current_txs.len(), txs.len());
|
||||
assert_eq!(common::wallet_progress(api, m)?, progress);
|
||||
|
||||
let wrong_args = ContractSetupArgsAPI {
|
||||
selection_args: common::contract_selection_args(),
|
||||
@@ -117,41 +132,82 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
if msg == "Expected net change 5000000000, got -5000000000 (did you mean --receive instead of --send?)"
|
||||
));
|
||||
|
||||
// Receive wallet calls --receive=5
|
||||
let args = &mut ContractSetupArgsAPI {
|
||||
selection_args: common::contract_selection_args(),
|
||||
net_change: Some(5_000_000_000),
|
||||
..Default::default()
|
||||
};
|
||||
let incoming = slate.clone();
|
||||
slate = api.contract_sign(m, &incoming, args)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
let child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
// Receive wallet signs through the foreign API.
|
||||
let args = ContractSetupArgsAPI {
|
||||
selection_args: common::contract_selection_args(),
|
||||
net_change: Some(5_000_000_000),
|
||||
..Default::default()
|
||||
};
|
||||
let incoming = slate.clone();
|
||||
{
|
||||
let api = grin_wallet_api::Foreign::new(
|
||||
recv_wallet.clone(),
|
||||
PathBuf::from(test_dir),
|
||||
recv_mask.cloned(),
|
||||
Some(reject_contract_slate),
|
||||
false,
|
||||
);
|
||||
let err = api.contract_sign(&incoming, &args).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
libwallet::Error::GenericError(ref msg)
|
||||
if msg == "Contract rejected by middleware"
|
||||
));
|
||||
}
|
||||
wallet::controller::foreign_single_use(
|
||||
recv_wallet.clone(),
|
||||
PathBuf::from(test_dir),
|
||||
recv_mask.cloned(),
|
||||
|api| {
|
||||
let rejected = ContractSetupArgsAPI {
|
||||
net_change: Some(-5_000_000_000),
|
||||
..args.clone()
|
||||
};
|
||||
let (_, txs) = api.retrieve_txs(m, false, None, None, None)?;
|
||||
let err = api.contract_sign(m, &incoming, args).unwrap_err();
|
||||
let err = api.contract_sign(&incoming, &rejected).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
libwallet::Error::GenericError(ref msg)
|
||||
if msg == "Can't sign a non-receiving contract from a foreign API."
|
||||
));
|
||||
slate = api.contract_sign(&incoming, &args)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
wallet::controller::owner_single_use(
|
||||
recv_wallet.clone(),
|
||||
recv_mask,
|
||||
PathBuf::from(test_dir),
|
||||
|api, m| {
|
||||
let progress = common::wallet_progress(api, m)?;
|
||||
let err = api.contract_sign(m, &incoming, &args).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
libwallet::Error::GenericError(ref msg)
|
||||
if msg == &format!("Slate with id:{} has already been signed.", incoming.id)
|
||||
));
|
||||
let current_child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
};
|
||||
let (_, current_txs) = api.retrieve_txs(m, false, None, None, None)?;
|
||||
assert_eq!(current_child_index, child_index);
|
||||
assert_eq!(current_txs.len(), txs.len());
|
||||
assert_eq!(common::wallet_progress(api, m)?, progress);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
assert_eq!(slate.state, SlateState::Standard2);
|
||||
common::assert_basic_contract_slate(
|
||||
&slate,
|
||||
common::ExpectedContractSlate {
|
||||
amount: 5_000_000_000,
|
||||
fee: 2 * participant_fee,
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
kernels: 1,
|
||||
num_participants: 2,
|
||||
participant_data: 2,
|
||||
signatures: 1,
|
||||
},
|
||||
);
|
||||
wallet::controller::owner_single_use(
|
||||
recv_wallet.clone(),
|
||||
recv_mask,
|
||||
@@ -186,12 +242,7 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
let (_, outputs) = api.retrieve_outputs(m, false, false, None)?;
|
||||
let tx = slate.tx_or_err()?;
|
||||
let input_commits = Vec::<CommitWrapper>::from(&tx.inputs());
|
||||
let child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
};
|
||||
let progress = common::wallet_progress(api, m)?;
|
||||
let mut changed = slate.clone();
|
||||
changed.tx = Some(
|
||||
Transaction::new(
|
||||
@@ -212,13 +263,7 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
}
|
||||
let view = api.contract_view(m, &changed)?;
|
||||
assert_eq!(view.own_commitment_status, OwnCommitmentStatus::Unknown);
|
||||
let current_child_index = {
|
||||
let mut wallet = api.wallet_inst.lock();
|
||||
let wallet = wallet.lc_provider()?.wallet_inst()?;
|
||||
let parent = wallet.parent_key_id();
|
||||
wallet.current_child_index(&parent)?
|
||||
};
|
||||
assert_eq!(current_child_index, child_index);
|
||||
assert_eq!(common::wallet_progress(api, m)?, progress);
|
||||
|
||||
let (output, pos) = outputs
|
||||
.iter()
|
||||
@@ -333,6 +378,19 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
},
|
||||
)?;
|
||||
assert_eq!(slate.state, SlateState::Standard3);
|
||||
common::assert_basic_contract_slate(
|
||||
&slate,
|
||||
common::ExpectedContractSlate {
|
||||
amount: 5_000_000_000,
|
||||
fee: 2 * participant_fee,
|
||||
inputs: 2,
|
||||
outputs: 2,
|
||||
kernels: 1,
|
||||
num_participants: 2,
|
||||
participant_data: 2,
|
||||
signatures: 2,
|
||||
},
|
||||
);
|
||||
wallet::controller::owner_single_use(
|
||||
recv_wallet.clone(),
|
||||
recv_mask,
|
||||
|
||||
@@ -259,6 +259,7 @@ where
|
||||
let net_change = args.setup_args.net_change.ok_or_else(|| {
|
||||
Error::GenericError("Contract requires a net change (--send or --receive)".to_string())
|
||||
})?;
|
||||
// The foreign API only handles the receiving side.
|
||||
if net_change <= 0 {
|
||||
return Err(Error::GenericError(
|
||||
"Can't create a non-receiving contract from a foreign API.".to_string(),
|
||||
@@ -281,6 +282,7 @@ where
|
||||
let net_change = args.net_change.ok_or_else(|| {
|
||||
Error::GenericError("Contract requires a net change (--send or --receive)".to_string())
|
||||
})?;
|
||||
// The foreign API only handles the receiving side.
|
||||
if net_change <= 0 {
|
||||
return Err(Error::GenericError(
|
||||
"Can't sign a non-receiving contract from a foreign API.".to_string(),
|
||||
|
||||
@@ -73,8 +73,6 @@ Separating side effects until the 'save_step' part would make these functions mu
|
||||
|
||||
#### Tests
|
||||
|
||||
- slate contents at each step, not only the end state
|
||||
- the foreign API entry points for `new` and `sign`
|
||||
- Slatepack version negotiation with the last supported wallet release
|
||||
|
||||
#### save_step
|
||||
|
||||
Reference in New Issue
Block a user