Skip to content

Commit 4a38fd2

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 ed16b5f commit 4a38fd2

File tree

3 files changed

+342
-8
lines changed

3 files changed

+342
-8
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ use bitcoin::hash_types::{BlockHash, Txid};
2929
use crate::chain;
3030
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
3131
use crate::chain::channelmonitor::{
32-
Balance, ChannelMonitor, ChannelMonitorUpdate, MonitorEvent, TransactionOutputs,
33-
WithChannelMonitor,
32+
Balance, ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, MonitorEvent,
33+
TransactionOutputs, WithChannelMonitor,
3434
};
3535
use crate::chain::transaction::{OutPoint, TransactionData};
3636
use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
@@ -1032,6 +1032,35 @@ where
10321032
panic!("{}", err_str);
10331033
},
10341034
}
1035+
1036+
// We may need to start monitoring for any alternative funding transactions.
1037+
if let Some(ref chain_source) = self.chain_source {
1038+
for update in &update.updates {
1039+
match update {
1040+
ChannelMonitorUpdateStep::RenegotiatedFunding {
1041+
channel_parameters, ..
1042+
} => {
1043+
let funding_outpoint = channel_parameters.funding_outpoint
1044+
.expect("Renegotiated funding must always have known outpoint");
1045+
let funding_script =
1046+
channel_parameters.make_funding_redeemscript().to_p2wsh();
1047+
log_trace!(
1048+
&logger,
1049+
"Registering renegotiated funding outpoint {} with the filter to monitor confirmations and spends",
1050+
&funding_outpoint
1051+
);
1052+
chain_source.register_tx(&funding_outpoint.txid, &funding_script);
1053+
chain_source.register_output(WatchedOutput {
1054+
block_hash: None,
1055+
outpoint: funding_outpoint,
1056+
script_pubkey: funding_script,
1057+
});
1058+
},
1059+
_ => {},
1060+
}
1061+
}
1062+
}
1063+
10351064
if update_res.is_err() {
10361065
ChannelMonitorUpdateStatus::InProgress
10371066
} else {

lightning/src/chain/channelmonitor.rs

Lines changed: 201 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,11 @@ pub(crate) enum ChannelMonitorUpdateStep {
640640
ShutdownScript {
641641
scriptpubkey: ScriptBuf,
642642
},
643+
RenegotiatedFunding {
644+
channel_parameters: ChannelTransactionParameters,
645+
holder_commitment_tx: HolderCommitmentTransaction,
646+
counterparty_commitment_tx: CommitmentTransaction,
647+
},
643648
}
644649

645650
impl ChannelMonitorUpdateStep {
@@ -653,6 +658,7 @@ impl ChannelMonitorUpdateStep {
653658
ChannelMonitorUpdateStep::CommitmentSecret { .. } => "CommitmentSecret",
654659
ChannelMonitorUpdateStep::ChannelForceClosed { .. } => "ChannelForceClosed",
655660
ChannelMonitorUpdateStep::ShutdownScript { .. } => "ShutdownScript",
661+
ChannelMonitorUpdateStep::RenegotiatedFunding { .. } => "RenegotiatedFunding",
656662
}
657663
}
658664
}
@@ -691,6 +697,11 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
691697
(0, htlc_outputs, required_vec),
692698
(2, commitment_tx, required),
693699
},
700+
(10, RenegotiatedFunding) => {
701+
(1, channel_parameters, (required: ReadableArgs, None)),
702+
(3, holder_commitment_tx, required),
703+
(5, counterparty_commitment_tx, required),
704+
},
694705
);
695706

696707
/// Indicates whether the balance is derived from a cooperative close, a force-close
@@ -1024,9 +1035,69 @@ struct FundingScope {
10241035
prev_holder_commitment_tx: Option<HolderCommitmentTransaction>,
10251036
}
10261037

1038+
impl FundingScope {
1039+
fn funding_outpoint(&self) -> OutPoint {
1040+
let funding_outpoint = self.channel_parameters.funding_outpoint.as_ref();
1041+
*funding_outpoint.expect("Funding outpoint must be set for active monitor")
1042+
}
1043+
1044+
fn funding_txid(&self) -> Txid {
1045+
self.funding_outpoint().txid
1046+
}
1047+
}
1048+
1049+
impl Writeable for FundingScope {
1050+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
1051+
write_tlv_fields!(w, {
1052+
(1, self.channel_parameters, (required: ReadableArgs, None)),
1053+
(3, self.current_counterparty_commitment_txid, required),
1054+
(5, self.prev_counterparty_commitment_txid, option),
1055+
(7, self.current_holder_commitment_tx, required),
1056+
(9, self.prev_holder_commitment_tx, option),
1057+
(11, self.counterparty_claimable_outpoints, required),
1058+
});
1059+
Ok(())
1060+
}
1061+
}
1062+
1063+
impl Readable for FundingScope {
1064+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
1065+
let mut channel_parameters = RequiredWrapper(None);
1066+
let mut current_counterparty_commitment_txid = RequiredWrapper(None);
1067+
let mut prev_counterparty_commitment_txid = None;
1068+
let mut current_holder_commitment_tx = RequiredWrapper(None);
1069+
let mut prev_holder_commitment_tx = None;
1070+
let mut counterparty_claimable_outpoints = RequiredWrapper(None);
1071+
1072+
read_tlv_fields!(r, {
1073+
(1, channel_parameters, (required: ReadableArgs, None)),
1074+
(3, current_counterparty_commitment_txid, required),
1075+
(5, prev_counterparty_commitment_txid, option),
1076+
(7, current_holder_commitment_tx, required),
1077+
(9, prev_holder_commitment_tx, option),
1078+
(11, counterparty_claimable_outpoints, required),
1079+
});
1080+
1081+
let channel_parameters: ChannelTransactionParameters = channel_parameters.0.unwrap();
1082+
let redeem_script = channel_parameters.make_funding_redeemscript();
1083+
1084+
Ok(Self {
1085+
script_pubkey: redeem_script.to_p2wsh(),
1086+
redeem_script,
1087+
channel_parameters,
1088+
current_counterparty_commitment_txid: current_counterparty_commitment_txid.0.unwrap(),
1089+
prev_counterparty_commitment_txid,
1090+
current_holder_commitment_tx: current_holder_commitment_tx.0.unwrap(),
1091+
prev_holder_commitment_tx,
1092+
counterparty_claimable_outpoints: counterparty_claimable_outpoints.0.unwrap(),
1093+
})
1094+
}
1095+
}
1096+
10271097
#[derive(Clone, PartialEq)]
10281098
pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
10291099
funding: FundingScope,
1100+
pending_funding: Vec<FundingScope>,
10301101

10311102
latest_update_id: u64,
10321103
commitment_transaction_number_obscure_factor: u64,
@@ -1467,6 +1538,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
14671538
(27, self.first_confirmed_funding_txo, required),
14681539
(29, self.initial_counterparty_commitment_tx, option),
14691540
(31, self.funding.channel_parameters, required),
1541+
(32, self.pending_funding, optional_vec),
14701542
});
14711543

14721544
Ok(())
@@ -1636,6 +1708,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
16361708
current_holder_commitment_tx: initial_holder_commitment_tx,
16371709
prev_holder_commitment_tx: None,
16381710
},
1711+
pending_funding: vec![],
16391712

16401713
latest_update_id: 0,
16411714
commitment_transaction_number_obscure_factor,
@@ -1862,14 +1935,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18621935
{
18631936
let lock = self.inner.lock().unwrap();
18641937
let logger = WithChannelMonitor::from_impl(logger, &*lock, None);
1865-
log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo());
1866-
let funding_outpoint = lock.get_funding_txo();
1867-
filter.register_tx(&funding_outpoint.txid, &lock.funding.script_pubkey);
1938+
for funding in core::iter::once(&lock.funding).chain(&lock.pending_funding) {
1939+
let funding_outpoint = funding.funding_outpoint();
1940+
log_trace!(&logger, "Registering funding outpoint {} with the filter to monitor confirmations", &funding_outpoint);
1941+
filter.register_tx(&funding_outpoint.txid, &funding.script_pubkey);
1942+
}
18681943
for (txid, outputs) in lock.get_outputs_to_watch().iter() {
18691944
for (index, script_pubkey) in outputs.iter() {
18701945
assert!(*index <= u16::MAX as u32);
18711946
let outpoint = OutPoint { txid: *txid, index: *index as u16 };
1872-
log_trace!(logger, "Registering outpoint {} with the filter for monitoring spends", outpoint);
1947+
log_trace!(logger, "Registering outpoint {} with the filter to monitor spend", outpoint);
18731948
filter.register_output(WatchedOutput {
18741949
block_hash: None,
18751950
outpoint,
@@ -3453,6 +3528,109 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34533528
);
34543529
}
34553530

3531+
fn renegotiated_funding<L: Deref>(
3532+
&mut self, logger: &WithChannelMonitor<L>,
3533+
channel_parameters: &ChannelTransactionParameters,
3534+
alternative_holder_commitment_tx: &HolderCommitmentTransaction,
3535+
alternative_counterparty_commitment_tx: &CommitmentTransaction,
3536+
) -> Result<(), ()>
3537+
where
3538+
L::Target: Logger,
3539+
{
3540+
let redeem_script = channel_parameters.make_funding_redeemscript();
3541+
let script_pubkey = redeem_script.to_p2wsh();
3542+
let alternative_counterparty_commitment_txid =
3543+
alternative_counterparty_commitment_tx.trust().txid();
3544+
3545+
// Both the current counterparty commitment and the alternative one share the same set of
3546+
// non-dust and dust HTLCs in the same order, though the index of each non-dust HTLC may be
3547+
// different.
3548+
//
3549+
// We clone all HTLCs and their sources to use in the alternative funding scope, and update
3550+
// each non-dust HTLC with their corresponding index in the alternative counterparty
3551+
// commitment.
3552+
let current_counterparty_commitment_htlcs =
3553+
if let Some(txid) = &self.funding.current_counterparty_commitment_txid {
3554+
self.funding.counterparty_claimable_outpoints.get(txid).unwrap()
3555+
} else {
3556+
debug_assert!(false);
3557+
log_error!(
3558+
logger,
3559+
"Received funding renegotiation while initial funding negotiation is still pending"
3560+
);
3561+
return Err(());
3562+
};
3563+
let mut htlcs_with_sources = current_counterparty_commitment_htlcs.clone();
3564+
let alternative_htlcs = alternative_counterparty_commitment_tx.nondust_htlcs();
3565+
// The left side only tracks non-dust, while the right side tracks dust as well.
3566+
debug_assert!(alternative_htlcs.len() <= current_counterparty_commitment_htlcs.len());
3567+
for (alternative_htlc, (htlc, _)) in
3568+
alternative_htlcs.iter().zip(htlcs_with_sources.iter_mut())
3569+
{
3570+
debug_assert!(htlc.transaction_output_index.is_some());
3571+
debug_assert!(alternative_htlc.transaction_output_index.is_some());
3572+
debug_assert!(alternative_htlc.is_data_equal(htlc));
3573+
htlc.transaction_output_index = alternative_htlc.transaction_output_index;
3574+
}
3575+
3576+
let mut counterparty_claimable_outpoints = new_hash_map();
3577+
counterparty_claimable_outpoints
3578+
.insert(alternative_counterparty_commitment_txid, htlcs_with_sources);
3579+
3580+
// TODO(splicing): Enforce any necessary RBF validity checks.
3581+
let alternative_funding = FundingScope {
3582+
script_pubkey: script_pubkey.clone(),
3583+
redeem_script,
3584+
channel_parameters: channel_parameters.clone(),
3585+
current_counterparty_commitment_txid: Some(alternative_counterparty_commitment_txid),
3586+
prev_counterparty_commitment_txid: None,
3587+
counterparty_claimable_outpoints,
3588+
current_holder_commitment_tx: alternative_holder_commitment_tx.clone(),
3589+
prev_holder_commitment_tx: None,
3590+
};
3591+
let alternative_funding_outpoint = alternative_funding.funding_outpoint();
3592+
3593+
if self
3594+
.pending_funding
3595+
.iter()
3596+
.any(|funding| funding.funding_txid() == alternative_funding_outpoint.txid)
3597+
{
3598+
log_error!(
3599+
logger,
3600+
"Renegotiated funding transaction with a duplicate funding txid {}",
3601+
alternative_funding_outpoint.txid
3602+
);
3603+
return Err(());
3604+
}
3605+
3606+
if let Some(parent_funding_txid) = channel_parameters.splice_parent_funding_txid.as_ref() {
3607+
// Only one splice can be negotiated at a time after we've exchanged `channel_ready`
3608+
// (implying our funding is confirmed) that spends our currently locked funding.
3609+
if !self.pending_funding.is_empty() {
3610+
log_error!(
3611+
logger,
3612+
"Negotiated splice while channel is pending channel_ready/splice_locked"
3613+
);
3614+
return Err(());
3615+
}
3616+
if *parent_funding_txid != self.funding.funding_txid() {
3617+
log_error!(
3618+
logger,
3619+
"Negotiated splice that does not spend currently locked funding transaction"
3620+
);
3621+
return Err(());
3622+
}
3623+
}
3624+
3625+
self.outputs_to_watch.insert(
3626+
alternative_funding_outpoint.txid,
3627+
vec![(alternative_funding_outpoint.index as u32, script_pubkey)],
3628+
);
3629+
self.pending_funding.push(alternative_funding);
3630+
3631+
Ok(())
3632+
}
3633+
34563634
#[rustfmt::skip]
34573635
fn update_monitor<B: Deref, F: Deref, L: Deref>(
34583636
&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &WithChannelMonitor<L>
@@ -3532,6 +3710,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35323710
ret = Err(());
35333711
}
35343712
},
3713+
ChannelMonitorUpdateStep::RenegotiatedFunding {
3714+
channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3715+
} => {
3716+
log_trace!(logger, "Updating ChannelMonitor with alternative holder and counterparty commitment transactions for funding txid {}",
3717+
channel_parameters.funding_outpoint.unwrap().txid);
3718+
if let Err(_) = self.renegotiated_funding(
3719+
logger, channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3720+
) {
3721+
ret = Err(());
3722+
}
3723+
},
35353724
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
35363725
log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
35373726
self.lockdown_from_offchain = true;
@@ -3583,7 +3772,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35833772
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. }
35843773
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. }
35853774
|ChannelMonitorUpdateStep::ShutdownScript { .. }
3586-
|ChannelMonitorUpdateStep::CommitmentSecret { .. } =>
3775+
|ChannelMonitorUpdateStep::CommitmentSecret { .. }
3776+
|ChannelMonitorUpdateStep::RenegotiatedFunding { .. } =>
35873777
is_pre_close_update = true,
35883778
// After a channel is closed, we don't communicate with our peer about it, so the
35893779
// only things we will update is getting a new preimage (from a different channel)
@@ -3765,6 +3955,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37653955
} => {
37663956
Some(commitment_tx.clone())
37673957
},
3958+
&ChannelMonitorUpdateStep::RenegotiatedFunding { ref counterparty_commitment_tx, .. } => {
3959+
Some(counterparty_commitment_tx.clone())
3960+
},
37683961
_ => None,
37693962
}
37703963
}).collect()
@@ -5438,6 +5631,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
54385631
let mut payment_preimages_with_info: Option<HashMap<_, _>> = None;
54395632
let mut first_confirmed_funding_txo = RequiredWrapper(None);
54405633
let mut channel_parameters = None;
5634+
let mut pending_funding = None;
54415635
read_tlv_fields!(reader, {
54425636
(1, funding_spend_confirmed, option),
54435637
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5455,6 +5649,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
54555649
(27, first_confirmed_funding_txo, (default_value, outpoint)),
54565650
(29, initial_counterparty_commitment_tx, option),
54575651
(31, channel_parameters, (option: ReadableArgs, None)),
5652+
(32, pending_funding, optional_vec),
54585653
});
54595654
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
54605655
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -5570,6 +5765,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
55705765
current_holder_commitment_tx,
55715766
prev_holder_commitment_tx,
55725767
},
5768+
pending_funding: pending_funding.unwrap_or(vec![]),
55735769

55745770
latest_update_id,
55755771
commitment_transaction_number_obscure_factor,

0 commit comments

Comments
 (0)