+ a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
+ b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
+ });
+
+ // Adding multiple outputs to the funding output pubkey is an error
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Adding two outputs to the funding output pubkey",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
+ outputs_a: generate_funding_output(100_000),
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(1_001_000)]),
+ outputs_b: generate_funding_output(100_000),
+ expect_error: Some((AbortReason::DuplicateFundingOutput, ErrorCulprit::NodeA)),
+ a_expected_remote_shared_output: None,
+ b_expected_remote_shared_output: None,
+ });
+
+ // We add the funding output, but we contribute a little
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Funding output by us, small contribution",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
+ outputs_a: generate_shared_funding_output(1_000_000, 10_000),
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
+ outputs_b: vec![],
+ expect_error: None,
+ a_expected_remote_shared_output: None,
+ b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 990_000)),
+ });
+
+ // They add the funding output, and we contribute a little
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Funding output by them, small contribution",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
+ outputs_a: vec![],
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
+ outputs_b: generate_shared_funding_output(1_000_000, 990_000),
+ expect_error: None,
+ a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 10_000)),
+ b_expected_remote_shared_output: None,
+ });
+
+ // We add the funding output, and we contribute most
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Funding output by us, large contribution",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
+ outputs_a: generate_shared_funding_output(1_000_000, 990_000),
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
+ outputs_b: vec![],
+ expect_error: None,
+ a_expected_remote_shared_output: None,
+ b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 10_000)),
+ });
+
+ // They add the funding output, but we contribute most
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Funding output by them, large contribution",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
+ outputs_a: vec![],
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
+ outputs_b: generate_shared_funding_output(1_000_000, 10_000),
+ expect_error: None,
+ a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 990_000)),
+ b_expected_remote_shared_output: None,
+ });
+
+ // During a splice-out, with peer providing more output value than input value
+ // but still pays enough fees due to their to_remote_value_satoshis portion in
+ // the shared input.
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Splice out with sufficient initiator balance",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(50_000)]),
+ outputs_a: generate_funding_output(120_000),
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(50_000)]),
+ outputs_b: vec![],
+ expect_error: None,
+ a_expected_remote_shared_output: None,
+ b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
+ });
+
+ // During a splice-out, with peer providing more output value than input value
+ // and the to_remote_value_satoshis portion in
+ // the shared input cannot cover fees
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Splice out with insufficient initiator balance",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
+ outputs_a: generate_funding_output(120_000),
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
+ outputs_b: vec![],
+ expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
+ a_expected_remote_shared_output: None,
+ b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
+ });
+
+ // The actual funding output value is lower than the intended local contribution by the same node
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Splice in, invalid intended local contribution",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
+ outputs_a: generate_shared_funding_output(100_000, 120_000), // local value is higher than the output value
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
+ outputs_b: vec![],
+ expect_error: Some((AbortReason::InvalidLowFundingOutputValue, ErrorCulprit::NodeA)),
+ a_expected_remote_shared_output: None,
+ b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 20_000)),
+ });
+
+ // The actual funding output value is lower than the intended local contribution of the other node
+ do_test_interactive_tx_constructor(TestSession {
+ description: "Splice in, invalid intended local contribution",
+ inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
+ outputs_a: vec![],
+ inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
+ outputs_b: generate_funding_output(100_000),
+ // The error is caused by NodeA, it occurs when nodeA prepares the message to be sent to NodeB, that's why here it shows up as NodeB
+ expect_error: Some((AbortReason::InvalidLowFundingOutputValue, ErrorCulprit::NodeB)),
+ a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 120_000)), // this is higher than the actual output value
+ b_expected_remote_shared_output: None,