Skip to content

Commit 689d0c1

Browse files
committed
Introduce RenegotiatedFunding monitor update variant
This is a new `ChannelMonitorUpdateStep` variant intended to be used whenever a new funding transaction for the channel has been negotiated via the `InteractiveTxConstructor`. This commit primarily focuses on its use for splices, but future work will expand where needed to support RBFs (both for the initial dual funding transaction, and splice transactions). To draw a parallel to channel open, we generally want to have the commitment transactions negotiated for the funding transaction and committed to the respective `ChannelMonitor` before attempting to sign the funding transaction itself. This monitor update fulfills this need for a newly negotiated splice; it includes both the new holder and counterparty commitment transactions, and the new set of applicable `ChannelTransactionParameters`. Once the monitor update has been applied to the monitor and persisted, we allow the release of our `tx_signatures` for the splice transaction to wait for its confirmation.
1 parent 0848e7a commit 689d0c1

File tree

3 files changed

+310
-7
lines changed

3 files changed

+310
-7
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use bitcoin::hash_types::{Txid, BlockHash};
3131
use crate::chain;
3232
use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
3333
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
34-
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, WithChannelMonitor};
34+
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, Balance, MonitorEvent, TransactionOutputs, WithChannelMonitor};
3535
use crate::chain::transaction::{OutPoint, TransactionData};
3636
use crate::ln::types::ChannelId;
3737
use crate::sign::ecdsa::EcdsaChannelSigner;
@@ -867,6 +867,35 @@ where C::Target: chain::Filter,
867867
panic!("{}", err_str);
868868
},
869869
}
870+
871+
// We may need to start monitoring for any alternative funding transactions.
872+
if let Some(ref chain_source) = self.chain_source {
873+
for update in &update.updates {
874+
match update {
875+
ChannelMonitorUpdateStep::RenegotiatedFunding {
876+
channel_parameters, ..
877+
} => {
878+
let funding_outpoint = channel_parameters.funding_outpoint
879+
.expect("Renegotiated funding must always have known outpoint");
880+
let funding_script =
881+
channel_parameters.make_funding_redeemscript().to_p2wsh();
882+
log_trace!(
883+
&logger,
884+
"Registering renegotiated funding outpoint {} with the filter to monitor confirmations and spends",
885+
&funding_outpoint
886+
);
887+
chain_source.register_tx(&funding_outpoint.txid, &funding_script);
888+
chain_source.register_output(WatchedOutput {
889+
block_hash: None,
890+
outpoint: funding_outpoint,
891+
script_pubkey: funding_script,
892+
});
893+
},
894+
_ => {},
895+
}
896+
}
897+
}
898+
870899
if update_res.is_err() {
871900
ChannelMonitorUpdateStatus::InProgress
872901
} else {

lightning/src/chain/channelmonitor.rs

Lines changed: 186 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,11 @@ pub(crate) enum ChannelMonitorUpdateStep {
621621
ShutdownScript {
622622
scriptpubkey: ScriptBuf,
623623
},
624+
RenegotiatedFunding {
625+
channel_parameters: ChannelTransactionParameters,
626+
holder_commitment_tx: HolderCommitmentTransaction,
627+
counterparty_commitment_tx: CommitmentTransaction,
628+
},
624629
}
625630

626631
impl ChannelMonitorUpdateStep {
@@ -633,6 +638,7 @@ impl ChannelMonitorUpdateStep {
633638
ChannelMonitorUpdateStep::CommitmentSecret { .. } => "CommitmentSecret",
634639
ChannelMonitorUpdateStep::ChannelForceClosed { .. } => "ChannelForceClosed",
635640
ChannelMonitorUpdateStep::ShutdownScript { .. } => "ShutdownScript",
641+
ChannelMonitorUpdateStep::RenegotiatedFunding { .. } => "RenegotiatedFunding",
636642
}
637643
}
638644
}
@@ -671,6 +677,11 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
671677
(0, htlc_outputs, required_vec),
672678
(2, commitment_tx, required),
673679
},
680+
(10, RenegotiatedFunding) => {
681+
(1, channel_parameters, (required: ReadableArgs, None)),
682+
(3, holder_commitment_tx, required),
683+
(5, counterparty_commitment_tx, required),
684+
},
674685
);
675686

676687
/// Indicates whether the balance is derived from a cooperative close, a force-close
@@ -997,9 +1008,69 @@ struct FundingScope {
9971008
prev_holder_commitment_tx: Option<HolderCommitmentTransaction>,
9981009
}
9991010

1011+
impl FundingScope {
1012+
fn funding_outpoint(&self) -> OutPoint {
1013+
*self.channel_parameters.funding_outpoint.as_ref()
1014+
.expect("Funding outpoint must be set for active monitor")
1015+
}
1016+
1017+
fn funding_txid(&self) -> Txid {
1018+
self.funding_outpoint().txid
1019+
}
1020+
}
1021+
1022+
impl Writeable for FundingScope {
1023+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
1024+
write_tlv_fields!(w, {
1025+
(1, self.channel_parameters, (required: ReadableArgs, None)),
1026+
(3, self.current_counterparty_commitment_txid, required),
1027+
(5, self.prev_counterparty_commitment_txid, option),
1028+
(7, self.current_holder_commitment_tx, required),
1029+
(9, self.prev_holder_commitment_tx, option),
1030+
(11, self.counterparty_claimable_outpoints, required),
1031+
});
1032+
Ok(())
1033+
}
1034+
}
1035+
1036+
impl Readable for FundingScope {
1037+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
1038+
let mut channel_parameters = RequiredWrapper(None);
1039+
let mut current_counterparty_commitment_txid = RequiredWrapper(None);
1040+
let mut prev_counterparty_commitment_txid = None;
1041+
let mut current_holder_commitment_tx = RequiredWrapper(None);
1042+
let mut prev_holder_commitment_tx = None;
1043+
let mut counterparty_claimable_outpoints = RequiredWrapper(None);
1044+
1045+
read_tlv_fields!(r, {
1046+
(1, channel_parameters, (required: ReadableArgs, None)),
1047+
(3, current_counterparty_commitment_txid, required),
1048+
(5, prev_counterparty_commitment_txid, option),
1049+
(7, current_holder_commitment_tx, required),
1050+
(9, prev_holder_commitment_tx, option),
1051+
(11, counterparty_claimable_outpoints, required),
1052+
});
1053+
1054+
let channel_parameters: ChannelTransactionParameters = channel_parameters.0.unwrap();
1055+
let redeem_script = channel_parameters.make_funding_redeemscript();
1056+
1057+
Ok(Self {
1058+
script_pubkey: redeem_script.to_p2wsh(),
1059+
redeem_script,
1060+
channel_parameters,
1061+
current_counterparty_commitment_txid: current_counterparty_commitment_txid.0.unwrap(),
1062+
prev_counterparty_commitment_txid,
1063+
current_holder_commitment_tx: current_holder_commitment_tx.0.unwrap(),
1064+
prev_holder_commitment_tx,
1065+
counterparty_claimable_outpoints: counterparty_claimable_outpoints.0.unwrap(),
1066+
})
1067+
}
1068+
}
1069+
10001070
#[derive(Clone, PartialEq)]
10011071
pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
10021072
funding: FundingScope,
1073+
pending_funding: Vec<FundingScope>,
10031074

10041075
latest_update_id: u64,
10051076
commitment_transaction_number_obscure_factor: u64,
@@ -1433,6 +1504,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
14331504
(27, self.first_confirmed_funding_txo, required),
14341505
(29, self.initial_counterparty_commitment_tx, option),
14351506
(31, self.funding.channel_parameters, required),
1507+
(32, self.pending_funding, optional_vec),
14361508
});
14371509

14381510
Ok(())
@@ -1588,6 +1660,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
15881660
current_holder_commitment_tx: initial_holder_commitment_tx,
15891661
prev_holder_commitment_tx: None,
15901662
},
1663+
pending_funding: vec![],
15911664

15921665
latest_update_id: 0,
15931666
commitment_transaction_number_obscure_factor,
@@ -1812,14 +1885,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18121885
{
18131886
let lock = self.inner.lock().unwrap();
18141887
let logger = WithChannelMonitor::from_impl(logger, &*lock, None);
1815-
log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo());
1816-
let funding_outpoint = lock.get_funding_txo();
1817-
filter.register_tx(&funding_outpoint.txid, &lock.funding.script_pubkey);
1888+
for funding in core::iter::once(&lock.funding).chain(&lock.pending_funding) {
1889+
let funding_outpoint = funding.funding_outpoint();
1890+
log_trace!(&logger, "Registering funding outpoint {} with the filter to monitor confirmations", &funding_outpoint);
1891+
filter.register_tx(&funding_outpoint.txid, &funding.script_pubkey);
1892+
}
18181893
for (txid, outputs) in lock.get_outputs_to_watch().iter() {
18191894
for (index, script_pubkey) in outputs.iter() {
18201895
assert!(*index <= u16::MAX as u32);
18211896
let outpoint = OutPoint { txid: *txid, index: *index as u16 };
1822-
log_trace!(logger, "Registering outpoint {} with the filter for monitoring spends", outpoint);
1897+
log_trace!(logger, "Registering outpoint {} with the filter to monitor spend", outpoint);
18231898
filter.register_output(WatchedOutput {
18241899
block_hash: None,
18251900
outpoint,
@@ -3364,6 +3439,94 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33643439
);
33653440
}
33663441

3442+
fn renegotiated_funding<L: Deref>(
3443+
&mut self, logger: &WithChannelMonitor<L>,
3444+
channel_parameters: &ChannelTransactionParameters,
3445+
alternative_holder_commitment_tx: &HolderCommitmentTransaction,
3446+
alternative_counterparty_commitment_tx: &CommitmentTransaction,
3447+
) -> Result<(), ()>
3448+
where
3449+
L::Target: Logger,
3450+
{
3451+
let redeem_script = channel_parameters.make_funding_redeemscript();
3452+
let script_pubkey = redeem_script.to_p2wsh();
3453+
let alternative_counterparty_commitment_txid =
3454+
alternative_counterparty_commitment_tx.trust().txid();
3455+
3456+
// Both the current counterparty commitment and the alternative one share the same set of
3457+
// non-dust and dust HTLCs in the same order, though the index of each non-dust HTLC may be
3458+
// different.
3459+
//
3460+
// We clone all HTLCs and their sources to use in the alternative funding scope, and update
3461+
// each non-dust HTLC with their corresponding index in the alternative counterparty
3462+
// commitment.
3463+
let current_counterparty_commitment_htlcs =
3464+
if let Some(txid) = &self.funding.current_counterparty_commitment_txid {
3465+
self.funding.counterparty_claimable_outpoints.get(txid).unwrap()
3466+
} else {
3467+
debug_assert!(false);
3468+
log_error!(logger, "Received funding renegotiation while initial funding negotiation is still pending");
3469+
return Err(());
3470+
};
3471+
let mut htlcs_with_sources = current_counterparty_commitment_htlcs.clone();
3472+
let alternative_htlcs = alternative_counterparty_commitment_tx.nondust_htlcs();
3473+
// The left side only tracks non-dust, while the right side tracks dust as well.
3474+
debug_assert!(alternative_htlcs.len() <= current_counterparty_commitment_htlcs.len());
3475+
for (alternative_htlc, (htlc, _)) in alternative_htlcs.iter().zip(htlcs_with_sources.iter_mut()) {
3476+
debug_assert!(htlc.transaction_output_index.is_some());
3477+
debug_assert!(alternative_htlc.transaction_output_index.is_some());
3478+
debug_assert!(alternative_htlc.is_data_equal(htlc));
3479+
htlc.transaction_output_index = alternative_htlc.transaction_output_index;
3480+
}
3481+
3482+
let mut counterparty_claimable_outpoints = new_hash_map();
3483+
counterparty_claimable_outpoints.insert(
3484+
alternative_counterparty_commitment_txid, htlcs_with_sources,
3485+
);
3486+
3487+
// TODO(splicing): Enforce any necessary RBF validity checks.
3488+
let alternative_funding = FundingScope {
3489+
script_pubkey: script_pubkey.clone(),
3490+
redeem_script,
3491+
channel_parameters: channel_parameters.clone(),
3492+
current_counterparty_commitment_txid: Some(alternative_counterparty_commitment_txid),
3493+
prev_counterparty_commitment_txid: None,
3494+
counterparty_claimable_outpoints,
3495+
current_holder_commitment_tx: alternative_holder_commitment_tx.clone(),
3496+
prev_holder_commitment_tx: None,
3497+
};
3498+
let alternative_funding_outpoint = alternative_funding.funding_outpoint();
3499+
3500+
if self.pending_funding.iter()
3501+
.any(|funding| funding.funding_txid() == alternative_funding_outpoint.txid)
3502+
{
3503+
log_error!(logger, "Renegotiated funding transaction with a duplicate funding txid {}",
3504+
alternative_funding_outpoint.txid);
3505+
return Err(());
3506+
}
3507+
3508+
if let Some(parent_funding_txid) = channel_parameters.splice_parent_funding_txid.as_ref() {
3509+
// Only one splice can be negotiated at a time after we've exchanged `channel_ready`
3510+
// (implying our funding is confirmed) that spends our currently locked funding.
3511+
if !self.pending_funding.is_empty() {
3512+
log_error!(logger, "Negotiated splice while channel is pending channel_ready/splice_locked");
3513+
return Err(());
3514+
}
3515+
if *parent_funding_txid != self.funding.funding_txid() {
3516+
log_error!(logger, "Negotiated splice that does not spend currently locked funding transaction");
3517+
return Err(());
3518+
}
3519+
}
3520+
3521+
self.outputs_to_watch.insert(
3522+
alternative_funding_outpoint.txid,
3523+
vec![(alternative_funding_outpoint.index as u32, script_pubkey)],
3524+
);
3525+
self.pending_funding.push(alternative_funding);
3526+
3527+
Ok(())
3528+
}
3529+
33673530
fn update_monitor<B: Deref, F: Deref, L: Deref>(
33683531
&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &WithChannelMonitor<L>
33693532
) -> Result<(), ()>
@@ -3442,6 +3605,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34423605
ret = Err(());
34433606
}
34443607
},
3608+
ChannelMonitorUpdateStep::RenegotiatedFunding {
3609+
channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3610+
} => {
3611+
log_trace!(logger, "Updating ChannelMonitor with alternative holder and counterparty commitment transactions for funding txid {}",
3612+
channel_parameters.funding_outpoint.unwrap().txid);
3613+
if let Err(_) = self.renegotiated_funding(
3614+
logger, channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3615+
) {
3616+
ret = Err(());
3617+
}
3618+
},
34453619
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
34463620
log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
34473621
self.lockdown_from_offchain = true;
@@ -3493,7 +3667,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34933667
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. }
34943668
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. }
34953669
|ChannelMonitorUpdateStep::ShutdownScript { .. }
3496-
|ChannelMonitorUpdateStep::CommitmentSecret { .. } =>
3670+
|ChannelMonitorUpdateStep::CommitmentSecret { .. }
3671+
|ChannelMonitorUpdateStep::RenegotiatedFunding { .. } =>
34973672
is_pre_close_update = true,
34983673
// After a channel is closed, we don't communicate with our peer about it, so the
34993674
// only things we will update is getting a new preimage (from a different channel)
@@ -3671,6 +3846,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36713846
} => {
36723847
Some(commitment_tx.clone())
36733848
},
3849+
&ChannelMonitorUpdateStep::RenegotiatedFunding { ref counterparty_commitment_tx, .. } => {
3850+
Some(counterparty_commitment_tx.clone())
3851+
},
36743852
_ => None,
36753853
}
36763854
}).collect()
@@ -5303,6 +5481,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
53035481
let mut payment_preimages_with_info: Option<HashMap<_, _>> = None;
53045482
let mut first_confirmed_funding_txo = RequiredWrapper(None);
53055483
let mut channel_parameters = None;
5484+
let mut pending_funding = None;
53065485
read_tlv_fields!(reader, {
53075486
(1, funding_spend_confirmed, option),
53085487
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5320,6 +5499,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
53205499
(27, first_confirmed_funding_txo, (default_value, outpoint)),
53215500
(29, initial_counterparty_commitment_tx, option),
53225501
(31, channel_parameters, (option: ReadableArgs, None)),
5502+
(32, pending_funding, optional_vec),
53235503
});
53245504
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
53255505
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -5435,6 +5615,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
54355615
current_holder_commitment_tx,
54365616
prev_holder_commitment_tx,
54375617
},
5618+
pending_funding: pending_funding.unwrap_or(vec![]),
54385619

54395620
latest_update_id,
54405621
commitment_transaction_number_obscure_factor,

0 commit comments

Comments
 (0)