mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-12 14:10:18 +01:00
Handle initial commitment_signed for V2 channels
This commit is contained in:
parent
684b3b7244
commit
982e25de0e
2 changed files with 462 additions and 53 deletions
|
@ -32,7 +32,7 @@ use crate::types::payment::{PaymentPreimage, PaymentHash};
|
||||||
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
|
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
|
||||||
use crate::ln::interactivetxs::{
|
use crate::ln::interactivetxs::{
|
||||||
get_output_weight, HandleTxCompleteResult, InteractiveTxConstructor, InteractiveTxConstructorArgs,
|
get_output_weight, HandleTxCompleteResult, InteractiveTxConstructor, InteractiveTxConstructorArgs,
|
||||||
InteractiveTxMessageSend, InteractiveTxMessageSendResult, TX_COMMON_FIELDS_WEIGHT,
|
InteractiveTxSigningSession, InteractiveTxMessageSendResult, TX_COMMON_FIELDS_WEIGHT,
|
||||||
};
|
};
|
||||||
use crate::ln::msgs;
|
use crate::ln::msgs;
|
||||||
use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError};
|
use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError};
|
||||||
|
@ -55,7 +55,7 @@ use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Channel
|
||||||
use crate::chain::transaction::{OutPoint, TransactionData};
|
use crate::chain::transaction::{OutPoint, TransactionData};
|
||||||
use crate::sign::ecdsa::EcdsaChannelSigner;
|
use crate::sign::ecdsa::EcdsaChannelSigner;
|
||||||
use crate::sign::{EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
|
use crate::sign::{EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
|
||||||
use crate::events::ClosureReason;
|
use crate::events::{ClosureReason, Event};
|
||||||
use crate::routing::gossip::NodeId;
|
use crate::routing::gossip::NodeId;
|
||||||
use crate::util::ser::{Readable, ReadableArgs, TransactionU16LenLimited, Writeable, Writer};
|
use crate::util::ser::{Readable, ReadableArgs, TransactionU16LenLimited, Writeable, Writer};
|
||||||
use crate::util::logger::{Logger, Record, WithContext};
|
use crate::util::logger::{Logger, Record, WithContext};
|
||||||
|
@ -1642,6 +1642,20 @@ impl<SP: Deref> InitialRemoteCommitmentReceiver<SP> for InboundV1Channel<SP> whe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<SP: Deref> InitialRemoteCommitmentReceiver<SP> for Channel<SP> where SP::Target: SignerProvider {
|
||||||
|
fn context(&self) -> &ChannelContext<SP> {
|
||||||
|
&self.context
|
||||||
|
}
|
||||||
|
|
||||||
|
fn context_mut(&mut self) -> &mut ChannelContext<SP> {
|
||||||
|
&mut self.context
|
||||||
|
}
|
||||||
|
|
||||||
|
fn received_msg(&self) -> &'static str {
|
||||||
|
"commitment_signed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) trait InteractivelyFunded<SP: Deref> where SP::Target: SignerProvider {
|
pub(super) trait InteractivelyFunded<SP: Deref> where SP::Target: SignerProvider {
|
||||||
fn context(&self) -> &ChannelContext<SP>;
|
fn context(&self) -> &ChannelContext<SP>;
|
||||||
|
|
||||||
|
@ -1705,6 +1719,87 @@ pub(super) trait InteractivelyFunded<SP: Deref> where SP::Target: SignerProvider
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn funding_tx_constructed<L: Deref>(
|
||||||
|
&mut self, signing_session: &mut InteractiveTxSigningSession, logger: &L
|
||||||
|
) -> Result<(msgs::CommitmentSigned, Option<Event>), ChannelError>
|
||||||
|
where
|
||||||
|
L::Target: Logger
|
||||||
|
{
|
||||||
|
let our_funding_satoshis = self.dual_funding_context().our_funding_satoshis;
|
||||||
|
let context = self.context_mut();
|
||||||
|
|
||||||
|
let mut output_index = None;
|
||||||
|
let expected_spk = context.get_funding_redeemscript().to_p2wsh();
|
||||||
|
for (idx, outp) in signing_session.unsigned_tx.outputs().enumerate() {
|
||||||
|
if outp.script_pubkey() == &expected_spk && outp.value() == context.get_value_satoshis() {
|
||||||
|
if output_index.is_some() {
|
||||||
|
return Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"Multiple outputs matched the expected script and value".to_owned(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
output_index = Some(idx as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let outpoint = if let Some(output_index) = output_index {
|
||||||
|
OutPoint { txid: signing_session.unsigned_tx.compute_txid(), index: output_index }
|
||||||
|
} else {
|
||||||
|
return Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"No output matched the funding script_pubkey".to_owned(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
context.channel_transaction_parameters.funding_outpoint = Some(outpoint);
|
||||||
|
context.holder_signer.as_mut().provide_channel_parameters(&context.channel_transaction_parameters);
|
||||||
|
|
||||||
|
let commitment_signed = context.get_initial_commitment_signed(logger);
|
||||||
|
let commitment_signed = match commitment_signed {
|
||||||
|
Ok(commitment_signed) => {
|
||||||
|
context.funding_transaction = Some(signing_session.unsigned_tx.build_unsigned_tx());
|
||||||
|
commitment_signed
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
context.channel_transaction_parameters.funding_outpoint = None;
|
||||||
|
return Err(ChannelError::Close((err.to_string(), ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) })))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let funding_ready_for_sig_event = None;
|
||||||
|
if signing_session.local_inputs_count() == 0 {
|
||||||
|
debug_assert_eq!(our_funding_satoshis, 0);
|
||||||
|
if signing_session.provide_holder_witnesses(context.channel_id, Vec::new()).is_err() {
|
||||||
|
debug_assert!(
|
||||||
|
false,
|
||||||
|
"Zero inputs were provided & zero witnesses were provided, but a count mismatch was somehow found",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO(dual_funding): Send event for signing if we've contributed funds.
|
||||||
|
// Inform the user that SIGHASH_ALL must be used for all signatures when contributing
|
||||||
|
// inputs/signatures.
|
||||||
|
// Also warn the user that we don't do anything to prevent the counterparty from
|
||||||
|
// providing non-standard witnesses which will prevent the funding transaction from
|
||||||
|
// confirming. This warning must appear in doc comments wherever the user is contributing
|
||||||
|
// funds, whether they are initiator or acceptor.
|
||||||
|
//
|
||||||
|
// The following warning can be used when the APIs allowing contributing inputs become available:
|
||||||
|
// <div class="warning">
|
||||||
|
// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
|
||||||
|
// will prevent the funding transaction from being relayed on the bitcoin network and hence being
|
||||||
|
// confirmed.
|
||||||
|
// </div>
|
||||||
|
}
|
||||||
|
|
||||||
|
context.channel_state = ChannelState::FundingNegotiated;
|
||||||
|
|
||||||
|
// Clear the interactive transaction constructor
|
||||||
|
self.interactive_tx_constructor_mut().take();
|
||||||
|
|
||||||
|
Ok((commitment_signed, funding_ready_for_sig_event))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SP: Deref> InteractivelyFunded<SP> for OutboundV2Channel<SP> where SP::Target: SignerProvider {
|
impl<SP: Deref> InteractivelyFunded<SP> for OutboundV2Channel<SP> where SP::Target: SignerProvider {
|
||||||
|
@ -3079,7 +3174,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the redeemscript for the funding transaction output (ie the funding transaction output
|
/// Gets the redeemscript for the funding transaction output (ie the funding transaction output
|
||||||
/// pays to get_funding_redeemscript().to_v0_p2wsh()).
|
/// pays to get_funding_redeemscript().to_p2wsh()).
|
||||||
/// Panics if called before accept_channel/InboundV1Channel::new
|
/// Panics if called before accept_channel/InboundV1Channel::new
|
||||||
pub fn get_funding_redeemscript(&self) -> ScriptBuf {
|
pub fn get_funding_redeemscript(&self) -> ScriptBuf {
|
||||||
make_funding_redeemscript(&self.get_holder_pubkeys().funding_pubkey, self.counterparty_funding_pubkey())
|
make_funding_redeemscript(&self.get_holder_pubkeys().funding_pubkey, self.counterparty_funding_pubkey())
|
||||||
|
@ -3867,14 +3962,75 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interactive transaction construction
|
/// Asserts that the commitment tx numbers have not advanced from their initial number.
|
||||||
|
fn assert_no_commitment_advancement(&self, msg_name: &str) {
|
||||||
pub fn tx_signatures(&self, msg: &msgs::TxSignatures) -> Result<InteractiveTxMessageSend, ChannelError> {
|
if self.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
|
||||||
todo!();
|
self.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
|
||||||
|
self.holder_commitment_point.transaction_number() != INITIAL_COMMITMENT_NUMBER {
|
||||||
|
debug_assert!(false, "Should not have advanced channel commitment tx numbers prior to {}",
|
||||||
|
msg_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tx_abort(&self, msg: &msgs::TxAbort) -> Result<InteractiveTxMessageSend, ChannelError> {
|
fn get_initial_counterparty_commitment_signature<L: Deref>(
|
||||||
todo!();
|
&self, logger: &L
|
||||||
|
) -> Result<Signature, ChannelError>
|
||||||
|
where
|
||||||
|
SP::Target: SignerProvider,
|
||||||
|
L::Target: Logger
|
||||||
|
{
|
||||||
|
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(ref ecdsa) => {
|
||||||
|
ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), Vec::new(), &self.secp_ctx)
|
||||||
|
.map(|(signature, _)| signature)
|
||||||
|
.map_err(|_| ChannelError::Close(
|
||||||
|
(
|
||||||
|
"Failed to get signatures for new commitment_signed".to_owned(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
// TODO (taproot|arik)
|
||||||
|
#[cfg(taproot)]
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_initial_commitment_signed<L: Deref>(
|
||||||
|
&mut self, logger: &L
|
||||||
|
) -> Result<msgs::CommitmentSigned, ChannelError>
|
||||||
|
where
|
||||||
|
SP::Target: SignerProvider,
|
||||||
|
L::Target: Logger
|
||||||
|
{
|
||||||
|
if !matches!(
|
||||||
|
self.channel_state, ChannelState::NegotiatingFunding(flags)
|
||||||
|
if flags == (NegotiatingFundingFlags::OUR_INIT_SENT | NegotiatingFundingFlags::THEIR_INIT_SENT)) {
|
||||||
|
panic!("Tried to get an initial commitment_signed messsage at a time other than immediately after initial handshake completion (or tried to get funding_created twice)");
|
||||||
|
}
|
||||||
|
self.assert_no_commitment_advancement("initial commitment_signed");
|
||||||
|
|
||||||
|
let signature = match self.get_initial_counterparty_commitment_signature(logger) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(e) => {
|
||||||
|
log_error!(logger, "Got bad signatures: {:?}!", e);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log_info!(logger, "Generated commitment_signed for peer for channel {}", &self.channel_id());
|
||||||
|
|
||||||
|
Ok(msgs::CommitmentSigned {
|
||||||
|
channel_id: self.channel_id,
|
||||||
|
htlc_signatures: vec![],
|
||||||
|
signature,
|
||||||
|
batch: None,
|
||||||
|
#[cfg(taproot)]
|
||||||
|
partial_signature_with_nonce: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3978,8 +4134,6 @@ pub(super) fn calculate_our_funding_satoshis(
|
||||||
pub(super) struct DualFundingChannelContext {
|
pub(super) struct DualFundingChannelContext {
|
||||||
/// The amount in satoshis we will be contributing to the channel.
|
/// The amount in satoshis we will be contributing to the channel.
|
||||||
pub our_funding_satoshis: u64,
|
pub our_funding_satoshis: u64,
|
||||||
/// The amount in satoshis our counterparty will be contributing to the channel.
|
|
||||||
pub their_funding_satoshis: u64,
|
|
||||||
/// The funding transaction locktime suggested by the initiator. If set by us, it is always set
|
/// The funding transaction locktime suggested by the initiator. If set by us, it is always set
|
||||||
/// to the current block height to align incentives against fee-sniping.
|
/// to the current block height to align incentives against fee-sniping.
|
||||||
pub funding_tx_locktime: LockTime,
|
pub funding_tx_locktime: LockTime,
|
||||||
|
@ -3997,6 +4151,7 @@ pub(super) struct DualFundingChannelContext {
|
||||||
// Counterparty designates channel data owned by the another channel participant entity.
|
// Counterparty designates channel data owned by the another channel participant entity.
|
||||||
pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
|
pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
|
||||||
pub context: ChannelContext<SP>,
|
pub context: ChannelContext<SP>,
|
||||||
|
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, fuzzing))]
|
#[cfg(any(test, fuzzing))]
|
||||||
|
@ -4776,6 +4931,33 @@ impl<SP: Deref> Channel<SP> where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn commitment_signed_initial_v2<L: Deref>(
|
||||||
|
&mut self, msg: &msgs::CommitmentSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
|
||||||
|
) -> Result<ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>, ChannelError>
|
||||||
|
where L::Target: Logger
|
||||||
|
{
|
||||||
|
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
|
||||||
|
return Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"Received initial commitment_signed before funding transaction constructed!".to_owned(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
self.context.assert_no_commitment_advancement("initial commitment_signed");
|
||||||
|
|
||||||
|
let (channel_monitor, _) = self.initial_commitment_signed(
|
||||||
|
self.context.channel_id(), msg.signature,
|
||||||
|
self.context.cur_counterparty_commitment_transaction_number, best_block, signer_provider, logger)?;
|
||||||
|
|
||||||
|
log_info!(logger, "Received initial commitment_signed from peer for channel {}", &self.context.channel_id());
|
||||||
|
|
||||||
|
let need_channel_ready = self.check_get_channel_ready(0, logger).is_some();
|
||||||
|
self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new());
|
||||||
|
self.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
|
||||||
|
|
||||||
|
Ok(channel_monitor)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
|
pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
|
||||||
where L::Target: Logger
|
where L::Target: Logger
|
||||||
{
|
{
|
||||||
|
@ -5446,6 +5628,56 @@ impl<SP: Deref> Channel<SP> where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tx_signatures(&mut self, msg: &msgs::TxSignatures) -> Result<(Option<msgs::TxSignatures>, Option<Transaction>), ChannelError> {
|
||||||
|
if let Some(ref mut signing_session) = self.interactive_tx_signing_session {
|
||||||
|
if msg.witnesses.len() != signing_session.remote_inputs_count() {
|
||||||
|
return Err(ChannelError::Warn(
|
||||||
|
"Witness count did not match contributed input count".to_string()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for witness in &msg.witnesses {
|
||||||
|
if witness.is_empty() {
|
||||||
|
return Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"Unexpected empty witness in tx_signatures received".to_string(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dual_funding): Check all sigs are SIGHASH_ALL.
|
||||||
|
|
||||||
|
// TODO(dual_funding): I don't see how we're going to be able to ensure witness-standardness
|
||||||
|
// for spending. Doesn't seem to be anything in rust-bitcoin.
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.tx_hash != signing_session.unsigned_tx.compute_txid() {
|
||||||
|
return Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"The txid for the transaction does not match".to_string(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (tx_signatures_opt, funding_tx_opt) = signing_session.received_tx_signatures(msg.clone())
|
||||||
|
.map_err(|_| ChannelError::Warn("Witness count did not match contributed input count".to_string()))?;
|
||||||
|
if funding_tx_opt.is_some() {
|
||||||
|
self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new());
|
||||||
|
}
|
||||||
|
self.context.funding_transaction = funding_tx_opt.clone();
|
||||||
|
|
||||||
|
// Clear out the signing session
|
||||||
|
self.interactive_tx_signing_session = None;
|
||||||
|
|
||||||
|
Ok((tx_signatures_opt, funding_tx_opt))
|
||||||
|
} else {
|
||||||
|
Err(ChannelError::Close((
|
||||||
|
"Unexpected tx_signatures. No funding transaction awaiting signatures".to_string(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Queues up an outbound update fee by placing it in the holding cell. You should call
|
/// Queues up an outbound update fee by placing it in the holding cell. You should call
|
||||||
/// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
|
/// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
|
||||||
/// commitment update.
|
/// commitment update.
|
||||||
|
@ -8033,11 +8265,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
|
||||||
) {
|
) {
|
||||||
panic!("Tried to get a funding_created messsage at a time other than immediately after initial handshake completion (or tried to get funding_created twice)");
|
panic!("Tried to get a funding_created messsage at a time other than immediately after initial handshake completion (or tried to get funding_created twice)");
|
||||||
}
|
}
|
||||||
if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
|
self.context.assert_no_commitment_advancement("funding_created");
|
||||||
self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
|
|
||||||
self.context.holder_commitment_point.transaction_number() != INITIAL_COMMITMENT_NUMBER {
|
|
||||||
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
|
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
|
||||||
self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
|
self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
|
||||||
|
@ -8144,7 +8372,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
|
||||||
&mut self, msg: &msgs::AcceptChannel, default_limits: &ChannelHandshakeLimits,
|
&mut self, msg: &msgs::AcceptChannel, default_limits: &ChannelHandshakeLimits,
|
||||||
their_features: &InitFeatures
|
their_features: &InitFeatures
|
||||||
) -> Result<(), ChannelError> {
|
) -> Result<(), ChannelError> {
|
||||||
self.context.do_accept_channel_checks(default_limits, their_features, &msg.common_fields, msg.channel_reserve_satoshis)
|
self.context.do_accept_channel_checks(
|
||||||
|
default_limits, their_features, &msg.common_fields, msg.channel_reserve_satoshis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles a funding_signed message from the remote end.
|
/// Handles a funding_signed message from the remote end.
|
||||||
|
@ -8161,11 +8390,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
|
||||||
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
|
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
|
||||||
return Err((self, ChannelError::close("Received funding_signed in strange state!".to_owned())));
|
return Err((self, ChannelError::close("Received funding_signed in strange state!".to_owned())));
|
||||||
}
|
}
|
||||||
if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
|
self.context.assert_no_commitment_advancement("funding_created");
|
||||||
self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
|
|
||||||
self.context.holder_commitment_point.transaction_number() != INITIAL_COMMITMENT_NUMBER {
|
|
||||||
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
|
|
||||||
}
|
|
||||||
|
|
||||||
let (channel_monitor, _) = match self.initial_commitment_signed(
|
let (channel_monitor, _) = match self.initial_commitment_signed(
|
||||||
self.context.channel_id(),
|
self.context.channel_id(),
|
||||||
|
@ -8180,6 +8405,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
|
||||||
|
|
||||||
let mut channel = Channel {
|
let mut channel = Channel {
|
||||||
context: self.context,
|
context: self.context,
|
||||||
|
interactive_tx_signing_session: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
|
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
|
||||||
|
@ -8378,11 +8604,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
|
||||||
// channel.
|
// channel.
|
||||||
return Err((self, ChannelError::close("Received funding_created after we got the channel!".to_owned())));
|
return Err((self, ChannelError::close("Received funding_created after we got the channel!".to_owned())));
|
||||||
}
|
}
|
||||||
if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
|
self.context.assert_no_commitment_advancement("funding_created");
|
||||||
self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
|
|
||||||
self.context.holder_commitment_point.transaction_number() != INITIAL_COMMITMENT_NUMBER {
|
|
||||||
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
|
|
||||||
}
|
|
||||||
|
|
||||||
let funding_txo = OutPoint { txid: msg.funding_txid, index: msg.funding_output_index };
|
let funding_txo = OutPoint { txid: msg.funding_txid, index: msg.funding_output_index };
|
||||||
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
|
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
|
||||||
|
@ -8408,6 +8630,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
|
||||||
// `ChannelMonitor`.
|
// `ChannelMonitor`.
|
||||||
let mut channel = Channel {
|
let mut channel = Channel {
|
||||||
context: self.context,
|
context: self.context,
|
||||||
|
interactive_tx_signing_session: None,
|
||||||
};
|
};
|
||||||
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
|
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
|
||||||
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
|
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
|
||||||
|
@ -8476,7 +8699,6 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
|
||||||
unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 },
|
unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 },
|
||||||
dual_funding_context: DualFundingChannelContext {
|
dual_funding_context: DualFundingChannelContext {
|
||||||
our_funding_satoshis: funding_satoshis,
|
our_funding_satoshis: funding_satoshis,
|
||||||
their_funding_satoshis: 0,
|
|
||||||
funding_tx_locktime,
|
funding_tx_locktime,
|
||||||
funding_feerate_sat_per_1000_weight,
|
funding_feerate_sat_per_1000_weight,
|
||||||
our_funding_inputs: funding_inputs,
|
our_funding_inputs: funding_inputs,
|
||||||
|
@ -8548,6 +8770,15 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
|
||||||
require_confirmed_inputs: None,
|
require_confirmed_inputs: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_channel(self, signing_session: InteractiveTxSigningSession) -> Result<Channel<SP>, ChannelError>{
|
||||||
|
let channel = Channel {
|
||||||
|
context: self.context,
|
||||||
|
interactive_tx_signing_session: Some(signing_session),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(channel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A not-yet-funded inbound (from counterparty) channel using V2 channel establishment.
|
// A not-yet-funded inbound (from counterparty) channel using V2 channel establishment.
|
||||||
|
@ -8631,7 +8862,6 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
|
||||||
|
|
||||||
let dual_funding_context = DualFundingChannelContext {
|
let dual_funding_context = DualFundingChannelContext {
|
||||||
our_funding_satoshis: funding_satoshis,
|
our_funding_satoshis: funding_satoshis,
|
||||||
their_funding_satoshis: msg.common_fields.funding_satoshis,
|
|
||||||
funding_tx_locktime: LockTime::from_consensus(msg.locktime),
|
funding_tx_locktime: LockTime::from_consensus(msg.locktime),
|
||||||
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
|
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
|
||||||
our_funding_inputs: funding_inputs.clone(),
|
our_funding_inputs: funding_inputs.clone(),
|
||||||
|
@ -8733,6 +8963,15 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
|
||||||
pub fn get_accept_channel_v2_message(&self) -> msgs::AcceptChannelV2 {
|
pub fn get_accept_channel_v2_message(&self) -> msgs::AcceptChannelV2 {
|
||||||
self.generate_accept_channel_v2_message()
|
self.generate_accept_channel_v2_message()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_channel(self, signing_session: InteractiveTxSigningSession) -> Result<Channel<SP>, ChannelError>{
|
||||||
|
let channel = Channel {
|
||||||
|
context: self.context,
|
||||||
|
interactive_tx_signing_session: Some(signing_session),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(channel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfunded channel utilities
|
// Unfunded channel utilities
|
||||||
|
@ -9798,6 +10037,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
|
||||||
blocked_monitor_updates: blocked_monitor_updates.unwrap(),
|
blocked_monitor_updates: blocked_monitor_updates.unwrap(),
|
||||||
is_manual_broadcast: is_manual_broadcast.unwrap_or(false),
|
is_manual_broadcast: is_manual_broadcast.unwrap_or(false),
|
||||||
},
|
},
|
||||||
|
interactive_tx_signing_session: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htl
|
||||||
use crate::ln::msgs;
|
use crate::ln::msgs;
|
||||||
use crate::ln::onion_utils;
|
use crate::ln::onion_utils;
|
||||||
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
|
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
|
||||||
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
|
use crate::ln::msgs::{ChannelMessageHandler, CommitmentUpdate, DecodeError, LightningError};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::ln::outbound_payment;
|
use crate::ln::outbound_payment;
|
||||||
use crate::ln::outbound_payment::{OutboundPayments, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs, StaleExpiration};
|
use crate::ln::outbound_payment::{OutboundPayments, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs, StaleExpiration};
|
||||||
|
@ -7322,7 +7322,7 @@ where
|
||||||
|
|
||||||
/// Gets the node_id held by this ChannelManager
|
/// Gets the node_id held by this ChannelManager
|
||||||
pub fn get_our_node_id(&self) -> PublicKey {
|
pub fn get_our_node_id(&self) -> PublicKey {
|
||||||
self.our_network_pubkey.clone()
|
self.our_network_pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_monitor_update_completion_actions<I: IntoIterator<Item=MonitorUpdateCompletionAction>>(&self, actions: I) {
|
fn handle_monitor_update_completion_actions<I: IntoIterator<Item=MonitorUpdateCompletionAction>>(&self, actions: I) {
|
||||||
|
@ -7599,6 +7599,10 @@ where
|
||||||
/// for zero confirmations. Instead, `accept_inbound_channel_from_trusted_peer_0conf` must be
|
/// for zero confirmations. Instead, `accept_inbound_channel_from_trusted_peer_0conf` must be
|
||||||
/// used to accept such channels.
|
/// used to accept such channels.
|
||||||
///
|
///
|
||||||
|
/// NOTE: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
|
||||||
|
/// will prevent the funding transaction from being relayed on the bitcoin network and hence being
|
||||||
|
/// confirmed.
|
||||||
|
///
|
||||||
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
|
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
|
||||||
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
|
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
|
||||||
pub fn accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128) -> Result<(), APIError> {
|
pub fn accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128) -> Result<(), APIError> {
|
||||||
|
@ -8279,9 +8283,46 @@ where
|
||||||
};
|
};
|
||||||
if let Some(msg_send_event) = msg_send_event_opt {
|
if let Some(msg_send_event) = msg_send_event_opt {
|
||||||
peer_state.pending_msg_events.push(msg_send_event);
|
peer_state.pending_msg_events.push(msg_send_event);
|
||||||
}
|
};
|
||||||
if let Some(signing_session) = signing_session_opt {
|
if let Some(mut signing_session) = signing_session_opt {
|
||||||
// TODO(dual_funding): Handle this unsigned transaction.
|
let (commitment_signed, funding_ready_for_sig_event_opt) = match chan_phase_entry.get_mut() {
|
||||||
|
ChannelPhase::UnfundedOutboundV2(chan) => {
|
||||||
|
chan.funding_tx_constructed(&mut signing_session, &self.logger)
|
||||||
|
},
|
||||||
|
ChannelPhase::UnfundedInboundV2(chan) => {
|
||||||
|
chan.funding_tx_constructed(&mut signing_session, &self.logger)
|
||||||
|
},
|
||||||
|
_ => Err(ChannelError::Warn(
|
||||||
|
"Got a tx_complete message with no interactive transaction construction expected or in-progress"
|
||||||
|
.into())),
|
||||||
|
}.map_err(|err| MsgHandleErrInternal::send_err_msg_no_close(format!("{}", err), msg.channel_id))?;
|
||||||
|
let (channel_id, channel_phase) = chan_phase_entry.remove_entry();
|
||||||
|
let channel = match channel_phase {
|
||||||
|
ChannelPhase::UnfundedOutboundV2(chan) => chan.into_channel(signing_session),
|
||||||
|
ChannelPhase::UnfundedInboundV2(chan) => chan.into_channel(signing_session),
|
||||||
|
_ => {
|
||||||
|
debug_assert!(false); // It cannot be another variant as we are in the `Ok` branch of the above match.
|
||||||
|
Err(ChannelError::Warn(
|
||||||
|
"Got a tx_complete message with no interactive transaction construction expected or in-progress"
|
||||||
|
.into()))
|
||||||
|
},
|
||||||
|
}.map_err(|err| MsgHandleErrInternal::send_err_msg_no_close(format!("{}", err), msg.channel_id))?;
|
||||||
|
peer_state.channel_by_id.insert(channel_id, ChannelPhase::Funded(channel));
|
||||||
|
if let Some(funding_ready_for_sig_event) = funding_ready_for_sig_event_opt {
|
||||||
|
let mut pending_events = self.pending_events.lock().unwrap();
|
||||||
|
pending_events.push_back((funding_ready_for_sig_event, None));
|
||||||
|
}
|
||||||
|
peer_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
|
||||||
|
node_id: counterparty_node_id,
|
||||||
|
updates: CommitmentUpdate {
|
||||||
|
commitment_signed,
|
||||||
|
update_add_htlcs: vec![],
|
||||||
|
update_fulfill_htlcs: vec![],
|
||||||
|
update_fail_htlcs: vec![],
|
||||||
|
update_fail_malformed_htlcs: vec![],
|
||||||
|
update_fee: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -8291,16 +8332,115 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_tx_signatures(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxSignatures) {
|
fn internal_tx_signatures(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxSignatures)
|
||||||
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
|
-> Result<(), MsgHandleErrInternal> {
|
||||||
"Dual-funded channels not supported".to_owned(),
|
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||||
msg.channel_id)), *counterparty_node_id);
|
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)
|
||||||
|
})?;
|
||||||
|
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::Occupied(mut chan_phase_entry) => {
|
||||||
|
let channel_phase = chan_phase_entry.get_mut();
|
||||||
|
match channel_phase {
|
||||||
|
ChannelPhase::Funded(chan) => {
|
||||||
|
let (tx_signatures_opt, funding_tx_opt) = try_chan_phase_entry!(self, peer_state, chan.tx_signatures(msg), chan_phase_entry);
|
||||||
|
if let Some(tx_signatures) = tx_signatures_opt {
|
||||||
|
peer_state.pending_msg_events.push(events::MessageSendEvent::SendTxSignatures {
|
||||||
|
node_id: *counterparty_node_id,
|
||||||
|
msg: tx_signatures,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(ref funding_tx) = funding_tx_opt {
|
||||||
|
self.tx_broadcaster.broadcast_transactions(&[funding_tx]);
|
||||||
|
{
|
||||||
|
let mut pending_events = self.pending_events.lock().unwrap();
|
||||||
|
emit_channel_pending_event!(pending_events, chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => try_chan_phase_entry!(self, peer_state, Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"Got an unexpected tx_signatures message".into(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
))), chan_phase_entry)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
hash_map::Entry::Vacant(_) => {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_tx_abort(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAbort) {
|
fn internal_tx_abort(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAbort)
|
||||||
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
|
-> Result<(), MsgHandleErrInternal> {
|
||||||
"Dual-funded channels not supported".to_owned(),
|
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||||
msg.channel_id)), *counterparty_node_id);
|
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)
|
||||||
|
})?;
|
||||||
|
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::Occupied(mut chan_phase_entry) => {
|
||||||
|
let channel_phase = chan_phase_entry.get_mut();
|
||||||
|
let tx_constructor = match channel_phase {
|
||||||
|
ChannelPhase::UnfundedInboundV2(chan) => chan.interactive_tx_constructor_mut(),
|
||||||
|
ChannelPhase::UnfundedOutboundV2(chan) => chan.interactive_tx_constructor_mut(),
|
||||||
|
ChannelPhase::Funded(_) => {
|
||||||
|
// TODO(splicing)/TODO(RBF): We'll also be doing interactive tx construction
|
||||||
|
// for a "ChannelPhase::Funded" when we want to bump the fee on an interactively
|
||||||
|
// constructed funding tx or during splicing. For now we send an error as we would
|
||||||
|
// never ack an RBF attempt or a splice for now:
|
||||||
|
try_chan_phase_entry!(self, peer_state, Err(ChannelError::Warn(
|
||||||
|
"Got an unexpected tx_abort message: After initial funding transaction is signed, \
|
||||||
|
splicing and RBF attempts of interactive funding transactions are not supported yet so \
|
||||||
|
we don't have any negotiation in progress".into(),
|
||||||
|
)), chan_phase_entry)
|
||||||
|
}
|
||||||
|
ChannelPhase::UnfundedInboundV1(_) | ChannelPhase::UnfundedOutboundV1(_) => {
|
||||||
|
try_chan_phase_entry!(self, peer_state, Err(ChannelError::Warn(
|
||||||
|
"Got an unexpected tx_abort message: This is an unfunded channel created with V1 channel \
|
||||||
|
establishment".into(),
|
||||||
|
)), chan_phase_entry)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// This checks for and resets the interactive negotiation state by `take()`ing it from the channel.
|
||||||
|
// The existence of the `tx_constructor` indicates that we have not moved into the signing
|
||||||
|
// phase for this interactively constructed transaction and hence we have not exchanged
|
||||||
|
// `tx_signatures`. Either way, we never close the channel upon receiving a `tx_abort`:
|
||||||
|
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L574-L576
|
||||||
|
if tx_constructor.take().is_some() {
|
||||||
|
let msg = msgs::TxAbort {
|
||||||
|
channel_id: msg.channel_id,
|
||||||
|
data: "Acknowledged tx_abort".to_string().into_bytes(),
|
||||||
|
};
|
||||||
|
// NOTE: Since at this point we have not sent a `tx_abort` message for this negotiation
|
||||||
|
// previously (tx_constructor was `Some`), we need to echo back a tx_abort message according
|
||||||
|
// to the spec:
|
||||||
|
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L560-L561
|
||||||
|
// For rationale why we echo back `tx_abort`:
|
||||||
|
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L578-L580
|
||||||
|
peer_state.pending_msg_events.push(events::MessageSendEvent::SendTxAbort {
|
||||||
|
node_id: *counterparty_node_id,
|
||||||
|
msg,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
hash_map::Entry::Vacant(_) => {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_channel_ready(&self, counterparty_node_id: &PublicKey, msg: &msgs::ChannelReady) -> Result<(), MsgHandleErrInternal> {
|
fn internal_channel_ready(&self, counterparty_node_id: &PublicKey, msg: &msgs::ChannelReady) -> Result<(), MsgHandleErrInternal> {
|
||||||
|
@ -8669,6 +8809,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_commitment_signed(&self, counterparty_node_id: &PublicKey, msg: &msgs::CommitmentSigned) -> Result<(), MsgHandleErrInternal> {
|
fn internal_commitment_signed(&self, counterparty_node_id: &PublicKey, msg: &msgs::CommitmentSigned) -> Result<(), MsgHandleErrInternal> {
|
||||||
|
let best_block = *self.best_block.read().unwrap();
|
||||||
let per_peer_state = self.per_peer_state.read().unwrap();
|
let per_peer_state = self.per_peer_state.read().unwrap();
|
||||||
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
|
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
|
@ -8682,10 +8823,32 @@ where
|
||||||
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
|
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
|
||||||
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
|
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
|
||||||
let funding_txo = chan.context.get_funding_txo();
|
let funding_txo = chan.context.get_funding_txo();
|
||||||
let monitor_update_opt = try_chan_phase_entry!(self, peer_state, chan.commitment_signed(&msg, &&logger), chan_phase_entry);
|
|
||||||
if let Some(monitor_update) = monitor_update_opt {
|
if chan.interactive_tx_signing_session.is_some() {
|
||||||
handle_new_monitor_update!(self, funding_txo.unwrap(), monitor_update, peer_state_lock,
|
let monitor = try_chan_phase_entry!(
|
||||||
peer_state, per_peer_state, chan);
|
self, peer_state, chan.commitment_signed_initial_v2(msg, best_block, &self.signer_provider, &&logger),
|
||||||
|
chan_phase_entry);
|
||||||
|
let monitor_res = self.chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor);
|
||||||
|
if let Ok(persist_state) = monitor_res {
|
||||||
|
handle_new_monitor_update!(self, persist_state, peer_state_lock, peer_state,
|
||||||
|
per_peer_state, chan, INITIAL_MONITOR);
|
||||||
|
} else {
|
||||||
|
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
|
||||||
|
log_error!(logger, "Persisting initial ChannelMonitor failed, implying the funding outpoint was duplicated");
|
||||||
|
try_chan_phase_entry!(self, peer_state, Err(ChannelError::Close(
|
||||||
|
(
|
||||||
|
"Channel funding outpoint was a duplicate".to_owned(),
|
||||||
|
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
|
||||||
|
)
|
||||||
|
)), chan_phase_entry)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let monitor_update_opt = try_chan_phase_entry!(
|
||||||
|
self, peer_state, chan.commitment_signed(msg, &&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,
|
||||||
|
peer_state, per_peer_state, chan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -8693,7 +8856,7 @@ where
|
||||||
"Got a commitment_signed message for an unfunded channel!".into())), chan_phase_entry);
|
"Got a commitment_signed message for an unfunded channel!".into())), chan_phase_entry);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
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::Vacant(_) => 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11657,9 +11820,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_tx_signatures(&self, counterparty_node_id: PublicKey, msg: &msgs::TxSignatures) {
|
fn handle_tx_signatures(&self, counterparty_node_id: PublicKey, msg: &msgs::TxSignatures) {
|
||||||
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
|
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
|
||||||
"Dual-funded channels not supported".to_owned(),
|
let _ = handle_error!(self, self.internal_tx_signatures(&counterparty_node_id, msg), counterparty_node_id);
|
||||||
msg.channel_id.clone())), counterparty_node_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_tx_init_rbf(&self, counterparty_node_id: PublicKey, msg: &msgs::TxInitRbf) {
|
fn handle_tx_init_rbf(&self, counterparty_node_id: PublicKey, msg: &msgs::TxInitRbf) {
|
||||||
|
@ -11675,9 +11837,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_tx_abort(&self, counterparty_node_id: PublicKey, msg: &msgs::TxAbort) {
|
fn handle_tx_abort(&self, counterparty_node_id: PublicKey, msg: &msgs::TxAbort) {
|
||||||
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
|
// Note that we never need to persist the updated ChannelManager for an inbound
|
||||||
"Dual-funded channels not supported".to_owned(),
|
// tx_abort message - interactive transaction construction does not need to
|
||||||
msg.channel_id.clone())), counterparty_node_id);
|
// be persisted before any signatures are exchanged.
|
||||||
|
let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || {
|
||||||
|
let _ = handle_error!(self, self.internal_tx_abort(&counterparty_node_id, msg), counterparty_node_id);
|
||||||
|
NotifyOption::SkipPersistHandleEvents
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_received(&self) {
|
fn message_received(&self) {
|
||||||
|
@ -15497,6 +15663,9 @@ mod tests {
|
||||||
|
|
||||||
expect_pending_htlcs_forwardable!(nodes[0]);
|
expect_pending_htlcs_forwardable!(nodes[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dual-funding: V2 Channel Establishment Tests
|
||||||
|
// TODO(dual_funding): Complete these.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(ldk_bench)]
|
#[cfg(ldk_bench)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue