Skip to content

Commit

Permalink
ChannelSigner now yields the counterparty payment script
Browse files Browse the repository at this point in the history
  • Loading branch information
tankyleo committed Nov 28, 2024
1 parent 4c35203 commit 63621ec
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 44 deletions.
18 changes: 9 additions & 9 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
) -> ChannelMonitor<Signer> {

assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
let counterparty_payment_script = chan_utils::get_counterparty_payment_script(
let counterparty_payment_script = keys.get_counterparty_payment_script(
&channel_parameters.channel_type_features, &keys.pubkeys().payment_point
);

Expand Down Expand Up @@ -3388,7 +3388,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
let channel_parameters =
&self.onchain_tx_handler.channel_transaction_parameters.as_counterparty_broadcastable();

CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
CommitmentTransaction::new_with_auxiliary_htlc_data(&self.onchain_tx_handler.signer, commitment_number,
to_broadcaster_value, to_countersignatory_value, broadcaster_funding_key,
countersignatory_funding_key, keys, feerate_per_kw, &mut nondust_htlcs,
channel_parameters)
Expand Down Expand Up @@ -5277,14 +5277,14 @@ mod tests {
// old state.
let shutdown_pubkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let best_block = BestBlock::from_network(Network::Testnet);
let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
let monitor = ChannelMonitor::new(Secp256k1::new(), keys.clone(),
Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &ScriptBuf::new(),
(OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, ScriptBuf::new()),
&channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
&channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&keys, &mut Vec::new()),
best_block, dummy_key, channel_id);

let mut htlcs = preimages_slice_to_htlcs!(preimages[0..10]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&keys, &mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
Expand Down Expand Up @@ -5323,7 +5323,7 @@ mod tests {
// Now update holder commitment tx info, pruning only element 18 as we still care about the
// previous commitment tx's preimages too
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&keys, &mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
Expand All @@ -5334,7 +5334,7 @@ mod tests {

// But if we do it again, we'll prune 5-10
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&keys, &mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
Expand Down Expand Up @@ -5527,10 +5527,10 @@ mod tests {
};
let shutdown_pubkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let best_block = BestBlock::from_network(Network::Testnet);
let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
let monitor = ChannelMonitor::new(Secp256k1::new(), keys.clone(),
Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &ScriptBuf::new(),
(OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, ScriptBuf::new()),
&channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
&channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&keys, &mut Vec::new()),
best_block, dummy_key, channel_id);

let chan_id = monitor.inner.lock().unwrap().channel_id();
Expand Down
93 changes: 66 additions & 27 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ use bitcoin::sighash::EcdsaSighashType;
use bitcoin::transaction::Version;

use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::hash160::Hash as Hash160;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
use bitcoin::hash_types::Txid;

use crate::chain::chaininterface::fee_for_weight;
use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
use crate::sign::EntropySource;
use crate::sign::{ChannelSigner, EntropySource};
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::types::payment::{PaymentHash, PaymentPreimage};
use crate::ln::msgs::DecodeError;
Expand Down Expand Up @@ -1153,7 +1152,7 @@ impl_writeable_tlv_based!(HolderCommitmentTransaction, {

impl HolderCommitmentTransaction {
#[cfg(test)]
pub fn dummy(htlcs: &mut Vec<(HTLCOutputInCommitment, ())>) -> Self {
pub fn dummy<Signer: ChannelSigner>(signer: &Signer, htlcs: &mut Vec<(HTLCOutputInCommitment, ())>) -> Self {
let secp_ctx = Secp256k1::new();
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_digest([42; 32]), &SecretKey::from_slice(&[42; 32]).unwrap());
Expand Down Expand Up @@ -1184,7 +1183,7 @@ impl HolderCommitmentTransaction {
for _ in 0..htlcs.len() {
counterparty_htlc_sigs.push(dummy_sig);
}
let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, dummy_key.clone(), dummy_key.clone(), keys, 0, htlcs, &channel_parameters.as_counterparty_broadcastable());
let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(signer, 0, 0, 0, dummy_key.clone(), dummy_key.clone(), keys, 0, htlcs, &channel_parameters.as_counterparty_broadcastable());
htlcs.sort_by_key(|htlc| htlc.0.transaction_output_index);
HolderCommitmentTransaction {
inner,
Expand Down Expand Up @@ -1494,12 +1493,12 @@ impl CommitmentTransaction {
/// Only include HTLCs that are above the dust limit for the channel.
///
/// This is not exported to bindings users due to the generic though we likely should expose a version without
pub fn new_with_auxiliary_htlc_data<T>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
pub fn new_with_auxiliary_htlc_data<Signer: ChannelSigner, T>(signer: &Signer, commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
let to_broadcaster_value_sat = Amount::from_sat(to_broadcaster_value_sat);
let to_countersignatory_value_sat = Amount::from_sat(to_countersignatory_value_sat);

// Sort outputs and populate output indices while keeping track of the auxiliary data
let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key).unwrap();
let (outputs, htlcs) = Self::internal_build_outputs(signer, &keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key).unwrap();

let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(commitment_number, channel_parameters);
let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
Expand Down Expand Up @@ -1528,11 +1527,11 @@ impl CommitmentTransaction {
self
}

fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<BuiltCommitmentTransaction, ()> {
fn internal_rebuild_transaction<Signer: ChannelSigner>(&self, signer: &Signer, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<BuiltCommitmentTransaction, ()> {
let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(self.commitment_number, channel_parameters);

let mut htlcs_with_aux = self.htlcs.iter().map(|h| (h.clone(), ())).collect();
let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, broadcaster_funding_key, countersignatory_funding_key)?;
let (outputs, _) = Self::internal_build_outputs(signer, keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, broadcaster_funding_key, countersignatory_funding_key)?;

let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
let txid = transaction.compute_txid();
Expand All @@ -1556,18 +1555,14 @@ impl CommitmentTransaction {
// - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the
// caller needs to have sorted together with the HTLCs so it can keep track of the output index
// - building of a bitcoin transaction during a verify() call, in which case T is just ()
fn internal_build_outputs<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: Amount, to_countersignatory_value_sat: Amount, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
fn internal_build_outputs<Signer: ChannelSigner, T>(signer: &Signer, keys: &TxCreationKeys, to_broadcaster_value_sat: Amount, to_countersignatory_value_sat: Amount, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys();
let contest_delay = channel_parameters.contest_delay();

let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new();

if to_countersignatory_value_sat > Amount::ZERO {
let script = if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_p2wsh()
} else {
ScriptBuf::new_p2wpkh(&Hash160::hash(&countersignatory_pubkeys.payment_point.serialize()).into())
};
let script = signer.get_counterparty_payment_script(channel_parameters.channel_type_features(), &countersignatory_pubkeys.payment_point);
txouts.push((
TxOut {
script_pubkey: script.clone(),
Expand Down Expand Up @@ -1729,14 +1724,14 @@ impl CommitmentTransaction {
///
/// An external validating signer must call this method before signing
/// or using the built transaction.
pub fn verify<T: secp256k1::Signing + secp256k1::Verification>(&self, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TrustedCommitmentTransaction, ()> {
pub fn verify<Signer: ChannelSigner, T: secp256k1::Signing + secp256k1::Verification>(&self, signer: &Signer, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TrustedCommitmentTransaction, ()> {
// This is the only field of the key cache that we trust
let per_commitment_point = self.keys.per_commitment_point;
let keys = TxCreationKeys::from_channel_static_keys(&per_commitment_point, broadcaster_keys, countersignatory_keys, secp_ctx);
if keys != self.keys {
return Err(());
}
let tx = self.internal_rebuild_transaction(&keys, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey)?;
let tx = self.internal_rebuild_transaction(signer, &keys, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey)?;
if self.built.transaction != tx.transaction || self.built.txid != tx.txid {
return Err(());
}
Expand Down Expand Up @@ -1952,6 +1947,7 @@ mod tests {
use crate::types::payment::PaymentHash;
use bitcoin::PublicKey as BitcoinPublicKey;
use crate::types::features::ChannelTypeFeatures;
use crate::sign::InMemorySigner;

#[allow(unused_imports)]
use crate::prelude::*;
Expand Down Expand Up @@ -2004,8 +2000,9 @@ mod tests {
}
}

fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
fn build<Signer: ChannelSigner>(&mut self, signer: &Signer, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
CommitmentTransaction::new_with_auxiliary_htlc_data(
signer,
self.commitment_number, to_broadcaster_sats, to_countersignatory_sats,
self.holder_funding_pubkey.clone(),
self.counterparty_funding_pubkey.clone(),
Expand All @@ -2017,25 +2014,39 @@ mod tests {

#[test]
fn test_anchors() {
let secp_ctx = Secp256k1::new();
let signer = InMemorySigner::new(
&secp_ctx,
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
[41; 32],
0,
[0; 32],
[0; 32],
);

let mut builder = TestCommitmentTxBuilder::new();

// Generate broadcaster and counterparty outputs
let tx = builder.build(1000, 2000);
let tx = builder.build(&signer, 1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[1].script_pubkey, bitcoin::address::Address::p2wpkh(&CompressedPublicKey(builder.counterparty_pubkeys.payment_point), Network::Testnet).script_pubkey());

// Generate broadcaster and counterparty outputs as well as two anchors
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
let tx = builder.build(1000, 2000);
let tx = builder.build(&signer, 1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 4);
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_p2wsh());

// Generate broadcaster output and anchor
let tx = builder.build(3000, 0);
let tx = builder.build(&signer, 3000, 0);
assert_eq!(tx.built.transaction.output.len(), 2);

// Generate counterparty output and anchor
let tx = builder.build(0, 3000);
let tx = builder.build(&signer, 0, 3000);
assert_eq!(tx.built.transaction.output.len(), 2);

let received_htlc = HTLCOutputInCommitment {
Expand All @@ -2057,7 +2068,7 @@ mod tests {
// Generate broadcaster output and received and offered HTLC outputs, w/o anchors
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
let tx = builder.build(3000, 0);
let tx = builder.build(&signer, 3000, 0);
let keys = &builder.keys.clone();
assert_eq!(tx.built.transaction.output.len(), 3);
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh());
Expand All @@ -2070,7 +2081,7 @@ mod tests {
// Generate broadcaster output and received and offered HTLC outputs, with anchors
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
let tx = builder.build(3000, 0);
let tx = builder.build(&signer, 3000, 0);
assert_eq!(tx.built.transaction.output.len(), 5);
assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh());
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh());
Expand All @@ -2082,10 +2093,24 @@ mod tests {

#[test]
fn test_finding_revokeable_output_index() {
let secp_ctx = Secp256k1::new();
let signer = InMemorySigner::new(
&secp_ctx,
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
[41; 32],
0,
[0; 32],
[0; 32],
);

let mut builder = TestCommitmentTxBuilder::new();

// Revokeable output present
let tx = builder.build(1000, 2000);
let tx = builder.build(&signer, 1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.trust().revokeable_output_index(), Some(0));

Expand All @@ -2095,22 +2120,36 @@ mod tests {
assert_eq!(tx.trust().revokeable_output_index(), None);

// Revokeable output not present (our balance is dust)
let tx = builder.build(0, 2000);
let tx = builder.build(&signer, 0, 2000);
assert_eq!(tx.built.transaction.output.len(), 1);
assert_eq!(tx.trust().revokeable_output_index(), None);
}

#[test]
fn test_building_to_local_justice_tx() {
let secp_ctx = Secp256k1::new();
let signer = InMemorySigner::new(
&secp_ctx,
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
[41; 32],
0,
[0; 32],
[0; 32],
);

let mut builder = TestCommitmentTxBuilder::new();

// Revokeable output not present (our balance is dust)
let tx = builder.build(0, 2000);
let tx = builder.build(&signer, 0, 2000);
assert_eq!(tx.built.transaction.output.len(), 1);
assert!(tx.trust().build_to_local_justice_tx(253, ScriptBuf::new()).is_err());

// Revokeable output present
let tx = builder.build(1000, 2000);
let tx = builder.build(&signer, 1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 2);

// Too high feerate
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3165,7 +3165,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
let channel_parameters =
if local { self.channel_transaction_parameters.as_holder_broadcastable() }
else { self.channel_transaction_parameters.as_counterparty_broadcastable() };
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(self.holder_signer.as_ecdsa().unwrap(), commitment_number,
value_to_a as u64,
value_to_b as u64,
funding_pubkey_a,
Expand Down
Loading

0 comments on commit 63621ec

Please sign in to comment.