test contract input features

This commit is contained in:
wiesche
2026-09-04 16:13:32 +02:00
parent 483b29597f
commit b8ede99308
6 changed files with 92 additions and 10 deletions
+15 -2
View File
@@ -22,7 +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::core::{FeeFields, Inputs, KernelFeatures, OutputFeatures};
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::{ExtKeychain, Keychain};
@@ -102,6 +102,7 @@ pub struct ExpectedContractSlate {
pub amount: u64,
pub fee: u64,
pub inputs: usize,
pub coinbase_inputs: usize,
pub outputs: usize,
pub kernels: usize,
pub num_participants: u8,
@@ -120,8 +121,20 @@ pub fn assert_basic_contract_slate(slate: &libwallet::Slate, expected: ExpectedC
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);
let inputs = tx.inputs();
assert_eq!(inputs.len(), expected.inputs);
match inputs {
Inputs::FeaturesAndCommit(inputs) => assert_eq!(
inputs
.iter()
.filter(|input| input.features == OutputFeatures::Coinbase)
.count(),
expected.coinbase_inputs
),
Inputs::CommitOnly(_) => panic!("contract inputs must include their features"),
}
assert_eq!(tx.outputs().len(), expected.outputs);
assert!(tx.outputs().iter().all(|output| output.is_plain()));
assert_eq!(tx.kernels().len(), expected.kernels);
assert_eq!(slate.participant_data.len(), expected.participant_data);
assert_eq!(
+3
View File
@@ -111,6 +111,7 @@ fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
amount: 5_000_000_000,
fee: participant_fee,
inputs: 0,
coinbase_inputs: 0,
outputs: 0,
kernels: 0,
num_participants: 2,
@@ -153,6 +154,7 @@ fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
amount: 5_000_000_000,
fee: 2 * participant_fee,
inputs: 1,
coinbase_inputs: 1,
outputs: 1,
kernels: 1,
num_participants: 2,
@@ -182,6 +184,7 @@ fn contract_rsr_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
amount: 5_000_000_000,
fee: 2 * participant_fee,
inputs: 2,
coinbase_inputs: 2,
outputs: 2,
kernels: 1,
num_participants: 2,
@@ -20,6 +20,7 @@ extern crate log;
use grin_wallet_libwallet as libwallet;
use grin_core::consensus;
use grin_core::core::transaction::CommitWrapper;
use impls::test_framework::{self};
use libwallet::contract::my_fee_contribution;
use libwallet::contract::types::{ContractNewArgsAPI, ContractSetupArgsAPI, OutputSelectionArgs};
@@ -43,6 +44,7 @@ fn contract_self_spend_custom_tx_impl(test_dir: &'static str) -> Result<(), libw
let mut use_inputs = String::from("");
let mut plain_input = None;
wallet::controller::owner_single_use(
send_wallet.clone(),
send_mask,
@@ -166,6 +168,15 @@ fn contract_self_spend_custom_tx_impl(test_dir: &'static str) -> Result<(), libw
// Assert expected outputs were created
// 88, 35, 3, 0.2, 15 and a change output
assert_eq!(commits[10].output.value, 88 * consensus::GRIN_BASE);
assert!(!commits[10].output.is_coinbase);
plain_input = Some((
commits[10]
.output
.commit
.clone()
.expect("plain output has no commitment"),
commits[10].commit,
));
assert_eq!(commits[11].output.value, 35 * consensus::GRIN_BASE);
assert_eq!(commits[12].output.value, 3 * consensus::GRIN_BASE);
assert_eq!(
@@ -184,6 +195,50 @@ fn contract_self_spend_custom_tx_impl(test_dir: &'static str) -> Result<(), libw
},
)?;
let (plain_input, plain_commitment) = plain_input.expect("plain output was not found");
wallet::controller::owner_single_use(
send_wallet.clone(),
send_mask,
PathBuf::from(test_dir),
|api, m| {
slate = api.contract_new(
m,
&ContractNewArgsAPI {
setup_args: ContractSetupArgsAPI {
net_change: Some(0),
num_participants: 1,
selection_args: OutputSelectionArgs {
minimum_confirmations: Some(1),
use_inputs: Some(plain_input),
..Default::default()
},
..Default::default()
},
..Default::default()
},
)?;
slate = api.contract_sign(m, &slate, &ContractSetupArgsAPI::default())?;
Ok(())
},
)?;
assert_eq!(slate.state, SlateState::Standard2);
common::assert_basic_contract_slate(
&slate,
common::ExpectedContractSlate {
amount: 0,
fee: my_fee_contribution(1, 1, 1, 1)?.fee(),
inputs: 1,
coinbase_inputs: 0,
outputs: 1,
kernels: 1,
num_participants: 1,
participant_data: 1,
signatures: 1,
},
);
let inputs = Vec::<CommitWrapper>::from(&slate.tx_or_err()?.inputs());
assert_eq!(inputs[0].commitment(), plain_commitment);
// let logging finish
stopper.store(false, Ordering::Relaxed);
thread::sleep(Duration::from_millis(200));
+3
View File
@@ -86,6 +86,7 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
amount: 5_000_000_000,
fee: participant_fee,
inputs: 0,
coinbase_inputs: 0,
outputs: 0,
kernels: 0,
num_participants: 2,
@@ -201,6 +202,7 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
amount: 5_000_000_000,
fee: 2 * participant_fee,
inputs: 1,
coinbase_inputs: 1,
outputs: 1,
kernels: 1,
num_participants: 2,
@@ -394,6 +396,7 @@ fn contract_srs_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
amount: 5_000_000_000,
fee: 2 * participant_fee,
inputs: 2,
coinbase_inputs: 2,
outputs: 2,
kernels: 1,
num_participants: 2,
-2
View File
@@ -39,8 +39,6 @@ Separating side effects until the 'save_step' part would make these functions mu
- Reserve funds across open late-locked contracts, or make the possible signing failure
clearer to callers.
- Handle the coinbase flows. Coinbase inputs are built into the slate, but nothing else
is exercised.
- Keep contracts at two participants until the known multi-party attack is addressed.
- For a payjoin, `--use-inputs any` picks an input for us; naming the commitment would be
clearer, for the API as much as the CLI.
+16 -6
View File
@@ -643,14 +643,24 @@ mod tests {
assert!(check_identifiers(&[], &[own_input], "input").is_err());
assert!(check_identifiers(&[own_input], &[own_output], "output").is_err());
assert!(check_identifiers(&[own_input, own_input], &[own_input], "input").is_err());
assert!(check_identifiers(
&[identifier(1, OutputFeatures::Coinbase)],
&[own_input],
"input"
)
.is_err());
assert!(cached_commitment("01").is_none());
assert!(cached_commitment("not hex").is_none());
assert!(cached_commitment(&"00".repeat(PEDERSEN_COMMITMENT_SIZE)).is_some());
}
#[test]
fn coinbase_features() {
let commit = Commitment::from_vec(vec![1]);
let plain = OutputIdentifier::new(OutputFeatures::Plain, &commit);
let coinbase = OutputIdentifier::new(OutputFeatures::Coinbase, &commit);
for (actual, expected) in [(plain, coinbase), (coinbase, plain)] {
let err = check_identifiers(&[actual], &[expected], "input").unwrap_err();
assert!(matches!(
err,
Error::GenericError(ref message)
if message == "Contract slate changed or duplicated one of our input commitments"
));
}
}
}