Skip to content

Commit 0f0560a

Browse files
committed
Add ChannelSigner::punish_revokeable_output
All LN-Penalty channel signers need to be able to punish the counterparty in case they broadcast an old state. In this commit, we ask implementers of `ChannelSigner` to produce the full transaction with the given input finalized to punish the corresponding previous output. Consumers of the `ChannelSigner` trait can now be agnostic to the specific scripts used in revokeable outputs. We leave passing to the `ChannelSigner` all the previous `TxOut`'s needed to produce valid schnorr signatures under BIP 341 spending rules to a later patch.
1 parent d3181c5 commit 0f0560a

File tree

6 files changed

+85
-36
lines changed

6 files changed

+85
-36
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ use bitcoin::secp256k1::PublicKey;
9696
/// provided for bulding transactions for a watchtower:
9797
/// [`ChannelMonitor::initial_counterparty_commitment_tx`],
9898
/// [`ChannelMonitor::counterparty_commitment_txs_from_update`],
99-
/// [`ChannelMonitor::sign_to_local_justice_tx`], [`TrustedCommitmentTransaction::revokeable_output_index`],
99+
/// [`ChannelMonitor::punish_revokeable_output`], [`TrustedCommitmentTransaction::revokeable_output_index`],
100100
/// [`TrustedCommitmentTransaction::build_to_local_justice_tx`].
101101
///
102102
/// [`TrustedCommitmentTransaction::revokeable_output_index`]: crate::ln::chan_utils::TrustedCommitmentTransaction::revokeable_output_index

lightning/src/chain/channelmonitor.rs

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ use bitcoin::hashes::Hash;
2929
use bitcoin::hashes::sha256::Hash as Sha256;
3030
use bitcoin::hash_types::{Txid, BlockHash};
3131

32-
use bitcoin::ecdsa::Signature as BitcoinSignature;
3332
use bitcoin::secp256k1::{self, SecretKey, PublicKey, Secp256k1, ecdsa::Signature};
3433

3534
use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
@@ -1675,8 +1674,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
16751674
/// This is provided so that watchtower clients in the persistence pipeline are able to build
16761675
/// justice transactions for each counterparty commitment upon each update. It's intended to be
16771676
/// used within an implementation of [`Persist::update_persisted_channel`], which is provided
1678-
/// with a monitor and an update. Once revoked, signing a justice transaction can be done using
1679-
/// [`Self::sign_to_local_justice_tx`].
1677+
/// with a monitor and an update. Once revoked, punishing a revokeable output can be done using
1678+
/// [`Self::punish_revokeable_output`].
16801679
///
16811680
/// It is expected that a watchtower client may use this method to retrieve the latest counterparty
16821681
/// commitment transaction(s), and then hold the necessary data until a later update in which
@@ -1692,12 +1691,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
16921691
self.inner.lock().unwrap().counterparty_commitment_txs_from_update(update)
16931692
}
16941693

1695-
/// Wrapper around [`EcdsaChannelSigner::sign_justice_revoked_output`] to make
1696-
/// signing the justice transaction easier for implementors of
1694+
/// Wrapper around [`ChannelSigner::punish_revokeable_output`] to make
1695+
/// punishing a revokeable output easier for implementors of
16971696
/// [`chain::chainmonitor::Persist`]. On success this method returns the provided transaction
1698-
/// signing the input at `input_idx`. This method will only produce a valid signature for
1697+
/// finalizing the input at `input_idx`. This method will only produce a valid transaction for
16991698
/// a transaction spending the `to_local` output of a commitment transaction, i.e. this cannot
1700-
/// be used for revoked HTLC outputs.
1699+
/// be used for revoked HTLC outputs of a commitment transaction.
17011700
///
17021701
/// `Value` is the value of the output being spent by the input at `input_idx`, committed
17031702
/// in the BIP 143 signature.
@@ -1707,10 +1706,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
17071706
/// to the commitment transaction being revoked, this will return a signed transaction, but
17081707
/// the signature will not be valid.
17091708
///
1710-
/// [`EcdsaChannelSigner::sign_justice_revoked_output`]: crate::sign::ecdsa::EcdsaChannelSigner::sign_justice_revoked_output
1709+
/// [`ChannelSigner::punish_revokeable_output`]: crate::sign::ChannelSigner::punish_revokeable_output
17111710
/// [`Persist`]: crate::chain::chainmonitor::Persist
1712-
pub fn sign_to_local_justice_tx(&self, justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64) -> Result<Transaction, ()> {
1713-
self.inner.lock().unwrap().sign_to_local_justice_tx(justice_tx, input_idx, value, commitment_number)
1711+
pub fn punish_revokeable_output(&self, justice_tx: &Transaction, input_idx: usize, value: u64, commitment_number: u64) -> Result<Transaction, ()> {
1712+
self.inner.lock().unwrap().punish_revokeable_output(justice_tx, input_idx, value, commitment_number)
17141713
}
17151714

17161715
pub(crate) fn get_min_seen_secret(&self) -> u64 {
@@ -3458,27 +3457,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34583457
}).collect()
34593458
}
34603459

3461-
fn sign_to_local_justice_tx(
3462-
&self, mut justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64
3460+
fn punish_revokeable_output(
3461+
&self, justice_tx: &Transaction, input_idx: usize, value: u64, commitment_number: u64
34633462
) -> Result<Transaction, ()> {
34643463
let secret = self.get_secret(commitment_number).ok_or(())?;
34653464
let per_commitment_key = SecretKey::from_slice(&secret).map_err(|_| ())?;
34663465
let their_per_commitment_point = PublicKey::from_secret_key(
34673466
&self.onchain_tx_handler.secp_ctx, &per_commitment_key);
3468-
3469-
let revocation_pubkey = RevocationKey::from_basepoint(&self.onchain_tx_handler.secp_ctx,
3470-
&self.holder_revocation_basepoint, &their_per_commitment_point);
3471-
let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx,
3472-
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &their_per_commitment_point);
3473-
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
3474-
self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
3475-
3476-
let sig = self.onchain_tx_handler.signer.sign_justice_revoked_output(
3477-
&justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx)?;
3478-
justice_tx.input[input_idx].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(sig));
3479-
justice_tx.input[input_idx].witness.push(&[1u8]);
3480-
justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes());
3481-
Ok(justice_tx)
3467+
self.onchain_tx_handler.signer.punish_revokeable_output(justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx, &their_per_commitment_point)
34823468
}
34833469

34843470
/// Can only fail if idx is < get_min_seen_secret

lightning/src/chain/package.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -604,15 +604,9 @@ impl PackageSolvingData {
604604
fn finalize_input<Signer: EcdsaChannelSigner>(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler<Signer>) -> bool {
605605
match self {
606606
PackageSolvingData::RevokedOutput(ref outp) => {
607-
let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
608-
let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key);
609607
//TODO: should we panic on signer failure ?
610-
if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount.to_sat(), &outp.per_commitment_key, &onchain_handler.secp_ctx) {
611-
let mut ser_sig = sig.serialize_der().to_vec();
612-
ser_sig.push(EcdsaSighashType::All as u8);
613-
bumped_tx.input[i].witness.push(ser_sig);
614-
bumped_tx.input[i].witness.push(vec!(1));
615-
bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
608+
if let Ok(tx) = onchain_handler.signer.punish_revokeable_output(bumped_tx, i, outp.amount.to_sat(), &outp.per_commitment_key, &onchain_handler.secp_ctx, &outp.per_commitment_point) {
609+
*bumped_tx = tx;
616610
} else { return false; }
617611
},
618612
PackageSolvingData::RevokedHTLCOutput(ref outp) => {

lightning/src/sign/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,34 @@ pub trait ChannelSigner {
795795
&self, to_self: bool, commitment_number: u64, per_commitment_point: &PublicKey,
796796
secp_ctx: &Secp256k1<secp256k1::All>,
797797
) -> ScriptBuf;
798+
799+
/// Finalize the given input in a transaction spending an HTLC transaction output
800+
/// or a commitment transaction `to_local` output when our counterparty broadcasts an old state.
801+
///
802+
/// A justice transaction may claim multiple outputs at the same time if timelocks are
803+
/// similar, but only the input at index `input` should be finalized here.
804+
/// It may be called multiple times for the same output(s) if a fee-bump is needed with regards
805+
/// to an upcoming timelock expiration.
806+
///
807+
/// Amount is the value of the output spent by this input, committed to in the BIP 143 signature.
808+
///
809+
/// `per_commitment_key` is revocation secret which was provided by our counterparty when they
810+
/// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
811+
/// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do
812+
/// so).
813+
///
814+
/// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid
815+
/// signature and should be retried later. Once the signer is ready to provide a signature after
816+
/// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its
817+
/// monitor.
818+
///
819+
/// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked
820+
///
821+
/// TODO(taproot): pass to the `ChannelSigner` all the `TxOut`'s spent by the justice transaction.
822+
fn punish_revokeable_output(
823+
&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
824+
secp_ctx: &Secp256k1<secp256k1::All>, per_commitment_point: &PublicKey,
825+
) -> Result<Transaction, ()>;
798826
}
799827

800828
/// Specifies the recipient of an invoice.
@@ -1421,6 +1449,40 @@ impl ChannelSigner for InMemorySigner {
14211449
)
14221450
.to_p2wsh()
14231451
}
1452+
1453+
fn punish_revokeable_output(
1454+
&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
1455+
secp_ctx: &Secp256k1<secp256k1::All>, per_commitment_point: &PublicKey,
1456+
) -> Result<Transaction, ()> {
1457+
let params = self.channel_parameters.as_ref().unwrap().as_counterparty_broadcastable();
1458+
let contest_delay = params.contest_delay();
1459+
let keys = TxCreationKeys::from_channel_static_keys(
1460+
per_commitment_point,
1461+
params.broadcaster_pubkeys(),
1462+
params.countersignatory_pubkeys(),
1463+
secp_ctx,
1464+
);
1465+
let witness_script = get_revokeable_redeemscript(
1466+
&keys.revocation_key,
1467+
contest_delay,
1468+
&keys.broadcaster_delayed_payment_key,
1469+
);
1470+
let sig = EcdsaChannelSigner::sign_justice_revoked_output(
1471+
self,
1472+
justice_tx,
1473+
input,
1474+
amount,
1475+
per_commitment_key,
1476+
secp_ctx,
1477+
)?;
1478+
let mut justice_tx = justice_tx.clone();
1479+
let mut ser_sig = sig.serialize_der().to_vec();
1480+
ser_sig.push(EcdsaSighashType::All as u8);
1481+
justice_tx.input[input].witness.push(ser_sig);
1482+
justice_tx.input[input].witness.push(vec![1]);
1483+
justice_tx.input[input].witness.push(witness_script.into_bytes());
1484+
Ok(justice_tx)
1485+
}
14241486
}
14251487

14261488
const MISSING_PARAMS_ERR: &'static str =

lightning/src/util/test_channel_signer.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ impl ChannelSigner for TestChannelSigner {
226226
fn get_revokeable_spk(&self, to_self: bool, commitment_number: u64, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ScriptBuf {
227227
self.inner.get_revokeable_spk(to_self, commitment_number, per_commitment_point, secp_ctx)
228228
}
229+
230+
fn punish_revokeable_output(
231+
&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
232+
secp_ctx: &Secp256k1<secp256k1::All>, per_commitment_point: &PublicKey,
233+
) -> Result<Transaction, ()> {
234+
self.inner.punish_revokeable_output(justice_tx, input, amount, per_commitment_key, secp_ctx, per_commitment_point)
235+
}
229236
}
230237

231238
impl EcdsaChannelSigner for TestChannelSigner {

lightning/src/util/test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ impl<Signer: sign::ecdsa::EcdsaChannelSigner> chainmonitor::Persist<Signer> for
517517
while let Some(JusticeTxData { justice_tx, value, commitment_number }) = channel_state.front() {
518518
let input_idx = 0;
519519
let commitment_txid = justice_tx.input[input_idx].previous_output.txid;
520-
match data.sign_to_local_justice_tx(justice_tx.clone(), input_idx, value.to_sat(), *commitment_number) {
520+
match data.punish_revokeable_output(justice_tx, input_idx, value.to_sat(), *commitment_number) {
521521
Ok(signed_justice_tx) => {
522522
let dup = self.watchtower_state.lock().unwrap()
523523
.get_mut(&funding_txo).unwrap()

0 commit comments

Comments
 (0)