From c691694b3e575a79bdcb71c5974c690db9e72305 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Mon, 12 Feb 2024 11:40:32 +0100 Subject: [PATCH] Splicing proto hapa7; reworked state handling and all --- README.md | 90 +- lightning/src/chain/channelmonitor.rs | 12 +- lightning/src/ln/channel.rs | 953 ++++------- lightning/src/ln/channel_id.rs | 8 +- lightning/src/ln/channel_splice.rs | 214 +++ lightning/src/ln/channelmanager.rs | 450 +++-- lightning/src/ln/functional_tests.rs | 1181 +------------ lightning/src/ln/functional_tests_splice.rs | 1695 +++++++++++++++++++ lightning/src/ln/interactivetxs.rs | 2 +- lightning/src/ln/mod.rs | 4 + lightning/src/ln/peer_handler.rs | 2 +- lightning/src/sign/mod.rs | 17 +- lightning/src/util/test_channel_signer.rs | 4 +- 13 files changed, 2587 insertions(+), 2045 deletions(-) create mode 100644 lightning/src/ln/channel_splice.rs create mode 100644 lightning/src/ln/functional_tests_splice.rs diff --git a/README.md b/README.md index 922cb1f095a..49b404633b9 100644 --- a/README.md +++ b/README.md @@ -41,70 +41,88 @@ Detailed steps (as of Jan 24, d0b0be1, splicing-hapa6) [S1I] splice_channel() - ChannelManager API -[S2I] get_splice() - Channel + Do checks, save pending splice parameters + get_splice() - Channel message out: splice -handle_splice() - ChannelManager +[A] handle_splice() - ChannelManager [S3A] internal_splice() - ChannelManager -[S4A] get_splice_ack - Channel -[A] begin_interactive_funding_tx_construction() - Channel -[A] begin_interactive_funding_tx_construction() - ChannelContext + Do checks. Check if channel ID would change. Cycle back the channel to UnfundedInboundV2 + splice_start() -- ChannelContext + Start the splice, update capacity, state, reset funding transaction + get_splice_ack() -- Channel + begin_interactive_funding_tx_construction() - Channel + begin_interactive_funding_tx_construction() - ChannelContext + Splicing specific: Add the previous funding as an input to the new one. + get_input_of_current_funding() + InteractiveTxConstructor::new() + Start interactive TX negotiation message in: splice_ack -handle_splice_ack() - ChannelManager +[I] handle_splice_ack() - ChannelManager [S5I] internal_splice_ack() - ChannelManager + Do checks, check against initial splice() + Cycle back the channel to UnfundedOutboundV2 + splice_start() -- ChannelContext + Start the splice, update capacity, state, reset funding transaction event: SpliceAckedInputsContributionReady -action by client, create input for new funding + contains the pre & post capacities, channel ID +action by client: provide extra input(s) for new funding [I] contribute_funding_inputs() - ChannelManager API -[I] begin_interactive_funding_tx_construction() - Channel -[I] begin_interactive_funding_tx_construction() - ChannelContext -[I] InteractiveTxConstructor::new() + begin_interactive_funding_tx_construction() - Channel + begin_interactive_funding_tx_construction() - ChannelContext + Splicing specific: Add the previous funding as an input to the new one. + get_input_of_current_funding() InteractiveTxConstructor::new() +Interactive tx construction flow follows (e.g. 2 inputs, 2 outputs) message out: tx_add_input -[A] handle_tx_add_input() - ChannelManager -[A] internal_tx_add_input() - ChannelManager -[A] tx_add_input() - Channel -[A] handle_tx_add_input - InteractiveTxConstructor message in: tx_complete -[I] handle_tx_complete() - ChannelManager -[I] internal_tx_complete() - ChannelManager -[I] tx_complete() - ChannelContext -[I] handle_tx_complete() - InteractiveTxConstructor message out: tx_add_input, second -[A] handle_tx_add_input() - ChannelManager message in: tx_complete -[I] handle_tx_complete() - ChannelManager message out: tx_add_output - for change -[A] handle_tx_add_output() - ChannelManager message in: tx_complete -[I] handle_tx_complete() - ChannelManager message out: tx_add_output - for new funding -[A] handle_tx_add_output() - ChannelManager message in: tx_complete [I] handle_tx_complete() - ChannelManager -[I] funding_tx_constructed() - Channel -[I] get_initial_commitment_signed() - Channel -[I] interactive_tx_constructor = None -[I] channel_state = FundingNegotiated + internal_tx_complete() -- ChannelManager + funding_tx_constructed() - Channel + get_initial_commitment_signed() - Channel + Splicing-specific: Add signature on the previous funding tx input + Mark finished interactive tx construction + Update channel state (FundingNegotiated) message out: tx_complete message out: commitment_signed event: FundingTransactionReadyForSigning + contains the new funding transaction with the signature on the previous tx input +action by client: Create and provide signature on the extra inputs [I] funding_transaction_signed() - ChannelManager -[I] verify_interactive_tx_signatures() - Channel -[I] provide_holder_witnesses() - InteractiveTxSigningSession + verify_interactive_tx_signatures() - Channel + splicing specific: Use the previously saved shared signature (tlvs field) + provide_holder_witnesses() - InteractiveTxSigningSession [A] handle_tx_complete() - ChannelManager + internal_tx_complete() -- ChannelManager message in: commitment_signed [A] handle_commitment_signed() - ChannelManager -[A] internal_commitment_signed() - ChannelManager -[A] commitment_signed_initial_v2() - Channel -[A] watch_channel() - ChainMonitor + internal_commitment_signed() - ChannelManager + commitment_signed_initial_v2() - Channel + watch_channel() - ChainMonitor [I] handle_commitment_signed() - ChannelManager + internal_commitment_signed() - ChannelManager + commitment_signed_initial_v2() - Channel + watch_channel() - ChainMonitor message in: tx_signatures [I] handle_tx_signatures - ChannelManager + internal_tx_signatures -- ChannelManager + tx_signatures() -- Channel + Check present sigantures, tlvs field + Update signature on previous tx input (with shared signature) + Update channel state (AwaitingChannelReady) message out: tx_signatures [A] handle_tx_signatures - ChannelManager -waiting for confirmation + internal_tx_signatures -- ChannelManager + tx_signatures() -- Channel +New funding tx gets broadcasted (both sides) +Waiting for confirmation [I] transactions_confirmed() - Channel -[I] commit_pending_splice() - ChannelContext -[I] channel_state = AwaitingChannelReady -[I] clear_pending_splice() - ChannelContext + splice_locked() -- ChannelContext + Mark splicing process as completed message out: channel_ready message in: channel_ready [A] handle_channel_ready() - ChannelManager diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 3358d810109..6573fa0df65 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -1298,9 +1298,8 @@ impl ChannelMonitor { /// possibly future revocation/preimage information) to claim outputs where possible. /// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers. /// #SPLICING - /// TODO changed to prod & pub(crate), from test & private - // #[cfg(test)] - pub(crate) fn provide_latest_counterparty_commitment_tx( + #[cfg(test)] + fn provide_latest_counterparty_commitment_tx( &self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, @@ -2440,8 +2439,8 @@ impl ChannelMonitorImpl { // TODO check reenable // #[cfg(debug_assertions)] { - // let rebuilt_commitment_tx = self.initial_counterparty_commitment_tx().unwrap(); - // debug_assert_eq!(rebuilt_commitment_tx.trust().txid(), txid); + // let rebuilt_commitment_tx = self.initial_counterparty_commitment_tx().unwrap(); + // debug_assert_eq!(rebuilt_commitment_tx.trust().txid(), txid); // } self.provide_latest_counterparty_commitment_tx(txid, htlc_outputs, commitment_number, @@ -3847,8 +3846,6 @@ impl ChannelMonitorImpl { if *idx == input.previous_output.vout { #[cfg(test)] { - // TODO put it back, add witness to splice tx - /* // If the expected script is a known type, check that the witness // appears to be spending the correct type (ie that the match would // actually succeed in BIP 158/159-style filters). @@ -3864,7 +3861,6 @@ impl ChannelMonitorImpl { } else if _script_pubkey.is_v0_p2wpkh() { assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey); } else { panic!(); } - */ } return true; } diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index a3f6638d683..ea4df8540c3 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -9,7 +9,7 @@ use bitcoin::blockdata::constants::{ChainHash, WITNESS_SCALE_FACTOR}; use bitcoin::blockdata::script::{Script, ScriptBuf, Builder}; -use bitcoin::blockdata::transaction::{Sequence, Transaction}; +use bitcoin::blockdata::transaction::Transaction; use bitcoin::sighash::{self, EcdsaSighashType}; use bitcoin::consensus::{encode, Encodable}; @@ -22,13 +22,14 @@ use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE; use bitcoin::secp256k1::{PublicKey,SecretKey}; use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature}; use bitcoin::{secp256k1, TxIn, TxOut}; -// #[cfg(dual_funding)] +// #[cfg(dual_funding)] // TODO splicing use bitcoin::Witness; #[cfg(dual_funding)] use bitcoin::locktime::absolute::LockTime; use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT}; use crate::ln::{ChannelId, PaymentPreimage, PaymentHash}; +use crate::ln::channel_splice::{PendingSpliceInfoPre, PendingSpliceInfoPost}; use crate::ln::features::{ChannelTypeFeatures, InitFeatures}; use crate::ln::interactivetxs::{AbortReason, InteractiveTxConstructor, InteractiveTxMessageSend, InteractiveTxSigningSession}; use crate::ln::msgs; @@ -58,7 +59,7 @@ use crate::io; use crate::io_extras::sink; use crate::prelude::*; use core::{cmp,mem,fmt}; -use core::convert::{TryFrom, TryInto}; +use core::convert::TryInto; use core::ops::Deref; #[cfg(any(test, fuzzing, debug_assertions))] use crate::sync::Mutex; @@ -959,62 +960,10 @@ impl UnfundedChannelContext { self.unfunded_channel_age_ticks += 1; self.unfunded_channel_age_ticks >= UNFUNDED_CHANNEL_AGE_LIMIT_TICKS } -} - -/// Info about a pending splice -#[derive(Clone)] -pub(super) struct PendingSpliceInfo { - /// The post splice value (current + relative) - pub post_channel_value: u64, - /// The pre splice value (a bit redundant) - pub pre_channel_value: u64, - /// Whether we are the initiator or not - pub is_outgoing: bool, - - prev_funding_input_index: Option, - // initial_commitment_tx: Option, - // cp_commitment_sig: Option, - - // the new funding transaction (candidate). TODO later should be TransactionConfirmation? - funding_transaction: Option, - // the new funding transaction txo (candidate) - pub(crate) funding_txo: Option, + pub fn default() -> Self { Self { unfunded_channel_age_ticks: 0 } } } -impl PendingSpliceInfo { - pub(crate) fn new(relative_satoshis: i64, pre_channel_value: u64, is_outgoing: bool) -> Self { - let post_channel_value = Self::add_checked(pre_channel_value, relative_satoshis); - Self { - post_channel_value, - pre_channel_value, - is_outgoing, - prev_funding_input_index: None, - // initial_commitment_tx: None, - // cp_commitment_sig: None, - funding_transaction: None, - funding_txo: None, - } - } - - /// Add a u64 and an i64, handling i64 overflow cases (doing without cast to i64) - pub(crate) fn add_checked(pre_channel_value: u64, relative_satoshis: i64) -> u64 { - if relative_satoshis >= 0 { - pre_channel_value + (relative_satoshis as u64) - } else { - pre_channel_value.checked_sub((-relative_satoshis) as u64).unwrap_or_default() - } - } - - /// The relative splice value (change in capacity value relative to current value) - pub(crate) fn relative_satoshis(&self) -> i64 { - if self.post_channel_value > self.pre_channel_value { - i64::try_from(self.post_channel_value.checked_sub(self.pre_channel_value).unwrap_or_default()).unwrap_or_default() - } else { - -i64::try_from(self.pre_channel_value.checked_sub(self.post_channel_value).unwrap_or_default()).unwrap_or_default() - } - } -} /// Contains everything about the channel including state, and various flags. pub(super) struct ChannelContext where SP::Target: SignerProvider { config: LegacyChannelConfig, @@ -1051,9 +1000,11 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { channel_value_satoshis: u64, /// #SPLICING - /// Info about an in-progress, pending splice (if any) - /// TODO: later support >1 outstanding splice (a map?) - pub(crate) pending_splice: Option, + /// Info about an in-progress, pending splice (if any), on the pre-splice channel + pub(crate) pending_splice_pre: Option, + /// #SPLICING + /// Info about an in-progress, pending splice (if any), on the post-splice channel + pub(crate) pending_splice_post: Option, latest_monitor_update_id: u64, @@ -1209,7 +1160,12 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { counterparty_forwarding_info: Option, pub(crate) channel_transaction_parameters: ChannelTransactionParameters, + /// The funding transaction is stored here, but only during the channel establishment phase. + /// Being set does not necessarily mean that is's already locked. funding_transaction: Option, + /// The funding transaction, similar to `funding_transaction` field, but stored here for the full lifecycle of the channel. + /// Being set does not necessarily mean that is's already locked. + funding_transaction_saved: Option, is_batch_funding: Option<()>, counterparty_cur_commitment_point: Option, @@ -1644,6 +1600,7 @@ impl ChannelContext where SP::Target: SignerProvider { channel_type_features: channel_type.clone() }, funding_transaction: None, + funding_transaction_saved: None, is_batch_funding: None, counterparty_cur_commitment_point: Some(msg_first_per_commitment_point), @@ -1682,7 +1639,8 @@ impl ChannelContext where SP::Target: SignerProvider { blocked_monitor_updates: Vec::new(), interactive_tx_constructor: None, - pending_splice: None, + pending_splice_pre: None, + pending_splice_post: None, }; Ok(channel_context) @@ -1864,6 +1822,7 @@ impl ChannelContext where SP::Target: SignerProvider { channel_type_features: channel_type.clone() }, funding_transaction: None, + funding_transaction_saved: None, is_batch_funding: None, counterparty_cur_commitment_point: None, @@ -1902,7 +1861,8 @@ impl ChannelContext where SP::Target: SignerProvider { blocked_monitor_updates: Vec::new(), interactive_tx_constructor: None, - pending_splice: None, + pending_splice_pre: None, + pending_splice_post: None, }) } @@ -2932,26 +2892,7 @@ impl ChannelContext where SP::Target: SignerProvider { } /// #SPLICING - /// Get a transaction input that is the current funding transaction - pub fn get_input_of_current_funding(&self) -> Result<(TxIn, Transaction), ()> { - if let Some(funding_transaction) = &self.funding_transaction { - if let Some(txo) = &self.get_funding_txo() { - Ok(( - TxIn { - previous_output: txo.into_bitcoin_outpoint(), - script_sig: ScriptBuf::new(), - sequence: Sequence::ZERO, - witness: Witness::new(), - }, - funding_transaction.clone(), - )) - } else { - panic!("Missing funding transaction outpoint"); // TODO return Err(()); - } - } else { - panic!("Missing funding transaction"); // TODO return Err(()); - } - } + pub(crate) fn funding_transaction_saved(&self) -> Option<&Transaction> { self.funding_transaction_saved.as_ref() } /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the /// number of pending HTLCs that are on track to be in our next commitment tx. @@ -3163,16 +3104,7 @@ impl ChannelContext where SP::Target: SignerProvider { /// Returns the transaction if there is a pending funding transaction that is yet to be /// broadcast. pub fn unbroadcasted_funding(&self) -> Option { - self.if_unbroadcasted_funding(|| - if self.funding_transaction.is_some() { - self.funding_transaction.clone() - // #SPLICING - } else if let Some(pending_splice) = &self.pending_splice { - pending_splice.funding_transaction.clone() - } else { - None - } - ) + self.if_unbroadcasted_funding(|| self.funding_transaction.clone()) } /// Returns the transaction ID if there is a pending funding transaction that is yet to be @@ -3290,150 +3222,15 @@ impl ChannelContext where SP::Target: SignerProvider { } } - /* - fn funding_created_signature(&mut self, sig: &Signature, logger: &L) -> Result<(CommitmentTransaction, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger { - let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number); - let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx; - - let funding_script = self.get_funding_redeemscript(); - { - let trusted_tx = initial_commitment_tx.trust(); - let initial_commitment_bitcoin_tx = trusted_tx.built_transaction(); - let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.channel_value_satoshis); - // They sign the holder commitment transaction... - log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.", - log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.counterparty_funding_pubkey().serialize()), - encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]), - encode::serialize_hex(&funding_script), &self.channel_id()); - secp_check!(self.secp_ctx.verify_ecdsa(&sighash, &sig, self.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned()); - } - - let counterparty_keys = self.build_remote_transaction_keys(); - let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; - - match &self.holder_signer { - // TODO (arik): move match into calling method for Taproot - ChannelSignerType::Ecdsa(ecdsa) => { - let counterparty_signature = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx) - .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0; - - // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish. - Ok((counterparty_initial_commitment_tx, initial_commitment_tx, counterparty_signature)) - } - } - } - */ - - /// #SPLICING Moved from InboundV1Channel to ChannelContext - fn check_funding_created_signature(&mut self, sig: &Signature, logger: &L) -> Result where L::Target: Logger { - let funding_script = self.get_funding_redeemscript(); - - let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number); - let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx; - let trusted_tx = initial_commitment_tx.trust(); - let initial_commitment_bitcoin_tx = trusted_tx.built_transaction(); - let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.channel_value_satoshis); - // They sign the holder commitment transaction... - log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.", - log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.counterparty_funding_pubkey().serialize()), - encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]), - encode::serialize_hex(&funding_script), &self.channel_id()); - secp_check!(self.secp_ctx.verify_ecdsa(&sighash, &sig, self.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned()); - - Ok(initial_commitment_tx) - } - - /// #SPLICING - /// Update channel capacity to a new value - /// It is assumed that the increase is coming to A's side - fn update_channel_value(&mut self, new_value_sats: u64, belongs_to_local: bool, logger: &L) -> Result<(), ChannelError> where L::Target: Logger { - let old_value = self.channel_value_satoshis; - let old_to_self = self.value_to_self_msat; - if belongs_to_local { - // TODO check for i64 overflow - let delta_msats = (new_value_sats as i64 - old_value as i64) * 1000; - // Check if not reducing by too much - if delta_msats < 0 && -delta_msats > self.value_to_self_msat as i64 { - return Err(ChannelError::Close("Cannot decrease channel value to requested amount, too low".to_string())); - } - self.value_to_self_msat = (self.value_to_self_msat as i64 + delta_msats) as u64; - } - - self.channel_value_satoshis = new_value_sats; - log_trace!(logger, "Changed channel value, channel_id {} value old {} new {} to_self old {} new {}", self.channel_id, old_value, self.channel_value_satoshis, old_to_self, self.value_to_self_msat); - Ok(()) - } - - /* - /// #SPLICING - fn get_pending_splice_or_default_mut(&mut self) -> &mut PendingSpliceInfo { - if self.pending_splice.is_none() { - self.pending_splice = Some(PendingSpliceInfo::default()); - } - self.pending_splice.as_mut().unwrap() - } - */ - - /// #SPLICING - /// Commit channel to the pending splice: once it completes, channel will be the new spliced, - /// from this point there is no going back to the old one, in case of error channel will error. - /// Update channel capacity, funding txid, etc. - pub fn commit_pending_splice(&mut self, logger: &L) -> Result<(), ChannelError> - where L::Target: Logger - { - if let Some(pending_splice) = &self.pending_splice { - self.funding_transaction = pending_splice.funding_transaction.clone(); - self.channel_transaction_parameters.funding_outpoint = pending_splice.funding_txo; - - let old_value_debug = self.channel_value_satoshis; - let _ = self.update_channel_value(pending_splice.post_channel_value, pending_splice.is_outgoing, logger)?; - - // Note: pending is not cleared here yet, some parts of it are needed, cleared later - - log_trace!(logger, "Committed channel to the splice, channel_id {} capacity old {} new {} new funding txid {}", - self.channel_id, old_value_debug, self.channel_value_satoshis, - if let Some(txo) = &self.pending_splice.as_ref().unwrap().funding_txo { txo.txid.to_string() } else { "missing txo".to_owned() } - ); - Ok(()) - } else { - Err(ChannelError::Warn("Internal error: No pending splice found".to_owned())) - } - } - - pub fn clear_pending_splice(&mut self, logger: &L) - where L::Target: Logger - { - self.pending_splice = None; - log_trace!(logger, "Cleared pending splice, channel_id {}", self.channel_id); - } - - /// If an Err is returned, it is a ChannelError::Close (for get_funding_created) - /// #SPLICING: Moved to ChannelContext from OutboundV1Channel - fn get_funding_created_signature(&mut self, logger: &L) - -> Result<(CommitmentTransaction, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger { - let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number); - let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx; - - let counterparty_keys = self.build_remote_transaction_keys(); - let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; - match &self.holder_signer { - // TODO (taproot|arik): move match into calling method for Taproot - ChannelSignerType::Ecdsa(ecdsa) => { - let signature = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), Vec::new(), &self.secp_ctx) - .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0; - Ok((initial_commitment_tx, counterparty_initial_commitment_tx, signature)) - } - } - } // Interactive transaction construction #[cfg(dual_funding)] - pub fn begin_interactive_funding_tx_construction( + pub fn begin_interactive_funding_tx_construction( &mut self, dual_funding_context: &DualFundingChannelContext, signer_provider: &SP, - entropy_source: &ES, holder_node_id: PublicKey, is_initiator: bool, is_splice: bool, - funding_inputs: Vec<(TxIn, Transaction)>, logger: &L, + entropy_source: &ES, holder_node_id: PublicKey, is_initiator: bool, + funding_inputs: Vec<(TxIn, Transaction)>, ) -> Result, APIError> - where ES::Target: EntropySource, L::Target: Logger + where ES::Target: EntropySource { // Check that vouts exist for each TxIn in provided transactions. for (idx, input) in funding_inputs.iter().enumerate() { @@ -3444,21 +3241,17 @@ impl ChannelContext where SP::Target: SignerProvider { } } - let mut funding_inputs_with_extra: Vec<(TxIn, Transaction)> = Vec::new(); - if self.pending_splice.is_some() { - // Splicing + let mut funding_inputs_with_extra = funding_inputs; + if let Some(pending_splice) = &self.pending_splice_post { + // #SPLICING if is_initiator { // Add current funding tx as an extra, shared input - let prev_funding_input = self.get_input_of_current_funding().unwrap(); // TODO err + let prev_funding_input = pending_splice.get_input_of_previous_funding() + .map_err(|e| APIError::APIMisuseError { + err: format!("Interal error: Could not create input for previous funding transaction, channel_id {}, {:?}", self.channel_id(), e) + })?; funding_inputs_with_extra.push(prev_funding_input); } - - // Commit to post-splice parameters prematurely, to have right values for commitment building - // TODO: commitment should happen only upon confirmation - let _ = self.commit_pending_splice(logger).unwrap(); - } - for i in funding_inputs { // rest - funding_inputs_with_extra.push(i); } let total_input_satoshis: u64 = funding_inputs_with_extra.iter().map(|input| input.1.output[input.0.previous_output.vout as usize].value).sum(); @@ -3472,14 +3265,9 @@ impl ChannelContext where SP::Target: SignerProvider { let mut funding_outputs = Vec::new(); if is_initiator { // add output - let output_value = if let Some(pending_splice) = &self.pending_splice { - // #SPLICING - pending_splice.post_channel_value - } else { - self.get_value_satoshis() - }; + // #SPLICING Note on splicing: channel value at this point is changed to the post-splice value, so no sepcial action needed funding_outputs.push(TxOut { - value: output_value, + value: self.get_value_satoshis(), script_pubkey: self.get_funding_redeemscript().to_v0_p2wsh(), }); } @@ -3580,9 +3368,171 @@ impl ChannelContext where SP::Target: SignerProvider { } } - pub fn tx_abort(&self, msg: &msgs::TxAbort)-> Result { + pub fn tx_abort(&self, _msg: &msgs::TxAbort)-> Result { todo!(); } + + pub fn generate_v2_channel_id_from_revocation_basepoints(&self) -> ChannelId { + ChannelId::v2_from_revocation_basepoints(&self.get_holder_pubkeys().revocation_basepoint, &self.get_counterparty_pubkeys().revocation_basepoint) + } + + /// #SPLICING + /// Update channel capacity to a new value + /// It is assumed that the increase is coming to A's side + fn update_channel_value(&mut self, new_value_sats: u64, belongs_to_local: bool, logger: &L) -> Result<(), ChannelError> where L::Target: Logger { + let old_value = self.channel_value_satoshis; + let old_to_self = self.value_to_self_msat; + if belongs_to_local { + // TODO check for i64 overflow + let delta_msats = (new_value_sats as i64 - old_value as i64) * 1000; + // Check if not reducing by too much + if delta_msats < 0 && -delta_msats > self.value_to_self_msat as i64 { + return Err(ChannelError::Close("Cannot decrease channel value to requested amount, too low".to_string())); + } + self.value_to_self_msat = (self.value_to_self_msat as i64 + delta_msats) as u64; + } + + self.channel_value_satoshis = new_value_sats; + + // set signer channel value + self.holder_signer.as_mut().update_channel_value(new_value_sats); + + log_trace!(logger, "Changed channel value, channel_id {} value old {} new {} to_self old {} new {}", self.channel_id, old_value, self.channel_value_satoshis, old_to_self, self.value_to_self_msat); + Ok(()) + } + + /// #SPLICING + /// Splice process starting; update state; update capacity, state, reset funding tx + pub(crate) fn splice_start(&mut self, is_outgoing: bool, relative_satoshis: i64, logger: &L) -> Result<(), ChannelError> + where L::Target: Logger + { + if self.pending_splice_post.is_some() { + return Err(ChannelError::Warn(format!("Internal error: Channel is already splicing, channel_id {}", self.channel_id()))); + } + + let pre_channel_value = self.channel_value_satoshis; + let post_channel_value = PendingSpliceInfoPre::add_checked(pre_channel_value, relative_satoshis); + + // Save the current funding transaction + let pre_funding_transaction = self.funding_transaction_saved.clone(); + let pre_funding_txo = self.get_funding_txo().clone(); + + let pending_splice_post = PendingSpliceInfoPost::new( + relative_satoshis, + pre_channel_value, + Some(self.channel_id), + pre_funding_transaction, + pre_funding_txo, + ); + self.pending_splice_post = Some(pending_splice_post); + + self.channel_state = ChannelState::NegotiatingFunding( + // TODO check + if is_outgoing { + NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT + } else { + NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT + } + ); + + let _ = self.update_channel_value(post_channel_value, is_outgoing, logger); // TODO error + + // Reset funding tx + self.funding_transaction = None; + self.funding_transaction_saved = None; + self.channel_transaction_parameters.funding_outpoint = None; + self.funding_tx_confirmed_in = None; + self.funding_tx_confirmation_height = 0; + + log_trace!(logger, "Splicing process started, new channel value {}, channel_id {}", self.channel_value_satoshis, self.channel_id); + + Ok(()) + } + + /// #SPLICING + /// Splice process finished, new funding transaction locked. + /// At this point the old funding transaction is spent. + pub(crate) fn splice_locked(&mut self, logger: &L) -> Result<(), ChannelError> + where L::Target: Logger + { + if self.pending_splice_post.is_none() { + return Err(ChannelError::Warn(format!("Internal error: Channel is not in currently splicing, channel_id {}", self.channel_id()))); + } + + // TODO: purge HTLCs + + // TODO: if there is a pre channel, with different channel ID, purge it + + self.pending_splice_pre = None; + self.pending_splice_post = None; + + log_trace!(logger, "Splicing completed, channel_id {}", self.channel_id); + + Ok(()) + } + + /// #SPLICING + /// Create signature for the current funding tx input, used in the splicing case. + fn prev_funding_tx_create_holder_sig(&self, transaction: &Transaction, input_index: u16, input_value: u64, redeem_script: &ScriptBuf) -> Result { + // #SPLICE-SIG + match &self.holder_signer { + ChannelSignerType::Ecdsa(ecdsa) => { + ecdsa.sign_splicing_funding_input(transaction, input_index, input_value, &redeem_script, &self.secp_ctx) + .map_err(|_e| ChannelError::Close("Failed to sign the previous funding input in the new splicing funding tx".to_owned())) + } + } + } + + /// #SPLICING + /// Prepare the witness on the current funding tx input (used in the splicing case), + /// containing our holder signature, and optionally the counterparty signature, or its empty placholder. + fn prev_funding_tx_sign( + &self, transaction: &Transaction, counterparty_sig: Option, logger: &L + ) -> Result<(Transaction, Signature), ChannelError> where L::Target: Logger { + let (prev_funding_input_index, pre_channel_value) = if let Some(pending_splice) = &self.pending_splice_post { + ( + pending_splice.find_input_of_previous_funding(&transaction)?, + pending_splice.pre_channel_value + ) + } else { + return Err(ChannelError::Warn(format!("Cannot sign splice transaction, channel is not in active splice, channel_id {}", self.channel_id))) + }; + debug_assert!((prev_funding_input_index as usize) < transaction.input.len()); + + // #SPLICE-SIG + // the redeem script + let sig_order_ours_first = self.get_holder_pubkeys().funding_pubkey.serialize() < self.counterparty_funding_pubkey().serialize(); + log_info!(logger, "Pubkeys used for redeem script: {} {} {}", &self.get_holder_pubkeys().funding_pubkey, &self.counterparty_funding_pubkey(), sig_order_ours_first); + + let redeem_script = self.get_funding_redeemscript(); + let holder_signature = self.prev_funding_tx_create_holder_sig(&transaction, prev_funding_input_index, pre_channel_value, &redeem_script)?; + let mut holder_sig = holder_signature.serialize_der().to_vec(); + holder_sig.push(EcdsaSighashType::All as u8); + // counterparty signature + let cp_sig = match counterparty_sig { + Some(s) => { + let mut sb = s.serialize_der().to_vec(); + sb.push(EcdsaSighashType::All as u8); + sb + }, + None => Vec::new(), // placeholder + }; + // prepare witness stack + let mut witness = Witness::new(); + witness.push(Vec::new()); + if sig_order_ours_first { + witness.push(holder_sig); + witness.push(cp_sig); + } else { + witness.push(cp_sig); + witness.push(holder_sig); + } + witness.push(redeem_script.clone().into_bytes()); + + let mut tx = transaction.clone(); + tx.input[prev_funding_input_index as usize].witness = witness; + Ok((tx, holder_signature)) + } } // Internal utility functions for channels @@ -4475,7 +4425,7 @@ impl Channel where if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) { return Err(ChannelError::Close(format!("Received initial commitment_signed before funding transaction constructed! {:?}", self.context.channel_state))); } - let is_splice = self.context.pending_splice.is_some(); + let is_splice = self.context.pending_splice_post.is_some(); if !is_splice { if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) || self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER || @@ -4483,7 +4433,7 @@ impl Channel where panic!("Should not have advanced channel commitment tx numbers prior to funding_created"); } } - let dual_funding_channel_context = self.dual_funding_channel_context.as_mut().ok_or( + let _dual_funding_channel_context = self.dual_funding_channel_context.as_mut().ok_or( ChannelError::Close("Have no context for dual-funded channel".to_owned()) )?; @@ -5186,8 +5136,8 @@ impl Channel where } #[cfg(dual_funding)] - pub fn verify_interactive_tx_signatures(&mut self, witnesses: &Vec) { - if let Some(ref mut signing_session) = self.interactive_tx_signing_session { + pub fn verify_interactive_tx_signatures(&mut self, _witnesses: &Vec) { + if let Some(ref mut _signing_session) = self.interactive_tx_signing_session { // Check that sighash_all was used: // TODO(dual_funding): Check sig for sighash } @@ -5203,7 +5153,7 @@ impl Channel where return Err(ChannelError::Close(format!("Witness count does not match contributed input count, {} {}", msg.witnesses.len(), expected_witness_count))); } - let expected_shared = if self.context.pending_splice.is_some() { 1 } else { 0 }; + let expected_shared = if self.context.pending_splice_post.is_some() { 1 } else { 0 }; let tlvs_count = if msg.tlvs.is_some() { 1 } else { 0 }; if tlvs_count != expected_shared { return Err(ChannelError::Close(format!("Shared signature count (tlvs) presence does not match expected, {} {}", @@ -5227,15 +5177,12 @@ impl Channel where let (tx_signatures_opt, mut funding_tx_opt) = signing_session.received_tx_signatures(msg.clone()); if let Some(funding_tx) = &funding_tx_opt { - if let Some(pending_splice) = &self.context.pending_splice { + if self.context.pending_splice_post.is_some() { if let Some(cp_sig) = &msg.tlvs { // Update signature on previous funding input: // - our signature is (re)generated (was overwritten by witness received in tx_signatures) // - counterparty signature is set, taken from tx_signatures tlvs field - let (updated_funding_tx, _) = self.prev_funding_tx_sign( - funding_tx, - pending_splice.prev_funding_input_index.unwrap(), pending_splice.pre_channel_value, - Some(cp_sig.clone()), logger)?; + let (updated_funding_tx, _) = self.context.prev_funding_tx_sign(funding_tx, Some(cp_sig.clone()), logger)?; funding_tx_opt = Some(updated_funding_tx); } } @@ -5243,6 +5190,9 @@ impl Channel where self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new()); } self.context.funding_transaction = funding_tx_opt.clone(); + self.context.funding_transaction_saved = funding_tx_opt.clone(); + // Mark that the interactive tx session is complete + self.interactive_tx_signing_session = None; Ok((tx_signatures_opt, funding_tx_opt)) } else { @@ -5251,11 +5201,11 @@ impl Channel where } } - pub fn tx_init_rbf(&self, msg: &msgs::TxInitRbf)-> Result { + pub fn tx_init_rbf(&self, _msg: &msgs::TxInitRbf)-> Result { todo!(); } - pub fn tx_ack_rbf(&self, msg: &msgs::TxAckRbf)-> Result { + pub fn tx_ack_rbf(&self, _msg: &msgs::TxAckRbf)-> Result { todo!(); } @@ -5463,15 +5413,7 @@ impl Channel where matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(flags) if !flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH)) || matches!(self.context.channel_state, ChannelState::ChannelReady(_)) { - // #SPLICING - // TODO move to a method in context, unbroadcast_transaction_take() - if self.context.funding_transaction.is_some() { - self.context.funding_transaction.take() - } else if let Some(pending_splice) = &self.context.pending_splice { - pending_splice.funding_transaction.clone() // take() - } else { - None - } + self.context.funding_transaction.take() } else { None }; // That said, if the funding transaction is already confirmed (ie we're active with a // minimum_depth over 0) don't bother re-broadcasting the confirmed funding tx. @@ -6606,13 +6548,7 @@ impl Channel where L::Target: Logger { let mut msgs = (None, None); - let (funding_txo_to_watch, funding_value, is_splicing) = - if let Some(pending_splice) = &self.context.pending_splice { - (pending_splice.funding_txo.clone(), pending_splice.post_channel_value, true) - } else { - (self.context.get_funding_txo(), self.context.channel_value_satoshis, false) - }; - if let Some(funding_txo) = funding_txo_to_watch { + if let Some(funding_txo) = self.context.get_funding_txo() { for &(index_in_block, tx) in txdata.iter() { // Check if the transaction is the expected funding transaction, and if it is, // check that it pays the right amount to the right script. @@ -6620,7 +6556,7 @@ impl Channel where if tx.txid() == funding_txo.txid { let txo_idx = funding_txo.index as usize; if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.context.get_funding_redeemscript().to_v0_p2wsh() || - tx.output[txo_idx].value != funding_value { + tx.output[txo_idx].value != self.context.channel_value_satoshis { if self.context.is_outbound() { // If we generated the funding transaction and it doesn't match what it // should, the client is really broken and we should just panic and @@ -6636,8 +6572,6 @@ impl Channel where } else { if self.context.is_outbound() { if !tx.is_coin_base() { - // TODO reenable - /* for input in tx.input.iter() { if input.witness.is_empty() { // We generated a malleable funding transaction, implying we've @@ -6646,24 +6580,18 @@ impl Channel where panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction! witness len {} txid {} inputs {} txlen {}", input.witness.len(), tx.txid(), tx.input.len(), tx.encode().len()); } } - */ - } - } - - // #SPLICING - if is_splicing { - let _ = self.context.commit_pending_splice(logger).unwrap(); - // TODO: Should this be set only later? or in commit? - log_debug!(logger, "transactions_confirmed: Updating state, from {:?} to ChannelReady", self.context.channel_state); - self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags(0)); // AwaitingChannelReady(AwaitingChannelReadyFlags::OUR_CHANNEL_READY); - self.context.clear_pending_splice(logger); + } } - self.context.funding_tx_confirmation_height = height; self.context.funding_tx_confirmed_in = Some(*block_hash); self.context.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) { Ok(scid) => Some(scid), Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"), + }; + + // #SPLICING + if self.context.pending_splice_post.is_some() { + self.context.splice_locked(logger).ok(); } } // If this is a coinbase transaction and not a 0-conf channel @@ -7088,11 +7016,9 @@ impl Channel where let first_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); */ - // TODO check + // TODO move from here, check self.context.channel_state = ChannelState::NegotiatingFunding(NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT); - let keys = self.context.get_holder_pubkeys(); - // TODO how to handle channel capacity, orig is stored in Channel, has to be updated, in the interim there are two msgs::Splice { chain_hash, @@ -7100,35 +7026,7 @@ impl Channel where relative_satoshis, funding_feerate_perkw, locktime, - funding_pubkey: keys.funding_pubkey, - } - } - - /// #SPLICING STEP4 A - /// Get the splice_ack message that can be sent in response to splice initiation - /// TODO move to ChannelContext - pub fn get_splice_ack(&mut self, chain_hash: ChainHash, - // TODO; should this be a param, or stored in the channel? - relative_satoshis: i64 - ) -> msgs::SpliceAck { - if self.context.is_outbound() { - panic!("Tried to accept a splice on an outound channel?"); - } - - // TODO checks - - // TODO check - self.context.channel_state = ChannelState::NegotiatingFunding(NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT); - - - let keys = self.context.get_holder_pubkeys(); - - // TODO how to handle channel capacity, orig is stored in Channel, has to be updated, in the interim there are two - msgs::SpliceAck { - chain_hash, - channel_id: self.context.channel_id, - relative_satoshis, - funding_pubkey: keys.funding_pubkey, + funding_pubkey: self.context.get_holder_pubkeys().funding_pubkey, } } @@ -7555,184 +7453,6 @@ impl Channel where }) .chain(self.context.pending_outbound_htlcs.iter().map(|htlc| (&htlc.source, &htlc.payment_hash))) } - - /// #SPLICING - /// This is neeed when Channel is not changed during splicing, and interactive transaction is - /// constructed on the Funded channel. TODO: remove if new unfunded channel instance is used - /// during splicing - pub fn begin_interactive_funding_tx_construction(&mut self, signer_provider: &SP, - entropy_source: &ES, holder_node_id: PublicKey, is_initiator: bool, is_splice: bool, funding_inputs: Vec<(TxIn, Transaction)>, logger: &L, - ) -> Result, APIError> - where ES::Target: EntropySource, L::Target: Logger - { - if self.context.pending_splice.is_some() { - if let Some(splice_dual_funding_context) = &self.dual_funding_channel_context { - return self.context.begin_interactive_funding_tx_construction( - &splice_dual_funding_context, - signer_provider, entropy_source, holder_node_id, is_initiator, is_splice, funding_inputs, logger, - ); - } - } - Err(APIError::APIMisuseError { - err: format!("Channel is funded and in not actively splicing {}", self.context.channel_id()) - }) - } - - /// #SPLICING - /// Create signature for the current funding tx input, used in the splicing case. - fn prev_funding_tx_create_holder_sig(&self, transaction: &Transaction, input_index: u16, input_value: u64, redeem_script: &ScriptBuf) -> Result { - // #SPLICE-SIG - match &self.context.holder_signer { - ChannelSignerType::Ecdsa(ecdsa) => { - ecdsa.sign_splicing_funding_input(transaction, input_index, input_value, &redeem_script, &self.context.secp_ctx) - .map_err(|_e| ChannelError::Close("Failed to sign the previous funding input in the new splicing funding tx".to_owned())) - } - } - } - - /// #SPLICING - /// Prepare the witness on the current funding tx input (used in the splicing case), - /// containing our holder signature, and optionally the counterparty signature, or its empty placholder. - /// See also - fn prev_funding_tx_sign( - &self, transaction: &Transaction, input_index: u16, input_value: u64, - counterparty_sig: Option, logger: &L - ) -> Result<(Transaction, Signature), ChannelError> where L::Target: Logger { - // #SPLICE-SIG - let mut tx = transaction.clone(); - assert!(tx.input.len() > input_index as usize); // TODO check - // the redeem script - let sig_order_ours_first = self.context.get_holder_pubkeys().funding_pubkey.serialize() < self.context.counterparty_funding_pubkey().serialize(); - log_info!(logger, "Pubkeys used for redeem script: {} {} {}", &self.context.get_holder_pubkeys().funding_pubkey, &self.context.counterparty_funding_pubkey(), sig_order_ours_first); - assert!(tx.input.len() > input_index as usize); // TODO check - - let redeem_script = self.context.get_funding_redeemscript(); - let holder_signature = self.prev_funding_tx_create_holder_sig(&tx, input_index, input_value, &redeem_script)?; - let mut holder_sig = holder_signature.serialize_der().to_vec(); - holder_sig.push(EcdsaSighashType::All as u8); - // counterparty signature - let cp_sig = match counterparty_sig { - Some(s) => { - let mut sb = s.serialize_der().to_vec(); - sb.push(EcdsaSighashType::All as u8); - sb - }, - None => Vec::new(), // placeholder - }; - // prepare witness stack - let mut witness = Witness::new(); - witness.push(Vec::new()); - if sig_order_ours_first { - witness.push(holder_sig); - witness.push(cp_sig); - } else { - witness.push(cp_sig); - witness.push(holder_sig); - } - witness.push(redeem_script.clone().into_bytes()); - - tx.input[input_index as usize].witness = witness; - Ok((tx, holder_signature)) - } - - /// #SPLICING - // This is neeed when Channel is not changed during splicing, and interactive - // transaction is constructed on the Funded channel. TODO: remove if new - // unfunded channel instance is used during splicing - #[cfg(dual_funding)] - pub fn funding_tx_constructed( - mut self, counterparty_node_id: &PublicKey, mut signing_session: InteractiveTxSigningSession, signer_provider: &SP, logger: &L - ) -> Result<(Channel, msgs::CommitmentSigned, Option), (Self, ChannelError)> - where - L::Target: Logger - { - log_trace!(logger, "funding_tx_constructed {}", self.context.cur_counterparty_commitment_transaction_number); - - let pending_splice_opt = match &self.context.pending_splice { - Some(ps) => Some(ps.clone()), - None => None, // note: simpler map() is not used here due to partial borrow - }; - if let Some(pending_splice) = pending_splice_opt { - if let Some(splice_dual_funding_context) = &self.dual_funding_channel_context { - let mut output_index = None; - let expected_spk = self.context.get_funding_redeemscript().to_v0_p2wsh(); - for (idx, outp) in signing_session.constructed_transaction.output.iter().enumerate() { - if outp.script_pubkey == expected_spk && outp.value == self.context.pending_splice.as_ref().unwrap().post_channel_value { - if output_index.is_some() { - return Err((self, ChannelError::Close("Multiple outputs matched the expected script and value".to_owned()))); - } - output_index = Some(idx as u16); - } - } - if output_index.is_none() { - // if output_index.is_some() { - return Err((self, ChannelError::Close("No output matched the script_pubkey and value in the FundingGenerationReady event".to_owned()))); - // } - } - let outpoint = OutPoint { txid: signing_session.constructed_transaction.txid(), index: output_index.unwrap() }; - log_trace!(logger, "funding_tx_constructed outpoint {} {}", outpoint.txid, outpoint.index); - - // Save transaction parameters for later - self.context.pending_splice.as_mut().unwrap().prev_funding_input_index = Some(1); // TODO is this always 1? splice_prev_funding_input_index); - - // Save funding transaction - self.context.pending_splice.as_mut().unwrap().funding_transaction = Some(signing_session.constructed_transaction.clone()); - self.context.pending_splice.as_mut().unwrap().funding_txo = Some(outpoint.clone()); - self.context.channel_transaction_parameters.funding_outpoint = Some(outpoint); - - // TODO check - self.context.holder_signer.as_mut().reprovide_channel_parameters(&self.context.channel_transaction_parameters, - self.context.pending_splice.as_ref().unwrap().post_channel_value); - - let commitment_signed = get_initial_commitment_signed(&mut self.context, signing_session.constructed_transaction.clone(), - signer_provider, true /* is_splicing */, logger); - let commitment_signed = match commitment_signed { - Ok(commitment_signed) => commitment_signed, - Err(err) => { return Err((self, err)) }, - }; - - // Add signature for prev funding input TODO - // TODO move to separate method - // #SPLICE-SIG - let unsigned_transaction = signing_session.constructed_transaction.clone(); - let prev_funding_input_index = 1u16; // TODO find this - assert!(unsigned_transaction.input.len() > prev_funding_input_index as usize); // TODO remove - - let (partly_signed_transaction, holder_signature) = match self.prev_funding_tx_sign(&unsigned_transaction, prev_funding_input_index, pending_splice.pre_channel_value, None, logger) { - Err(e) => return Err((self, e)), - Ok(t) => t, - }; - signing_session.shared_signature = Some(holder_signature); - - let mut funding_ready_for_sig_event = None; - if self.dual_funding_channel_context.is_some() { // TODO if let - if self.dual_funding_channel_context.as_ref().unwrap().our_funding_satoshis == 0 { - signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new(), Some(holder_signature)); - } else { - funding_ready_for_sig_event = Some(Event::FundingTransactionReadyForSigning { - channel_id: self.context.channel_id, - counterparty_node_id: *counterparty_node_id, - user_channel_id: self.context.user_id, - unsigned_transaction: partly_signed_transaction, - }); - } - } - - // Clear out the interactive tx constructor. - self.context.interactive_tx_constructor = None; - self.context.channel_state = ChannelState::FundingNegotiated; - - let channel = Channel { - context: self.context, - dual_funding_channel_context: Some(splice_dual_funding_context.clone()), - interactive_tx_signing_session: Some(signing_session), - }; - - return Ok((channel, commitment_signed, funding_ready_for_sig_event)); - } - } - Err((self, ChannelError::Close("funding_tx_constructed: Funded channel has no splice in progress".to_owned()))) - } } /// A not-yet-funded outbound (from holder) channel using V1 channel establishment. @@ -7787,15 +7507,6 @@ impl OutboundV1Channel where SP::Target: SignerProvider { Ok(chan) } - /* - /// If an Err is returned, it is a ChannelError::Close (for get_funding_created) - /// #SPLICING: Impl. moved to ChannelContext - fn get_funding_created_signature(&mut self, logger: &L) - -> Result<(CommitmentTransaction, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger { - self.context.get_funding_created_signature(logger) - } - */ - /// Only allowed after [`ChannelContext::channel_transaction_parameters`] is set. fn get_funding_created_msg(&mut self, logger: &L) -> Option where L::Target: Logger { let counterparty_keys = self.context.build_remote_transaction_keys(); @@ -7855,19 +7566,6 @@ impl OutboundV1Channel where SP::Target: SignerProvider { self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo); self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters); - /* - let signature = match self.get_funding_created_signature(logger) { - Ok((_, _, s)) => s, - Err(e) => { - log_error!(logger, "Got bad signatures: {:?}!", e); - self.context.channel_transaction_parameters.funding_outpoint = None; - return Err((self, e)); - } - }; - - let temporary_channel_id = self.context.channel_id; - */ - // Now that we're past error-generating stuff, update our local state: self.context.channel_state = ChannelState::FundingNegotiated; @@ -7881,7 +7579,8 @@ impl OutboundV1Channel where SP::Target: SignerProvider { self.context.minimum_depth = Some(COINBASE_MATURITY); } - self.context.funding_transaction = Some(funding_transaction); + self.context.funding_transaction = Some(funding_transaction.clone()); + self.context.funding_transaction_saved = Some(funding_transaction); self.context.is_batch_funding = Some(()).filter(|_| is_batch_funding); let funding_created = self.get_funding_created_msg(logger); @@ -8233,8 +7932,23 @@ impl InboundV1Channel where SP::Target: SignerProvider { self.generate_accept_channel_message() } - /// #SPLICE moved to ChannelContext from InboundV1Channel - //fn check_funding_created_signature(&mut self, sig: &Signature, logger: &L) -> Result where L::Target: Logger { + fn check_funding_created_signature(&mut self, sig: &Signature, logger: &L) -> Result where L::Target: Logger { + let funding_script = self.context.get_funding_redeemscript(); + + let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number); + let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx; + let trusted_tx = initial_commitment_tx.trust(); + let initial_commitment_bitcoin_tx = trusted_tx.built_transaction(); + let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis); + // They sign the holder commitment transaction... + log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.", + log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context.counterparty_funding_pubkey().serialize()), + encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]), + encode::serialize_hex(&funding_script), &self.context.channel_id()); + secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &sig, self.context.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned()); + + Ok(initial_commitment_tx) + } pub fn funding_created( mut self, msg: &msgs::FundingCreated, best_block: BestBlock, signer_provider: &SP, logger: &L @@ -8266,7 +7980,7 @@ impl InboundV1Channel where SP::Target: SignerProvider { // check_funding_created_signature may fail. self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters); - let initial_commitment_tx = match self.context.check_funding_created_signature(&msg.signature, logger) { + let initial_commitment_tx = match self.check_funding_created_signature(&msg.signature, logger) { Ok(res) => res, Err(ChannelError::Close(e)) => { self.context.channel_transaction_parameters.funding_outpoint = None; @@ -8450,25 +8164,32 @@ impl OutboundV2Channel where SP::Target: SignerProvider { } } - pub fn begin_interactive_funding_tx_construction(&mut self, signer_provider: &SP, - entropy_source: &ES, holder_node_id: PublicKey, is_splice: bool, funding_inputs: Vec<(TxIn, Transaction)>, logger: &L, + pub fn begin_interactive_funding_tx_construction(&mut self, signer_provider: &SP, + entropy_source: &ES, holder_node_id: PublicKey, funding_inputs: Vec<(TxIn, Transaction)> ) -> Result, APIError> - where ES::Target: EntropySource, L::Target: Logger + where ES::Target: EntropySource { self.context.begin_interactive_funding_tx_construction(&self.dual_funding_context, - signer_provider, entropy_source, holder_node_id, true /* is_initiator */, is_splice, funding_inputs, logger) + signer_provider, entropy_source, holder_node_id, true /* is_initiator */, funding_inputs) } pub fn funding_tx_constructed( - mut self, counterparty_node_id: &PublicKey, signing_session: InteractiveTxSigningSession, signer_provider: &SP, logger: &L + mut self, counterparty_node_id: &PublicKey, mut signing_session: InteractiveTxSigningSession, signer_provider: &SP, logger: &L ) -> Result<(Channel, msgs::CommitmentSigned, Option), (Self, ChannelError)> where L::Target: Logger { + let pending_splice_opt = match &self.context.pending_splice_post { + Some(ps) => Some(ps.clone()), + None => None, // note: simpler map() is not used here due to partial borrow + }; + let mut output_index = None; let expected_spk = self.context.get_funding_redeemscript().to_v0_p2wsh(); + // Splicing note: the channel value at this time is already the post-splice value, so no special handling is needed + let expected_output_amount = self.context.get_value_satoshis(); for (idx, outp) in signing_session.constructed_transaction.output.iter().enumerate() { - if outp.script_pubkey == expected_spk && outp.value == self.context.get_value_satoshis() { + if outp.script_pubkey == expected_spk && outp.value == expected_output_amount { if output_index.is_some() { return Err((self, ChannelError::Close("Multiple outputs matched the expected script and value".to_owned()))); } @@ -8483,12 +8204,26 @@ impl OutboundV2Channel where SP::Target: SignerProvider { self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters); let commitment_signed = get_initial_commitment_signed(&mut self.context, signing_session.constructed_transaction.clone(), - signer_provider, false, logger); + signer_provider, pending_splice_opt.is_some(), logger); let commitment_signed = match commitment_signed { Ok(commitment_signed) => commitment_signed, Err(err) => return Err((self, err)), }; + let partly_signed_transaction = if pending_splice_opt.is_some() { + // #SPLICING + // #SPLICE-SIG + // Add signature for prev funding input + let (partly_signed_transaction, holder_signature) = match self.context.prev_funding_tx_sign(&signing_session.constructed_transaction, None, logger) { + Err(e) => return Err((self, e)), // simpler map_err() could not be used here due to move issue + Ok(t) => t, + }; + signing_session.shared_signature = Some(holder_signature); + partly_signed_transaction + } else { + signing_session.constructed_transaction.clone() + }; + // Clear our the interactive tx constructor. self.context.interactive_tx_constructor = None; self.context.channel_state = ChannelState::FundingNegotiated; @@ -8497,7 +8232,7 @@ impl OutboundV2Channel where SP::Target: SignerProvider { channel_id: self.context.channel_id, counterparty_node_id: *counterparty_node_id, user_channel_id: self.context.user_id, - unsigned_transaction: signing_session.constructed_transaction.clone(), + unsigned_transaction: partly_signed_transaction, }; let channel = Channel { @@ -8685,13 +8420,13 @@ impl InboundV2Channel where SP::Target: SignerProvider { self.generate_accept_channel_v2_message() } - pub fn begin_interactive_funding_tx_construction(&mut self, signer_provider: &SP, - entropy_source: &ES, holder_node_id: PublicKey, is_splice: bool, funding_inputs: Vec<(TxIn, Transaction)>, logger: &L, + pub fn begin_interactive_funding_tx_construction(&mut self, signer_provider: &SP, + entropy_source: &ES, holder_node_id: PublicKey, funding_inputs: Vec<(TxIn, Transaction)> ) -> Result, APIError> - where ES::Target: EntropySource, L::Target: Logger + where ES::Target: EntropySource { self.context.begin_interactive_funding_tx_construction(&self.dual_funding_context, - signer_provider, entropy_source, holder_node_id, false /* is_initiator */, is_splice, funding_inputs, logger) + signer_provider, entropy_source, holder_node_id, false /* is_initiator */, funding_inputs) } pub fn funding_tx_constructed( @@ -8700,6 +8435,11 @@ impl InboundV2Channel where SP::Target: SignerProvider { where L::Target: Logger { + let pending_splice_opt = match &self.context.pending_splice_post { + Some(ps) => Some(ps.clone()), + None => None, // note: simpler map() is not used here due to partial borrow + }; + let mut output_index = None; let expected_spk = self.context.get_funding_redeemscript().to_v0_p2wsh(); for (idx, outp) in signing_session.constructed_transaction.output.iter().enumerate() { @@ -8720,21 +8460,35 @@ impl InboundV2Channel where SP::Target: SignerProvider { self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters); let commitment_signed = get_initial_commitment_signed(&mut self.context, signing_session.constructed_transaction.clone(), - signer_provider, false, logger); + signer_provider, pending_splice_opt.is_some(), logger); let commitment_signed = match commitment_signed { Ok(commitment_signed) => commitment_signed, Err(err) => return Err((self, err)), }; + let (partly_signed_transaction, shared_signature) = if pending_splice_opt.is_some() { + // #SPLICING + // #SPLICE-SIG + // Add signature for prev funding input + let (partly_signed_transaction, holder_signature) = match self.context.prev_funding_tx_sign(&signing_session.constructed_transaction, None, logger) { + Err(e) => return Err((self, e)), // simpler map_err() could not be used here due to move issue + Ok(t) => t, + }; + signing_session.shared_signature = Some(holder_signature); + (partly_signed_transaction, Some(holder_signature)) + } else { + (signing_session.constructed_transaction.clone(), None) + }; + let mut funding_ready_for_sig_event = None; if self.dual_funding_context.our_funding_satoshis == 0 { - signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new(), None); + signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new(), shared_signature); } else { funding_ready_for_sig_event = Some(Event::FundingTransactionReadyForSigning { channel_id: self.context.channel_id, counterparty_node_id: *counterparty_node_id, user_channel_id: self.context.user_id, - unsigned_transaction: signing_session.constructed_transaction.clone(), + unsigned_transaction: partly_signed_transaction, }); } @@ -8750,6 +8504,34 @@ impl InboundV2Channel where SP::Target: SignerProvider { Ok((channel, commitment_signed, funding_ready_for_sig_event)) } + + /// #SPLICING STEP4 A + /// Get the splice_ack message that can be sent in response to splice initiation + /// TODO move to ChannelContext + pub fn get_splice_ack(&mut self, chain_hash: ChainHash) -> Result { + if self.context.is_outbound() { + panic!("Tried to accept a splice on an outound channel?"); + } + + let pending_splice = match &self.context.pending_splice_post { + None => return Err(ChannelError::Close("get_splice_ack() is invalid on a channel with no active splice".to_owned())), + Some(ps) => ps.clone(), + }; + + // TODO checks + + // TODO check + // self.context.channel_state = ChannelState::NegotiatingFunding(NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT); + + let keys = self.context.get_holder_pubkeys(); + // TODO how to handle channel capacity, orig is stored in Channel, has to be updated, in the interim there are two + Ok(msgs::SpliceAck { + chain_hash, + channel_id: self.context.channel_id, // pending_splice.pre_channel_id.unwrap(), // TODO + relative_satoshis: pending_splice.relative_satoshis(), //self.context.get_value_satoshis()), + funding_pubkey: keys.funding_pubkey, + }) + } } // Unfunded channel utilities @@ -9135,6 +8917,8 @@ impl Writeable for Channel where SP::Target: SignerProvider { self.context.channel_transaction_parameters.write(writer)?; self.context.funding_transaction.write(writer)?; + // TODO check BW compatibility; we may go with being non saved; if it is missing, spliceing will not be possible + self.context.funding_transaction_saved.write(writer)?; self.context.counterparty_cur_commitment_point.write(writer)?; self.context.counterparty_prev_commitment_point.write(writer)?; @@ -9443,6 +9227,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch let mut channel_parameters: ChannelTransactionParameters = Readable::read(reader)?; let funding_transaction: Option = Readable::read(reader)?; + let funding_transaction_saved: Option = Readable::read(reader)?; let counterparty_cur_commitment_point = Readable::read(reader)?; @@ -9714,6 +9499,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch channel_transaction_parameters: channel_parameters, funding_transaction, + funding_transaction_saved, is_batch_funding, counterparty_cur_commitment_point, @@ -9755,7 +9541,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch interactive_tx_constructor: None, // pending_monitor_updates: Vec::new(), - pending_splice: None, + pending_splice_pre: None, + pending_splice_post: None, }, #[cfg(dual_funding)] dual_funding_channel_context: None, @@ -9777,7 +9564,7 @@ mod tests { use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint}; use crate::ln::channelmanager::{self, HTLCSource, PaymentId}; use crate::ln::channel::InitFeatures; - use crate::ln::channel::{AwaitingChannelReadyFlags, Channel, ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, HTLCUpdateAwaitingACK, PendingSpliceInfo, commit_tx_fee_msat}; + use crate::ln::channel::{AwaitingChannelReadyFlags, Channel, ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, HTLCUpdateAwaitingACK, commit_tx_fee_msat}; use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS}; use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures}; use crate::ln::msgs; @@ -9821,82 +9608,6 @@ mod tests { "MAX_FUNDING_SATOSHIS_NO_WUMBO is greater than all satoshis in existence"); } - fn create_pending_splice_info(pre_channel_value: u64, post_channel_value: u64) -> PendingSpliceInfo { - PendingSpliceInfo { - post_channel_value, - pre_channel_value, - is_outgoing: true, - prev_funding_input_index: None, - // initial_commitment_tx: None, - // cp_commitment_sig: None, - funding_transaction: None, - funding_txo: None, - } - } - - #[test] - fn test_pending_splice_info_new() { - { - // increase, small amounts - let ps = create_pending_splice_info(9_000, 15_000); - assert_eq!(ps.pre_channel_value, 9_000); - assert_eq!(ps.post_channel_value, 15_000); - assert_eq!(ps.relative_satoshis(), 6_000); - } - { - // decrease, small amounts - let ps = create_pending_splice_info(15_000, 9_000); - assert_eq!(ps.pre_channel_value, 15_000); - assert_eq!(ps.post_channel_value, 9_000); - assert_eq!(ps.relative_satoshis(), -6_000); - } - let base2: u64 = 2; - let huge63 = base2.pow(63); - assert_eq!(huge63, 9223372036854775808); - { - // increase, one huge amount - let ps = create_pending_splice_info(9_000, huge63 + 9_000 - 1); - assert_eq!(ps.pre_channel_value, 9_000); - assert_eq!(ps.post_channel_value, 9223372036854784807); // 2^63 + 9000 - 1 - assert_eq!(ps.relative_satoshis(), 9223372036854775807); // 2^63 - 1 - } - { - // decrease, one huge amount - let ps = create_pending_splice_info(huge63 + 9_000 - 1, 9_000); - assert_eq!(ps.pre_channel_value, 9223372036854784807); // 2^63 + 9000 - 1 - assert_eq!(ps.post_channel_value, 9_000); - assert_eq!(ps.relative_satoshis(), -9223372036854775807); // 2^63 - 1 - } - { - // increase, two huge amounts - let ps = create_pending_splice_info(huge63 + 9_000, huge63 + 15_000); - assert_eq!(ps.pre_channel_value, 9223372036854784808); // 2^63 + 9000 - assert_eq!(ps.post_channel_value, 9223372036854790808); // 2^63 + 15000 - assert_eq!(ps.relative_satoshis(), 6_000); - } - { - // decrease, two huge amounts - let ps = create_pending_splice_info(huge63 + 15_000, huge63 + 9_000); - assert_eq!(ps.pre_channel_value, 9223372036854790808); // 2^63 + 15000 - assert_eq!(ps.post_channel_value, 9223372036854784808); // 2^63 + 9000 - assert_eq!(ps.relative_satoshis(), -6_000); - } - { - // underflow - let ps = create_pending_splice_info(9_000, huge63 + 9_000 + 20); - assert_eq!(ps.pre_channel_value, 9_000); - assert_eq!(ps.post_channel_value, 9223372036854784828); // 2^63 + 9000 + 20 - assert_eq!(ps.relative_satoshis(), -0); - } - { - // underflow - let ps = create_pending_splice_info(huge63 + 9_000 + 20, 9_000); - assert_eq!(ps.pre_channel_value, 9223372036854784828); // 2^63 + 9000 + 20 - assert_eq!(ps.post_channel_value, 9_000); - assert_eq!(ps.relative_satoshis(), -0); - } - } - struct Keys { signer: InMemorySigner, } diff --git a/lightning/src/ln/channel_id.rs b/lightning/src/ln/channel_id.rs index 35e90ed3c44..9404320cec2 100644 --- a/lightning/src/ln/channel_id.rs +++ b/lightning/src/ln/channel_id.rs @@ -9,6 +9,8 @@ //! ChannelId definition. +use crate::chain::transaction::OutPoint; +use crate::io; use crate::ln::msgs::DecodeError; use crate::sign::EntropySource; use crate::util::ser::{Readable, Writeable, Writer}; @@ -16,7 +18,6 @@ use crate::util::ser::{Readable, Writeable, Writer}; use bitcoin::hashes::Hash as _; use bitcoin::hashes::sha256::Hash as Sha256; -use crate::io; use core::fmt; use core::ops::Deref; @@ -45,6 +46,11 @@ impl ChannelId { Self(res) } + /// Create _v1_ channel ID from a funding tx outpoint + pub fn v1_from_funding_outpoint(outpoint: OutPoint) -> Self { + Self::v1_from_funding_txid(outpoint.txid.as_byte_array(), outpoint.index) + } + /// Create a _temporary_ channel ID randomly, based on an entropy source. pub fn temporary_from_entropy_source(entropy_source: &ES) -> Self where ES::Target: EntropySource { diff --git a/lightning/src/ln/channel_splice.rs b/lightning/src/ln/channel_splice.rs new file mode 100644 index 00000000000..39bb1371939 --- /dev/null +++ b/lightning/src/ln/channel_splice.rs @@ -0,0 +1,214 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +// Splicing related utilities + +use crate::chain::transaction::OutPoint; +use crate::ln::ChannelId; +use crate::ln::channel::ChannelError; +use bitcoin::{ScriptBuf, Sequence, Transaction, TxIn, Witness}; +use core::convert::TryFrom; + +/// Info about a pending splice, used in the pre-splice channel +#[derive(Clone)] +pub(crate) struct PendingSpliceInfoPre { + /// The post splice value (current + relative) + pub post_channel_value: u64, + /// Reference to the post-splice channel (may be missing if channel_id is the same) + pub post_channel_id: Option, + pub funding_feerate_perkw: u32, + pub locktime: u32, +} + +/// Info about a pending splice, used in the post-splice channel +#[derive(Clone)] +pub(crate) struct PendingSpliceInfoPost { + /// The post splice value (current + relative) + pub post_channel_value: u64, // TODO may be removed, it's in the channel capacity + /// The pre splice value (a bit redundant) + pub pre_channel_value: u64, + /// Reference to the pre-splice channel (may be missing if channel_id was the same) + pub pre_channel_id: Option, + + /// Save here the previous funding transaction + pub pre_funding_transaction: Option, + /// Save here the previous funding TXO + pub pre_funding_txo: Option, +} + +impl PendingSpliceInfoPre { + pub(crate) fn new(relative_satoshis: i64, pre_channel_value: u64, + post_channel_id: Option, funding_feerate_perkw: u32, locktime: u32 + ) -> Self { + let post_channel_value = Self::add_checked(pre_channel_value, relative_satoshis); + Self { + post_channel_value, + post_channel_id, + funding_feerate_perkw, + locktime, + } + } + + /// Add a u64 and an i64, handling i64 overflow cases (doing without cast to i64) + pub(crate) fn add_checked(pre_channel_value: u64, relative_satoshis: i64) -> u64 { + if relative_satoshis >= 0 { + pre_channel_value.saturating_add(relative_satoshis as u64) + } else { + pre_channel_value.saturating_sub((-relative_satoshis) as u64) + } + } + + /// The relative splice value (change in capacity value relative to current value) + pub(crate) fn relative_satoshis(&self, pre_channel_value: u64) -> i64 { + if self.post_channel_value > pre_channel_value { + i64::try_from(self.post_channel_value.saturating_sub(pre_channel_value)).unwrap_or_default() + } else { + -i64::try_from(pre_channel_value.saturating_sub(self.post_channel_value)).unwrap_or_default() + } + } +} + +impl PendingSpliceInfoPost { + pub(crate) fn new(relative_satoshis: i64, pre_channel_value: u64, pre_channel_id: Option, + pre_funding_transaction: Option, pre_funding_txo: Option + ) -> Self { + let post_channel_value = PendingSpliceInfoPre::add_checked(pre_channel_value, relative_satoshis); + Self { + post_channel_value, + pre_channel_value, + pre_channel_id, + pre_funding_transaction, + pre_funding_txo, + } + } + + /// The relative splice value (change in capacity value relative to current value) + pub(crate) fn relative_satoshis(&self) -> i64 { + if self.post_channel_value > self.pre_channel_value { + i64::try_from(self.post_channel_value.saturating_sub(self.pre_channel_value)).unwrap_or_default() + } else { + -i64::try_from(self.pre_channel_value.saturating_sub(self.post_channel_value)).unwrap_or_default() + } + } + + /// Get a transaction input that is the previous funding transaction + pub(super) fn get_input_of_previous_funding(&self) -> Result<(TxIn, Transaction), ChannelError> { + if let Some(pre_funding_transaction) = &self.pre_funding_transaction { + if let Some(pre_funding_txo) = &self.pre_funding_txo { + Ok(( + TxIn { + previous_output: pre_funding_txo.into_bitcoin_outpoint(), + script_sig: ScriptBuf::new(), + sequence: Sequence::ZERO, + witness: Witness::new(), + }, + pre_funding_transaction.clone(), + )) + } else { + Err(ChannelError::Warn("Internal error: Missing previous funding transaction outpoint".to_string())) + } + } else { + Err(ChannelError::Warn("Internal error: Missing previous funding transaction".to_string())) + } + } + + /// Within the given transaction, find the input that corresponds to the previous funding transaction + pub(super) fn find_input_of_previous_funding(&self, tx: &Transaction) -> Result { + if let Some(pre_funding_txo) = &self.pre_funding_txo { + for idx in 0..tx.input.len() { + if tx.input[idx].previous_output == pre_funding_txo.into_bitcoin_outpoint() { + return Ok(idx as u16); + } + } + // Not found + Err(ChannelError::Warn("Internal error: Previous funding transaction not found in the inputs of the new funding transaction".to_string())) + } else { + Err(ChannelError::Warn("Internal error: Missing previous funding transaction outpoint".to_string())) + } + } +} + + +#[cfg(test)] +mod tests { + use crate::ln::channel_splice::PendingSpliceInfoPost; + + fn create_pending_splice_info(pre_channel_value: u64, post_channel_value: u64) -> PendingSpliceInfoPost { + PendingSpliceInfoPost { + post_channel_value, + pre_channel_value, + pre_channel_id: None, + pre_funding_transaction: None, + pre_funding_txo: None, + } + } + + #[test] + fn test_pending_splice_info_new() { + { + // increase, small amounts + let ps = create_pending_splice_info(9_000, 15_000); + assert_eq!(ps.pre_channel_value, 9_000); + assert_eq!(ps.post_channel_value, 15_000); + assert_eq!(ps.relative_satoshis(), 6_000); + } + { + // decrease, small amounts + let ps = create_pending_splice_info(15_000, 9_000); + assert_eq!(ps.pre_channel_value, 15_000); + assert_eq!(ps.post_channel_value, 9_000); + assert_eq!(ps.relative_satoshis(), -6_000); + } + let base2: u64 = 2; + let huge63 = base2.pow(63); + assert_eq!(huge63, 9223372036854775808); + { + // increase, one huge amount + let ps = create_pending_splice_info(9_000, huge63 + 9_000 - 1); + assert_eq!(ps.pre_channel_value, 9_000); + assert_eq!(ps.post_channel_value, 9223372036854784807); // 2^63 + 9000 - 1 + assert_eq!(ps.relative_satoshis(), 9223372036854775807); // 2^63 - 1 + } + { + // decrease, one huge amount + let ps = create_pending_splice_info(huge63 + 9_000 - 1, 9_000); + assert_eq!(ps.pre_channel_value, 9223372036854784807); // 2^63 + 9000 - 1 + assert_eq!(ps.post_channel_value, 9_000); + assert_eq!(ps.relative_satoshis(), -9223372036854775807); // 2^63 - 1 + } + { + // increase, two huge amounts + let ps = create_pending_splice_info(huge63 + 9_000, huge63 + 15_000); + assert_eq!(ps.pre_channel_value, 9223372036854784808); // 2^63 + 9000 + assert_eq!(ps.post_channel_value, 9223372036854790808); // 2^63 + 15000 + assert_eq!(ps.relative_satoshis(), 6_000); + } + { + // decrease, two huge amounts + let ps = create_pending_splice_info(huge63 + 15_000, huge63 + 9_000); + assert_eq!(ps.pre_channel_value, 9223372036854790808); // 2^63 + 15000 + assert_eq!(ps.post_channel_value, 9223372036854784808); // 2^63 + 9000 + assert_eq!(ps.relative_satoshis(), -6_000); + } + { + // underflow + let ps = create_pending_splice_info(9_000, huge63 + 9_000 + 20); + assert_eq!(ps.pre_channel_value, 9_000); + assert_eq!(ps.post_channel_value, 9223372036854784828); // 2^63 + 9000 + 20 + assert_eq!(ps.relative_satoshis(), -0); + } + { + // underflow + let ps = create_pending_splice_info(huge63 + 9_000 + 20, 9_000); + assert_eq!(ps.pre_channel_value, 9223372036854784828); // 2^63 + 9000 + 20 + assert_eq!(ps.post_channel_value, 9_000); + assert_eq!(ps.relative_satoshis(), -0); + } + } +} diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 817d84b8c92..fbba513e8e7 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -20,8 +20,8 @@ use bitcoin::blockdata::block::Header; use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::constants::ChainHash; -use bitcoin::locktime::absolute::LockTime; use bitcoin::key::constants::SECRET_KEY_SIZE; +use bitcoin::locktime::absolute::LockTime; use bitcoin::network::constants::Network; use bitcoin::hashes::Hash; @@ -44,9 +44,10 @@ use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, Messa // Since this struct is returned in `list_channels` methods, expose it here in case users want to // construct one themselves. use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; -use crate::ln::channel::{Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, DualFundingChannelContext, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, PendingSpliceInfo, WithChannelContext}; +use crate::ln::channel::{Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, DualFundingChannelContext, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext}; #[cfg(dual_funding)] use crate::ln::channel::{InboundV2Channel, OutboundV2Channel}; +use crate::ln::channel_splice::PendingSpliceInfoPre; use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; #[cfg(any(feature = "_test_utils", test))] use crate::ln::features::Bolt11InvoiceFeatures; @@ -283,6 +284,7 @@ pub(super) struct PendingAddHTLCInfo { // Note that this may be an outbound SCID alias for the associated channel. prev_short_channel_id: u64, prev_htlc_id: u64, + prev_channel_id: ChannelId, prev_funding_outpoint: OutPoint, prev_user_channel_id: u128, } @@ -312,6 +314,7 @@ pub(crate) struct HTLCPreviousHopData { incoming_packet_shared_secret: [u8; 32], phantom_shared_secret: Option<[u8; 32]>, blinded_failure: Option, + channel_id: ChannelId, // This field is consumed by `claim_funds_from_hop()` when updating a force-closed backwards // channel with a preimage provided by the forward channel. @@ -352,7 +355,7 @@ struct ClaimableHTLC { impl From<&ClaimableHTLC> for events::ClaimedHTLC { fn from(val: &ClaimableHTLC) -> Self { events::ClaimedHTLC { - channel_id: val.prev_hop.outpoint.to_channel_id(), + channel_id: val.prev_hop.channel_id, user_channel_id: val.prev_hop.user_channel_id.unwrap_or(0), cltv_expiry: val.cltv_expiry, value_msat: val.value, @@ -810,7 +813,7 @@ pub(crate) enum RAAMonitorUpdateBlockingAction { impl RAAMonitorUpdateBlockingAction { fn from_prev_hop_data(prev_hop: &HTLCPreviousHopData) -> Self { Self::ForwardedPaymentInboundClaim { - channel_id: prev_hop.outpoint.to_channel_id(), + channel_id: prev_hop.channel_id, htlc_id: prev_hop.htlc_id, } } @@ -2293,7 +2296,7 @@ macro_rules! handle_new_monitor_update { handle_new_monitor_update!($self, $update_res, $chan, _internal, handle_monitor_update_completion!($self, $peer_state_lock, $peer_state, $per_peer_state_lock, $chan)) }; - ($self: ident, $funding_txo: expr, $update: expr, $peer_state_lock: expr, $peer_state: expr, $per_peer_state_lock: expr, $chan: expr) => { { + ($self: ident, $funding_txo: expr, $channel_id: expr, $update: expr, $peer_state_lock: expr, $peer_state: expr, $per_peer_state_lock: expr, $chan: expr) => { { let in_flight_updates = $peer_state.in_flight_monitor_updates.entry($funding_txo) .or_insert_with(Vec::new); // During startup, we push monitor updates as background events through to here in @@ -2653,10 +2656,10 @@ where }, } }; - let res = channel.get_open_channel_v2(self.chain_hash); + let msg = channel.get_open_channel_v2(self.chain_hash); let event = events::MessageSendEvent::SendOpenChannelV2 { node_id: their_network_key, - msg: res, + msg, }; (ChannelPhase::UnfundedOutboundV2(channel), event) } else { @@ -2699,6 +2702,7 @@ where /// #SPLICING STEP1 I /// Inspired by create_channel() and close_channel() /// Initiate a splice, to change the channel capacity + /// TODO update docu flow /// TODO funding_feerate_perkw /// TODO locktime /// @@ -2716,44 +2720,48 @@ where /// <------- splice_signed_ack --- send signature on funding tx. In future this should be tx_signatures /// [new funding tx can be broadcast] pub fn splice_channel(&self, channel_id: &ChannelId, their_network_key: &PublicKey, relative_satoshis: i64, funding_feerate_perkw: u32, locktime: u32) -> Result<(), APIError> { - // TODO handle code duplication with create_channel - let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); // We want to make sure the lock is actually acquired by PersistenceNotifierGuard. debug_assert!(&self.total_consistency_lock.try_write().is_err()); let per_peer_state = self.per_peer_state.read().unwrap(); - let peer_state_mutex = per_peer_state.get(their_network_key) .ok_or_else(|| APIError::APIMisuseError{ err: format!("Not connected to node: {}", their_network_key) })?; - // Look for channel; taken from close_channel() let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; + // Look for channel match peer_state.channel_by_id.entry(channel_id.clone()) { hash_map::Entry::Vacant(_) => return Err(APIError::ChannelUnavailable{err: format!("Channel with id {} not found for the passed counterparty node_id {}", channel_id, their_network_key) }), hash_map::Entry::Occupied(mut chan_phase_entry) => { if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() { - let current_value_sats = chan.context.get_value_satoshis(); + if relative_satoshis < 0 { + return Err(APIError::APIMisuseError { err: format!("Splice/out not supported, only splice in, relative {}, channel_id {}", -relative_satoshis, channel_id) }); + } + + let pre_channel_value = chan.context.get_value_satoshis(); // TODO check for i64 overflow - if relative_satoshis < 0 && -relative_satoshis > (current_value_sats as i64) { - return Err(APIError::APIMisuseError { err: format!("Post-splicing channel value cannot be negative. It was {} - {}", current_value_sats, -relative_satoshis) }); + if relative_satoshis < 0 && -relative_satoshis > (pre_channel_value as i64) { + return Err(APIError::APIMisuseError { err: format!("Post-splicing channel value cannot be negative. It was {} - {}", pre_channel_value, -relative_satoshis) }); } - let post_splice_funding_satoshis = PendingSpliceInfo::add_checked(current_value_sats, relative_satoshis); - // TODO handle code duplication with create_channel - if post_splice_funding_satoshis < 1000 { - return Err(APIError::APIMisuseError { err: format!("Post-splicing channel value must be at least 1000 satoshis. It was {}", post_splice_funding_satoshis) }); + let post_channel_value = PendingSpliceInfoPre::add_checked(pre_channel_value, relative_satoshis); + if post_channel_value < 1000 { + return Err(APIError::APIMisuseError { err: format!("Post-splicing channel value must be at least 1000 satoshis. It was {}", post_channel_value) }); + } + + if chan.context.pending_splice_pre.is_some() { + return Err(APIError::ChannelUnavailable { err: format!("Channel has already a splice pending, channel id {}", channel_id) }); + } + + chan.context.pending_splice_pre = Some(PendingSpliceInfoPre::new(relative_satoshis, pre_channel_value, None, funding_feerate_perkw, locktime)); + + // Check channel id + let post_splice_v2_channel_id = chan.context.generate_v2_channel_id_from_revocation_basepoints(); + if post_splice_v2_channel_id != chan.context.channel_id() { + return Err(APIError::APIMisuseError { err: format!("Channel ID would change during splicing (e.g. splice on V1 channel), not yet supported, channel id {} {}", + chan.context.channel_id(), post_splice_v2_channel_id) }); } - // Store post-splicing channel value (pending) - // TODO check if pending_splice is already set - chan.context.pending_splice = Some(PendingSpliceInfo::new(relative_satoshis, current_value_sats, true)); - chan.dual_funding_channel_context = Some(DualFundingChannelContext { - our_funding_satoshis: chan.context.pending_splice.as_ref().unwrap().post_channel_value, - their_funding_satoshis: 0, - funding_tx_locktime: LockTime::ZERO, // TODO - funding_feerate_sat_per_1000_weight: funding_feerate_perkw, - }); let msg = chan.get_splice(self.chain_hash.clone(), relative_satoshis, funding_feerate_perkw, locktime); @@ -2761,9 +2769,10 @@ where node_id: *their_network_key, msg, }); + Ok(()) } else { - return Err(APIError::ChannelUnavailable{err: format!("Channel with id {} is not funded", channel_id) }); + return Err(APIError::ChannelUnavailable { err: format!("Channel with id {} is not funded", channel_id) }); } }, @@ -2954,7 +2963,7 @@ where // Update the monitor with the shutdown script if necessary. if let Some(monitor_update) = monitor_update_opt.take() { - handle_new_monitor_update!(self, funding_txo_opt.unwrap(), monitor_update, + handle_new_monitor_update!(self, funding_txo_opt.unwrap(), chan.context.channel_id(), monitor_update, peer_state_lock, peer_state, per_peer_state, chan); } } else { @@ -3583,7 +3592,7 @@ where }, onion_packet, None, &self.fee_estimator, &&logger); match break_chan_phase_entry!(self, send_res, chan_phase_entry) { Some(monitor_update) => { - match handle_new_monitor_update!(self, funding_txo, monitor_update, peer_state_lock, peer_state, per_peer_state, chan) { + match handle_new_monitor_update!(self, funding_txo, channel_id, monitor_update, peer_state_lock, peer_state, per_peer_state, chan) { false => { // Note that MonitorUpdateInProgress here indicates (per function // docs) that we will resend the commitment update once monitor @@ -4185,32 +4194,11 @@ where let tx_msg_opt = match phase.get_mut() { ChannelPhase::UnfundedOutboundV2(chan) => { chan.begin_interactive_funding_tx_construction(&self.signer_provider, - &self.entropy_source, self.get_our_node_id(), false, funding_inputs, &self.logger, - )? + &self.entropy_source, self.get_our_node_id(), funding_inputs)? }, ChannelPhase::UnfundedInboundV2(chan) => { chan.begin_interactive_funding_tx_construction(&self.signer_provider, - &self.entropy_source, self.get_our_node_id(), false, funding_inputs, &self.logger, - )? - }, - // This is neeed when Channel is not changed during splicing, and interactive - // transaction is constructed on the Funded channel. TODO: remove if new - // unfunded channel instance is used during splicing - ChannelPhase::Funded(chan) => { - if chan.context.pending_splice.is_some() { - let msg = chan.begin_interactive_funding_tx_construction(&self.signer_provider, - &self.entropy_source, self.get_our_node_id(), true, true, funding_inputs, &self.logger)?; - - // Commit to post-splice parameters prematurely, to have right values for commitment building - // TODO: commitment should happen only upon confirmation - let _ = chan.context.commit_pending_splice(&self.logger).unwrap(); - - msg - } else { - return Err(APIError::APIMisuseError { - err: format!("Channel is funded and in not actively splicing {}", chan.context.channel_id()) - }); - } + &self.entropy_source, self.get_our_node_id(), funding_inputs)? }, _ => { return Err(APIError::ChannelUnavailable { @@ -4245,7 +4233,7 @@ where #[cfg(dual_funding)] pub fn funding_transaction_signed(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, transaction: Transaction) -> Result<(), APIError> { - let witnesses: Vec<_> = transaction.input.clone().into_iter().enumerate().filter_map(|(idx, input)| { + let witnesses: Vec<_> = transaction.input.clone().into_iter().enumerate().filter_map(|(_idx, input)| { if input.witness.is_empty() { None } else { Some(input.witness) } }).collect(); @@ -4260,10 +4248,11 @@ where Some(ChannelPhase::Funded(chan)) => { chan.verify_interactive_tx_signatures(&witnesses); if let Some(ref mut signing_session) = chan.interactive_tx_signing_session { - // Shared signature (splicing): holder signature on the prev funding tx input should have been saved. + // Splicing + // Shared signature (used in splicing): holder signature on the prev funding tx input should have been saved. // include it in tlvs field let mut tlvs = None; - if chan.context.pending_splice.is_some() { + if chan.context.pending_splice_post.is_some() { if let Some(s) = signing_session.shared_signature { tlvs = Some(s); } // TODO error @@ -4473,6 +4462,7 @@ where let mut per_source_pending_forward = [( payment.prev_short_channel_id, + payment.prev_channel_id, payment.prev_funding_outpoint, payment.prev_user_channel_id, vec![(pending_htlc_info, payment.prev_htlc_id)] @@ -4500,6 +4490,7 @@ where let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: payment.prev_short_channel_id, user_channel_id: Some(payment.prev_user_channel_id), + channel_id: payment.prev_channel_id, outpoint: payment.prev_funding_outpoint, htlc_id: payment.prev_htlc_id, incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret, @@ -4524,7 +4515,7 @@ where let mut new_events = VecDeque::new(); let mut failed_forwards = Vec::new(); - let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new(); + let mut phantom_receives: Vec<(u64, ChannelId, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new(); { let mut forward_htlcs = HashMap::new(); mem::swap(&mut forward_htlcs, &mut self.forward_htlcs.lock().unwrap()); @@ -4537,7 +4528,7 @@ where for forward_info in pending_forwards.drain(..) { match forward_info { HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id, + prev_short_channel_id, prev_htlc_id, prev_channel_id, prev_funding_outpoint, prev_user_channel_id, forward_info: PendingHTLCInfo { routing, incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value, .. @@ -4551,6 +4542,7 @@ where let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: prev_short_channel_id, user_channel_id: Some(prev_user_channel_id), + channel_id: prev_channel_id, outpoint: prev_funding_outpoint, htlc_id: prev_htlc_id, incoming_packet_shared_secret: incoming_shared_secret, @@ -4614,7 +4606,7 @@ where outgoing_cltv_value, Some(phantom_shared_secret), false, None, current_height, self.default_configuration.accept_mpp_keysend) { - Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])), + Ok(info) => phantom_receives.push((prev_short_channel_id, prev_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])), Err(InboundOnionErr { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret)) } }, @@ -4659,7 +4651,7 @@ where for forward_info in pending_forwards.drain(..) { match forward_info { HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id, + prev_short_channel_id, prev_htlc_id, prev_channel_id, prev_funding_outpoint, prev_user_channel_id, forward_info: PendingHTLCInfo { incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value, routing: PendingHTLCRouting::Forward { @@ -4671,6 +4663,7 @@ where let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: prev_short_channel_id, user_channel_id: Some(prev_user_channel_id), + channel_id: prev_channel_id, outpoint: prev_funding_outpoint, htlc_id: prev_htlc_id, incoming_packet_shared_secret: incoming_shared_secret, @@ -4733,7 +4726,7 @@ where 'next_forwardable_htlc: for forward_info in pending_forwards.drain(..) { match forward_info { HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id, + prev_short_channel_id, prev_htlc_id, prev_channel_id, prev_funding_outpoint, prev_user_channel_id, forward_info: PendingHTLCInfo { routing, incoming_shared_secret, payment_hash, incoming_amt_msat, outgoing_amt_msat, skimmed_fee_msat, .. @@ -4765,6 +4758,7 @@ where prev_hop: HTLCPreviousHopData { short_channel_id: prev_short_channel_id, user_channel_id: Some(prev_user_channel_id), + channel_id: prev_channel_id, outpoint: prev_funding_outpoint, htlc_id: prev_htlc_id, incoming_packet_shared_secret: incoming_shared_secret, @@ -4796,6 +4790,7 @@ where failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: $htlc.prev_hop.short_channel_id, user_channel_id: $htlc.prev_hop.user_channel_id, + channel_id: prev_channel_id, outpoint: prev_funding_outpoint, htlc_id: $htlc.prev_hop.htlc_id, incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret, @@ -5036,7 +5031,7 @@ where hash_map::Entry::Occupied(mut chan_phase) => { if let ChannelPhase::Funded(chan) = chan_phase.get_mut() { updated_chan = true; - handle_new_monitor_update!(self, funding_txo, update.clone(), + handle_new_monitor_update!(self, funding_txo, chan.context.channel_id, update.clone(), peer_state_lock, peer_state, per_peer_state, chan); } else { debug_assert!(false, "We shouldn't have an update for a non-funded channel"); @@ -5759,7 +5754,7 @@ where } if valid_mpp { for htlc in sources.drain(..) { - let prev_hop_chan_id = htlc.prev_hop.outpoint.to_channel_id(); + let prev_hop_chan_id = htlc.prev_hop.channel_id; if let Err((pk, err)) = self.claim_funds_from_hop( htlc.prev_hop, payment_preimage, |_, definitely_duplicate| { @@ -5812,7 +5807,7 @@ where { let per_peer_state = self.per_peer_state.read().unwrap(); - let chan_id = prev_hop.outpoint.to_channel_id(); + let chan_id = prev_hop.channel_id; let counterparty_node_id_opt = match self.short_to_chan_info.read().unwrap().get(&prev_hop.short_channel_id) { Some((cp_id, _dup_chan_id)) => Some(cp_id.clone()), None => None @@ -5840,7 +5835,7 @@ where peer_state.monitor_update_blocked_actions.entry(chan_id).or_insert(Vec::new()).push(action); } if !during_init { - handle_new_monitor_update!(self, prev_hop.outpoint, monitor_update, peer_state_lock, + handle_new_monitor_update!(self, prev_hop.outpoint, prev_hop.channel_id, monitor_update, peer_state_lock, peer_state, per_peer_state, chan); } else { // If we're running during init we cannot update a monitor directly - @@ -5919,7 +5914,7 @@ where // with a preimage we *must* somehow manage to propagate it to the upstream // channel, or we must have an ability to receive the same event and try // again on restart. - log_error!(WithContext::from(&self.logger, None, Some(prev_hop.outpoint.to_channel_id())), "Critical error: failed to update channel monitor with preimage {:?}: {:?}", + log_error!(WithContext::from(&self.logger, None, Some(prev_hop.channel_id)), "Critical error: failed to update channel monitor with preimage {:?}: {:?}", payment_preimage, update_res); } } else { @@ -5937,7 +5932,7 @@ where BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup(( prev_hop.outpoint, preimage_update, ))); - } + } // Note that we do process the completion action here. This totally could be a // duplicate claim, but we have no way of knowing without interrogating the // `ChannelMonitor` we've provided the above update to. Instead, note that `Event`s are @@ -6130,7 +6125,7 @@ where commitment_update: Option, order: RAACommitmentOrder, pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option, channel_ready: Option, announcement_sigs: Option) - -> Option<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> { + -> Option<(u64, ChannelId, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> { let logger = WithChannelContext::from(&self.logger, &channel.context); log_trace!(logger, "Handling channel resumption for channel {} with {} RAA, {} commitment update, {} pending forwards, {}broadcasting funding, {} channel ready, {} announcement", &channel.context.channel_id(), @@ -6145,7 +6140,7 @@ where let counterparty_node_id = channel.context.get_counterparty_node_id(); if !pending_forwards.is_empty() { htlc_forwards = Some((channel.context.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias()), - channel.context.get_funding_txo().unwrap(), channel.context.get_user_id(), pending_forwards)); + channel.context.channel_id(), channel.context.get_funding_txo().unwrap(), channel.context.get_user_id(), pending_forwards)); } if let Some(msg) = channel_ready { @@ -6682,7 +6677,7 @@ where channel.context.set_outbound_scid_alias(outbound_scid_alias); channel.begin_interactive_funding_tx_construction(&self.signer_provider, &self.entropy_source, - self.get_our_node_id(), false, Vec::new(), &self.logger).map_err(|_| MsgHandleErrInternal::send_err_msg_no_close( + self.get_our_node_id(), Vec::new()).map_err(|_| MsgHandleErrInternal::send_err_msg_no_close( "Failed to start interactive transaction construction".to_owned(), msg.temporary_channel_id))?; peer_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannelV2 { @@ -6746,7 +6741,7 @@ where let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; - let (chan, channel_id, holder_funding_satoshis, counterparty_funding_satoshis, user_channel_id) = { + let (chan, new_channel_id, holder_funding_satoshis, counterparty_funding_satoshis, user_channel_id) = { match peer_state.channel_by_id.remove(&msg.temporary_channel_id) { Some(phase) => { match phase { @@ -6755,10 +6750,10 @@ where let (_, res) = convert_chan_phase_err!(self, err, chan, &msg.temporary_channel_id, UNFUNDED_CHANNEL); let _: Result<(), _> = handle_error!(self, Err(res), *counterparty_node_id); } - let channel_id = chan.context.channel_id(); + let new_channel_id = chan.context.channel_id(); let holder_funding_satoshis = chan.dual_funding_context.our_funding_satoshis; let user_channel_id = chan.context.get_user_id(); - (chan, channel_id, holder_funding_satoshis, msg.funding_satoshis, user_channel_id) + (chan, new_channel_id, holder_funding_satoshis, msg.funding_satoshis, user_channel_id) }, _ => { peer_state.channel_by_id.insert(msg.temporary_channel_id, phase); @@ -6770,10 +6765,10 @@ where } }; - peer_state.channel_by_id.insert(chan.context.channel_id(), ChannelPhase::UnfundedOutboundV2(chan)); + peer_state.channel_by_id.insert(new_channel_id, ChannelPhase::UnfundedOutboundV2(chan)); let mut pending_events = self.pending_events.lock().unwrap(); pending_events.push_back((events::Event::FundingInputsContributionReady { - channel_id, + channel_id: new_channel_id, counterparty_node_id: *counterparty_node_id, holder_funding_satoshis, counterparty_funding_satoshis, @@ -6945,12 +6940,7 @@ where hash_map::Entry::Occupied(mut chan_phase_entry) => { let channel_phase = chan_phase_entry.get_mut(); match channel_phase { - ChannelPhase::UnfundedInboundV2(_) | - ChannelPhase::UnfundedOutboundV2(_) | - // This is neeed when Channel is not changed during splicing, and interactive - // transaction is constructed on the Funded channel. TODO: remove if new - // unfunded channel instance is used during splicing - ChannelPhase::Funded(_) => { + ChannelPhase::UnfundedInboundV2(_) | ChannelPhase::UnfundedOutboundV2(_) => { let tx_msg = channel_phase.context_mut().tx_add_input(msg); let msg_send_event = match tx_msg { Ok(InteractiveTxMessageSend::TxAddInput(msg)) => events::MessageSendEvent::SendTxAddInput { @@ -6992,12 +6982,7 @@ where hash_map::Entry::Occupied(mut chan_phase_entry) => { let channel_phase = chan_phase_entry.get_mut(); match channel_phase { - ChannelPhase::UnfundedInboundV2(_) | - ChannelPhase::UnfundedOutboundV2(_) | - // This is neeed when Channel is not changed during splicing, and interactive - // transaction is constructed on the Funded channel. TODO: remove if new - // unfunded channel instance is used during splicing - ChannelPhase::Funded(_) => { + ChannelPhase::UnfundedInboundV2(_) | ChannelPhase::UnfundedOutboundV2(_) => { let tx_msg = channel_phase.context_mut().tx_add_output(msg); let msg_send_event = match tx_msg { Ok(InteractiveTxMessageSend::TxAddInput(msg)) => events::MessageSendEvent::SendTxAddInput { @@ -7123,12 +7108,7 @@ where hash_map::Entry::Occupied(mut chan_phase_entry) => { let channel_phase = chan_phase_entry.get_mut(); match channel_phase { - ChannelPhase::UnfundedInboundV2(_) | - ChannelPhase::UnfundedOutboundV2(_) | - // This is neeed when Channel is not changed during splicing, and interactive - // transaction is constructed on the Funded channel. TODO: remove if new - // unfunded channel instance is used during splicing - ChannelPhase::Funded(_) => { + ChannelPhase::UnfundedInboundV2(_) | ChannelPhase::UnfundedOutboundV2(_) => { let result = channel_phase.context_mut().tx_complete(msg); match result { Ok((tx_msg_opt, signing_session_opt)) => { @@ -7160,16 +7140,6 @@ where } ) }, - // This is neeed when Channel is not changed during splicing, and interactive - // transaction is constructed on the Funded channel. TODO: remove if new - // unfunded channel instance is used during splicing - ChannelPhase::Funded(chan) => { - chan.funding_tx_constructed(counterparty_node_id, signing_session, &self.signer_provider, &self.logger).map_err( - |(chan, err)| { - (ChannelPhase::Funded(chan), err) - } - ) - }, _ => { todo!(); }, @@ -7373,7 +7343,7 @@ where } // Update the monitor with the shutdown script if necessary. if let Some(monitor_update) = monitor_update_opt { - handle_new_monitor_update!(self, funding_txo_opt.unwrap(), monitor_update, + handle_new_monitor_update!(self, funding_txo_opt.unwrap(), chan.context.channel_id, monitor_update, peer_state_lock, peer_state, per_peer_state, chan); } }, @@ -7685,7 +7655,7 @@ where } else { let monitor_update_opt = try_chan_phase_entry!(self, chan.commitment_signed(&msg, &self.logger), chan_phase_entry); if let Some(monitor_update) = monitor_update_opt { - handle_new_monitor_update!(self, funding_txo.unwrap(), monitor_update, peer_state_lock, + handle_new_monitor_update!(self, funding_txo.unwrap(), chan.context.channel_id(), monitor_update, peer_state_lock, peer_state, per_peer_state, chan); } } @@ -7700,8 +7670,8 @@ where } #[inline] - fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) { - for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards { + fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, ChannelId, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) { + for &mut (prev_short_channel_id, prev_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards { let mut push_forward_event = false; let mut new_intercept_events = VecDeque::new(); let mut failed_intercept_forwards = Vec::new(); @@ -7720,7 +7690,7 @@ where match forward_htlcs.entry(scid) { hash_map::Entry::Occupied(mut entry) => { entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })); + prev_short_channel_id, prev_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })); }, hash_map::Entry::Vacant(entry) => { if !is_our_scid && forward_info.incoming_amt_msat.is_some() && @@ -7738,7 +7708,7 @@ where intercept_id }, None)); entry.insert(PendingAddHTLCInfo { - prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }); + prev_short_channel_id, prev_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }); }, hash_map::Entry::Occupied(_) => { let logger = WithContext::from(&self.logger, None, Some(prev_funding_outpoint.to_channel_id())); @@ -7746,6 +7716,7 @@ where let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: prev_short_channel_id, user_channel_id: Some(prev_user_channel_id), + channel_id: prev_channel_id, outpoint: prev_funding_outpoint, htlc_id: prev_htlc_id, incoming_packet_shared_secret: forward_info.incoming_shared_secret, @@ -7766,7 +7737,7 @@ where push_forward_event = true; } entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }))); + prev_short_channel_id, prev_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }))); } } } @@ -7863,7 +7834,7 @@ where if let Some(monitor_update) = monitor_update_opt { let funding_txo = funding_txo_opt .expect("Funding outpoint must have been set for RAA handling to succeed"); - handle_new_monitor_update!(self, funding_txo, monitor_update, + handle_new_monitor_update!(self, funding_txo, chan.context.channe_id(), monitor_update, peer_state_lock, peer_state, per_peer_state, chan); } htlcs_to_fail @@ -8105,13 +8076,6 @@ where // TODO checks // TODO check if we accept splicing, quiscence - /* - // Get the number of peers with channels, but without funded ones. We don't care too much - // about peers that never open a channel, so we filter by peers that have at least one - // channel, and then limit the number of those with unfunded channels. - let _channeled_peers_without_funding = self.peers_without_funded_channels(|node| !node.channel_by_id.is_empty()); - */ - let per_peer_state = self.per_peer_state.read().unwrap(); let peer_state_mutex = per_peer_state.get(counterparty_node_id) .ok_or_else(|| { @@ -8121,48 +8085,108 @@ where let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; - match peer_state.channel_by_id.entry(msg.channel_id) { - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id)), + // Look for channel + let post_channel_value = match peer_state.channel_by_id.entry(msg.channel_id) { + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}, channel_id {}", counterparty_node_id, msg.channel_id), msg.channel_id)), + hash_map::Entry::Occupied(chan_entry) => { + if let ChannelPhase::Funded(chan) = chan_entry.get() { + if msg.relative_satoshis < 0 { + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Splice/out not supported, only splice in, relative {}", -msg.relative_satoshis), msg.channel_id)); + } + + let pre_channel_value = chan.context.get_value_satoshis(); + // TODO check for i64 overflow + if msg.relative_satoshis < 0 && -msg.relative_satoshis > (pre_channel_value as i64) { + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Post-splicing channel value cannot be negative. It was {} - {}", pre_channel_value, -msg.relative_satoshis), msg.channel_id)); + } + + let post_channel_value = PendingSpliceInfoPre::add_checked(pre_channel_value, msg.relative_satoshis); + if post_channel_value < 1000 { + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Post-splicing channel value must be at least 1000 satoshis. It was {}", post_channel_value), msg.channel_id)); + } + + // Check if a splice has been initiated already. Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways + if chan.context.pending_splice_pre.is_some() { + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Channel has already a splice pending, channel id {}", msg.channel_id), msg.channel_id)); + } + + // Check channel id + let post_splice_v2_channel_id = chan.context.generate_v2_channel_id_from_revocation_basepoints(); + if post_splice_v2_channel_id != chan.context.channel_id() { + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Channel ID would change during splicing (e.g. splice on V1 channel), not yet supported, channel id {} {}", + chan.context.channel_id(), post_splice_v2_channel_id), msg.channel_id)); + } + + post_channel_value + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel in wrong state".to_owned(), msg.channel_id.clone())); + } + }, + }; + + // Change channel, phase changes, remove and add + // Remove the pre channel + let prev_chan = match peer_state.channel_by_id.remove(&msg.channel_id) { + None => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}, channel_id {}", counterparty_node_id, msg.channel_id), msg.channel_id)), + Some(chan_phase) => { + if let ChannelPhase::Funded(chan) = chan_phase { + chan + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel in wrong state".to_owned(), msg.channel_id.clone())); + } + } + }; + + let post_chan = InboundV2Channel { + context: prev_chan.context, + unfunded_context: UnfundedChannelContext::default(), + dual_funding_context: DualFundingChannelContext { + our_funding_satoshis: 0, + their_funding_satoshis: post_channel_value, // msg.relative_satoshis as u64, + funding_tx_locktime: LockTime::from_consensus(msg.locktime), + funding_feerate_sat_per_1000_weight: msg.funding_feerate_perkw, + }, + }; + + // Add the modified channel + let post_chan_id = post_chan.context.channel_id(); + peer_state.channel_by_id.insert(post_chan_id, ChannelPhase::UnfundedInboundV2(post_chan)); + + // Perform state changes + match peer_state.channel_by_id.entry(post_chan_id) { + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)), hash_map::Entry::Occupied(mut chan_entry) => { - if let ChannelPhase::Funded(chan) = chan_entry.get_mut() { - // Store post-splicing channel value (pending) - // TODO check if there is a pending already - chan.context.pending_splice = Some(PendingSpliceInfo::new(msg.relative_satoshis, chan.context.get_value_satoshis(), false)); - chan.dual_funding_channel_context = Some(DualFundingChannelContext { - our_funding_satoshis: 0, - their_funding_satoshis: 0, - funding_tx_locktime: LockTime::ZERO, // TODO - funding_feerate_sat_per_1000_weight: msg.funding_feerate_perkw, - }); + if let ChannelPhase::UnfundedInboundV2(post_chan) = chan_entry.get_mut() { + let pre_channel_value = post_chan.context.get_value_satoshis(); - let new_msg = chan.get_splice_ack(self.chain_hash.clone(), msg.relative_satoshis); + post_chan.context.pending_splice_pre = Some(PendingSpliceInfoPre::new(msg.relative_satoshis, pre_channel_value, Some(post_chan_id), msg.funding_feerate_perkw, msg.locktime)); + + // Apply start of splice changed in the state (update state, capacity funding tx, ...) + post_chan.context.splice_start(false, msg.relative_satoshis, &self.logger) + .map_err(|ce| MsgHandleErrInternal::send_err_msg_no_close(ce.to_string(), msg.channel_id.clone()))?; + + let new_msg = post_chan.get_splice_ack(self.chain_hash.clone()).unwrap(); // TODO error peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceAck { node_id: *counterparty_node_id, msg: new_msg, }); - let _msg = chan.begin_interactive_funding_tx_construction( + let _msg = post_chan.begin_interactive_funding_tx_construction( &self.signer_provider, &self.entropy_source, self.get_our_node_id(), - false, - true, + // false, Vec::new(), - &self.logger, - ).map_err(|e0| MsgHandleErrInternal::send_err_msg_no_close( - format!("Failed to start interactive transaction construction, {:?}", e0), msg.channel_id + ).map_err(|e| MsgHandleErrInternal::send_err_msg_no_close( + format!("Failed to start interactive transaction construction, {:?}", e), msg.channel_id ))?; - - // Commit to post-splice parameters prematurely, to have right values for commitment building - // TODO: commitment should happen only upon confirmation - let _ = chan.context.commit_pending_splice(&self.logger).unwrap(); - - Ok(()) } else { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel in wrong state".to_owned(), msg.channel_id.clone())); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)); } - }, + } } + + Ok(()) } // #SPLICING STEP5 I @@ -8177,49 +8201,96 @@ where // Note that the ChannelManager is NOT re-persisted on disk after this, so any changes are // likely to be lost on restart! - let (pre_channel_value_satoshis, post_channel_value_satoshis, holder_funding_satoshis, counterparty_funding_satoshis) = { - let per_peer_state = self.per_peer_state.read().unwrap(); - let peer_state_mutex = per_peer_state.get(counterparty_node_id) - .ok_or_else(|| { - debug_assert!(false); - MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id.clone()) - })?; - let mut peer_state_lock = peer_state_mutex.lock().unwrap(); - let peer_state = &mut *peer_state_lock; + let per_peer_state = self.per_peer_state.read().unwrap(); + let peer_state_mutex = per_peer_state.get(counterparty_node_id) + .ok_or_else(|| { + debug_assert!(false); + MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id.clone()) + })?; + let mut peer_state_lock = peer_state_mutex.lock().unwrap(); + let peer_state = &mut *peer_state_lock; - match peer_state.channel_by_id.entry(msg.channel_id) { - hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id)), - hash_map::Entry::Occupied(mut chan) => { - if let ChannelPhase::Funded(chan) = chan.get_mut() { - if let Some(pending_splice) = &chan.context.pending_splice { - ( - pending_splice.pre_channel_value, - pending_splice.post_channel_value, - pending_splice.post_channel_value, - 0, - // chan.context.channel_transaction_parameters.funding_outpoint.unwrap().into_bitcoin_outpoint(), - // chan.context.get_funding_redeemscript().to_v0_p2wsh(), // TODO check wether this is correct, correct keys used (from splice negot, and not from pre) - ) - } else { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel has no pending splice".to_owned(), msg.channel_id.clone())); + // Look for channel + let pending_splice = match peer_state.channel_by_id.entry(msg.channel_id) { + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id)), + hash_map::Entry::Occupied(chan) => { + if let ChannelPhase::Funded(chan) = chan.get() { + // check if splice is pending + if let Some(pending_splice) = &chan.context.pending_splice_pre { + // Check the splice_ack parameters (relative amnt) match saved splice parematers + let pending_relative = pending_splice.relative_satoshis(chan.context.get_value_satoshis()); + if msg.relative_satoshis != pending_relative { + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("The splice_ack parameters do not match the pending splice parameters. {} vs. {}", msg.relative_satoshis, pending_relative), msg.channel_id.clone())); } + pending_splice.clone() } else { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel in wrong state".to_owned(), msg.channel_id.clone())); + return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not in pending splice".to_owned(), msg.channel_id.clone())); } - }, + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel in wrong state".to_owned(), msg.channel_id.clone())); + } + }, + }; + + // Change channel, phase changes, remove and add + // Remove the pre channel + let prev_chan = match peer_state.channel_by_id.remove(&msg.channel_id) { + None => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}, channel_id {}", counterparty_node_id, msg.channel_id), msg.channel_id)), + Some(chan_phase) => { + if let ChannelPhase::Funded(chan) = chan_phase { + chan + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel in wrong state".to_owned(), msg.channel_id.clone())); + } } }; - // Prepare SpliceAckedInputsContributionReady event - let mut pending_events = self.pending_events.lock().unwrap(); - pending_events.push_back((events::Event::SpliceAckedInputsContributionReady { - channel_id: msg.channel_id, - counterparty_node_id: *counterparty_node_id, - pre_channel_value_satoshis, - post_channel_value_satoshis, - holder_funding_satoshis, - counterparty_funding_satoshis, - } , None)); + let post_chan = OutboundV2Channel { + context: prev_chan.context, + unfunded_context: UnfundedChannelContext::default(), + dual_funding_context: DualFundingChannelContext { + our_funding_satoshis: pending_splice.post_channel_value, // msg.relative_satoshis as u64, + their_funding_satoshis: 0, + funding_tx_locktime: LockTime::from_consensus(pending_splice.locktime), + funding_feerate_sat_per_1000_weight: pending_splice.funding_feerate_perkw, + }, + }; + + // Add the modified channel + let post_chan_id = post_chan.context.channel_id(); + peer_state.channel_by_id.insert(post_chan_id, ChannelPhase::UnfundedOutboundV2(post_chan)); + + // Perform state changes + match peer_state.channel_by_id.entry(post_chan_id) { + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)), + hash_map::Entry::Occupied(mut chan_entry) => { + if let ChannelPhase::UnfundedOutboundV2(post_chan) = chan_entry.get_mut() { + let pre_channel_value = post_chan.context.get_value_satoshis(); + let post_channel_value = PendingSpliceInfoPre::add_checked(pre_channel_value, msg.relative_satoshis); + + // Update pre-splice info with the new channel ID of the post channel + post_chan.context.pending_splice_pre.as_mut().unwrap().post_channel_id = Some(post_chan_id); + + // Apply start of splice changed in the state (update state, capacity funding tx, ...) + post_chan.context.splice_start(true, msg.relative_satoshis, &self.logger) + .map_err(|ce| MsgHandleErrInternal::send_err_msg_no_close(ce.to_string(), post_chan_id))?; + + // Prepare SpliceAckedInputsContributionReady event + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back((events::Event::SpliceAckedInputsContributionReady { + channel_id: post_chan_id, + counterparty_node_id: *counterparty_node_id, + pre_channel_value_satoshis: pre_channel_value, + post_channel_value_satoshis: post_channel_value, + holder_funding_satoshis: post_channel_value, + counterparty_funding_satoshis: 0, + } , None)); + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)); + } + } + } + Ok(()) } @@ -8334,7 +8405,7 @@ where if let Some(monitor_update) = monitor_opt { has_monitor_update = true; - handle_new_monitor_update!(self, funding_txo.unwrap(), monitor_update, + handle_new_monitor_update!(self, funding_txo.unwrap(), chan.context.channel_id(), monitor_update, peer_state_lock, peer_state, per_peer_state, chan); continue 'peer_loop; } @@ -8394,7 +8465,7 @@ where }); } } - ChannelPhase::UnfundedOutboundV2(chan) => { + ChannelPhase::UnfundedOutboundV2(_chan) => { todo!("dual_funding"); } ChannelPhase::UnfundedInboundV1(_) | @@ -9069,7 +9140,7 @@ where if let Some((monitor_update, further_update_exists)) = chan.unblock_next_blocked_monitor_update() { log_debug!(logger, "Unlocking monitor updating for channel {} and updating monitor", channel_funding_outpoint.to_channel_id()); - handle_new_monitor_update!(self, channel_funding_outpoint, monitor_update, + handle_new_monitor_update!(self, channel_funding_outpoint, channel_funding_outpoint.to_channel_id(), monitor_update, peer_state_lck, peer_state, per_peer_state, chan); if further_update_exists { // If there are more `ChannelMonitorUpdate`s to process, restart at the @@ -9496,6 +9567,7 @@ where htlc_id: htlc.prev_htlc_id, incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret, phantom_shared_secret: None, + channel_id: htlc.prev_channel_id, outpoint: htlc.prev_funding_outpoint, blinded_failure: htlc.forward_info.routing.blinded_failure(), }); @@ -10641,6 +10713,9 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, { (4, htlc_id, required), (6, incoming_packet_shared_secret, required), (7, user_channel_id, option), + // Note that by the time we get past the required read for type 2 above, outpoint will be + // filled in, so we can safely unwrap it here. + (9, channel_id, (default_value, ChannelId::v1_from_funding_outpoint(outpoint.0.unwrap()))), }); impl Writeable for ClaimableHTLC { @@ -10792,6 +10867,9 @@ impl_writeable_tlv_based!(PendingAddHTLCInfo, { (2, prev_short_channel_id, required), (4, prev_htlc_id, required), (6, prev_funding_outpoint, required), + // Note that by the time we get past the required read for type 6 above, prev_funding_outpoint will be + // filled in, so we can safely unwrap it here. + (7, prev_channel_id, (default_value, ChannelId::v1_from_funding_outpoint(prev_funding_outpoint.0.unwrap()))), }); impl_writeable_tlv_based_enum!(HTLCForwardInfo, @@ -11660,7 +11738,7 @@ where // 0.0.102+ for (_, monitor) in args.channel_monitors.iter() { let counterparty_opt = id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()); - let chan_id = monitor.get_funding_txo().0.to_channel_id(); + let _chan_id = monitor.get_funding_txo().0.to_channel_id(); if counterparty_opt.is_none() { let logger = WithChannelMonitor::from(&args.logger, monitor); for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() { @@ -11952,7 +12030,7 @@ where // this channel as well. On the flip side, there's no harm in restarting // without the new monitor persisted - we'll end up right back here on // restart. - let previous_channel_id = claimable_htlc.prev_hop.outpoint.to_channel_id(); + let previous_channel_id = claimable_htlc.prev_hop.channel_id; if let Some(peer_node_id) = id_to_peer.get(&previous_channel_id){ let peer_state_mutex = per_peer_state.get(peer_node_id).unwrap(); let mut peer_state_lock = peer_state_mutex.lock().unwrap(); diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index ce23546a998..2594068f671 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -35,9 +35,9 @@ use crate::util::test_utils::{self, WatchtowerPersister}; use crate::util::errors::APIError; use crate::util::ser::{Writeable, ReadableArgs}; use crate::util::string::UntrustedString; -use crate::util::config::{ChannelHandshakeConfig, UserConfig, MaxDustHTLCExposure}; +use crate::util::config::{UserConfig, MaxDustHTLCExposure}; -use bitcoin::hash_types::{BlockHash, Txid}; +use bitcoin::hash_types::BlockHash; use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::blockdata::script::{Builder, ScriptBuf}; use bitcoin::blockdata::opcodes; @@ -46,11 +46,8 @@ use bitcoin::network::constants::Network; use bitcoin::{Sequence, Transaction, TxIn, TxOut, Witness}; use bitcoin::OutPoint as BitcoinOutPoint; -use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; -use bitcoin::secp256k1::ecdsa::Signature; -use bitcoin::sighash::{EcdsaSighashType, SighashCache}; +use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; -use hex::DisplayHex; use regex; use crate::io; @@ -66,1178 +63,6 @@ use crate::ln::chan_utils::CommitmentTransaction; use super::channel::UNFUNDED_CHANNEL_AGE_LIMIT_TICKS; -// Create a 2-of-2 multisig redeem script. Return the script, and the two keys in the order they appear in the script. -fn create_multisig_redeem_script(key1: &PublicKey, key2: &PublicKey) -> (ScriptBuf, PublicKey, PublicKey) { - let (smaller_key, larger_key) = if key1.serialize() < key2.serialize() { - (key1, key2) - } else { - (key2, key1) - }; - let script = Builder::new() - .push_opcode(opcodes::all::OP_PUSHNUM_2) - .push_slice(&smaller_key.serialize()) - .push_slice(&larger_key.serialize()) - .push_opcode(opcodes::all::OP_PUSHNUM_2) - .push_opcode(opcodes::all::OP_CHECKMULTISIG) - .into_script(); - (script, smaller_key.clone(), larger_key.clone()) -} - -// Create an output script for a 2-of-2 multisig. -fn create_multisig_output_script(key1: &PublicKey, key2: &PublicKey) -> ScriptBuf { - let (redeem_script, _k1, _k2) = create_multisig_redeem_script(key1, key2); - Builder::new() - .push_opcode(opcodes::all::OP_PUSHBYTES_0) - .push_slice(&AsRef::<[u8; 32]>::as_ref(&redeem_script.wscript_hash())) - .into_script() -} - -// Verify a 2-of-2 multisig redeem script. Return the same keys, but in the order as they appear in the script -fn verify_multisig_redeem_script(script: &Vec, exp_key_1: &PublicKey, exp_key_2: &PublicKey) -> (PublicKey, PublicKey) { - let (exp_script,exp_smaller_key, exp_larger_key) = create_multisig_redeem_script(exp_key_1, exp_key_2); - assert_eq!(script.as_hex().to_string(), exp_script.as_bytes().as_hex().to_string()); - (exp_smaller_key, exp_larger_key) -} - -// Verify a 2-of-2 multisig output script. -fn verify_multisig_output_script(script: &ScriptBuf, exp_key_1: &PublicKey, exp_key_2: &PublicKey) { - let exp_script = create_multisig_output_script(exp_key_1, exp_key_2); - assert_eq!(script.to_hex_string(), exp_script.to_hex_string()); -} - -// Get the funding key of a node towards another node -fn get_funding_key(node: &Node, counterparty_node: &Node, channel_id: &ChannelId) -> PublicKey { - let per_peer_state = node.node.per_peer_state.read().unwrap(); - let chan_lock = per_peer_state.get(&counterparty_node.node.get_our_node_id()).unwrap().lock().unwrap(); - let local_chan = chan_lock.channel_by_id.get(&channel_id).map( - |phase| if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None } - ).flatten().unwrap(); - local_chan.get_signer().as_ref().pubkeys().funding_pubkey -} - -/// Verify the funding output of a funding tx -fn verify_funding_output(funding_txo: &TxOut, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { - let act_script = &funding_txo.script_pubkey; - verify_multisig_output_script(&act_script, funding_key_1, funding_key_2); -} - -/// Do checks on a funding tx -fn verify_funding_tx(funding_tx: &Transaction, value: u64, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { - // find the output with the given value - let mut funding_output_opt: Option<&TxOut> = None; - for o in &funding_tx.output { - if o.value == value { - funding_output_opt = Some(o); - } - } - if funding_output_opt.is_none() { - panic!("Funding output not found, no output with value {}", value); - } - verify_funding_output(funding_output_opt.unwrap(), funding_key_1, funding_key_2) -} - -/// Simple end-to-end open channel flow, with close, and verification checks. -/// The steps are mostly on ChannelManager level. -#[test] -fn test_channel_open_and_close() { - // Set up a network of 2 nodes - let cfg = UserConfig { - channel_handshake_config: ChannelHandshakeConfig { - announced_channel: true, - ..Default::default() - }, - ..Default::default() - }; - let chanmon_cfgs = create_chanmon_cfgs(2); - let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. - let initiator_node_index = 0; - let initiator_node = &nodes[initiator_node_index]; - let acceptor_node = &nodes[1]; - - // Instantiate channel parameters where we push the maximum msats given our funding satoshis - let channel_value_sat = 100000; // same as funding satoshis - let push_msat = 0; - - // Have node0 initiate a channel to node1 with aforementioned parameters - let _res = initiator_node.node.create_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, push_msat, 42, None, None).unwrap(); - - // Extract the channel open message from node0 to node1 - let open_channel_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannel, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_open_channel(&initiator_node.node.get_our_node_id(), &open_channel_message.clone()); - // Extract the accept channel message from node1 to node0 - let accept_channel_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannel, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_accept_channel(&acceptor_node.node.get_our_node_id(), &accept_channel_message.clone()); - // Note: FundingGenerationReady emitted, checked and used below - let (temporary_channel_id, funding_tx, _funding_output) = create_funding_transaction(&initiator_node, &acceptor_node.node.get_our_node_id(), channel_value_sat, 42); - assert_eq!(funding_tx.encode().len(), 55); - - // Funding transation created, provide it - let _res = initiator_node.node.funding_transaction_generated(&temporary_channel_id, &acceptor_node.node.get_our_node_id(), funding_tx.clone()).unwrap(); - - let funding_created_message = get_event_msg!(initiator_node, MessageSendEvent::SendFundingCreated, acceptor_node.node.get_our_node_id()); - let _res = acceptor_node.node.handle_funding_created(&initiator_node.node.get_our_node_id(), &funding_created_message); - - let funding_signed_message = get_event_msg!(acceptor_node, MessageSendEvent::SendFundingSigned, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_funding_signed(&acceptor_node.node.get_our_node_id(), &funding_signed_message); - // Take new channel ID - let channel_id = funding_signed_message.channel_id; - - // Check that funding transaction has been broadcasted - assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); - assert_eq!(broadcasted_funding_tx.encode().len(), 55); - assert_eq!(broadcasted_funding_tx.txid(), funding_tx.txid()); - assert_eq!(broadcasted_funding_tx.encode(), funding_tx.encode()); - assert_eq!(&broadcasted_funding_tx.encode().as_hex().to_string(), - "0000000000010001a086010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3a00000000"); - - check_added_monitors!(initiator_node, 1); - let _ev = get_event!(initiator_node, Event::ChannelPending); - check_added_monitors!(acceptor_node, 1); - let _ev = get_event!(acceptor_node, Event::ChannelPending); - - // Simulate confirmation of the funding tx - confirm_transaction(&initiator_node, &broadcasted_funding_tx); - let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); - - confirm_transaction(&acceptor_node, &broadcasted_funding_tx); - let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); - let _ev = get_event!(acceptor_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); - let _ev = get_event!(initiator_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); - - // check channel capacity and other parameters - assert_eq!(initiator_node.node.list_channels().len(), 1); - let channel = &initiator_node.node.list_channels()[0]; - { - assert!(channel.is_usable); - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 100000000 - 1000000); - assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - // do checks on the acceptor node as well (capacity, etc.) - assert_eq!(acceptor_node.node.list_channels().len(), 1); - { - let channel = &acceptor_node.node.list_channels()[0]; - assert!(channel.is_usable); - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 0); - assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // Verify the funding transaction - let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel.channel_id); - let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel.channel_id); - - verify_funding_tx(&broadcasted_funding_tx, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); - - // Channel is ready now for normal operation - - // close channel, cooperatively - initiator_node.node.close_channel(&channel_id, &acceptor_node.node.get_our_node_id()).unwrap(); - let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); - acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); - let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); - initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); - let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); -} - -/// End-to-end V2 open channel flow, with close, and verification checks. -/// The steps are mostly on ChannelManager level. -#[cfg(dual_funding)] -#[test] -fn test_channel_open_v2_and_close() { - // Set up a network of 2 nodes - let cfg = UserConfig { - channel_handshake_config: ChannelHandshakeConfig { - announced_channel: true, - ..Default::default() - }, - ..Default::default() - }; - let chanmon_cfgs = create_chanmon_cfgs(2); - let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. - let initiator_node_index = 0; - let acceptor_node_index = 1; - let initiator_node = &nodes[initiator_node_index]; - let acceptor_node = &nodes[acceptor_node_index]; - - // Instantiate channel parameters where we push the maximum msats given our funding satoshis - let channel_value_sat = 100000; // same as funding satoshis - - // Have node0 initiate a channel to node1 with aforementioned parameters - let _res = initiator_node.node.create_dual_funded_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, None, 42, None).unwrap(); - - // Extract the channel open message from node0 to node1 - let open_channel2_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannelV2, acceptor_node.node.get_our_node_id()); - assert_eq!(initiator_node.node.list_channels().len(), 1); - - let _res = acceptor_node.node.handle_open_channel_v2(&initiator_node.node.get_our_node_id(), &open_channel2_message.clone()); - // Extract the accept channel message from node1 to node0 - let accept_channel2_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannelV2, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_accept_channel_v2(&acceptor_node.node.get_our_node_id(), &accept_channel2_message.clone()); - - // Note: FundingInputsContributionReady emitted - let events = initiator_node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - let channel_id = match events[0] { - Event::FundingInputsContributionReady { channel_id, counterparty_node_id, .. } => { - assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); - channel_id - } - _ => panic!("FundingInputsContributionReady event missing {:?}", events[0]), - }; - let custom_input_secret_key = SecretKey::from_slice(&[2; 32]).unwrap(); - let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, channel_value_sat * 2, &PublicKey::from_secret_key(&Secp256k1::new(), &custom_input_secret_key))]; - let _res = initiator_node.node.contribute_funding_inputs(&channel_id, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); - - // initiator_node will generate a TxAddInput message to kickstart the interactive transaction construction protocol - let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - - // First output we send is the change output. - let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); - assert!(tx_add_output_msg.script.is_v0_p2wpkh()); - - let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); - let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); - - // Check we get the channel funding output. - assert!(tx_add_output_msg.script.is_v0_p2wsh()); - assert_eq!(tx_add_output_msg.sats, channel_value_sat); - - let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert_eq!(msg_events.len(), 2); - let tx_complete_msg = match msg_events[0] { - MessageSendEvent::SendTxComplete { ref node_id, ref msg } => { - assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); - (*msg).clone() - }, - _ => panic!("Unexpected event"), - }; - let msg_commitment_signed_from_0 = match msg_events[1] { - MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { - assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); - updates.commitment_signed.clone() - }, - _ => panic!("Unexpected event"), - }; - if let Event::FundingTransactionReadyForSigning { - channel_id, - counterparty_node_id, - mut unsigned_transaction, - .. - } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { - assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); - - let mut witness = Witness::new(); - witness.push(vec![0]); - unsigned_transaction.input[0].witness = witness; - - let _res = initiator_node.node.funding_transaction_signed(&channel_id, &counterparty_node_id, unsigned_transaction).unwrap(); - } else { panic!(); } - - let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); - let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); - // First messsage is commitment_signed, second is tx_signatures (see below for more) - assert_eq!(msg_events.len(), 1); - let msg_commitment_signed_from_1 = match msg_events[0] { - MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { - assert_eq!(*node_id, initiator_node.node.get_our_node_id()); - updates.commitment_signed.clone() - }, - _ => panic!("Unexpected event"), - }; - - // Handle the initial commitment_signed exchange. Order is not important here. - acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); - initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); - check_added_monitors(&initiator_node, 1); - check_added_monitors(&acceptor_node, 1); - - // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures - // only after receiving tx_signatures from the non-initiator in this case. - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert!(msg_events.is_empty()); - let tx_signatures_from_1 = get_event_msg!(acceptor_node, MessageSendEvent::SendTxSignatures, nodes[0].node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_from_1); - let events_0 = initiator_node.node.get_and_clear_pending_events(); - assert_eq!(events_0.len(), 1); - match events_0[0] { - Event::ChannelPending{ ref counterparty_node_id, .. } => { - assert_eq!(*counterparty_node_id, acceptor_node.node.get_our_node_id()); - }, - _ => panic!("Unexpected event"), - } - let tx_signatures_from_0 = get_event_msg!(initiator_node, MessageSendEvent::SendTxSignatures, nodes[1].node.get_our_node_id()); - let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_from_0); - let events_1 = acceptor_node.node.get_and_clear_pending_events(); - assert_eq!(events_1.len(), 1); - match events_1[0] { - Event::ChannelPending{ ref counterparty_node_id, .. } => { - assert_eq!(*counterparty_node_id, initiator_node.node.get_our_node_id()); - }, - _ => panic!("Unexpected event"), - } - - // Check that funding transaction has been broadcasted - assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); - let expected_funding_tx = "02000000000101642e738998d812934e2848b409068a836bdd15f561b493ae58be53d286db2558000000000000000000021f86010000000000160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665fa086010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3a01010000000000"; - assert_eq!(broadcasted_funding_tx.encode().len(), 130); - assert_eq!(&broadcasted_funding_tx.encode().as_hex().to_string(), expected_funding_tx); - // Check that funding transaction has been broadcasted on the acceptor side as well - assert_eq!(chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - let broadcasted_funding_tx_acc = chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); - assert_eq!(broadcasted_funding_tx_acc.encode().len(), 130); - assert_eq!(&broadcasted_funding_tx_acc.encode().as_hex().to_string(), expected_funding_tx); - - // Simulate confirmation of the funding tx - confirm_transaction(&initiator_node, &broadcasted_funding_tx); - let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); - - confirm_transaction(&acceptor_node, &broadcasted_funding_tx); - let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); - let _ev = get_event!(acceptor_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); - let _ev = get_event!(initiator_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); - - // check channel capacity and other parameters - let reserve = 1000000; - assert_eq!(initiator_node.node.list_channels().len(), 1); - let channel = &initiator_node.node.list_channels()[0]; - { - assert!(channel.is_usable); - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 100000000 - reserve); - assert_eq!(channel.confirmations.unwrap(), 10); - } - // do checks on the acceptor node as well (capacity, etc.) - assert_eq!(acceptor_node.node.list_channels().len(), 1); - { - let channel = &acceptor_node.node.list_channels()[0]; - assert!(channel.is_usable); - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 0); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // Verify the funding transaction - let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel.channel_id); - let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel.channel_id); - - verify_funding_tx(&broadcasted_funding_tx, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); - - // Channel is ready now for normal operation - - // close channel, cooperatively - initiator_node.node.close_channel(&channel_id, &acceptor_node.node.get_our_node_id()).unwrap(); - let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); - acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); - let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); - initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); - let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); -} - -fn verify_signature(msg: &Vec, sig: &Vec, pubkey: &PublicKey) -> Result<(), String> { - let m = Message::from_slice(&msg).unwrap(); - let s = Signature::from_der(&sig).unwrap(); - let ctx = Secp256k1::new(); - match ctx.verify_ecdsa(&m, &s, &pubkey) { - Ok(_) => Ok(()), - Err(e) => Err(format!("Signature verification failed! err {} msg {} sig {} pk {}", e, &msg.as_hex(), &sig.as_hex(), &pubkey.serialize().as_hex())), - } -} - -/// #SPLICING -/// Verify the previous funding input on a splicing funding transaction -fn verify_splice_funding_input(splice_tx: &Transaction, prev_funding_txid: &Txid, prev_funding_value: u64, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { - // check that the previous funding tx is an input - let mut prev_fund_input_idx: Option = None; - for idx in 0..splice_tx.input.len() { - if splice_tx.input[idx].previous_output.txid == *prev_funding_txid { - prev_fund_input_idx = Some(idx); - } - } - if prev_fund_input_idx.is_none() { - panic!("Splice tx should contain the pervious funding tx as input! {} {}", prev_funding_txid, splice_tx.encode().as_hex()); - } - let prev_fund_input = &splice_tx.input[prev_fund_input_idx.unwrap()]; - let witness = &prev_fund_input.witness.to_vec(); - let witness_count = witness.len(); - let expected_witness_count = 4; - if witness_count != expected_witness_count { - panic!("Prev funding tx input should have {} witness elements! {} {}", expected_witness_count, witness_count, prev_fund_input_idx.unwrap()); - } - if witness[0].len() != 0 { - panic!("First multisig witness should be empty! {}", witness[0].len()); - } - // check witness 1&2, signatures - let wit1_sig = &witness[1]; - let wit2_sig = &witness[2]; - if wit1_sig.len() < 70 || wit1_sig.len() > 72 || wit2_sig.len() < 70 || wit2_sig.len() > 72 { - panic!("Witness entries 2&3 should be signatures! {} {}", wit1_sig.as_hex(), wit2_sig.as_hex()); - } - if wit1_sig[wit1_sig.len()-1] != 1 || wit2_sig[wit2_sig.len()-1] != 1 { - panic!("Witness entries 2&3 should be signatures with SIGHASHALL! {} {}", wit1_sig.as_hex(), wit2_sig.as_hex()); - } - let (script_key1, script_key2) = verify_multisig_redeem_script(&witness[3], funding_key_1, funding_key_2); - let redeemscript = ScriptBuf::from(witness[3].to_vec()); - // check signatures, sigs are in same order as keys - let sighash = &SighashCache::new(splice_tx).segwit_signature_hash(prev_fund_input_idx.unwrap(), &redeemscript, prev_funding_value, EcdsaSighashType::All).unwrap()[..].to_vec(); - let sig1 = wit1_sig[0..(wit1_sig.len()-1)].to_vec(); - let sig2 = wit2_sig[0..(wit2_sig.len()-1)].to_vec(); - if let Err(e1) = verify_signature(sighash, &sig1, &script_key1) { - panic!("Sig 1 check fails {}", e1); - } - if let Err(e2) = verify_signature(sighash, &sig2, &script_key2) { - panic!("Sig 2 check fails {}", e2); - } -} - -/// #SPLICING -/// Do checks on a splice funding tx -fn verify_splice_funding_tx(splice_tx: &Transaction, prev_funding_txid: &Txid, funding_value: u64, prev_funding_value: u64, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { - verify_splice_funding_input(splice_tx, prev_funding_txid, prev_funding_value, funding_key_1, funding_key_2); - verify_funding_tx(splice_tx, funding_value, funding_key_1, funding_key_2); -} - -/* -// This test has been disabled, splice is not dual funding-based, see test_v2_splice_in() -/// #SPLICING Builds on test_channel_open_simple() -/// Splicing test, simple splice-in flow. Starts with opening a channel first. -#[test] -fn test_splice_in_simple() { - // Set up a network of 2 nodes - let cfg = UserConfig { - channel_handshake_config: ChannelHandshakeConfig { - announced_channel: true, - ..Default::default() - }, - ..Default::default() - }; - let chanmon_cfgs = create_chanmon_cfgs(2); - let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - // Initiator and Acceptor nodes - let initiator_node_index = 1; - let initiator_node = &nodes[initiator_node_index]; - let acceptor_node = &nodes[0]; - - // Instantiate channel parameters where we push the maximum msats given our funding satoshis - let channel_value_sat = 100000; // same as funding satoshis - let push_msat = 0; - - // Have node0 initiate a channel to node1 with aforementioned parameters - let _res = initiator_node.node.create_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, push_msat, 42, None, None).unwrap(); - - // Extract the channel open message from node0 to node1 - let open_channel_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannel, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_open_channel(&initiator_node.node.get_our_node_id(), &open_channel_message.clone()); - // Extract the accept channel message from node1 to node0 - let accept_channel_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannel, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_accept_channel(&acceptor_node.node.get_our_node_id(), &accept_channel_message.clone()); - // Note: FundingGenerationReady emitted, checked and used below - let (temporary_channel_id, funding_tx, _funding_output) = create_funding_transaction(&initiator_node, &acceptor_node.node.get_our_node_id(), channel_value_sat, 42); - - // Funding transation created, provide it - let _res = initiator_node.node.funding_transaction_generated(&temporary_channel_id, &acceptor_node.node.get_our_node_id(), funding_tx.clone()).unwrap(); - - let funding_created_message = get_event_msg!(initiator_node, MessageSendEvent::SendFundingCreated, acceptor_node.node.get_our_node_id()); - let _res = acceptor_node.node.handle_funding_created(&initiator_node.node.get_our_node_id(), &funding_created_message); - - let funding_signed_message = get_event_msg!(acceptor_node, MessageSendEvent::SendFundingSigned, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_funding_signed(&acceptor_node.node.get_our_node_id(), &funding_signed_message); - // Take new channel ID - let channel_id = funding_signed_message.channel_id; - - // Check that funding transaction has been broadcasted - assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); - - check_added_monitors!(initiator_node, 1); - let _ev = get_event!(initiator_node, Event::ChannelPending); - check_added_monitors!(acceptor_node, 1); - let _ev = get_event!(acceptor_node, Event::ChannelPending); - - // Simulate confirmation of the funding tx - confirm_transaction(&initiator_node, &broadcasted_funding_tx); - let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); - - confirm_transaction(&acceptor_node, &broadcasted_funding_tx); - let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); - let _ev = get_event!(acceptor_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); - let _ev = get_event!(initiator_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); - - // check channel capacity and other parameters - let reserve = 1000000; - assert_eq!(initiator_node.node.list_channels().len(), 1); - let channel = &initiator_node.node.list_channels()[0]; - { - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 100000000 - reserve); - assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - // do checks on the acceptor node as well (capacity, etc.) - assert_eq!(acceptor_node.node.list_channels().len(), 1); - { - let channel = &acceptor_node.node.list_channels()[0]; - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 0); - assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // Verify the funding transaction - let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel.channel_id); - let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel.channel_id); - - verify_funding_tx(&broadcasted_funding_tx, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); - // TODO value changed with dual_funding - // assert_eq!(&broadcasted_funding_tx.encode().as_hex().to_string(), - // "0000000000010001a08601000000000022002034c0cc0ad0dd5fe61dcf7ef58f995e3d34f8dbd24aa2a6fae68fefe102bf025c00000000"); - assert_eq!(&broadcasted_funding_tx.encode().as_hex().to_string(), - "0000000000010001a0860100000000002200201a786d17d588868dea8da15e1e2e2d76f208a8ece1a014497849fc56743968f000000000"); - - // // Although this condition only depends on the test nodes used (and their order), we verify it - // // here to make sure we test the case when the initiator funding pubkey is the 'larger', as this - // // is the more error prone case (e.g. signature order) - // TODO Taken out during dual_funding, ?, initiator key is the smaller even if init/acceptor indices (0/1) are reverted... - // assert!(initiator_funding_key.to_string() > acceptor_funding_key.to_string()); - - // Channel is ready now for normal operation - - // Start of Splicing ... - - // Amount being added to the channel through the splice-in - let splice_in_sats: u64 = 20000; - let post_splice_channel_value = channel_value_sat + splice_in_sats; - let funding_feerate_perkw = 1024; // TODO - let locktime = 0; // TODO - - // Initiate splice-in (on node0) - let _res = initiator_node.node.splice_channel(&channel_id, &acceptor_node.node.get_our_node_id(), splice_in_sats as i64, funding_feerate_perkw, locktime).unwrap(); - // Extract the splice message from node0 to node1 - let splice_message = get_event_msg!(initiator_node, MessageSendEvent::SendSplice, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_splice(&initiator_node.node.get_our_node_id(), &splice_message); - // Extract the splice_ack message from node1 to node0 - let splice_ack_message = get_event_msg!(acceptor_node, MessageSendEvent::SendSpliceAck, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_splice_ack(&acceptor_node.node.get_our_node_id(), &splice_ack_message); - // Note: SpliceAcked emitted - let events = initiator_node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - let (current_funding_outpoint, output_script) = match events[0] { - Event::SpliceAcked { channel_id: ref ch_id0, ref current_funding_outpoint, ref post_channel_value_satoshis, ref output_script, .. } => { - assert_eq!(*ch_id0, channel_id); - assert_eq!(*post_channel_value_satoshis, post_splice_channel_value); - (*current_funding_outpoint, (*output_script).clone()) - }, - _ => panic!("Unexpected event"), - }; - // Create splice tx - let version = *initiator_node.network_chan_count.borrow(); - let (splice_tx, _funding_output) = create_splice_in_transaction(current_funding_outpoint, post_splice_channel_value, output_script, version as i32); - assert_eq!(splice_tx.encode().len(), 94); - - // Splice transaction has been created, provide it - let _res = initiator_node.node.splice_transaction_generated(&channel_id, &acceptor_node.node.get_our_node_id(), splice_tx.clone()).unwrap(); - // Extract the splice_created message from node0 to node1 - let splice_created_message = get_event_msg!(initiator_node, MessageSendEvent::SendSpliceCreated, acceptor_node.node.get_our_node_id()); - let _res = acceptor_node.node.handle_splice_created(&initiator_node.node.get_our_node_id(), &splice_created_message); - - let tx_complete_message = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_tx_complete_splice(&acceptor_node.node.get_our_node_id(), &tx_complete_message); - - let splice_comm_signed_message = get_event_msg!(initiator_node, MessageSendEvent::SendSpliceCommSigned, acceptor_node.node.get_our_node_id()); - let _res = acceptor_node.node.handle_splice_comm_signed(&initiator_node.node.get_our_node_id(), &splice_comm_signed_message); - - let splice_comm_ack_message = get_event_msg!(acceptor_node, MessageSendEvent::SendSpliceCommAck, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_splice_comm_ack(&acceptor_node.node.get_our_node_id(), &splice_comm_ack_message); - - let splice_signed_message = get_event_msg!(initiator_node, MessageSendEvent::SendSpliceSigned, acceptor_node.node.get_our_node_id()); - let _res = acceptor_node.node.handle_splice_signed(&initiator_node.node.get_our_node_id(), &splice_signed_message); - - let splice_signed_ack_message = get_event_msg!(acceptor_node, MessageSendEvent::SendSpliceSignedAck, initiator_node.node.get_our_node_id()); - let _res = initiator_node.node.handle_splice_signed_ack(&acceptor_node.node.get_our_node_id(), &splice_signed_ack_message); - - // Check that signed splice funding transaction has been broadcasted - assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 2); - let broadcasted_splice_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone(); - assert!(broadcasted_splice_tx.encode().len() >= 314 && broadcasted_splice_tx.encode().len() <= 315); - assert_eq!(broadcasted_splice_tx.txid(), splice_tx.txid()); - assert_ne!(broadcasted_splice_tx.encode(), splice_tx.encode()); - verify_splice_funding_tx(&broadcasted_splice_tx, &broadcasted_funding_tx.txid(), post_splice_channel_value, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); - assert_eq!(broadcasted_splice_tx.encode().as_hex().to_string(), - "00000000000101e8ef56e12db94c1f62797f65b2093a76d4a198b657547ac75dcc087282930c840000000000fdffffff01c0d40100000000002200201a786d17d588868dea8da15e1e2e2d76f208a8ece1a014497849fc56743968f004004730440220190b1c02f8bfadb5630165ba6f70be93a1a5184efbf7f34b7f1fc1012412f311022077cf50e766ce03751a3137ba2582ce7c4afe69601ab1a67c27a4c619074248860147304402206bc28db47e6767876d1fbcca2429e04dae30bb50cf825fa1e32388a5c3422d1a0220173f0d6c33399c7bb9369bff094fe47f062fe0f982f722e0f9f2d7ad0a5b65e301475221027cb543835655982e9e6c45ace5d284696f98f37e71fac4a6ba161d4d8eae302621038b4d8740642bce8846a967c88c8c7dbff6a84edbbf63673745e49d03c52c340452ae00000000"); - - check_added_monitors!(initiator_node, 1); - check_added_monitors!(acceptor_node, 1); - - // check that capacity has _not_ been changed yet - assert_eq!(initiator_node.node.list_channels().len(), 1); - { - let channel = &initiator_node.node.list_channels()[0]; - assert!(!channel.is_usable); // TODO check - assert!(!channel.is_channel_ready); // TODO check - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 100000000 - reserve); - assert_eq!(channel.funding_txo.unwrap().txid, splice_tx.txid()); // TODO check - assert_eq!(channel.confirmations.unwrap(), 0); // TODO check - } - - confirm_transaction(&initiator_node, &broadcasted_splice_tx); - let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); - - confirm_transaction(&acceptor_node, &broadcasted_splice_tx); - let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); - let _channel_update = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelUpdate, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); - let _channel_update = get_event_msg!(initiator_node, MessageSendEvent::SendChannelUpdate, acceptor_node.node.get_our_node_id()); - - // check new channel capacity and other parameters - assert_eq!(initiator_node.node.list_channels().len(), 1); - { - let channel = &initiator_node.node.list_channels()[0]; - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); - assert_eq!(channel.outbound_capacity_msat, 120000000 - reserve); - assert_eq!(channel.funding_txo.unwrap().txid, splice_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // do the checks on acceptor side as well - assert_eq!(acceptor_node.node.list_channels().len(), 1); - { - let channel = &acceptor_node.node.list_channels()[0]; - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); - assert_eq!(channel.outbound_capacity_msat, 0); - assert_eq!(channel.funding_txo.unwrap().txid, splice_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // ... End of Splicing - - // close channel - initiator_node.node.close_channel(&channel_id, &acceptor_node.node.get_our_node_id()).unwrap(); - let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); - acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); - let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); - initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); - let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); -} -*/ - -/// #SPLICING Builds on test_channel_open_v2_and_close() -/// Splicing test, simple splice-in flow. Starts with opening a V2 channel first. -/// The steps are mostly on ChannelManager level. -#[test] -fn test_v2_splice_in() { - // Set up a network of 2 nodes - let cfg = UserConfig { - channel_handshake_config: ChannelHandshakeConfig { - announced_channel: true, - ..Default::default() - }, - ..Default::default() - }; - let chanmon_cfgs = create_chanmon_cfgs(2); - let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. - let initiator_node_index = 0; - let acceptor_node_index = 1; - let initiator_node = &nodes[initiator_node_index]; - let acceptor_node = &nodes[acceptor_node_index]; - - // Instantiate channel parameters where we push the maximum msats given our funding satoshis - let channel_value_sat = 100000; // same as funding satoshis - - // Have node0 initiate a channel to node1 with aforementioned parameters - let _res = initiator_node.node.create_dual_funded_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, None, 42, None).unwrap(); - - // Extract the channel open message from node0 to node1 - let open_channel2_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannelV2, acceptor_node.node.get_our_node_id()); - assert_eq!(initiator_node.node.list_channels().len(), 1); - - let _res = acceptor_node.node.handle_open_channel_v2(&initiator_node.node.get_our_node_id(), &open_channel2_message.clone()); - // Extract the accept channel message from node1 to node0 - let accept_channel2_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannelV2, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_accept_channel_v2(&acceptor_node.node.get_our_node_id(), &accept_channel2_message.clone()); - - // Note: FundingInputsContributionReady emitted - let events = initiator_node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - let channel_id = match events[0] { - Event::FundingInputsContributionReady { channel_id, .. } => { channel_id } - _ => panic!("FundingInputsContributionReady event missing {:?}", events[0]), - }; - - let custom_input_secret_key = SecretKey::from_slice(&[2; 32]).unwrap(); - let custom_input_pubkey = PublicKey::from_secret_key(&Secp256k1::new(), &custom_input_secret_key); - let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, channel_value_sat * 2, &custom_input_pubkey)]; - let _res = initiator_node.node.contribute_funding_inputs(&channel_id, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); - - // initiator_node will generate a TxAddInput message to kickstart the interactive transaction construction protocol - let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - - // First output we send is the change output. - let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); - let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); - assert_eq!(tx_add_output_msg.sats, channel_value_sat); - - let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert_eq!(msg_events.len(), 2); - assert_event_type!(msg_events[0], MessageSendEvent::SendTxComplete); - assert_event_type!(msg_events[1], MessageSendEvent::UpdateHTLCs); - let msg_commitment_signed_from_0 = match msg_events[1] { - MessageSendEvent::UpdateHTLCs { ref updates, .. } => { - updates.commitment_signed.clone() - }, - _ => panic!("Unexpected event"), - }; - if let Event::FundingTransactionReadyForSigning { - channel_id, - counterparty_node_id, - mut unsigned_transaction, - .. - } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { - let mut witness = Witness::new(); - witness.push(vec![0]); - unsigned_transaction.input[0].witness = witness; - let _res = initiator_node.node.funding_transaction_signed(&channel_id, &counterparty_node_id, unsigned_transaction).unwrap(); - } else { panic!(); } - - let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); - let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); - // First messsage is commitment_signed, second is tx_signatures (see below for more) - assert_eq!(msg_events.len(), 1); - let msg_commitment_signed_from_1 = match msg_events[0] { - MessageSendEvent::UpdateHTLCs { ref updates, .. } => { - updates.commitment_signed.clone() - }, - _ => panic!("Unexpected event"), - }; - - // Handle the initial commitment_signed exchange. Order is not important here. - acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); - initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); - check_added_monitors(&initiator_node, 1); - check_added_monitors(&acceptor_node, 1); - - // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures - // only after receiving tx_signatures from the non-initiator in this case. - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert!(msg_events.is_empty()); - let tx_signatures_from_1 = get_event_msg!(acceptor_node, MessageSendEvent::SendTxSignatures, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_from_1); - get_event!(initiator_node, Event::ChannelPending); - let tx_signatures_from_0 = get_event_msg!(initiator_node, MessageSendEvent::SendTxSignatures, acceptor_node.node.get_our_node_id()); - let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_from_0); - get_event!(acceptor_node, Event::ChannelPending); - - // Check that funding transaction has been broadcasted - assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); - assert_eq!(broadcasted_funding_tx.encode().len(), 130); - - // Simulate confirmation of the funding tx - confirm_transaction(&initiator_node, &broadcasted_funding_tx); - let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); - - confirm_transaction(&acceptor_node, &broadcasted_funding_tx); - let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); - let _ev = get_event!(acceptor_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); - let _ev = get_event!(initiator_node, Event::ChannelReady); - let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); - - // check channel capacity and other parameters - let reserve = 1000000; - assert_eq!(initiator_node.node.list_channels().len(), 1); - let channel = &initiator_node.node.list_channels()[0]; - { - assert!(channel.is_usable); - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 100000000 - reserve); - assert_eq!(channel.confirmations.unwrap(), 10); - } - // do checks on the acceptor node as well (capacity, etc.) - assert_eq!(acceptor_node.node.list_channels().len(), 1); - { - let channel = &acceptor_node.node.list_channels()[0]; - assert!(channel.is_usable); - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, channel_value_sat); - assert_eq!(channel.outbound_capacity_msat, 0); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // Channel is ready now for normal operation - - // Start of Splicing ... - - // Amount being added to the channel through the splice-in - let splice_in_sats: u64 = 20000; - let post_splice_channel_value = channel_value_sat + splice_in_sats; - let funding_feerate_perkw = 1024; // TODO - let locktime = 0; // TODO - - // Initiate splice-in (on node0) - let _res = initiator_node.node.splice_channel(&channel_id, &acceptor_node.node.get_our_node_id(), splice_in_sats as i64, funding_feerate_perkw, locktime).unwrap(); - // Extract the splice message from node0 to node1 - let splice_msg = get_event_msg!(initiator_node, MessageSendEvent::SendSplice, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_splice(&initiator_node.node.get_our_node_id(), &splice_msg); - // Extract the splice_ack message from node1 to node0 - let splice_ack_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendSpliceAck, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_splice_ack(&acceptor_node.node.get_our_node_id(), &splice_ack_msg); - - // Note: SpliceAckedInputsContributionReady emitted - let events = initiator_node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - match events[0] { - Event::SpliceAckedInputsContributionReady { channel_id: ch_id0, pre_channel_value_satoshis, post_channel_value_satoshis, holder_funding_satoshis, counterparty_funding_satoshis, .. } => { - assert_eq!(ch_id0, channel_id); - assert_eq!(pre_channel_value_satoshis, channel_value_sat); - assert_eq!(post_channel_value_satoshis, post_splice_channel_value); - assert_eq!(holder_funding_satoshis, post_splice_channel_value); - assert_eq!(counterparty_funding_satoshis, 0); - }, - _ => panic!("SpliceAckedInputsContributionReady event missing {:?}", events[0]), - }; - - let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, splice_in_sats * 2, &custom_input_pubkey)]; - - let _res = initiator_node.node.contribute_funding_inputs(&channel_id, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); - - // Initiator_node will generate first TxAddInput message - let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - // second TxAddInput message for the second input - let tx_add_input2_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input2_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - - // First TxAddOutput is for the change output - let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); - assert!(tx_add_output_msg.script.is_v0_p2wpkh()); - - let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); - let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - // TxAddOutput for the splice funding - let tx_add_output2_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); - // Check we get the channel funding output. - assert!(tx_add_output2_msg.script.is_v0_p2wsh()); - assert_eq!(tx_add_output2_msg.sats, post_splice_channel_value); - - let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output2_msg); - let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); - - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert_eq!(msg_events.len(), 2); - let tx_complete_msg = match msg_events[0] { - MessageSendEvent::SendTxComplete { ref node_id, ref msg } => { - assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); - (*msg).clone() - }, - _ => panic!("Unexpected event"), - }; - let msg_commitment_signed_from_0 = match msg_events[1] { - MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { - assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); - updates.commitment_signed.clone() - }, - _ => panic!("Unexpected event"), - }; - if let Event::FundingTransactionReadyForSigning { - channel_id, - counterparty_node_id, - mut unsigned_transaction, - .. - } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { - assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); - assert_eq!(unsigned_transaction.input.len(), 2); - assert_eq!(unsigned_transaction.input[0].witness.len(), 0); - - // This is the previous funding tx input, already signed (partially) - assert_eq!(unsigned_transaction.input[1].witness.len(), 4); - - // Placeholder for signature on the contributed input - let mut witness1 = Witness::new(); - witness1.push(vec![0]); - unsigned_transaction.input[0].witness = witness1; - - let _res = initiator_node.node.funding_transaction_signed(&channel_id, &counterparty_node_id, unsigned_transaction).unwrap(); - } else { panic!(); } - - let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); - let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); - // First messsage is commitment_signed, second is tx_signatures (see below for more) - assert_eq!(msg_events.len(), 1); - let msg_commitment_signed_from_1 = match msg_events[0] { - MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { - assert_eq!(*node_id, initiator_node.node.get_our_node_id()); - let res = updates.commitment_signed.clone(); - res - }, - _ => panic!("Unexpected event {:?}", msg_events[0]), - }; - - // Handle the initial commitment_signed exchange. Order is not important here. - let _res = acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); - let _res = initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); - check_added_monitors(&initiator_node, 1); - check_added_monitors(&acceptor_node, 1); - - // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures - // only after receiving tx_signatures from the non-initiator in this case. - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert!(msg_events.is_empty()); - let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); - assert_eq!(msg_events.len(), 1); - let tx_signatures_1 = match msg_events[0] { - MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => { - assert_eq!(*node_id, initiator_node.node.get_our_node_id()); - // Here we only get the signature for the shared input - assert_eq!(msg.witnesses.len(), 0); - assert!(msg.tlvs.is_some()); - msg - }, - _ => panic!("Unexpected event {:?}", msg_events[0]), - }; - - let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_1); - let events_0 = initiator_node.node.get_and_clear_pending_events(); - assert_eq!(events_0.len(), 0); - // match events_0[0] { - // Event::ChannelPending{ ref counterparty_node_id, .. } => { - // assert_eq!(*counterparty_node_id, acceptor_node.node.get_our_node_id()); - // }, - // _ => panic!("Unexpected event {:?}", events_0[0]), - // } - let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); - assert_eq!(msg_events.len(), 1); - let tx_signatures_0 = match msg_events[0] { - MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => { - assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); - // Here we get the witnesses for the two inputs: - // - the custom input, and - // - the previous funding tx, also in the tlvs - assert_eq!(msg.witnesses.len(), 2); - assert_eq!(msg.witnesses[0].len(), 1); - assert_eq!(msg.witnesses[1].len(), 4); - assert!(msg.tlvs.is_some()); - msg - }, - _ => panic!("Unexpected event {:?}", msg_events[0]), - }; - let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_0); - - // // TODO we shouldn't this get on acceptor! - // if let Event::FundingTransactionReadyForSigning { - // channel_id, - // counterparty_node_id, - // mut unsigned_transaction, - // .. - // } = get_event!(acceptor_node, Event::FundingTransactionReadyForSigning) { - // assert_eq!(counterparty_node_id, initiator_node.node.get_our_node_id()); - // assert_eq!(unsigned_transaction.input.len(), 2); - // // ... - // } else { panic!("Unexpected event"); } - - let events_1 = acceptor_node.node.get_and_clear_pending_events(); - assert_eq!(events_1.len(), 0); - // match events_1[0] { - // Event::ChannelPending{ ref counterparty_node_id, .. } => { - // assert_eq!(*counterparty_node_id, initiator_node.node.get_our_node_id()); - // }, - // _ => panic!("Unexpected event {:?}", events_1[0]), - // } - - // Check that funding transaction has been broadcasted - assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 3); // Why 3? - let broadcasted_splice_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[2].clone(); - let expected_funding_tx = "020000000001027b987b4668b22e5b047b133585390e73a94ba98b93cf65b0d0360bd555868bd0000000000000000000d262d78acb96819d5755ff9d0b69be8fdd752f99fe930700adb2642cc95bc25001000000000000000002724b000000000000160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665fc0d4010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3a0101000400473044022005f643b6d170a6ff4d850e0183a8868bfd49d548cf38530b8e73c9d91e14dc55022009e2855df94f8771b85a72c36d4e88f8830166df3e413e9c87338518b907a709014730440220252b4c3d66b3b3d474f2b5a1391979696aece1190c8490f3aebb3ca3b18db78c02205239059f069b7c7e42276ef21afce2f323f5eb9c63c43b6c74bdee9e0ab2685301475221026a2a07ee436cc6745aed846a76275f66ed16a469706aca3aa581f20b85d85396210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db352ae00000000"; - assert_eq!(broadcasted_splice_tx.encode().len(), 389); - assert_eq!(broadcasted_splice_tx.encode().as_hex().to_string(), expected_funding_tx); - let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel.channel_id); - let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel.channel_id); - verify_splice_funding_tx(&broadcasted_splice_tx, &broadcasted_funding_tx.txid(), post_splice_channel_value, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); - - // Check that funding transaction has been broadcasted on acceptor side as well - assert_eq!(chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 2); - let broadcasted_splice_tx_acc = chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone(); - assert_eq!(broadcasted_splice_tx_acc.encode().len(), 389); - assert_eq!(broadcasted_splice_tx_acc.encode().as_hex().to_string(), expected_funding_tx); - - // check_added_monitors!(initiator_node, 1); - // check_added_monitors!(acceptor_node, 1); - - // check that capacity has _not_ been changed yet - assert_eq!(initiator_node.node.list_channels().len(), 1); - { - let channel = &initiator_node.node.list_channels()[0]; - assert!(!channel.is_usable); // TODO check - assert!(!channel.is_channel_ready); // TODO check - // TODO disabled, this is done now earlier - // assert_eq!(channel.channel_value_satoshis, channel_value_sat); - // assert_eq!(channel.outbound_capacity_msat, 100000000 - reserve); - // assert_eq!(channel.funding_txo.unwrap().txid, splice_tx.txid()); // TODO check - assert_eq!(channel.confirmations.unwrap(), 0); // TODO check - } - - // Simulate confirmation of the funding tx - confirm_transaction(&initiator_node, &broadcasted_splice_tx); - let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); - - confirm_transaction(&acceptor_node, &broadcasted_splice_tx); - let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); - - let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); - // let _ev = get_event!(acceptor_node, Event::ChannelReady); - // let _ev = get_event!(acceptor_node, Event::FundingTransactionReadyForSigning); // ?? - let events_0 = acceptor_node.node.get_and_clear_pending_events(); - assert_eq!(events_0.len(), 0); - // match events_0[0] { - // Event::ChannelPending{ ref counterparty_node_id, .. } => { - // assert_eq!(*counterparty_node_id, acceptor_node.node.get_our_node_id()); - // }, - // _ => panic!("Unexpected event {:?}", events_0[0]), - // } - - let _channel_update = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelUpdate, initiator_node.node.get_our_node_id()); - // let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); - - let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); - // let _ev = get_event!(initiator_node, Event::ChannelReady); - let _channel_update = get_event_msg!(initiator_node, MessageSendEvent::SendChannelUpdate, acceptor_node.node.get_our_node_id()); - // let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); - - - // check new channel capacity and other parameters - assert_eq!(initiator_node.node.list_channels().len(), 1); - { - let channel = &initiator_node.node.list_channels()[0]; - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); - assert_eq!(channel.outbound_capacity_msat, 120000000 - reserve); - // assert_eq!(channel.funding_txo.unwrap().txid, splice_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // do the checks on acceptor side as well - assert_eq!(acceptor_node.node.list_channels().len(), 1); - { - let channel = &acceptor_node.node.list_channels()[0]; - assert!(channel.is_channel_ready); - assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); - assert_eq!(channel.outbound_capacity_msat, 0); - // assert_eq!(channel.funding_txo.unwrap().txid, splice_tx.txid()); - assert_eq!(channel.confirmations.unwrap(), 10); - } - - // ... End of Splicing - - // close channel, cooperatively - initiator_node.node.close_channel(&channel_id, &acceptor_node.node.get_our_node_id()).unwrap(); - let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); - acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); - let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); - initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); - let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); -} - #[test] fn test_insane_channel_opens() { // Stand up a network of 2 nodes diff --git a/lightning/src/ln/functional_tests_splice.rs b/lightning/src/ln/functional_tests_splice.rs new file mode 100644 index 00000000000..3a906ef38a2 --- /dev/null +++ b/lightning/src/ln/functional_tests_splice.rs @@ -0,0 +1,1695 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Tests that test standing up a network of ChannelManagers, creating channels, sending +//! payments/messages between them, and often checking the resulting ChannelMonitors are able to +//! claim outputs on-chain. + +use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider}; +use crate::ln::ChannelId; +use crate::ln::channel::ChannelPhase; +use crate::ln::functional_test_utils::*; +use crate::ln::msgs::ChannelMessageHandler; +use crate::util::ser::Writeable; +use crate::util::config::{ChannelHandshakeConfig, UserConfig}; +use crate::prelude::*; + +use bitcoin::{Transaction, TxOut, Witness}; +use bitcoin::blockdata::opcodes; +use bitcoin::blockdata::script::{Builder, ScriptBuf}; +use bitcoin::hash_types::Txid; +use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; +use bitcoin::secp256k1::ecdsa::Signature; +use bitcoin::sighash::{EcdsaSighashType, SighashCache}; + +use hex::DisplayHex; +use core::default::Default; + + +// Create a 2-of-2 multisig redeem script. Return the script, and the two keys in the order they appear in the script. +fn create_multisig_redeem_script(key1: &PublicKey, key2: &PublicKey) -> (ScriptBuf, PublicKey, PublicKey) { + let (smaller_key, larger_key) = if key1.serialize() < key2.serialize() { + (key1, key2) + } else { + (key2, key1) + }; + let script = Builder::new() + .push_opcode(opcodes::all::OP_PUSHNUM_2) + .push_slice(&smaller_key.serialize()) + .push_slice(&larger_key.serialize()) + .push_opcode(opcodes::all::OP_PUSHNUM_2) + .push_opcode(opcodes::all::OP_CHECKMULTISIG) + .into_script(); + (script, smaller_key.clone(), larger_key.clone()) +} + +// Create an output script for a 2-of-2 multisig. +fn create_multisig_output_script(key1: &PublicKey, key2: &PublicKey) -> ScriptBuf { + let (redeem_script, _k1, _k2) = create_multisig_redeem_script(key1, key2); + Builder::new() + .push_opcode(opcodes::all::OP_PUSHBYTES_0) + .push_slice(&AsRef::<[u8; 32]>::as_ref(&redeem_script.wscript_hash())) + .into_script() +} + +// Verify a 2-of-2 multisig redeem script. Return the same keys, but in the order as they appear in the script +fn verify_multisig_redeem_script(script: &Vec, exp_key_1: &PublicKey, exp_key_2: &PublicKey) -> (PublicKey, PublicKey) { + let (exp_script,exp_smaller_key, exp_larger_key) = create_multisig_redeem_script(exp_key_1, exp_key_2); + assert_eq!(script.as_hex().to_string(), exp_script.as_bytes().as_hex().to_string()); + (exp_smaller_key, exp_larger_key) +} + +// Verify a 2-of-2 multisig output script. +fn verify_multisig_output_script(script: &ScriptBuf, exp_key_1: &PublicKey, exp_key_2: &PublicKey) { + let exp_script = create_multisig_output_script(exp_key_1, exp_key_2); + assert_eq!(script.to_hex_string(), exp_script.to_hex_string()); +} + +// Get the funding key of a node towards another node +fn get_funding_key(node: &Node, counterparty_node: &Node, channel_id: &ChannelId) -> PublicKey { + let per_peer_state = node.node.per_peer_state.read().unwrap(); + let chan_lock = per_peer_state.get(&counterparty_node.node.get_our_node_id()).unwrap().lock().unwrap(); + let local_chan = chan_lock.channel_by_id.get(&channel_id).map( + |phase| if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None } + ).flatten().unwrap(); + local_chan.get_signer().as_ref().pubkeys().funding_pubkey +} + +/// Verify the funding output of a funding tx +fn verify_funding_output(funding_txo: &TxOut, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { + let act_script = &funding_txo.script_pubkey; + verify_multisig_output_script(&act_script, funding_key_1, funding_key_2); +} + +/// Do checks on a funding tx +fn verify_funding_tx(funding_tx: &Transaction, value: u64, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { + // find the output with the given value + let mut funding_output_opt: Option<&TxOut> = None; + for o in &funding_tx.output { + if o.value == value { + funding_output_opt = Some(o); + } + } + if funding_output_opt.is_none() { + panic!("Funding output not found, no output with value {}", value); + } + verify_funding_output(funding_output_opt.unwrap(), funding_key_1, funding_key_2) +} + +/// Simple end-to-end open channel flow, with close, and verification checks. +/// The steps are mostly on ChannelManager level. +#[test] +fn test_channel_open_and_close() { + // Set up a network of 2 nodes + let cfg = UserConfig { + channel_handshake_config: ChannelHandshakeConfig { + announced_channel: true, + ..Default::default() + }, + ..Default::default() + }; + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. + let initiator_node_index = 0; + let initiator_node = &nodes[initiator_node_index]; + let acceptor_node = &nodes[1]; + + // Instantiate channel parameters where we push the maximum msats given our funding satoshis + let channel_value_sat = 100000; // same as funding satoshis + let push_msat = 0; + + let expected_temporary_channel_id = "f9fe5e552aa0fc45020f0505efde432a4e373e5d393863973a6899f8c26d33d1"; + let expected_funded_channel_id = "42056a074feeea62aec646e815197539483a458bf6e9e04d115f72b851265385"; + + // Have node0 initiate a channel to node1 with aforementioned parameters + let channel_id_temp1 = initiator_node.node.create_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, push_msat, 42, None, None).unwrap(); + assert_eq!(channel_id_temp1.to_string(), expected_temporary_channel_id); + + // Extract the channel open message from node0 to node1 + let open_channel_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannel, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_open_channel(&initiator_node.node.get_our_node_id(), &open_channel_message.clone()); + // Extract the accept channel message from node1 to node0 + let accept_channel_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannel, initiator_node.node.get_our_node_id()); + let _res = initiator_node.node.handle_accept_channel(&acceptor_node.node.get_our_node_id(), &accept_channel_message.clone()); + // Note: FundingGenerationReady emitted, checked and used below + let (channel_id_temp2, funding_tx, _funding_output) = create_funding_transaction(&initiator_node, &acceptor_node.node.get_our_node_id(), channel_value_sat, 42); + assert_eq!(channel_id_temp2.to_string(), expected_temporary_channel_id); + assert_eq!(funding_tx.encode().len(), 55); + let expected_funding_tx = "0000000000010001a086010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3a00000000"; + assert_eq!(&funding_tx.encode().as_hex().to_string(), expected_funding_tx); + + // Funding transation created, provide it + let _res = initiator_node.node.funding_transaction_generated(&channel_id_temp1, &acceptor_node.node.get_our_node_id(), funding_tx.clone()).unwrap(); + + let funding_created_message = get_event_msg!(initiator_node, MessageSendEvent::SendFundingCreated, acceptor_node.node.get_our_node_id()); + assert_eq!(funding_created_message.temporary_channel_id.to_string(), expected_temporary_channel_id); + + let _res = acceptor_node.node.handle_funding_created(&initiator_node.node.get_our_node_id(), &funding_created_message); + + let funding_signed_message = get_event_msg!(acceptor_node, MessageSendEvent::SendFundingSigned, initiator_node.node.get_our_node_id()); + let _res = initiator_node.node.handle_funding_signed(&acceptor_node.node.get_our_node_id(), &funding_signed_message); + // Take new channel ID + let channel_id2 = funding_signed_message.channel_id; + assert_eq!(channel_id2.to_string(), expected_funded_channel_id); + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + assert_eq!(broadcasted_funding_tx.encode().len(), 55); + assert_eq!(broadcasted_funding_tx.txid(), funding_tx.txid()); + assert_eq!(broadcasted_funding_tx.encode(), funding_tx.encode()); + assert_eq!(&broadcasted_funding_tx.encode().as_hex().to_string(), expected_funding_tx); + // // Check that funding transaction has been broadcasted on the acceptor side too + // assert_eq!(chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + // let broadcasted_funding_tx_acc = chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + // assert_eq!(broadcasted_funding_tx_acc.encode().len(), 55); + // assert_eq!(broadcasted_funding_tx_acc.txid(), funding_tx.txid()); + // assert_eq!(&broadcasted_funding_tx_acc.encode().as_hex().to_string(), expected_funding_tx); + + check_added_monitors!(initiator_node, 1); + let _ev = get_event!(initiator_node, Event::ChannelPending); + check_added_monitors!(acceptor_node, 1); + let _ev = get_event!(acceptor_node, Event::ChannelPending); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_funding_tx); + let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_funding_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); + let _ev = get_event!(acceptor_node, Event::ChannelReady); + let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + let _ev = get_event!(initiator_node, Event::ChannelReady); + let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // check channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + let channel = &initiator_node.node.list_channels()[0]; + { + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat); + assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + // do checks on the acceptor node as well (capacity, etc.) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 0); + assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // Verify the funding transaction + let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel.channel_id); + let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel.channel_id); + + verify_funding_tx(&broadcasted_funding_tx, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); + + // Channel is ready now for normal operation + + // close channel, cooperatively + initiator_node.node.close_channel(&channel_id2, &acceptor_node.node.get_our_node_id()).unwrap(); + let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); + acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); + let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); + initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); + let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); +} + +/// End-to-end V2 open channel flow, with close, and verification checks. +/// The steps are mostly on ChannelManager level. +#[cfg(dual_funding)] +#[test] +fn test_channel_open_v2_and_close() { + // Set up a network of 2 nodes + let cfg = UserConfig { + channel_handshake_config: ChannelHandshakeConfig { + announced_channel: true, + ..Default::default() + }, + ..Default::default() + }; + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. + let initiator_node_index = 0; + let acceptor_node_index = 1; + let initiator_node = &nodes[initiator_node_index]; + let acceptor_node = &nodes[acceptor_node_index]; + + // Instantiate channel parameters where we push the maximum msats given our funding satoshis + let channel_value_sat = 100000; // same as funding satoshis + + let expected_temporary_channel_id = "4cd87e42e1cf2813f604ec16e42297f1723c5dfc100593b83eead1588d30b671"; + let expected_funded_channel_id = "ca537cbb1f386065003906d85719965f67f22687600bc497b29c7d5d4736f0ce"; + + // Have node0 initiate a channel to node1 with aforementioned parameters + let channel_id_temp1 = initiator_node.node.create_dual_funded_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, None, 42, None).unwrap(); + assert_eq!(channel_id_temp1.to_string(), expected_temporary_channel_id); + + // Extract the channel open message from node0 to node1 + let open_channel2_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannelV2, acceptor_node.node.get_our_node_id()); + assert_eq!(initiator_node.node.list_channels().len(), 1); + + let _res = acceptor_node.node.handle_open_channel_v2(&initiator_node.node.get_our_node_id(), &open_channel2_message.clone()); + // Extract the accept channel message from node1 to node0 + let accept_channel2_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannelV2, initiator_node.node.get_our_node_id()); + assert_eq!(accept_channel2_message.temporary_channel_id.to_string(), expected_temporary_channel_id); + + let _res = initiator_node.node.handle_accept_channel_v2(&acceptor_node.node.get_our_node_id(), &accept_channel2_message.clone()); + + // Note: FundingInputsContributionReady emitted + let events = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + let channel_id1 = match events[0] { + Event::FundingInputsContributionReady { channel_id, counterparty_node_id, .. } => { + // Here we have the final channel_id now + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); + channel_id + } + _ => panic!("FundingInputsContributionReady event missing {:?}", events[0]), + }; + + let extra_funding_input_sats = channel_value_sat + 35_000; + let custom_input_secret_key = SecretKey::from_slice(&[2; 32]).unwrap(); + let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, extra_funding_input_sats, &PublicKey::from_secret_key(&Secp256k1::new(), &custom_input_secret_key))]; + let _res = initiator_node.node.contribute_funding_inputs(&channel_id1, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); + + // let events = acceptor_node.node.get_and_clear_pending_events(); + // println!("acceptor_node events: {}", events.len()); + // assert_eq!(events.len(), 0); + + // initiator_node will generate a TxAddInput message to kickstart the interactive transaction construction protocol + let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + // First output we send is the change output. + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + assert!(tx_add_output_msg.script.is_v0_p2wpkh()); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + + // Check we get the channel funding output. + assert!(tx_add_output_msg.script.is_v0_p2wsh()); + assert_eq!(tx_add_output_msg.sats, channel_value_sat); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 2); + let tx_complete_msg = match msg_events[0] { + MessageSendEvent::SendTxComplete { ref node_id, ref msg } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + (*msg).clone() + }, + _ => panic!("Unexpected event"), + }; + let msg_commitment_signed_from_0 = match msg_events[1] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + if let Event::FundingTransactionReadyForSigning { + channel_id, + counterparty_node_id, + mut unsigned_transaction, + .. + } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); + + let mut witness = Witness::new(); + witness.push(vec![0]); + unsigned_transaction.input[0].witness = witness; + + let _res = initiator_node.node.funding_transaction_signed(&channel_id1, &counterparty_node_id, unsigned_transaction).unwrap(); + } else { panic!(); } + + let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + // First messsage is commitment_signed, second is tx_signatures (see below for more) + assert_eq!(msg_events.len(), 1); + let msg_commitment_signed_from_1 = match msg_events[0] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(*node_id, initiator_node.node.get_our_node_id()); + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + + // Handle the initial commitment_signed exchange. Order is not important here. + acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); + initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); + check_added_monitors(&initiator_node, 1); + check_added_monitors(&acceptor_node, 1); + + // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures + // only after receiving tx_signatures from the non-initiator in this case. + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert!(msg_events.is_empty()); + let tx_signatures_from_1 = get_event_msg!(acceptor_node, MessageSendEvent::SendTxSignatures, nodes[0].node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_from_1); + let events_0 = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 1); + match events_0[0] { + Event::ChannelPending{ ref counterparty_node_id, .. } => { + assert_eq!(*counterparty_node_id, acceptor_node.node.get_our_node_id()); + }, + _ => panic!("Unexpected event"), + } + let tx_signatures_from_0 = get_event_msg!(initiator_node, MessageSendEvent::SendTxSignatures, nodes[1].node.get_our_node_id()); + let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_from_0); + let events_1 = acceptor_node.node.get_and_clear_pending_events(); + assert_eq!(events_1.len(), 1); + match events_1[0] { + Event::ChannelPending{ ref channel_id, ref counterparty_node_id, .. } => { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(*counterparty_node_id, initiator_node.node.get_our_node_id()); + }, + _ => panic!("Unexpected event"), + } + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + let expected_funding_tx = "020000000001019c76affec45612929f824230eacc67dc7b3db1072c39d0e62f4f557a34e141fc000000000000000000023788000000000000160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665fa086010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3a01010000000000"; + assert_eq!(broadcasted_funding_tx.encode().len(), 130); + assert_eq!(&broadcasted_funding_tx.encode().as_hex().to_string(), expected_funding_tx); + // Check that funding transaction has been broadcasted on the acceptor side as well + assert_eq!(chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + let broadcasted_funding_tx_acc = chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + assert_eq!(broadcasted_funding_tx_acc.encode().len(), 130); + assert_eq!(&broadcasted_funding_tx_acc.encode().as_hex().to_string(), expected_funding_tx); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_funding_tx); + let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_funding_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); + let _ev = get_event!(acceptor_node, Event::ChannelReady); + let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + let _ev = get_event!(initiator_node, Event::ChannelReady); + let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // check channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + let channel = &initiator_node.node.list_channels()[0]; + { + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat); + assert_eq!(channel.confirmations.unwrap(), 10); + } + // do checks on the acceptor node as well (capacity, etc.) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.balance_msat, 0); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // Verify the funding transaction + let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel_id1); + let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel_id1); + + verify_funding_tx(&broadcasted_funding_tx, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); + + // Channel is ready now for normal operation + + // close channel, cooperatively + initiator_node.node.close_channel(&channel_id1, &acceptor_node.node.get_our_node_id()).unwrap(); + let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); + acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); + let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); + initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); + let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); +} + +fn verify_signature(msg: &Vec, sig: &Vec, pubkey: &PublicKey) -> Result<(), String> { + let m = Message::from_slice(&msg).unwrap(); + let s = Signature::from_der(&sig).unwrap(); + let ctx = Secp256k1::new(); + match ctx.verify_ecdsa(&m, &s, &pubkey) { + Ok(_) => Ok(()), + Err(e) => Err(format!("Signature verification failed! err {} msg {} sig {} pk {}", e, &msg.as_hex(), &sig.as_hex(), &pubkey.serialize().as_hex())), + } +} + +/// #SPLICING +/// Verify the previous funding input on a splicing funding transaction +fn verify_splice_funding_input(splice_tx: &Transaction, prev_funding_txid: &Txid, prev_funding_value: u64, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { + // check that the previous funding tx is an input + let mut prev_fund_input_idx: Option = None; + for idx in 0..splice_tx.input.len() { + if splice_tx.input[idx].previous_output.txid == *prev_funding_txid { + prev_fund_input_idx = Some(idx); + } + } + if prev_fund_input_idx.is_none() { + panic!("Splice tx should contain the pervious funding tx as input! {} {}", prev_funding_txid, splice_tx.encode().as_hex()); + } + let prev_fund_input = &splice_tx.input[prev_fund_input_idx.unwrap()]; + let witness = &prev_fund_input.witness.to_vec(); + let witness_count = witness.len(); + let expected_witness_count = 4; + if witness_count != expected_witness_count { + panic!("Prev funding tx input should have {} witness elements! {} {}", expected_witness_count, witness_count, prev_fund_input_idx.unwrap()); + } + if witness[0].len() != 0 { + panic!("First multisig witness should be empty! {}", witness[0].len()); + } + // check witness 1&2, signatures + let wit1_sig = &witness[1]; + let wit2_sig = &witness[2]; + if wit1_sig.len() < 70 || wit1_sig.len() > 72 || wit2_sig.len() < 70 || wit2_sig.len() > 72 { + panic!("Witness entries 2&3 should be signatures! {} {}", wit1_sig.as_hex(), wit2_sig.as_hex()); + } + if wit1_sig[wit1_sig.len()-1] != 1 || wit2_sig[wit2_sig.len()-1] != 1 { + panic!("Witness entries 2&3 should be signatures with SIGHASHALL! {} {}", wit1_sig.as_hex(), wit2_sig.as_hex()); + } + let (script_key1, script_key2) = verify_multisig_redeem_script(&witness[3], funding_key_1, funding_key_2); + let redeemscript = ScriptBuf::from(witness[3].to_vec()); + // check signatures, sigs are in same order as keys + let sighash = &SighashCache::new(splice_tx).segwit_signature_hash(prev_fund_input_idx.unwrap(), &redeemscript, prev_funding_value, EcdsaSighashType::All).unwrap()[..].to_vec(); + let sig1 = wit1_sig[0..(wit1_sig.len()-1)].to_vec(); + let sig2 = wit2_sig[0..(wit2_sig.len()-1)].to_vec(); + if let Err(e1) = verify_signature(sighash, &sig1, &script_key1) { + panic!("Sig 1 check fails {}", e1); + } + if let Err(e2) = verify_signature(sighash, &sig2, &script_key2) { + panic!("Sig 2 check fails {}", e2); + } +} + +/// #SPLICING +/// Do checks on a splice funding tx +fn verify_splice_funding_tx(splice_tx: &Transaction, prev_funding_txid: &Txid, funding_value: u64, prev_funding_value: u64, funding_key_1: &PublicKey, funding_key_2: &PublicKey) { + verify_splice_funding_input(splice_tx, prev_funding_txid, prev_funding_value, funding_key_1, funding_key_2); + verify_funding_tx(splice_tx, funding_value, funding_key_1, funding_key_2); +} + +/// #SPLICING Builds on test_channel_open_simple() +/// Splicing test, simple splice-in flow. Starts with opening a V1 channel first. +#[test] +fn test_v1_splice_in() { + // Set up a network of 2 nodes + let cfg = UserConfig { + channel_handshake_config: ChannelHandshakeConfig { + announced_channel: true, + ..Default::default() + }, + ..Default::default() + }; + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Initiator and Acceptor nodes + let initiator_node_index = 1; + let initiator_node = &nodes[initiator_node_index]; + let acceptor_node = &nodes[0]; + + // Instantiate channel parameters where we push the maximum msats given our funding satoshis + let channel_value_sat = 100000; // same as funding satoshis + let push_msat = 0; + + let expected_funded_channel_id = "e8ef56e12db94c1f62797f65b2093a76d4a198b657547ac75dcc087282930c84"; + + // Have node0 initiate a channel to node1 with aforementioned parameters + let channel_id_temp1 = initiator_node.node.create_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, push_msat, 42, None, None).unwrap(); + + // Extract the channel open message from node0 to node1 + let open_channel_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannel, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_open_channel(&initiator_node.node.get_our_node_id(), &open_channel_message.clone()); + // Extract the accept channel message from node1 to node0 + let accept_channel_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannel, initiator_node.node.get_our_node_id()); + let _res = initiator_node.node.handle_accept_channel(&acceptor_node.node.get_our_node_id(), &accept_channel_message.clone()); + // Note: FundingGenerationReady emitted, checked and used below + let (_channel_id_temp2, funding_tx, _funding_output) = create_funding_transaction(&initiator_node, &acceptor_node.node.get_our_node_id(), channel_value_sat, 42); + + // Funding transation created, provide it + let _res = initiator_node.node.funding_transaction_generated(&channel_id_temp1, &acceptor_node.node.get_our_node_id(), funding_tx.clone()).unwrap(); + + let funding_created_message = get_event_msg!(initiator_node, MessageSendEvent::SendFundingCreated, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_funding_created(&initiator_node.node.get_our_node_id(), &funding_created_message); + + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert!(!channel.is_channel_ready); + } + // do checks on the acceptor node as well (capacity, etc.) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert!(!channel.is_channel_ready); + } + + let funding_signed_message = get_event_msg!(acceptor_node, MessageSendEvent::SendFundingSigned, initiator_node.node.get_our_node_id()); + let _res = initiator_node.node.handle_funding_signed(&acceptor_node.node.get_our_node_id(), &funding_signed_message); + // Take new channel ID + let channel_id2 = funding_signed_message.channel_id; + assert_eq!(channel_id2.to_string(), expected_funded_channel_id); + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + + check_added_monitors!(initiator_node, 1); + let _ev = get_event!(initiator_node, Event::ChannelPending); + check_added_monitors!(acceptor_node, 1); + let _ev = get_event!(acceptor_node, Event::ChannelPending); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_funding_tx); + let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_funding_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); + let _ev = get_event!(acceptor_node, Event::ChannelReady); + let _announcement_signatures = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + let _ev = get_event!(initiator_node, Event::ChannelReady); + let _announcement_signatures = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // check channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat); + assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + // do checks on the acceptor node as well (capacity, etc.) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 0); + assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // ==== Channel is now ready for normal operation + + // === Start of Splicing + println!("Start of Splicing ..., channel_id {}", channel_id2); + + // Amount being added to the channel through the splice-in + let splice_in_sats: u64 = 20000; + let _post_splice_channel_value = channel_value_sat + splice_in_sats; + let funding_feerate_perkw = 1024; // TODO + let locktime = 0; // TODO + + // Initiate splice-in (on node0) + let res_error = initiator_node.node.splice_channel(&channel_id2, &acceptor_node.node.get_our_node_id(), splice_in_sats as i64, funding_feerate_perkw, locktime); + assert!(res_error.is_err()); + assert_eq!(format!("{:?}", res_error.err().unwrap())[..53].to_string(), "Misuse error: Channel ID would change during splicing".to_string()); + + // no change + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat); + assert_eq!(channel.funding_txo.unwrap().txid, funding_tx.txid()); + } + + // === End of Splicing + + // === Close channel, cooperatively + initiator_node.node.close_channel(&channel_id2, &acceptor_node.node.get_our_node_id()).unwrap(); + let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); + acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); + let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); + initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); + let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); +} + +// TODO: Test with 2nd splice (open, splice, splice) + +/// #SPLICING Builds on test_channel_open_v2_and_close() +/// Splicing test, simple splice-in flow. Starts with opening a V2 channel first. +/// The steps are mostly on ChannelManager level. +#[test] +fn test_v2_splice_in() { + // Set up a network of 2 nodes + let cfg = UserConfig { + channel_handshake_config: ChannelHandshakeConfig { + announced_channel: true, + ..Default::default() + }, + ..Default::default() + }; + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. + let initiator_node_index = 0; + let acceptor_node_index = 1; + let initiator_node = &nodes[initiator_node_index]; + let acceptor_node = &nodes[acceptor_node_index]; + + // Instantiate channel parameters where we push the maximum msats given our funding satoshis + let channel_value_sat = 100000; // same as funding satoshis + + let expected_temporary_channel_id = "4cd87e42e1cf2813f604ec16e42297f1723c5dfc100593b83eead1588d30b671"; + let expected_funded_channel_id = "ca537cbb1f386065003906d85719965f67f22687600bc497b29c7d5d4736f0ce"; + + // Have node0 initiate a channel to node1 with aforementioned parameters + let channel_id_temp1 = initiator_node.node.create_dual_funded_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, None, 42, None).unwrap(); + assert_eq!(channel_id_temp1.to_string(), expected_temporary_channel_id); + + // Extract the channel open message from node0 to node1 + let open_channel2_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannelV2, acceptor_node.node.get_our_node_id()); + assert_eq!(initiator_node.node.list_channels().len(), 1); + + let _res = acceptor_node.node.handle_open_channel_v2(&initiator_node.node.get_our_node_id(), &open_channel2_message.clone()); + // Extract the accept channel message from node1 to node0 + let accept_channel2_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannelV2, initiator_node.node.get_our_node_id()); + assert_eq!(accept_channel2_message.temporary_channel_id.to_string(), expected_temporary_channel_id); + + let _res = initiator_node.node.handle_accept_channel_v2(&acceptor_node.node.get_our_node_id(), &accept_channel2_message.clone()); + + // Note: FundingInputsContributionReady emitted + let events = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + let channel_id1 = match events[0] { + Event::FundingInputsContributionReady { channel_id, .. } => { + // Here we have the final channel_id now + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + channel_id + } + _ => panic!("FundingInputsContributionReady event missing {:?}", events[0]), + }; + + let extra_funding_input_sats = channel_value_sat + 35_000; + let custom_input_secret_key = SecretKey::from_slice(&[2; 32]).unwrap(); + let custom_input_pubkey = PublicKey::from_secret_key(&Secp256k1::new(), &custom_input_secret_key); + let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, extra_funding_input_sats, &custom_input_pubkey)]; + let _res = initiator_node.node.contribute_funding_inputs(&channel_id1, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); + + // initiator_node will generate a TxAddInput message to kickstart the interactive transaction construction protocol + let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + // First output we send is the change output. + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + assert_eq!(tx_add_output_msg.sats, channel_value_sat); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 2); + assert_event_type!(msg_events[0], MessageSendEvent::SendTxComplete); + assert_event_type!(msg_events[1], MessageSendEvent::UpdateHTLCs); + let msg_commitment_signed_from_0 = match msg_events[1] { + MessageSendEvent::UpdateHTLCs { ref updates, .. } => { + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + if let Event::FundingTransactionReadyForSigning { + channel_id, + counterparty_node_id, + mut unsigned_transaction, + .. + } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + let mut witness = Witness::new(); + witness.push(vec![0]); + unsigned_transaction.input[0].witness = witness; + let _res = initiator_node.node.funding_transaction_signed(&channel_id1, &counterparty_node_id, unsigned_transaction).unwrap(); + } else { panic!(); } + + let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + // First messsage is commitment_signed, second is tx_signatures (see below for more) + assert_eq!(msg_events.len(), 1); + let msg_commitment_signed_from_1 = match msg_events[0] { + MessageSendEvent::UpdateHTLCs { ref updates, .. } => { + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + + // Handle the initial commitment_signed exchange. Order is not important here. + acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); + initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); + check_added_monitors(&initiator_node, 1); + check_added_monitors(&acceptor_node, 1); + + // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures + // only after receiving tx_signatures from the non-initiator in this case. + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert!(msg_events.is_empty()); + let tx_signatures_from_1 = get_event_msg!(acceptor_node, MessageSendEvent::SendTxSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_from_1); + get_event!(initiator_node, Event::ChannelPending); + let tx_signatures_from_0 = get_event_msg!(initiator_node, MessageSendEvent::SendTxSignatures, acceptor_node.node.get_our_node_id()); + let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_from_0); + get_event!(acceptor_node, Event::ChannelPending); + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + assert_eq!(broadcasted_funding_tx.encode().len(), 130); + assert_eq!(broadcasted_funding_tx.txid().to_string(), "6d895eaa56b6c8d896e45d9f54744ac5c075f3e961ae376546e167ec75dbd7be"); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_funding_tx); + let channel_ready_message1 = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_funding_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message1); + let _ev = get_event!(acceptor_node, Event::ChannelReady); + let _announcement_signatures2 = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + let _ev = get_event!(initiator_node, Event::ChannelReady); + let _announcement_signatures1 = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // let (announcement1, update1, update2) = create_chan_between_nodes_with_value_b(&initiator_node, &acceptor_node, &(channel_ready_message1, announcement_signatures1)); + // `update_nodes_with_chan_announce`(&nodes, initiator_node_index, acceptor_node_index, &announcement1, &update1, &update2); + + // check channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat); + assert_eq!(channel.confirmations.unwrap(), 10); + assert!(channel.funding_txo.is_some()); + } + // do checks on the acceptor node as well (capacity, etc.) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 0); + assert_eq!(channel.confirmations.unwrap(), 10); + assert!(channel.funding_txo.is_some()); + } + + // === Channel is now ready for normal operation + + // === Start of Splicing + println!("Start of Splicing ..., channel_id {}", channel_id1); + + // Amount being added to the channel through the splice-in + let splice_in_sats: u64 = 20000; + let post_splice_channel_value = channel_value_sat + splice_in_sats; + let funding_feerate_perkw = 1024; // TODO + let locktime = 0; // TODO + + // Initiate splice-in (on node0) + let _res = initiator_node.node.splice_channel(&channel_id1, &acceptor_node.node.get_our_node_id(), splice_in_sats as i64, funding_feerate_perkw, locktime).unwrap(); + // Extract the splice message from node0 to node1 + let splice_msg = get_event_msg!(initiator_node, MessageSendEvent::SendSplice, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_splice(&initiator_node.node.get_our_node_id(), &splice_msg); + // Extract the splice_ack message from node1 to node0 + let splice_ack_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendSpliceAck, initiator_node.node.get_our_node_id()); + + // check that capacity has been updated, channel is not usable, and funding tx is unset + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(!channel.is_usable); + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, 0); + assert!(channel.funding_txo.is_none()); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + let _res = initiator_node.node.handle_splice_ack(&acceptor_node.node.get_our_node_id(), &splice_ack_msg); + + // Note: SpliceAckedInputsContributionReady emitted + let events = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::SpliceAckedInputsContributionReady { channel_id, pre_channel_value_satoshis, post_channel_value_satoshis, holder_funding_satoshis, counterparty_funding_satoshis, .. } => { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(pre_channel_value_satoshis, channel_value_sat); + assert_eq!(post_channel_value_satoshis, post_splice_channel_value); + assert_eq!(holder_funding_satoshis, post_splice_channel_value); + assert_eq!(counterparty_funding_satoshis, 0); + }, + _ => panic!("SpliceAckedInputsContributionReady event missing {:?}", events[0]), + }; + + // check that capacity has been updated, channel is not usable, and funding tx is unset + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(!channel.is_usable); + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, 1000 * post_splice_channel_value); + assert!(channel.funding_txo.is_none()); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + let extra_splice_funding_input_sats = 35_000; + let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, extra_splice_funding_input_sats, &custom_input_pubkey)]; + + let _res = initiator_node.node.contribute_funding_inputs(&channel_id1, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); + + // Initiator_node will generate first TxAddInput message + let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + assert_eq!(tx_add_input_msg.prevtx.0.output[tx_add_input_msg.prevtx_out as usize].value, channel_value_sat); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + // second TxAddInput message for the second input + let tx_add_input2_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + assert_eq!(tx_add_input2_msg.prevtx.0.output[tx_add_input2_msg.prevtx_out as usize].value, extra_splice_funding_input_sats); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input2_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + // First TxAddOutput is for the change output + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + assert!(tx_add_output_msg.script.is_v0_p2wpkh()); + assert_eq!(tx_add_output_msg.sats, 14314); // extra_splice_input_sats - splice_in_sats - fee + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + // TxAddOutput for the splice funding + let tx_add_output2_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + // Check we get the channel funding output. + assert!(tx_add_output2_msg.script.is_v0_p2wsh()); + assert_eq!(tx_add_output2_msg.sats, post_splice_channel_value); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output2_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 2); + let tx_complete_msg = match msg_events[0] { + MessageSendEvent::SendTxComplete { ref node_id, ref msg } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + (*msg).clone() + }, + _ => panic!("Unexpected event"), + }; + let msg_commitment_signed_from_0 = match msg_events[1] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + if let Event::FundingTransactionReadyForSigning { + channel_id, + counterparty_node_id, + mut unsigned_transaction, + .. + } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); + assert_eq!(unsigned_transaction.input.len(), 2); + // This is the previous funding tx input, already signed (partially) + assert_eq!(unsigned_transaction.input[0].witness.len(), 4); + // This is the extra input, not yet signed + assert_eq!(unsigned_transaction.input[1].witness.len(), 0); + + // Placeholder for signature on the contributed input + let mut witness1 = Witness::new(); + witness1.push(vec![0]); + unsigned_transaction.input[1].witness = witness1; + + let _res = initiator_node.node.funding_transaction_signed(&channel_id, &counterparty_node_id, unsigned_transaction).unwrap(); + } else { panic!(); } + + let expected_splice_funding_txid = "3d43a4cafc53c75e794e7122c120effc03ad96c15904f8e0de134187f2982426"; + // check new funding tx + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.funding_txo.unwrap().txid.to_string(), expected_splice_funding_txid); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + // First messsage is commitment_signed, second is tx_signatures (see below for more) + assert_eq!(msg_events.len(), 1); + let msg_commitment_signed_from_1 = match msg_events[0] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(*node_id, initiator_node.node.get_our_node_id()); + let res = updates.commitment_signed.clone(); + res + }, + _ => panic!("Unexpected event {:?}", msg_events[0]), + }; + + // check new funding tx (acceptor side) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.funding_txo.unwrap().txid.to_string(), expected_splice_funding_txid); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + // Handle the initial commitment_signed exchange. Order is not important here. + let _res = acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); + let _res = initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); + check_added_monitors(&initiator_node, 1); + check_added_monitors(&acceptor_node, 1); + + // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures + // only after receiving tx_signatures from the non-initiator in this case. + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert!(msg_events.is_empty()); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 1); + let tx_signatures_1 = match msg_events[0] { + MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => { + assert_eq!(*node_id, initiator_node.node.get_our_node_id()); + // Here we only get the signature for the shared input + assert_eq!(msg.witnesses.len(), 0); + assert!(msg.tlvs.is_some()); + msg + }, + _ => panic!("Unexpected event {:?}", msg_events[0]), + }; + + let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_1); + let events_0 = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 0); + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 1); + let tx_signatures_0 = match msg_events[0] { + MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + // Here we get the witnesses for the two inputs: + // - the custom input, and + // - the previous funding tx, also in the tlvs + assert_eq!(msg.witnesses.len(), 2); + assert_eq!(msg.witnesses[0].len(), 4); + assert_eq!(msg.witnesses[1].len(), 1); + assert!(msg.tlvs.is_some()); + msg + }, + _ => panic!("Unexpected event {:?}", msg_events[0]), + }; + let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_0); + + let events_1 = acceptor_node.node.get_and_clear_pending_events(); + assert_eq!(events_1.len(), 0); + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 2); + let broadcasted_splice_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone(); + let expected_funding_tx = "02000000000102bed7db75ec67e1466537ae61e9f375c0c54a74549f5de496d8c8b656aa5e896d010000000000000000a29ca934f2f9e07815e35099881dc8c0de1847ce0f00154de3d66c0133384b7900000000000000000002ea37000000000000160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665fc0d4010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3a040047304402200900f05f4b09711287cbe827d9bdfe3206c12e6b930d4f68b4a227c011d2f49002205dd7218638af091d32b7afe454a258553b93d57cf681c9e7ca4e5d0ce79bf8cb014730440220717835e6d81afe5a8a955b81b600feb12b1e16f45a7b295a24fd01064918251202201601c199f988bd57b4138c1369ede9b5ed8540ac77fd7b7541d5d837a424365b01475221026a2a07ee436cc6745aed846a76275f66ed16a469706aca3aa581f20b85d85396210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db352ae01010000000000"; + assert_eq!(broadcasted_splice_tx.encode().len(), 389); + assert_eq!(broadcasted_splice_tx.encode().as_hex().to_string(), expected_funding_tx); + let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel_id1); + let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel_id1); + verify_splice_funding_tx(&broadcasted_splice_tx, &broadcasted_funding_tx.txid(), post_splice_channel_value, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); + + // Check that funding transaction has been broadcasted on acceptor side as well + assert_eq!(chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 2); + let broadcasted_splice_tx_acc = chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone(); + assert_eq!(broadcasted_splice_tx_acc.encode().len(), 389); + assert_eq!(broadcasted_splice_tx_acc.encode().as_hex().to_string(), expected_funding_tx); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_splice_tx); + let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_splice_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); + // let _ev = get_event!(acceptor_node, Event::ChannelReady); + let events_0 = acceptor_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 0); + let _channel_update = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelUpdate, initiator_node.node.get_our_node_id()); + // let _announcement_signatures2 = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + // let _ev = get_event!(initiator_node, Event::ChannelReady); + let events_0 = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 0); + let _channel_update = get_event_msg!(initiator_node, MessageSendEvent::SendChannelUpdate, acceptor_node.node.get_our_node_id()); + // let _announcement_signatures1 = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // check new channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, 1000 * post_splice_channel_value); + assert_eq!(channel.funding_txo.unwrap().txid, broadcasted_splice_tx_acc.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // do the checks on acceptor side as well + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, 0); + assert_eq!(channel.funding_txo.unwrap().txid, broadcasted_splice_tx_acc.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // === End of Splicing + + // === Close channel, cooperatively + initiator_node.node.close_channel(&channel_id1, &acceptor_node.node.get_our_node_id()).unwrap(); + let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); + acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); + let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); + initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); + let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); +} + +/// #SPLICING Builds on test_channel_open_v2_and_close() +/// Splicing test, simple splice-in flow. Starts with opening a V2 channel first. +/// The steps are mostly on ChannelManager level. +#[test] +fn test_v2_payment_splice_in_payment() { + // Set up a network of 2 nodes + let cfg = UserConfig { + channel_handshake_config: ChannelHandshakeConfig { + announced_channel: true, + ..Default::default() + }, + ..Default::default() + }; + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Initiator and Acceptor nodes. Order matters, we want the case when initiator pubkey is larger. + let initiator_node_index = 0; + let acceptor_node_index = 1; + let initiator_node = &nodes[initiator_node_index]; + let acceptor_node = &nodes[acceptor_node_index]; + + // Instantiate channel parameters where we push the maximum msats given our funding satoshis + let channel_value_sat = 100000; // same as funding satoshis + + let expected_temporary_channel_id = "4cd87e42e1cf2813f604ec16e42297f1723c5dfc100593b83eead1588d30b671"; + let expected_funded_channel_id = "ca537cbb1f386065003906d85719965f67f22687600bc497b29c7d5d4736f0ce"; + + // Have node0 initiate a channel to node1 with aforementioned parameters + let channel_id_temp1 = initiator_node.node.create_dual_funded_channel(acceptor_node.node.get_our_node_id(), channel_value_sat, None, 42, None).unwrap(); + assert_eq!(channel_id_temp1.to_string(), expected_temporary_channel_id); + + // Extract the channel open message from node0 to node1 + let open_channel2_message = get_event_msg!(initiator_node, MessageSendEvent::SendOpenChannelV2, acceptor_node.node.get_our_node_id()); + assert_eq!(initiator_node.node.list_channels().len(), 1); + + let _res = acceptor_node.node.handle_open_channel_v2(&initiator_node.node.get_our_node_id(), &open_channel2_message.clone()); + // Extract the accept channel message from node1 to node0 + let accept_channel2_message = get_event_msg!(acceptor_node, MessageSendEvent::SendAcceptChannelV2, initiator_node.node.get_our_node_id()); + assert_eq!(accept_channel2_message.temporary_channel_id.to_string(), expected_temporary_channel_id); + + let _res = initiator_node.node.handle_accept_channel_v2(&acceptor_node.node.get_our_node_id(), &accept_channel2_message.clone()); + + // Note: FundingInputsContributionReady emitted + let events = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + let channel_id1 = match events[0] { + Event::FundingInputsContributionReady { channel_id, .. } => { + // Here we have the final channel_id now + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + channel_id + } + _ => panic!("FundingInputsContributionReady event missing {:?}", events[0]), + }; + + let extra_funding_input_sats = channel_value_sat + 35_000; + let custom_input_secret_key = SecretKey::from_slice(&[2; 32]).unwrap(); + let custom_input_pubkey = PublicKey::from_secret_key(&Secp256k1::new(), &custom_input_secret_key); + let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, extra_funding_input_sats, &custom_input_pubkey)]; + let _res = initiator_node.node.contribute_funding_inputs(&channel_id1, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); + + // initiator_node will generate a TxAddInput message to kickstart the interactive transaction construction protocol + let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + // First output we send is the change output. + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + assert_eq!(tx_add_output_msg.sats, channel_value_sat); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 2); + assert_event_type!(msg_events[0], MessageSendEvent::SendTxComplete); + assert_event_type!(msg_events[1], MessageSendEvent::UpdateHTLCs); + let msg_commitment_signed_from_0 = match msg_events[1] { + MessageSendEvent::UpdateHTLCs { ref updates, .. } => { + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + if let Event::FundingTransactionReadyForSigning { + channel_id, + counterparty_node_id, + mut unsigned_transaction, + .. + } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + let mut witness = Witness::new(); + witness.push(vec![0]); + unsigned_transaction.input[0].witness = witness; + let _res = initiator_node.node.funding_transaction_signed(&channel_id1, &counterparty_node_id, unsigned_transaction).unwrap(); + } else { panic!(); } + + let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + // First messsage is commitment_signed, second is tx_signatures (see below for more) + assert_eq!(msg_events.len(), 1); + let msg_commitment_signed_from_1 = match msg_events[0] { + MessageSendEvent::UpdateHTLCs { ref updates, .. } => { + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + + // Handle the initial commitment_signed exchange. Order is not important here. + acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); + initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); + check_added_monitors(&initiator_node, 1); + check_added_monitors(&acceptor_node, 1); + + // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures + // only after receiving tx_signatures from the non-initiator in this case. + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert!(msg_events.is_empty()); + let tx_signatures_from_1 = get_event_msg!(acceptor_node, MessageSendEvent::SendTxSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_from_1); + get_event!(initiator_node, Event::ChannelPending); + let tx_signatures_from_0 = get_event_msg!(initiator_node, MessageSendEvent::SendTxSignatures, acceptor_node.node.get_our_node_id()); + let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_from_0); + get_event!(acceptor_node, Event::ChannelPending); + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + let broadcasted_funding_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].clone(); + assert_eq!(broadcasted_funding_tx.encode().len(), 130); + assert_eq!(broadcasted_funding_tx.txid().to_string(), "6d895eaa56b6c8d896e45d9f54744ac5c075f3e961ae376546e167ec75dbd7be"); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_funding_tx); + let channel_ready_message1 = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_funding_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message1); + let _ev = get_event!(acceptor_node, Event::ChannelReady); + let announcement_signatures2 = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + let _ev = get_event!(initiator_node, Event::ChannelReady); + let announcement_signatures1 = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // let (announcement1, update1, update2) = create_chan_between_nodes_with_value_b(&initiator_node, &acceptor_node, &(channel_ready_message1, announcement_signatures1)); + // `update_nodes_with_chan_announce`(&nodes, initiator_node_index, acceptor_node_index, &announcement1, &update1, &update2); + + // check channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat); + assert_eq!(channel.confirmations.unwrap(), 10); + assert!(channel.funding_txo.is_some()); + } + // do checks on the acceptor node as well (capacity, etc.) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 0); + assert_eq!(channel.confirmations.unwrap(), 10); + assert!(channel.funding_txo.is_some()); + } + + // === Channel is now ready for normal operation + + // === Send a payment + let payment1_amount_msat = 6001_000; + + let payment_res = send_payment(&initiator_node, &[acceptor_node], payment1_amount_msat); + + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat - payment1_amount_msat); + assert!(channel.funding_txo.is_some()); + } + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert!(channel.is_usable); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, payment1_amount_msat); + assert!(channel.funding_txo.is_some()); + } + + // === Start of Splicing + println!("Start of Splicing ..., channel_id {}", channel_id1); + + // Amount being added to the channel through the splice-in + let splice_in_sats: u64 = 20000; + let post_splice_channel_value = channel_value_sat + splice_in_sats; + let funding_feerate_perkw = 1024; // TODO + let locktime = 0; // TODO + + // Initiate splice-in (on node0) + let _res = initiator_node.node.splice_channel(&channel_id1, &acceptor_node.node.get_our_node_id(), splice_in_sats as i64, funding_feerate_perkw, locktime).unwrap(); + // Extract the splice message from node0 to node1 + let splice_msg = get_event_msg!(initiator_node, MessageSendEvent::SendSplice, acceptor_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_splice(&initiator_node.node.get_our_node_id(), &splice_msg); + // Extract the splice_ack message from node1 to node0 + let splice_ack_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendSpliceAck, initiator_node.node.get_our_node_id()); + + // check that capacity has been updated, channel is not usable, and funding tx is unset + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(!channel.is_usable); + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, payment1_amount_msat); + assert!(channel.funding_txo.is_none()); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + let _res = initiator_node.node.handle_splice_ack(&acceptor_node.node.get_our_node_id(), &splice_ack_msg); + + // Note: SpliceAckedInputsContributionReady emitted + let events = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::SpliceAckedInputsContributionReady { channel_id, pre_channel_value_satoshis, post_channel_value_satoshis, holder_funding_satoshis, counterparty_funding_satoshis, .. } => { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(pre_channel_value_satoshis, channel_value_sat); + assert_eq!(post_channel_value_satoshis, post_splice_channel_value); + assert_eq!(holder_funding_satoshis, post_splice_channel_value); + assert_eq!(counterparty_funding_satoshis, 0); + }, + _ => panic!("SpliceAckedInputsContributionReady event missing {:?}", events[0]), + }; + + // check that capacity has been updated, channel is not usable, and funding tx is unset + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(!channel.is_usable); + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, 1000 * post_splice_channel_value - payment1_amount_msat); + assert!(channel.funding_txo.is_none()); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + let extra_splice_funding_input_sats = 35_000; + let funding_inputs = vec![create_custom_dual_funding_input_with_pubkey(&initiator_node, extra_splice_funding_input_sats, &custom_input_pubkey)]; + + let _res = initiator_node.node.contribute_funding_inputs(&channel_id1, &acceptor_node.node.get_our_node_id(), funding_inputs).unwrap(); + + // Initiator_node will generate first TxAddInput message + let tx_add_input_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + assert_eq!(tx_add_input_msg.prevtx.0.output[tx_add_input_msg.prevtx_out as usize].value, channel_value_sat); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + // second TxAddInput message for the second input + let tx_add_input2_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddInput, acceptor_node.node.get_our_node_id()); + assert_eq!(tx_add_input2_msg.prevtx.0.output[tx_add_input2_msg.prevtx_out as usize].value, extra_splice_funding_input_sats); + + let _res = acceptor_node.node.handle_tx_add_input(&initiator_node.node.get_our_node_id(), &tx_add_input2_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + // First TxAddOutput is for the change output + let tx_add_output_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + assert!(tx_add_output_msg.script.is_v0_p2wpkh()); + assert_eq!(tx_add_output_msg.sats, 14314); // extra_splice_input_sats - splice_in_sats - fee + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output_msg); + let tx_complete_msg = get_event_msg!(&acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + // TxAddOutput for the splice funding + let tx_add_output2_msg = get_event_msg!(&initiator_node, MessageSendEvent::SendTxAddOutput, acceptor_node.node.get_our_node_id()); + // Check we get the channel funding output. + assert!(tx_add_output2_msg.script.is_v0_p2wsh()); + assert_eq!(tx_add_output2_msg.sats, post_splice_channel_value); + + let _res = acceptor_node.node.handle_tx_add_output(&initiator_node.node.get_our_node_id(), &tx_add_output2_msg); + let tx_complete_msg = get_event_msg!(acceptor_node, MessageSendEvent::SendTxComplete, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_tx_complete(&acceptor_node.node.get_our_node_id(), &tx_complete_msg); + + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 2); + let tx_complete_msg = match msg_events[0] { + MessageSendEvent::SendTxComplete { ref node_id, ref msg } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + (*msg).clone() + }, + _ => panic!("Unexpected event"), + }; + let msg_commitment_signed_from_0 = match msg_events[1] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + updates.commitment_signed.clone() + }, + _ => panic!("Unexpected event"), + }; + if let Event::FundingTransactionReadyForSigning { + channel_id, + counterparty_node_id, + mut unsigned_transaction, + .. + } = get_event!(initiator_node, Event::FundingTransactionReadyForSigning) { + assert_eq!(channel_id.to_string(), expected_funded_channel_id); + assert_eq!(counterparty_node_id, acceptor_node.node.get_our_node_id()); + assert_eq!(unsigned_transaction.input.len(), 2); + // This is the previous funding tx input, already signed (partially) + assert_eq!(unsigned_transaction.input[0].witness.len(), 4); + // This is the extra input, not yet signed + assert_eq!(unsigned_transaction.input[1].witness.len(), 0); + + // Placeholder for signature on the contributed input + let mut witness1 = Witness::new(); + witness1.push(vec![0]); + unsigned_transaction.input[1].witness = witness1; + + let _res = initiator_node.node.funding_transaction_signed(&channel_id, &counterparty_node_id, unsigned_transaction).unwrap(); + } else { panic!(); } + + let expected_splice_funding_txid = "039ce00c0a71623b15f7906882a225b4fdc763548c5caf9f71ff7c5bebfe16a4"; + // check new funding tx + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.funding_txo.unwrap().txid.to_string(), expected_splice_funding_txid); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + let _res = acceptor_node.node.handle_tx_complete(&initiator_node.node.get_our_node_id(), &tx_complete_msg); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + // First messsage is commitment_signed, second is tx_signatures (see below for more) + assert_eq!(msg_events.len(), 1); + let msg_commitment_signed_from_1 = match msg_events[0] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(*node_id, initiator_node.node.get_our_node_id()); + let res = updates.commitment_signed.clone(); + res + }, + _ => panic!("Unexpected event {:?}", msg_events[0]), + }; + + // check new funding tx (acceptor side) + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert!(!channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.funding_txo.unwrap().txid.to_string(), expected_splice_funding_txid); + assert_eq!(channel.confirmations.unwrap(), 0); + } + + // Handle the initial commitment_signed exchange. Order is not important here. + let _res = acceptor_node.node.handle_commitment_signed(&initiator_node.node.get_our_node_id(), &msg_commitment_signed_from_0); + let _res = initiator_node.node.handle_commitment_signed(&acceptor_node.node.get_our_node_id(), &msg_commitment_signed_from_1); + check_added_monitors(&initiator_node, 1); + check_added_monitors(&acceptor_node, 1); + + // The initiator is the only party that contributed any inputs so they should definitely be the one to send tx_signatures + // only after receiving tx_signatures from the non-initiator in this case. + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert!(msg_events.is_empty()); + let msg_events = acceptor_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 1); + let tx_signatures_1 = match msg_events[0] { + MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => { + assert_eq!(*node_id, initiator_node.node.get_our_node_id()); + // Here we only get the signature for the shared input + assert_eq!(msg.witnesses.len(), 0); + assert!(msg.tlvs.is_some()); + msg + }, + _ => panic!("Unexpected event {:?}", msg_events[0]), + }; + + let _res = initiator_node.node.handle_tx_signatures(&acceptor_node.node.get_our_node_id(), &tx_signatures_1); + let events_0 = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 0); + let msg_events = initiator_node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 1); + let tx_signatures_0 = match msg_events[0] { + MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => { + assert_eq!(*node_id, acceptor_node.node.get_our_node_id()); + // Here we get the witnesses for the two inputs: + // - the custom input, and + // - the previous funding tx, also in the tlvs + assert_eq!(msg.witnesses.len(), 2); + assert_eq!(msg.witnesses[0].len(), 4); + assert_eq!(msg.witnesses[1].len(), 1); + assert!(msg.tlvs.is_some()); + msg + }, + _ => panic!("Unexpected event {:?}", msg_events[0]), + }; + let _res = acceptor_node.node.handle_tx_signatures(&initiator_node.node.get_our_node_id(), &tx_signatures_0); + + let events_1 = acceptor_node.node.get_and_clear_pending_events(); + assert_eq!(events_1.len(), 0); + + // Check that funding transaction has been broadcasted + assert_eq!(chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 2); + let broadcasted_splice_tx = chanmon_cfgs[initiator_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone(); + let expected_funding_tx = "02000000000102bed7db75ec67e1466537ae61e9f375c0c54a74549f5de496d8c8b656aa5e896d010000000000000000a29ca934f2f9e07815e35099881dc8c0de1847ce0f00154de3d66c0133384b7900000000000000000002c0d4010000000000220020203e7308dfff4f1923d21ca23f45545965d1f6b44ea0e2184c5db4d649a61c3aea37000000000000160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665f040047304402205c607d85347d9f22874500df846f27132ba0cc63cdb758a9cbecfc995bedc3780220200750390185cf03b34896dad01cd885208f0e1105c4cf88145f6342ab2fbe740147304402207325b2cd0eebe00df23f84487ccb5395b6983b35c6206b7aa3f2f47b6f22e449022039b1d3a8d4446f2a466e4f6883eb70b9d805ae5b8ddb7d2e6247c39a75fd411c01475221026a2a07ee436cc6745aed846a76275f66ed16a469706aca3aa581f20b85d85396210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db352ae01010000000000"; + assert_eq!(broadcasted_splice_tx.encode().len(), 389); + assert_eq!(broadcasted_splice_tx.encode().as_hex().to_string(), expected_funding_tx); + let initiator_funding_key = get_funding_key(&initiator_node, &acceptor_node, &channel_id1); + let acceptor_funding_key = get_funding_key(&acceptor_node, &initiator_node, &channel_id1); + verify_splice_funding_tx(&broadcasted_splice_tx, &broadcasted_funding_tx.txid(), post_splice_channel_value, channel_value_sat, &initiator_funding_key, &acceptor_funding_key); + + // Check that funding transaction has been broadcasted on acceptor side as well + assert_eq!(chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 2); + let broadcasted_splice_tx_acc = chanmon_cfgs[acceptor_node_index].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone(); + assert_eq!(broadcasted_splice_tx_acc.encode().len(), 389); + assert_eq!(broadcasted_splice_tx_acc.encode().as_hex().to_string(), expected_funding_tx); + + // Simulate confirmation of the funding tx + confirm_transaction(&initiator_node, &broadcasted_splice_tx); + let channel_ready_message = get_event_msg!(initiator_node, MessageSendEvent::SendChannelReady, acceptor_node.node.get_our_node_id()); + + confirm_transaction(&acceptor_node, &broadcasted_splice_tx); + let channel_ready_message2 = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelReady, initiator_node.node.get_our_node_id()); + + let _res = acceptor_node.node.handle_channel_ready(&initiator_node.node.get_our_node_id(), &channel_ready_message); + // let _ev = get_event!(acceptor_node, Event::ChannelReady); + let events_0 = acceptor_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 0); + let _channel_update = get_event_msg!(acceptor_node, MessageSendEvent::SendChannelUpdate, initiator_node.node.get_our_node_id()); + // let announcement_signatures2 = get_event_msg!(acceptor_node, MessageSendEvent::SendAnnouncementSignatures, initiator_node.node.get_our_node_id()); + + let _res = initiator_node.node.handle_channel_ready(&acceptor_node.node.get_our_node_id(), &channel_ready_message2); + // let _ev = get_event!(initiator_node, Event::ChannelReady); + let events_0 = initiator_node.node.get_and_clear_pending_events(); + assert_eq!(events_0.len(), 0); + let _channel_update = get_event_msg!(initiator_node, MessageSendEvent::SendChannelUpdate, acceptor_node.node.get_our_node_id()); + // let announcement_signatures1 = get_event_msg!(initiator_node, MessageSendEvent::SendAnnouncementSignatures, acceptor_node.node.get_our_node_id()); + + // check new channel capacity and other parameters + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, 1000 * post_splice_channel_value - payment1_amount_msat); + assert_eq!(channel.funding_txo.unwrap().txid, broadcasted_splice_tx_acc.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // do the checks on acceptor side as well + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_id.to_string(), expected_funded_channel_id); + assert!(channel.is_channel_ready); + assert_eq!(channel.channel_value_satoshis, post_splice_channel_value); + assert_eq!(channel.balance_msat, payment1_amount_msat); + assert_eq!(channel.funding_txo.unwrap().txid, broadcasted_splice_tx_acc.txid()); + assert_eq!(channel.confirmations.unwrap(), 10); + } + + // === End of Splicing + + // === Send another payment + let payment2_amount_msat = 3002_000; + + let _payment_res = send_payment(&initiator_node, &[acceptor_node], payment2_amount_msat); + + // check changed balances + assert_eq!(initiator_node.node.list_channels().len(), 1); + { + let channel = &initiator_node.node.list_channels()[0]; + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, 1000 * channel_value_sat - payment1_amount_msat - payment2_amount_msat); + } + // do checks on the acceptor node as well + assert_eq!(acceptor_node.node.list_channels().len(), 1); + { + let channel = &acceptor_node.node.list_channels()[0]; + assert_eq!(channel.channel_value_satoshis, channel_value_sat); + assert_eq!(channel.balance_msat, payment1_amount_msat + payment2_amount_msat); + } + + // === Close channel, cooperatively + initiator_node.node.close_channel(&channel_id1, &acceptor_node.node.get_our_node_id()).unwrap(); + let node0_shutdown_message = get_event_msg!(initiator_node, MessageSendEvent::SendShutdown, acceptor_node.node.get_our_node_id()); + acceptor_node.node.handle_shutdown(&initiator_node.node.get_our_node_id(), &node0_shutdown_message); + let nodes_1_shutdown = get_event_msg!(acceptor_node, MessageSendEvent::SendShutdown, initiator_node.node.get_our_node_id()); + initiator_node.node.handle_shutdown(&acceptor_node.node.get_our_node_id(), &nodes_1_shutdown); + let _ = get_event_msg!(initiator_node, MessageSendEvent::SendClosingSigned, acceptor_node.node.get_our_node_id()); +} diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs index 84e6718dc04..0fd1bbb4ab0 100644 --- a/lightning/src/ln/interactivetxs.rs +++ b/lightning/src/ln/interactivetxs.rs @@ -936,7 +936,7 @@ mod tests { entropy_source, channel_id, FEERATE_FLOOR_SATS_PER_KW * 10, holder_node_id, counterparty_node_id, true, tx_locktime, session.inputs_a.clone(), session.outputs_a.clone() ); let (mut constructor_b, first_message_b) = InteractiveTxConstructor::new( - entropy_source, channel_id, FEERATE_FLOOR_SATS_PER_KW * 10, holder_node_id, counterparty_node_id, false, tx_locktime, session.inputs_b.clone(), session.outputs_b.clone() + entropy_source, channel_id, FEERATE_FLOOR_SATS_PER_KW * 10, holder_node_id, counterparty_node_id, false, tx_locktime, session.inputs_b.clone(), session.outputs_b.clone() ); let handle_message_send = |msg: InteractiveTxMessageSend, for_constructor: &mut InteractiveTxConstructor| { diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 4267008520b..68d60adea9f 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -23,6 +23,7 @@ pub mod chan_utils; pub mod features; pub mod script; mod channel_id; +mod channel_splice; #[cfg(fuzzing)] pub mod peer_channel_encryptor; @@ -54,6 +55,9 @@ mod blinded_payment_tests; mod functional_tests; #[cfg(test)] #[allow(unused_mut)] +mod functional_tests_splice; +#[cfg(test)] +#[allow(unused_mut)] mod payment_tests; #[cfg(test)] #[allow(unused_mut)] diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index bd68cb1e8ca..5622476b0d2 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -274,7 +274,7 @@ impl ChannelMessageHandler for ErroringMessageHandler { fn handle_channel_reestablish(&self, their_node_id: &PublicKey, msg: &msgs::ChannelReestablish) { ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id); } - // msgs::ChannelUpdate does not contain the channel_id field, so we just drop them. + // msgs::ChannelUpdate does not contain the channel_id field, so we just drop them. fn handle_channel_update(&self, _their_node_id: &PublicKey, _msg: &msgs::ChannelUpdate) {} fn peer_disconnected(&self, _their_node_id: &PublicKey) {} fn peer_connected(&self, _their_node_id: &PublicKey, _init: &msgs::Init, _inbound: bool) -> Result<(), ()> { Ok(()) } diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 6a44bc07438..3913e0a3ef3 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -629,8 +629,8 @@ pub trait ChannelSigner { fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters); /// #SPLICING - /// Similar to provide_channel_parameters(), but can be called a second time (or even more). Also update channel value (capacity) - fn reprovide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters, channel_value_satoshis: u64); + /// Update the channel value, and also reset the channel parameters + fn update_channel_value(&mut self, channel_value_satoshis: u64); } /// Specifies the recipient of an invoice. @@ -1113,13 +1113,11 @@ impl ChannelSigner for InMemorySigner { } /// #SPLICING - fn reprovide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters, channel_value_satoshis: u64) { - assert!(self.channel_parameters.is_some()); - assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated"); - self.channel_parameters = Some(channel_parameters.clone()); - + /// Update the channel value, and also reset the channel parameters + fn update_channel_value(&mut self, channel_value_satoshis: u64) { self.channel_value_satoshis = channel_value_satoshis; - // println!("reprovide_channel_parameters channel_value {}", self.channel_value_satoshis); + assert!(self.channel_parameters.is_some()); + self.channel_parameters = None; } } @@ -1135,7 +1133,6 @@ impl EcdsaChannelSigner for InMemorySigner { let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); let built_tx = trusted_tx.built_transaction(); - // println!("sign_counterparty_commitment tx {:?} {:?} channel_value_satoshis {}", built_tx.txid, built_tx.transaction.encode(), self.channel_value_satoshis); let commitment_sig = built_tx.sign_counterparty_commitment(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx); let commitment_txid = built_tx.txid; @@ -1271,11 +1268,9 @@ impl EcdsaChannelSigner for InMemorySigner { /// #SPLICING /// #SPLICE-SIG fn sign_splicing_funding_input(&self, splicing_tx: &Transaction, splice_prev_funding_input_index: u16, splice_prev_funding_input_value: u64, redeem_script: &Script, secp_ctx: &Secp256k1) -> Result { - // println!("sign_splicing_funding_input txlen {} idx {} val {} tx {}", splicing_tx.encode().len(), splice_prev_funding_input_index, prev_funding_value, splicing_tx.encode().to_hex()); let sighash = &sighash::SighashCache::new(splicing_tx).segwit_signature_hash(splice_prev_funding_input_index as usize, &redeem_script, splice_prev_funding_input_value, EcdsaSighashType::All).unwrap()[..]; let msg = hash_to_message!(sighash); let sig = sign(secp_ctx, &msg, &self.funding_key); - // println!("sign_splicing_funding_input hash {} msg{} pubkey {} sig {} val {}", sighash.to_hex(), msg.to_hex(), &self.funding_key.public_key(secp_ctx), sig.serialize_der().to_hex(), splice_prev_funding_input_value); Ok(sig) } diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index 4d868fc493e..fdfcdea0a50 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -166,8 +166,8 @@ impl ChannelSigner for TestChannelSigner { } /// #SPLICING - fn reprovide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters, channel_value_satoshis: u64) { - self.inner.reprovide_channel_parameters(channel_parameters, channel_value_satoshis) + fn update_channel_value(&mut self, channel_value_satoshis: u64) { + self.inner.update_channel_value(channel_value_satoshis) } }