diff --git a/controller/src/command.rs b/controller/src/command.rs index 8fed82a..d92d6d8 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -2065,19 +2065,48 @@ mod contract_tests { } #[test] - fn contract_sign_fee_rate() { + fn contract_sign_args() { let args = ContractSetupArgs { counterparty_addr: None, receive: None, send: None, as_json: false, - use_inputs: None, + use_inputs: Some("commitment".to_string()), make_outputs: None, minimum_confirmations: None, fee_rate: Some(2), outfile: None, add_outputs: false, }; - assert_eq!(args.to_api_args().unwrap().fee_rate, Some(2)); + let api_args = args.to_api_args().unwrap(); + assert_eq!(api_args.fee_rate, Some(2)); + assert_eq!( + api_args.selection_args.use_inputs.as_deref(), + Some("commitment") + ); + } + + #[test] + fn contract_new_args() { + let args = ContractNewArgs { + counterparty_addr: None, + receive: Some(1), + send: None, + src_acct_name: None, + num_participants: 2, + as_json: false, + use_inputs: Some("commitment".to_string()), + make_outputs: None, + minimum_confirmations: 1, + ttl_blocks: None, + fee_rate: None, + outfile: None, + add_outputs: false, + }; + let api_args = args.to_api_args().unwrap(); + assert_eq!( + api_args.setup_args.selection_args.use_inputs.as_deref(), + Some("commitment") + ); } } diff --git a/doc/contracts.md b/doc/contracts.md index 0723da5..9c4107d 100644 --- a/doc/contracts.md +++ b/doc/contracts.md @@ -29,7 +29,8 @@ to balance that side of the transaction. Payjoin is used by default. A receiver adds an input when one is available. An empty wallet can still receive; the fee is then taken from the received output. `--no-payjoin` prevents -the receiver from adding an input and cannot be used together with `--use-inputs`. +the receiver from adding an input and cannot be used together with `--use-inputs`. Pass one +or more comma-separated output commitments to `--use-inputs` to select specific payjoin inputs. Inputs and outputs are normally picked when signing. `--add-outputs` picks and locks them during `new`. Prepared outputs appear as unconfirmed in the wallet, but are only added to diff --git a/libwallet/src/contract/actions/README.md b/libwallet/src/contract/actions/README.md index b2862bc..acc2e79 100644 --- a/libwallet/src/contract/actions/README.md +++ b/libwallet/src/contract/actions/README.md @@ -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. - - 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. - Raise the replacement fee when `revoke` races a transaction already in the mempool. - Merge invoice-proof retrieval with the existing payment-proof API. - Add a Grin node API for kernel lookup by MMR index, then use the witness index instead diff --git a/libwallet/src/contract/selection.rs b/libwallet/src/contract/selection.rs index 688fc78..cac174c 100644 --- a/libwallet/src/contract/selection.rs +++ b/libwallet/src/contract/selection.rs @@ -586,8 +586,8 @@ mod tests { } #[test] - fn receiver_payjoin_exact() { - // net_change=-my_fees(1, 1), inputs=[my_fees(1, 0)], no fee committed => Ok([3, my_fees(2, 1)], fees) + fn receiver_payjoin_inputs() { + // A receiver payjoin uses the smallest input unless one is named let setup_args = ContractSetupArgsAPI { // we expect to receive exactly our fee contribution my_fees(1, 1) net_change: Some(my_fee_contribution(1, 1, 1, 2).unwrap().fee() as i64), @@ -608,6 +608,11 @@ mod tests { result_ref, (expected_inputs, expected_output_amounts, expected_fee) ); + + let mut named_args = setup_args.clone(); + named_args.selection_args.use_inputs = Some("abc1".to_string()); + let result = compute(&named_args, None, &mut inputs.clone()).unwrap(); + assert_eq!(result.0, vec![inputs[1].clone()]); } #[test] diff --git a/tests/cmd_line_contract.rs b/tests/cmd_line_contract.rs index be7c5cf..50e01d5 100644 --- a/tests/cmd_line_contract.rs +++ b/tests/cmd_line_contract.rs @@ -374,6 +374,10 @@ fn parses_contract_options() { "grin-wallet", "contract", "sign", + "--receive", + "1", + "--use-inputs", + "commitment", "--min_conf", "3", "--fee_rate", @@ -388,6 +392,7 @@ fn parses_contract_options() { assert_eq!(parsed.minimum_confirmations, Some(3)); assert_eq!(parsed.fee_rate, Some(2)); assert_eq!(parsed.outfile.as_deref(), Some("sign.slatepack")); + assert_eq!(parsed.use_inputs.as_deref(), Some("commitment")); let args = app .get_matches_from_safe(vec![ @@ -421,6 +426,8 @@ fn selection_locks_early() { }; assert!(!parse(&[]).add_outputs); - assert!(parse(&["--use-inputs", "commitment"]).add_outputs); + let selected = parse(&["--use-inputs", "commitment"]); + assert!(selected.add_outputs); + assert_eq!(selected.use_inputs.as_deref(), Some("commitment")); assert!(parse(&["--make-outputs", "1"]).add_outputs); }