@@ -262,6 +262,17 @@ impl_writeable_tlv_based!(HTLCUpdate, {
262262 ( 4 , payment_preimage, option) ,
263263} ) ;
264264
265+ /// A signed justice transaction ready for broadcast or watchtower submission.
266+ #[ derive( Clone , Debug ) ]
267+ pub struct JusticeTransaction {
268+ /// The fully signed justice transaction.
269+ pub tx : Transaction ,
270+ /// The txid of the revoked counterparty commitment transaction.
271+ pub revoked_commitment_txid : Txid ,
272+ /// The commitment number of the revoked commitment transaction.
273+ pub commitment_number : u64 ,
274+ }
275+
265276/// If an output goes from claimable only by us to claimable by us or our counterparty within this
266277/// many blocks, we consider it pinnable for the purposes of aggregating claims in a single
267278/// transaction.
@@ -1166,6 +1177,11 @@ struct FundingScope {
11661177 // transaction for which we have deleted claim information on some watchtowers.
11671178 current_holder_commitment_tx : HolderCommitmentTransaction ,
11681179 prev_holder_commitment_tx : Option < HolderCommitmentTransaction > ,
1180+
1181+ /// The current counterparty commitment transaction, stored for justice tx signing.
1182+ cur_counterparty_commitment_tx : Option < CommitmentTransaction > ,
1183+ /// The previous counterparty commitment transaction, stored for justice tx signing.
1184+ prev_counterparty_commitment_tx : Option < CommitmentTransaction > ,
11691185}
11701186
11711187impl FundingScope {
@@ -1194,6 +1210,8 @@ impl_writeable_tlv_based!(FundingScope, {
11941210 ( 7 , current_holder_commitment_tx, required) ,
11951211 ( 9 , prev_holder_commitment_tx, option) ,
11961212 ( 11 , counterparty_claimable_outpoints, required) ,
1213+ ( 13 , cur_counterparty_commitment_tx, option) ,
1214+ ( 15 , prev_counterparty_commitment_tx, option) ,
11971215} ) ;
11981216
11991217#[ derive( Clone , PartialEq ) ]
@@ -1755,6 +1773,8 @@ pub(crate) fn write_chanmon_internal<Signer: EcdsaChannelSigner, W: Writer>(
17551773 ( 34 , channel_monitor. alternative_funding_confirmed, option) ,
17561774 ( 35 , channel_monitor. is_manual_broadcast, required) ,
17571775 ( 37 , channel_monitor. funding_seen_onchain, required) ,
1776+ ( 39 , channel_monitor. funding. cur_counterparty_commitment_tx, option) ,
1777+ ( 41 , channel_monitor. funding. prev_counterparty_commitment_tx, option) ,
17581778 } ) ;
17591779
17601780 Ok ( ( ) )
@@ -1904,6 +1924,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
19041924
19051925 current_holder_commitment_tx : initial_holder_commitment_tx,
19061926 prev_holder_commitment_tx : None ,
1927+
1928+ cur_counterparty_commitment_tx : None ,
1929+ prev_counterparty_commitment_tx : None ,
19071930 } ,
19081931 pending_funding : vec ! [ ] ,
19091932
@@ -2271,6 +2294,37 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
22712294 self . inner . lock ( ) . unwrap ( ) . sign_to_local_justice_tx ( justice_tx, input_idx, value, commitment_number)
22722295 }
22732296
2297+ /// Stores the initial counterparty commitment and returns a signed justice transaction
2298+ /// if the commitment has already been revoked, or `None` otherwise.
2299+ ///
2300+ /// Intended to be called during [`Persist::persist_new_channel`].
2301+ ///
2302+ /// [`Persist::persist_new_channel`]: crate::chain::chainmonitor::Persist::persist_new_channel
2303+ pub fn sign_initial_justice_tx (
2304+ & self , feerate_per_kw : u64 , destination_script : ScriptBuf ,
2305+ ) -> Option < JusticeTransaction > {
2306+ self . inner . lock ( ) . unwrap ( ) . sign_initial_justice_tx ( feerate_per_kw, destination_script)
2307+ }
2308+
2309+ /// Returns signed justice transactions for any counterparty commitments that
2310+ /// became revoked as a result of applying the given update.
2311+ ///
2312+ /// Stores new counterparty commitment(s) from the update and signs any
2313+ /// previously-stored commitments whose revocation secrets are now available.
2314+ ///
2315+ /// Intended to be called during [`Persist::update_persisted_channel`].
2316+ ///
2317+ /// [`Persist::update_persisted_channel`]: crate::chain::chainmonitor::Persist::update_persisted_channel
2318+ pub fn sign_justice_txs_from_update (
2319+ & self , update : & ChannelMonitorUpdate , feerate_per_kw : u64 , destination_script : ScriptBuf ,
2320+ ) -> Vec < JusticeTransaction > {
2321+ self . inner . lock ( ) . unwrap ( ) . sign_justice_txs_from_update (
2322+ update,
2323+ feerate_per_kw,
2324+ destination_script,
2325+ )
2326+ }
2327+
22742328 pub ( crate ) fn get_min_seen_secret ( & self ) -> u64 {
22752329 self . inner . lock ( ) . unwrap ( ) . get_min_seen_secret ( )
22762330 }
@@ -3482,6 +3536,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34823536
34833537 self . provide_latest_counterparty_commitment_tx ( commitment_tx. trust ( ) . txid ( ) , Vec :: new ( ) , commitment_tx. commitment_number ( ) ,
34843538 commitment_tx. per_commitment_point ( ) ) ;
3539+ self . funding . cur_counterparty_commitment_tx = Some ( commitment_tx. clone ( ) ) ;
34853540 // Soon, we will only populate this field
34863541 self . initial_counterparty_commitment_tx = Some ( commitment_tx) ;
34873542 }
@@ -4022,6 +4077,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
40224077 counterparty_claimable_outpoints,
40234078 current_holder_commitment_tx : alternative_holder_commitment_tx. clone ( ) ,
40244079 prev_holder_commitment_tx : None ,
4080+ cur_counterparty_commitment_tx : None ,
4081+ prev_counterparty_commitment_tx : None ,
40254082 } ;
40264083 let alternative_funding_outpoint = alternative_funding. funding_outpoint ( ) ;
40274084
@@ -4563,6 +4620,77 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45634620 Ok ( justice_tx)
45644621 }
45654622
4623+ fn sign_initial_justice_tx (
4624+ & mut self , feerate_per_kw : u64 , destination_script : ScriptBuf ,
4625+ ) -> Option < JusticeTransaction > {
4626+ let commitment_tx = self . initial_counterparty_commitment_tx ( ) ?;
4627+ self . funding . cur_counterparty_commitment_tx = Some ( commitment_tx. clone ( ) ) ;
4628+ self . try_sign_justice_tx ( & commitment_tx, feerate_per_kw, destination_script)
4629+ }
4630+
4631+ fn sign_justice_txs_from_update (
4632+ & mut self , update : & ChannelMonitorUpdate , feerate_per_kw : u64 ,
4633+ destination_script : ScriptBuf ,
4634+ ) -> Vec < JusticeTransaction > {
4635+ let new_commitment_txs = self . counterparty_commitment_txs_from_update ( update) ;
4636+
4637+ // Store new commitments, rotating cur -> prev per funding scope.
4638+ for commitment_tx in new_commitment_txs {
4639+ let txid = commitment_tx. trust ( ) . built_transaction ( ) . txid ;
4640+ let funding = core:: iter:: once ( & mut self . funding )
4641+ . chain ( self . pending_funding . iter_mut ( ) )
4642+ . find ( |f| f. current_counterparty_commitment_txid == Some ( txid) ) ;
4643+ if let Some ( funding) = funding {
4644+ funding. prev_counterparty_commitment_tx =
4645+ funding. cur_counterparty_commitment_tx . take ( ) ;
4646+ funding. cur_counterparty_commitment_tx = Some ( commitment_tx) ;
4647+ }
4648+ }
4649+
4650+ // Collect prev commitments that have revocation secrets available, clearing them
4651+ // from storage so they aren't signed again on subsequent calls.
4652+ let mut to_sign = Vec :: new ( ) ;
4653+ for funding in core:: iter:: once ( & mut self . funding ) . chain ( self . pending_funding . iter_mut ( ) ) {
4654+ let should_take =
4655+ funding. prev_counterparty_commitment_tx . as_ref ( ) . is_some_and ( |prev| {
4656+ self . commitment_secrets . get_secret ( prev. commitment_number ( ) ) . is_some ( )
4657+ } ) ;
4658+ if should_take {
4659+ to_sign. push ( funding. prev_counterparty_commitment_tx . take ( ) . unwrap ( ) ) ;
4660+ }
4661+ }
4662+
4663+ let mut result = Vec :: new ( ) ;
4664+ for commitment_tx in & to_sign {
4665+ if let Some ( jtx) =
4666+ self . try_sign_justice_tx ( commitment_tx, feerate_per_kw, destination_script. clone ( ) )
4667+ {
4668+ result. push ( jtx) ;
4669+ }
4670+ }
4671+ result
4672+ }
4673+
4674+ fn try_sign_justice_tx (
4675+ & self , commitment_tx : & CommitmentTransaction , feerate_per_kw : u64 ,
4676+ destination_script : ScriptBuf ,
4677+ ) -> Option < JusticeTransaction > {
4678+ let commitment_number = commitment_tx. commitment_number ( ) ;
4679+ self . get_secret ( commitment_number) ?;
4680+
4681+ let trusted = commitment_tx. trust ( ) ;
4682+ let output_idx = trusted. revokeable_output_index ( ) ?;
4683+ let built = trusted. built_transaction ( ) ;
4684+ let value = built. transaction . output [ output_idx] . value ;
4685+ let txid = built. txid ;
4686+
4687+ let justice_tx =
4688+ trusted. build_to_local_justice_tx ( feerate_per_kw, destination_script) . ok ( ) ?;
4689+ let signed =
4690+ self . sign_to_local_justice_tx ( justice_tx, 0 , value. to_sat ( ) , commitment_number) . ok ( ) ?;
4691+ Some ( JusticeTransaction { tx : signed, revoked_commitment_txid : txid, commitment_number } )
4692+ }
4693+
45664694 /// Can only fail if idx is < get_min_seen_secret
45674695 fn get_secret ( & self , idx : u64 ) -> Option < [ u8 ; 32 ] > {
45684696 self . commitment_secrets . get_secret ( idx)
@@ -6521,6 +6649,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
65216649 let mut alternative_funding_confirmed = None ;
65226650 let mut is_manual_broadcast = RequiredWrapper ( None ) ;
65236651 let mut funding_seen_onchain = RequiredWrapper ( None ) ;
6652+ let mut cur_counterparty_commitment_tx: Option < CommitmentTransaction > = None ;
6653+ let mut prev_counterparty_commitment_tx_deser: Option < CommitmentTransaction > = None ;
65246654 read_tlv_fields ! ( reader, {
65256655 ( 1 , funding_spend_confirmed, option) ,
65266656 ( 3 , htlcs_resolved_on_chain, optional_vec) ,
@@ -6543,6 +6673,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
65436673 ( 34 , alternative_funding_confirmed, option) ,
65446674 ( 35 , is_manual_broadcast, ( default_value, false ) ) ,
65456675 ( 37 , funding_seen_onchain, ( default_value, true ) ) ,
6676+ ( 39 , cur_counterparty_commitment_tx, option) ,
6677+ ( 41 , prev_counterparty_commitment_tx_deser, option) ,
65466678 } ) ;
65476679 // Note that `payment_preimages_with_info` was added (and is always written) in LDK 0.1, so
65486680 // we can use it to determine if this monitor was last written by LDK 0.1 or later.
@@ -6657,6 +6789,9 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
66576789
66586790 current_holder_commitment_tx,
66596791 prev_holder_commitment_tx,
6792+
6793+ cur_counterparty_commitment_tx,
6794+ prev_counterparty_commitment_tx : prev_counterparty_commitment_tx_deser,
66606795 } ,
66616796 pending_funding : pending_funding. unwrap_or ( vec ! [ ] ) ,
66626797 is_manual_broadcast : is_manual_broadcast. 0 . unwrap ( ) ,
0 commit comments