Skip to content

Commit 7b577bf

Browse files
committed
Add manual testing for accepting dual-funded channels
1 parent 253c53f commit 7b577bf

File tree

3 files changed

+209
-11
lines changed

3 files changed

+209
-11
lines changed

lightning/src/ln/channel.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3981,6 +3981,20 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
39813981
_ => todo!(),
39823982
}
39833983
}
3984+
3985+
#[cfg(test)]
3986+
pub fn get_initial_counterparty_commitment_signature_for_test<L: Deref>(
3987+
&mut self, logger: &L, channel_transaction_parameters: ChannelTransactionParameters,
3988+
counterparty_cur_commitment_point_override: PublicKey,
3989+
) -> Result<Signature, ChannelError>
3990+
where
3991+
SP::Target: SignerProvider,
3992+
L::Target: Logger
3993+
{
3994+
self.counterparty_cur_commitment_point = Some(counterparty_cur_commitment_point_override);
3995+
self.channel_transaction_parameters = channel_transaction_parameters;
3996+
self.get_initial_counterparty_commitment_signature(logger)
3997+
}
39843998
}
39853999

39864000
// Internal utility functions for channels
@@ -8824,7 +8838,7 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
88248838
is_initiator: false,
88258839
inputs_to_contribute: dual_funding_context.our_funding_inputs.clone(),
88268840
outputs_to_contribute: Vec::new(),
8827-
expected_remote_shared_funding_output: Some((context.get_funding_redeemscript(), context.channel_value_satoshis)),
8841+
expected_remote_shared_funding_output: Some((context.get_funding_redeemscript().to_p2wsh(), context.channel_value_satoshis)),
88288842
}
88298843
).map_err(|_| ChannelError::Close((
88308844
"V2 channel rejected due to sender error".into(),

lightning/src/ln/channelmanager.rs

Lines changed: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13514,20 +13514,24 @@ mod tests {
1351413514
use bitcoin::hashes::sha256::Hash as Sha256;
1351513515
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
1351613516
use core::sync::atomic::Ordering;
13517+
use crate::chain::chaininterface::{ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
1351713518
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
1351813519
use crate::ln::types::ChannelId;
1351913520
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
13521+
use crate::ln::chan_utils::{make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters};
13522+
use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint};
13523+
use crate::ln::channel::{calculate_our_funding_satoshis, OutboundV2Channel, MIN_CHAN_DUST_LIMIT_SATOSHIS};
1352013524
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
1352113525
use crate::ln::functional_test_utils::*;
13522-
use crate::ln::msgs::{self, ErrorAction};
13526+
use crate::ln::msgs::{self, CommitmentSigned, ErrorAction, TxAddInput, TxAddOutput, TxComplete};
1352313527
use crate::ln::msgs::ChannelMessageHandler;
1352413528
use crate::prelude::*;
1352513529
use crate::routing::router::{PaymentParameters, RouteParameters, find_route};
1352613530
use crate::util::errors::APIError;
13527-
use crate::util::ser::Writeable;
13531+
use crate::util::ser::{TransactionU16LenLimited, Writeable};
1352813532
use crate::util::test_utils;
1352913533
use crate::util::config::{ChannelConfig, ChannelConfigUpdate};
13530-
use crate::sign::EntropySource;
13534+
use crate::sign::{EntropySource, ChannelSigner as _};
1353113535

1353213536
#[test]
1353313537
fn test_notify_limits() {
@@ -14918,7 +14922,153 @@ mod tests {
1491814922
}
1491914923

1492014924
// Dual-funding: V2 Channel Establishment Tests
14921-
// TODO(dual_funding): Complete these.
14925+
struct V2ChannelEstablishmentTestSession {
14926+
initiator_input_value_satoshis: u64,
14927+
}
14928+
14929+
// TODO(dual_funding): Use real node and API for creating V2 channels as initiator when available,
14930+
// instead of manually constructing messages.
14931+
fn do_test_v2_channel_establishment(session: V2ChannelEstablishmentTestSession) {
14932+
let chanmon_cfgs = create_chanmon_cfgs(2);
14933+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
14934+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
14935+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
14936+
let logger_a = test_utils::TestLogger::with_id("node a".to_owned());
14937+
14938+
// Create a funding input for the new channel along with its previous transaction.
14939+
let initiator_funding_inputs: Vec<_> = create_dual_funding_utxos_with_prev_txs(
14940+
&nodes[0], &[session.initiator_input_value_satoshis]
14941+
).into_iter().map(|(txin, tx)| (txin, TransactionU16LenLimited::new(tx).unwrap())).collect();
14942+
14943+
// Alice creates a dual-funded channel as initiator.
14944+
let funding_feerate = node_cfgs[0].fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
14945+
let funding_satoshis = calculate_our_funding_satoshis(
14946+
true, &initiator_funding_inputs[..], funding_feerate, MIN_CHAN_DUST_LIMIT_SATOSHIS
14947+
).unwrap();
14948+
let mut channel = OutboundV2Channel::new(
14949+
&LowerBoundedFeeEstimator(node_cfgs[0].fee_estimator), &nodes[0].node.entropy_source, &nodes[0].node.signer_provider,
14950+
nodes[1].node.get_our_node_id(), &nodes[1].node.init_features(), funding_satoshis,
14951+
initiator_funding_inputs.clone(), 42 /* user_channel_id */, &nodes[0].node.default_configuration, nodes[0].best_block_info().1,
14952+
nodes[0].node.create_and_insert_outbound_scid_alias(), ConfirmationTarget::NonAnchorChannelFee, &logger_a).unwrap();
14953+
let open_channel_v2_msg = channel.get_open_channel_v2(nodes[0].chain_source.chain_hash);
14954+
14955+
nodes[1].node.handle_open_channel_v2(nodes[0].node.get_our_node_id(), &open_channel_v2_msg);
14956+
14957+
let accept_channel_v2_msg = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannelV2, nodes[0].node.get_our_node_id());
14958+
let channel_id = ChannelId::v2_from_revocation_basepoints(
14959+
&RevocationBasepoint::from(accept_channel_v2_msg.common_fields.revocation_basepoint),
14960+
&RevocationBasepoint::from(open_channel_v2_msg.common_fields.revocation_basepoint),
14961+
);
14962+
14963+
let tx_add_input_msg = TxAddInput {
14964+
channel_id,
14965+
serial_id: 2, // Even serial_id from initiator.
14966+
prevtx: initiator_funding_inputs[0].1.clone(),
14967+
prevtx_out: 0,
14968+
sequence: initiator_funding_inputs[0].0.sequence.0,
14969+
shared_input_txid: None,
14970+
};
14971+
let input_value = tx_add_input_msg.prevtx.as_transaction().output[tx_add_input_msg.prevtx_out as usize].value;
14972+
assert_eq!(input_value.to_sat(), session.initiator_input_value_satoshis);
14973+
14974+
nodes[1].node.handle_tx_add_input(nodes[0].node.get_our_node_id(), &tx_add_input_msg);
14975+
14976+
let _tx_complete_msg = get_event_msg!(nodes[1], MessageSendEvent::SendTxComplete, nodes[0].node.get_our_node_id());
14977+
14978+
let tx_add_output_msg = TxAddOutput {
14979+
channel_id,
14980+
serial_id: 4,
14981+
sats: funding_satoshis,
14982+
script: make_funding_redeemscript(
14983+
&open_channel_v2_msg.common_fields.funding_pubkey,
14984+
&accept_channel_v2_msg.common_fields.funding_pubkey,
14985+
).to_p2wsh(),
14986+
};
14987+
nodes[1].node.handle_tx_add_output(nodes[0].node.get_our_node_id(), &tx_add_output_msg);
14988+
14989+
let _tx_complete_msg = get_event_msg!(nodes[1], MessageSendEvent::SendTxComplete, nodes[0].node.get_our_node_id());
14990+
14991+
let tx_complete_msg = TxComplete {
14992+
channel_id,
14993+
};
14994+
14995+
nodes[1].node.handle_tx_complete(nodes[0].node.get_our_node_id(), &tx_complete_msg);
14996+
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
14997+
assert_eq!(msg_events.len(), 1);
14998+
let _msg_commitment_signed_from_1 = match msg_events[0] {
14999+
MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
15000+
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
15001+
updates.commitment_signed.clone()
15002+
},
15003+
_ => panic!("Unexpected event"),
15004+
};
15005+
15006+
let (funding_outpoint, channel_type_features) = {
15007+
let per_peer_state = nodes[1].node.per_peer_state.read().unwrap();
15008+
let peer_state = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap();
15009+
let channel_context = peer_state
15010+
.channel_by_id.get(&tx_complete_msg.channel_id).unwrap().context();
15011+
(channel_context.get_funding_txo(), channel_context.get_channel_type().clone())
15012+
};
15013+
15014+
let channel_transaction_parameters = ChannelTransactionParameters {
15015+
counterparty_parameters: Some(CounterpartyChannelTransactionParameters {
15016+
pubkeys: ChannelPublicKeys {
15017+
funding_pubkey: accept_channel_v2_msg.common_fields.funding_pubkey,
15018+
revocation_basepoint: RevocationBasepoint(accept_channel_v2_msg.common_fields.revocation_basepoint),
15019+
payment_point: accept_channel_v2_msg.common_fields.payment_basepoint,
15020+
delayed_payment_basepoint: DelayedPaymentBasepoint(accept_channel_v2_msg.common_fields.delayed_payment_basepoint),
15021+
htlc_basepoint: HtlcBasepoint(accept_channel_v2_msg.common_fields.htlc_basepoint),
15022+
},
15023+
selected_contest_delay: accept_channel_v2_msg.common_fields.to_self_delay,
15024+
}),
15025+
holder_pubkeys: ChannelPublicKeys {
15026+
funding_pubkey: open_channel_v2_msg.common_fields.funding_pubkey,
15027+
revocation_basepoint: RevocationBasepoint(open_channel_v2_msg.common_fields.revocation_basepoint),
15028+
payment_point: open_channel_v2_msg.common_fields.payment_basepoint,
15029+
delayed_payment_basepoint: DelayedPaymentBasepoint(open_channel_v2_msg.common_fields.delayed_payment_basepoint),
15030+
htlc_basepoint: HtlcBasepoint(open_channel_v2_msg.common_fields.htlc_basepoint),
15031+
},
15032+
holder_selected_contest_delay: open_channel_v2_msg.common_fields.to_self_delay,
15033+
is_outbound_from_holder: true,
15034+
funding_outpoint,
15035+
channel_type_features,
15036+
};
15037+
15038+
channel.context.get_mut_signer().as_mut_ecdsa().unwrap().provide_channel_parameters(&channel_transaction_parameters);
15039+
15040+
let msg_commitment_signed_from_0 = CommitmentSigned {
15041+
channel_id,
15042+
signature: channel.context.get_initial_counterparty_commitment_signature_for_test(
15043+
&&logger_a,
15044+
channel_transaction_parameters,
15045+
accept_channel_v2_msg.common_fields.first_per_commitment_point,
15046+
).unwrap(),
15047+
htlc_signatures: vec![],
15048+
batch: None,
15049+
#[cfg(taproot)]
15050+
partial_signature_with_nonce: None,
15051+
};
15052+
15053+
// Handle the initial commitment_signed exchange. Order is not important here.
15054+
nodes[1].node.handle_commitment_signed(nodes[0].node.get_our_node_id(), &msg_commitment_signed_from_0);
15055+
check_added_monitors(&nodes[1], 1);
15056+
15057+
let events = nodes[1].node.get_and_clear_pending_events();
15058+
assert_eq!(events.len(), 1);
15059+
match events[0] {
15060+
Event::ChannelPending { channel_id, .. } => channel_id == channel.context.channel_id(),
15061+
_ => panic!("Unexpected event"),
15062+
};
15063+
}
15064+
15065+
#[test]
15066+
fn test_v2_channel_establishment() {
15067+
// Only initiator contributes
15068+
do_test_v2_channel_establishment(V2ChannelEstablishmentTestSession {
15069+
initiator_input_value_satoshis: 100_000,
15070+
});
15071+
}
1492215072
}
1492315073

1492415074
#[cfg(ldk_bench)]

lightning/src/ln/functional_test_utils.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,20 @@ use crate::util::test_utils;
3838
use crate::util::test_utils::{TestChainMonitor, TestScorer, TestKeysInterface};
3939
use crate::util::ser::{ReadableArgs, Writeable};
4040

41+
use bitcoin::WPubkeyHash;
4142
use bitcoin::amount::Amount;
42-
use bitcoin::block::{Block, Header, Version};
43-
use bitcoin::locktime::absolute::LockTime;
44-
use bitcoin::transaction::{Transaction, TxIn, TxOut};
43+
use bitcoin::block::{Block, Header, Version as BlockVersion};
44+
use bitcoin::locktime::absolute::{LockTime, LOCK_TIME_THRESHOLD};
45+
use bitcoin::transaction::{Sequence, Transaction, TxIn, TxOut};
4546
use bitcoin::hash_types::{BlockHash, TxMerkleNode};
4647
use bitcoin::hashes::sha256::Hash as Sha256;
4748
use bitcoin::hashes::Hash as _;
4849
use bitcoin::network::Network;
4950
use bitcoin::pow::CompactTarget;
51+
use bitcoin::script::ScriptBuf;
5052
use bitcoin::secp256k1::{PublicKey, SecretKey};
51-
use bitcoin::transaction;
53+
use bitcoin::transaction::{self, Version as TxVersion};
54+
use bitcoin::witness::Witness;
5255

5356
use alloc::rc::Rc;
5457
use core::cell::RefCell;
@@ -90,7 +93,7 @@ pub fn mine_transaction_without_consistency_checks<'a, 'b, 'c, 'd>(node: &'a Nod
9093
let height = node.best_block_info().1 + 1;
9194
let mut block = Block {
9295
header: Header {
93-
version: Version::NO_SOFT_FORK_SIGNALLING,
96+
version: BlockVersion::NO_SOFT_FORK_SIGNALLING,
9497
prev_blockhash: node.best_block_hash(),
9598
merkle_root: TxMerkleNode::all_zeros(),
9699
time: height,
@@ -217,7 +220,7 @@ impl ConnectStyle {
217220

218221
pub fn create_dummy_header(prev_blockhash: BlockHash, time: u32) -> Header {
219222
Header {
220-
version: Version::NO_SOFT_FORK_SIGNALLING,
223+
version: BlockVersion::NO_SOFT_FORK_SIGNALLING,
221224
prev_blockhash,
222225
merkle_root: TxMerkleNode::all_zeros(),
223226
time,
@@ -1221,6 +1224,37 @@ fn internal_create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>,
12211224
}
12221225
}
12231226

1227+
pub fn create_dual_funding_utxos_with_prev_txs(
1228+
node: &Node<'_, '_, '_>, utxo_values_in_satoshis: &[u64],
1229+
) -> Vec<(TxIn, Transaction)> {
1230+
// Ensure we have unique transactions per node by using the locktime.
1231+
let tx = Transaction {
1232+
version: TxVersion::TWO,
1233+
lock_time: LockTime::from_height(
1234+
u32::from_be_bytes(node.keys_manager.get_secure_random_bytes()[0..4].try_into().unwrap()) % LOCK_TIME_THRESHOLD
1235+
).unwrap(),
1236+
input: vec![],
1237+
output: utxo_values_in_satoshis.iter().map(|value_satoshis| TxOut {
1238+
value: Amount::from_sat(*value_satoshis), script_pubkey: ScriptBuf::new_p2wpkh(&WPubkeyHash::all_zeros()),
1239+
}).collect()
1240+
};
1241+
1242+
let mut result = vec![];
1243+
for i in 0..utxo_values_in_satoshis.len() {
1244+
result.push(
1245+
(TxIn {
1246+
previous_output: OutPoint {
1247+
txid: tx.compute_txid(),
1248+
index: i as u16,
1249+
}.into_bitcoin_outpoint(),
1250+
script_sig: ScriptBuf::new(),
1251+
sequence: Sequence::ZERO,
1252+
witness: Witness::new(),
1253+
}, tx.clone()));
1254+
}
1255+
result
1256+
}
1257+
12241258
pub fn sign_funding_transaction<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, expected_temporary_channel_id: ChannelId) -> Transaction {
12251259
let (temporary_channel_id, tx, funding_output) = create_funding_transaction(node_a, &node_b.node.get_our_node_id(), channel_value, 42);
12261260
assert_eq!(temporary_channel_id, expected_temporary_channel_id);

0 commit comments

Comments
 (0)