Skip to content

Commit

Permalink
customize to_local output
Browse files Browse the repository at this point in the history
  • Loading branch information
tankyleo committed Dec 2, 2024
1 parent a22c8a7 commit 9dbd6ab
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 64 deletions.
36 changes: 18 additions & 18 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hash_types::{Txid, BlockHash};

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

use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
Expand Down Expand Up @@ -3433,14 +3432,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
&self.holder_revocation_basepoint, &their_per_commitment_point);
let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx,
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &their_per_commitment_point);
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);

let sig = self.onchain_tx_handler.signer.sign_justice_revoked_output(
&justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx)?;
justice_tx.input[input_idx].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(sig));
justice_tx.input[input_idx].witness.push(&[1u8]);
justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes());
let witness = self.onchain_tx_handler.signer.get_justice_witness(
&revocation_pubkey,
self.counterparty_commitment_params.on_counterparty_tx_csv,
&delayed_key,
&justice_tx,
input_idx,
value,
&per_commitment_key,
&self.onchain_tx_handler.secp_ctx
);
justice_tx.input[input_idx].witness = witness;
Ok(justice_tx)
}

Expand Down Expand Up @@ -3495,13 +3497,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
let per_commitment_point = PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key);
let revocation_pubkey = RevocationKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.holder_revocation_basepoint, &per_commitment_point,);
let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key));

let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
let revokeable_p2wsh = revokeable_redeemscript.to_p2wsh();
let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);

// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
if outp.script_pubkey == revokeable_spk {
let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx());
let justice_package = PackageTemplate::build_package(
commitment_txid, idx as u32,
Expand Down Expand Up @@ -3616,11 +3616,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {

let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &per_commitment_point);

let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(&revocation_pubkey,
self.counterparty_commitment_params.on_counterparty_tx_csv,
&delayed_key).to_p2wsh();
&delayed_key);
for (idx, outp) in transaction.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
if outp.script_pubkey == revokeable_spk {
to_counterparty_output_info =
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
}
Expand Down Expand Up @@ -3713,8 +3713,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32) -> (Vec<PackageTemplate>, Option<(ScriptBuf, PublicKey, RevocationKey)>) {
let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());

let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
let broadcasted_holder_revokable_script = Some((redeemscript.to_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
let broadcasted_holder_revokable_script = Some((revokeable_spk, holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));

for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
Expand Down
12 changes: 3 additions & 9 deletions lightning/src/chain/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,15 +592,9 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(ref outp) => {
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);
let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key);
//TODO: should we panic on signer failure ?
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) {
let mut ser_sig = sig.serialize_der().to_vec();
ser_sig.push(EcdsaSighashType::All as u8);
bumped_tx.input[i].witness.push(ser_sig);
bumped_tx.input[i].witness.push(vec!(1));
bumped_tx.input[i].witness.push(witness_script.clone().into_bytes());
} else { return false; }
//TODO: What if the signer fails here? Previously, we just returned false.
let witness = onchain_handler.signer.get_justice_witness(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key, &bumped_tx, i, outp.amount.to_sat(), &outp.per_commitment_key, &onchain_handler.secp_ctx);
bumped_tx.input[i].witness = witness;
},
PackageSolvingData::RevokedHTLCOutput(ref outp) => {
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);
Expand Down
52 changes: 46 additions & 6 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
use bitcoin::hash_types::Txid;

#[cfg(test)]
use crate::chain::chaininterface::fee_for_weight;
#[cfg(test)]
use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
use crate::sign::{ChannelSigner, EntropySource};
use crate::types::payment::{PaymentHash, PaymentPreimage};
Expand Down Expand Up @@ -547,6 +549,43 @@ pub fn get_revokeable_redeemscript(revocation_key: &RevocationKey, contest_delay
res
}

/// Document this
pub fn get_revokeable_spk(revocation_key: &RevocationKey, contest_delay: u16, broadcaster_delayed_payment_key: &DelayedPaymentKey) -> ScriptBuf {
let revokeable_redeemscript = get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key);
revokeable_redeemscript.to_p2wsh()
}

/// Document this please
pub fn get_to_local_witness<C: secp256k1::Signing, ES: Deref>(revocation_key: &RevocationKey, contest_delay: u16, delayed_payment_key: &SecretKey, spend_tx: &Transaction, input_idx: usize, amount: Amount, secp_ctx: &Secp256k1<C>, entropy_source: &ES) -> Witness
where ES::Target: crate::sign::EntropySource
{
let delayed_payment_pubkey = DelayedPaymentKey::from_secret_key(secp_ctx, delayed_payment_key);
let witness_script = get_revokeable_redeemscript(
revocation_key,
contest_delay,
&delayed_payment_pubkey
);
let sighash = hash_to_message!(
&sighash::SighashCache::new(spend_tx)
.p2wsh_signature_hash(
input_idx,
&witness_script,
amount,
EcdsaSighashType::All
)
.unwrap()[..]
);
let local_delayedsig = bitcoin::ecdsa::Signature {
signature: sign_with_aux_rand(secp_ctx, &sighash, delayed_payment_key, entropy_source),
sighash_type: EcdsaSighashType::All,
};
Witness::from_slice(&[
&local_delayedsig.serialize()[..],
&[],
witness_script.as_bytes(),
])
}

/// Returns the script for the counterparty's output on a holder's commitment transaction based on
/// the channel type.
pub fn get_counterparty_payment_script(channel_type_features: &ChannelTypeFeatures, payment_key: &PublicKey) -> ScriptBuf {
Expand Down Expand Up @@ -749,7 +788,7 @@ pub(crate) fn build_htlc_output(
};

TxOut {
script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_p2wsh(),
script_pubkey: get_revokeable_spk(revocation_key, contest_delay, broadcaster_delayed_payment_key),
value: output_value,
}
}
Expand Down Expand Up @@ -1524,14 +1563,14 @@ impl CommitmentTransaction {
}

if to_broadcaster_value_sat > Amount::ZERO {
let redeem_script = get_revokeable_redeemscript(
let script_pubkey = signer.get_revokeable_spk(
&keys.revocation_key,
contest_delay,
&keys.broadcaster_delayed_payment_key,
);
txouts.push((
TxOut {
script_pubkey: redeem_script.to_p2wsh(),
script_pubkey,
value: to_broadcaster_value_sat,
},
None,
Expand Down Expand Up @@ -1800,16 +1839,16 @@ impl<'a> TrustedCommitmentTransaction<'a> {
/// - This commitment was created before LDK 0.0.117. In this case, the
/// commitment transaction previously didn't contain enough information to locate the
/// revokeable output.
#[cfg(test)]
pub fn revokeable_output_index(&self) -> Option<usize> {
let revokeable_redeemscript = get_revokeable_redeemscript(
let revokeable_scriptpubkey = get_revokeable_spk(
&self.keys.revocation_key,
self.to_broadcaster_delay?,
&self.keys.broadcaster_delayed_payment_key,
);
let revokeable_p2wsh = revokeable_redeemscript.to_p2wsh();
let outputs = &self.inner.built.transaction.output;
outputs.iter().enumerate()
.find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
.find(|(_, out)| out.script_pubkey == revokeable_scriptpubkey)
.map(|(idx, _)| idx)
}

Expand All @@ -1824,6 +1863,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
/// The built transaction will allow fee bumping with RBF, and this method takes
/// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different
/// fee rates may be built.
#[cfg(test)]
pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: ScriptBuf)
-> Result<Transaction, ()> {
let output_idx = self.revokeable_output_index().ok_or(())?;
Expand Down
35 changes: 32 additions & 3 deletions lightning/src/sign/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@
use bitcoin::transaction::Transaction;

use bitcoin::ecdsa::Signature as BitcoinSignature;
use bitcoin::secp256k1;
use bitcoin::secp256k1::ecdsa::Signature;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::secp256k1::{All, PublicKey, Secp256k1, SecretKey};
use bitcoin::Witness;

use crate::ln::chan_utils::{
ClosingTransaction, CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
self, ClosingTransaction, CommitmentTransaction, HTLCOutputInCommitment,
HolderCommitmentTransaction,
};
use crate::ln::msgs::UnsignedChannelAnnouncement;
use crate::types::payment::PaymentPreimage;

#[allow(unused_imports)]
use crate::prelude::*;

use crate::sign::{ChannelSigner, HTLCDescriptor};
use crate::sign::{ChannelSigner, DelayedPaymentKey, HTLCDescriptor, RevocationKey};

/// A trait to sign Lightning channel transactions as described in
/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
Expand All @@ -25,6 +28,32 @@ use crate::sign::{ChannelSigner, HTLCDescriptor};
/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md)
/// for an example of such policies.
pub trait EcdsaChannelSigner: ChannelSigner {
/// Document this next please
fn get_justice_witness(
&self, revocation_key: &RevocationKey, contest_delay: u16,
broadcaster_delayed_payment_key: &DelayedPaymentKey, justice_tx: &Transaction,
input_idx: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<All>,
) -> Witness {
let sig = self
.sign_justice_revoked_output(
justice_tx,
input_idx,
amount,
per_commitment_key,
secp_ctx,
)
.unwrap();
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(
revocation_key,
contest_delay,
broadcaster_delayed_payment_key,
);
let mut witness = Witness::new();
witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(sig));
witness.push(&[1u8]);
witness.push(revokeable_redeemscript.as_bytes());
witness
}
/// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
///
/// Note that if signing fails or is rejected, the channel will be force-closed.
Expand Down
54 changes: 26 additions & 28 deletions lightning/src/sign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use bitcoin::amount::Amount;
use bitcoin::bip32::{ChildNumber, Xpriv, Xpub};
use bitcoin::ecdsa::Signature as EcdsaSignature;
use bitcoin::locktime::absolute::LockTime;
use bitcoin::network::Network;
use bitcoin::opcodes;
Expand All @@ -31,8 +30,6 @@ use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr;
#[cfg(taproot)]
use bitcoin::secp256k1::All;
use bitcoin::secp256k1::{Keypair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness};

Expand All @@ -42,7 +39,7 @@ use crate::chain::transaction::OutPoint;
use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
use crate::ln::chan_utils;
use crate::ln::chan_utils::{
get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys,
get_revokeable_redeemscript, get_revokeable_spk, make_funding_redeemscript, ChannelPublicKeys,
ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
HTLCOutputInCommitment, HolderCommitmentTransaction,
};
Expand Down Expand Up @@ -729,6 +726,18 @@ impl HTLCDescriptor {
/// is not yet complete, and panics may occur in certain situations when returning errors
/// for these methods.
pub trait ChannelSigner {
/// Should this go on a channel signer? We'll see later. See above eas well.
fn get_revokeable_spk(
&self, revocation_key: &RevocationKey, contest_delay: u16,
broadcaster_delayed_payment_key: &DelayedPaymentKey,
) -> ScriptBuf {
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(
revocation_key,
contest_delay,
broadcaster_delayed_payment_key,
);
revokeable_redeemscript.to_p2wsh()
}
/// Document this next
fn get_counterparty_payment_script(
&self, channel_type_features: &ChannelTypeFeatures, payment_key: &PublicKey,
Expand Down Expand Up @@ -1300,37 +1309,26 @@ impl InMemorySigner {
);
let delayed_payment_pubkey =
DelayedPaymentKey::from_secret_key(&secp_ctx, &delayed_payment_key);
let witness_script = chan_utils::get_revokeable_redeemscript(

let revokeable_spk = get_revokeable_spk(
&descriptor.revocation_pubkey,
descriptor.to_self_delay,
&delayed_payment_pubkey,
);
let sighash = hash_to_message!(
&sighash::SighashCache::new(spend_tx)
.p2wsh_signature_hash(
input_idx,
&witness_script,
descriptor.output.value,
EcdsaSighashType::All
)
.unwrap()[..]
);
let local_delayedsig = EcdsaSignature {
signature: sign_with_aux_rand(secp_ctx, &sighash, &delayed_payment_key, &self),
sighash_type: EcdsaSighashType::All,
};
let payment_script =
bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();

if descriptor.output.script_pubkey != payment_script {
if descriptor.output.script_pubkey != revokeable_spk {
return Err(());
}

Ok(Witness::from_slice(&[
&local_delayedsig.serialize()[..],
&[], // MINIMALIF
witness_script.as_bytes(),
]))
Ok(chan_utils::get_to_local_witness(
&descriptor.revocation_pubkey,
descriptor.to_self_delay,
&delayed_payment_key,
spend_tx,
input_idx,
descriptor.output.value,
secp_ctx,
&self,
))
}
}

Expand Down

0 comments on commit 9dbd6ab

Please sign in to comment.