Skip to content

Commit 70747bb

Browse files
committed
Enable signing a justice tx using the channel monitor
1 parent 42b75ca commit 70747bb

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ impl MonitorUpdateId {
9494
/// [`ChannelMonitorUpdateStatus::PermanentFailure`], in which case the channel will likely be
9595
/// closed without broadcasting the latest state. See
9696
/// [`ChannelMonitorUpdateStatus::PermanentFailure`] for more details.
97+
///
98+
/// Third-party watchtowers may be built as a part of an implementation of this trait, with the
99+
/// advantage that you can control whether to resume channel operation depending on if an update
100+
/// has been persisted to a watchtower. For this, you may find the following methods useful:
101+
/// [`ChannelMonitor::initial_counterparty_commitment_tx`],
102+
/// [`ChannelMonitor::counterparty_commitment_txs_from_update`],
103+
/// [`ChannelMonitor::sign_justice_tx`], [`CommitmentTransaction::revokeable_output_index`],
104+
/// [`CommitmentTransaction::build_to_local_justice_tx`].
105+
///
106+
/// [`CommitmentTransaction::revokeable_output_index`]: crate::ln::chan_utils::CommitmentTransaction::revokeable_output_index
107+
/// [`CommitmentTransaction::build_to_local_justice_tx`]: crate::ln::chan_utils::CommitmentTransaction::build_to_local_justice_tx
97108
pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
98109
/// Persist a new channel's data in response to a [`chain::Watch::watch_channel`] call. This is
99110
/// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup.

lightning/src/chain/channelmonitor.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
3131

3232
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
3333
use bitcoin::secp256k1::{SecretKey, PublicKey};
34-
use bitcoin::secp256k1;
34+
use bitcoin::{secp256k1, EcdsaSighashType};
3535

3636
use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
3737
use crate::ln::{PaymentHash, PaymentPreimage};
@@ -1438,7 +1438,8 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
14381438
/// This is provided so that watchtower clients in the persistence pipeline are able to build
14391439
/// justice transactions for each counterparty commitment upon each update. It's intended to be
14401440
/// used within an implementation of [`Persist::update_persisted_channel`], which is provided
1441-
/// with a monitor and an update.
1441+
/// with a monitor and an update. Once revoked, signing a justice transaction can be done using
1442+
/// [`Self::sign_justice_tx`].
14421443
///
14431444
/// It is expected that a watchtower may use this method to retrieve the latest counterparty
14441445
/// commitment transaction(s), and then hold the necessary data until a later update in which
@@ -1454,6 +1455,25 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
14541455
self.inner.lock().unwrap().counterparty_commitment_txs_from_update(update)
14551456
}
14561457

1458+
/// Wrapper around [`EcdsaChannelSigner::sign_justice_revoked_output`] to make
1459+
/// signing the justice transaction easier for implementors of
1460+
/// [`chain::chainmonitor::Persist`]. On success this method returns the provided transaction
1461+
/// signing the input at `input_idx`.
1462+
///
1463+
/// `Value` is the value of the output being spent by the input at `input_idx`, committed
1464+
/// in the BIP 143 signature.
1465+
///
1466+
/// This method will only succeed if this monitor has received the revocation secret for the
1467+
/// provided `commitment_number`. If a commitment number is provided that does not correspond
1468+
/// to the commitment transaction being revoked, this will return a signed transaction, but
1469+
/// the signature will not be valid.
1470+
///
1471+
/// [`EcdsaChannelSigner::sign_justice_revoked_output`]: crate::sign::EcdsaChannelSigner::sign_justice_revoked_output
1472+
/// [`Persist`]: crate::chain::chainmonitor::Persist
1473+
pub fn sign_justice_tx(&self, justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64) -> Result<Transaction, ()> {
1474+
self.inner.lock().unwrap().sign_justice_tx(justice_tx, input_idx, value, commitment_number)
1475+
}
1476+
14571477
pub(crate) fn get_min_seen_secret(&self) -> u64 {
14581478
self.inner.lock().unwrap().get_min_seen_secret()
14591479
}
@@ -2834,6 +2854,31 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
28342854
}).collect()
28352855
}
28362856

2857+
pub(crate) fn sign_justice_tx(
2858+
&self, mut justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64
2859+
) -> Result<Transaction, ()> {
2860+
let secret = self.get_secret(commitment_number).ok_or(())?;
2861+
let per_commitment_key = SecretKey::from_slice(&secret).map_err(|_| ())?;
2862+
let their_per_commitment_point = PublicKey::from_secret_key(
2863+
&self.onchain_tx_handler.secp_ctx, &per_commitment_key);
2864+
2865+
let revocation_pubkey = chan_utils::derive_public_revocation_key(
2866+
&self.onchain_tx_handler.secp_ctx, &their_per_commitment_point,
2867+
&self.holder_revocation_basepoint);
2868+
let delayed_key = chan_utils::derive_public_key(&self.onchain_tx_handler.secp_ctx,
2869+
&their_per_commitment_point,
2870+
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
2871+
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2872+
self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
2873+
2874+
let sig = self.onchain_tx_handler.signer.sign_justice_revoked_output(
2875+
&justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx)?;
2876+
justice_tx.input[input_idx].witness.push_bitcoin_signature(&sig.serialize_der(), EcdsaSighashType::All);
2877+
justice_tx.input[input_idx].witness.push(&[1u8]);
2878+
justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes());
2879+
Ok(justice_tx)
2880+
}
2881+
28372882
/// Can only fail if idx is < get_min_seen_secret
28382883
fn get_secret(&self, idx: u64) -> Option<[u8; 32]> {
28392884
self.commitment_secrets.get_secret(idx)

0 commit comments

Comments
 (0)