Skip to content

Commit

Permalink
anchors
Browse files Browse the repository at this point in the history
  • Loading branch information
tankyleo committed Dec 24, 2024
1 parent 43ddd63 commit cab22c2
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 65 deletions.
6 changes: 2 additions & 4 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3409,16 +3409,14 @@ impl<Signer: ChannelSigner> ChannelMonitorImpl<Signer> {
let countersignatory_keys =
&self.onchain_tx_handler.channel_transaction_parameters.holder_pubkeys;

let broadcaster_funding_key = broadcaster_keys.funding_pubkey;
let countersignatory_funding_key = countersignatory_keys.funding_pubkey;
let keys = TxCreationKeys::from_channel_static_keys(&their_per_commitment_point,
&broadcaster_keys, &countersignatory_keys, &self.onchain_tx_handler.secp_ctx);
let channel_parameters =
&self.onchain_tx_handler.channel_transaction_parameters.as_counterparty_broadcastable();

CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
to_broadcaster_value, to_countersignatory_value, broadcaster_funding_key,
countersignatory_funding_key, keys, feerate_per_kw, &mut nondust_htlcs,
to_broadcaster_value, to_countersignatory_value,
keys, feerate_per_kw, &mut nondust_htlcs,
channel_parameters, &self.onchain_tx_handler.signer, &self.onchain_tx_handler.secp_ctx, false)
}

Expand Down
50 changes: 16 additions & 34 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use bitcoin::{secp256k1, Sequence, Witness};
use crate::io;
use core::cmp;
use crate::util::transaction_utils::sort_outputs;
use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
use core::ops::Deref;
use crate::chain;
use crate::types::features::ChannelTypeFeatures;
Expand Down Expand Up @@ -1136,7 +1136,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(), &signer, &secp_ctx, false);
let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, keys, 0, htlcs, &channel_parameters.as_counterparty_broadcastable(), &signer, &secp_ctx, false);
htlcs.sort_by_key(|htlc| htlc.0.transaction_output_index);
HolderCommitmentTransaction {
inner,
Expand Down Expand Up @@ -1446,12 +1446,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, Signer: ChannelSigner>(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, signer: &Signer, secp_ctx: &Secp256k1<secp256k1::All>, holder_tx: bool) -> CommitmentTransaction {
pub fn new_with_auxiliary_htlc_data<T, Signer: ChannelSigner>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, signer: &Signer, secp_ctx: &Secp256k1<secp256k1::All>, holder_tx: bool) -> 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(commitment_number, &keys.per_commitment_point, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key, signer, secp_ctx, holder_tx).unwrap();
let (outputs, htlcs) = Self::internal_build_outputs(commitment_number, &keys.per_commitment_point, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, signer, secp_ctx, holder_tx).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 @@ -1480,11 +1480,11 @@ impl CommitmentTransaction {
self
}

fn internal_rebuild_transaction<Signer: ChannelSigner>(&self, per_commitment_point: &PublicKey, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey, signer: &Signer, secp_ctx: &Secp256k1<secp256k1::All>, holder_tx: bool) -> Result<BuiltCommitmentTransaction, ()> {
fn internal_rebuild_transaction<Signer: ChannelSigner>(&self, per_commitment_point: &PublicKey, channel_parameters: &DirectedChannelTransactionParameters, signer: &Signer, secp_ctx: &Secp256k1<secp256k1::All>, holder_tx: bool) -> 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(self.commitment_number, per_commitment_point, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, broadcaster_funding_key, countersignatory_funding_key, signer, secp_ctx, holder_tx)?;
let (outputs, _) = Self::internal_build_outputs(self.commitment_number, per_commitment_point, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, signer, secp_ctx, holder_tx)?;

let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
let txid = transaction.compute_txid();
Expand All @@ -1508,7 +1508,7 @@ 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, Signer: ChannelSigner>(commitment_number: u64, per_commitment_point: &PublicKey, 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, signer: &Signer, secp_ctx: &Secp256k1<secp256k1::All>, holder_tx: bool) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
fn internal_build_outputs<T, Signer: ChannelSigner>(commitment_number: u64, per_commitment_point: &PublicKey, to_broadcaster_value_sat: Amount, to_countersignatory_value_sat: Amount, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, signer: &Signer, secp_ctx: &Secp256k1<secp256k1::All>, holder_tx: bool) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new();

if to_countersignatory_value_sat > Amount::ZERO {
Expand All @@ -1531,27 +1531,15 @@ impl CommitmentTransaction {
));
}

if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
if to_broadcaster_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() {
let anchor_script = get_anchor_redeemscript(broadcaster_funding_key);
txouts.push((
TxOut {
script_pubkey: anchor_script.to_p2wsh(),
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
},
None,
));
if to_broadcaster_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() {
if let Some(txout) = signer.get_broadcaster_anchor_txout(holder_tx) {
txouts.push((txout, None));
}
}

if to_countersignatory_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() {
let anchor_script = get_anchor_redeemscript(countersignatory_funding_key);
txouts.push((
TxOut {
script_pubkey: anchor_script.to_p2wsh(),
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
},
None,
));
if to_countersignatory_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() {
if let Some(txout) = signer.get_counterparty_anchor_txout(holder_tx) {
txouts.push((txout, None));
}
}

Expand Down Expand Up @@ -1667,10 +1655,10 @@ impl CommitmentTransaction {
///
/// An external validating signer must call this method before signing
/// or using the built transaction.
pub fn verify<Signer: ChannelSigner>(&self, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<secp256k1::All>, signer: &Signer, holder_tx: bool) -> Result<TrustedCommitmentTransaction, ()> {
pub fn verify<Signer: ChannelSigner>(&self, channel_parameters: &DirectedChannelTransactionParameters, secp_ctx: &Secp256k1<secp256k1::All>, signer: &Signer, holder_tx: bool) -> Result<TrustedCommitmentTransaction, ()> {
// This is the only field of the key cache that we trust
let per_commitment_point = self.keys.per_commitment_point;
let tx = self.internal_rebuild_transaction(&per_commitment_point, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey, signer, secp_ctx, holder_tx)?;
let tx = self.internal_rebuild_transaction(&per_commitment_point, channel_parameters, signer, secp_ctx, holder_tx)?;
if self.built.transaction != tx.transaction || self.built.txid != tx.txid {
return Err(());
}
Expand Down Expand Up @@ -1872,8 +1860,6 @@ mod tests {

struct TestCommitmentTxBuilder {
commitment_number: u64,
holder_funding_pubkey: PublicKey,
counterparty_funding_pubkey: PublicKey,
keys: TxCreationKeys,
feerate_per_kw: u32,
htlcs_with_aux: Vec<(HTLCOutputInCommitment, ())>,
Expand Down Expand Up @@ -1909,8 +1895,6 @@ mod tests {

Self {
commitment_number: 0,
holder_funding_pubkey: holder_pubkeys.funding_pubkey,
counterparty_funding_pubkey: counterparty_pubkeys.funding_pubkey,
keys,
feerate_per_kw: 1,
htlcs_with_aux,
Expand All @@ -1926,8 +1910,6 @@ mod tests {
self.commitment_number,
to_broadcaster_sats,
to_countersignatory_sats,
self.holder_funding_pubkey.clone(),
self.counterparty_funding_pubkey.clone(),
self.keys.clone(), self.feerate_per_kw,
&mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable(),
&self.signer,
Expand Down
7 changes: 0 additions & 7 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3154,11 +3154,6 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {

let mut value_to_a = if local { value_to_self } else { value_to_remote };
let mut value_to_b = if local { value_to_remote } else { value_to_self };
let (funding_pubkey_a, funding_pubkey_b) = if local {
(self.get_holder_pubkeys().funding_pubkey, self.get_counterparty_pubkeys().funding_pubkey)
} else {
(self.get_counterparty_pubkeys().funding_pubkey, self.get_holder_pubkeys().funding_pubkey)
};

if value_to_a >= (broadcaster_dust_limit_satoshis as i64) {
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
Expand All @@ -3180,8 +3175,6 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
value_to_a as u64,
value_to_b as u64,
funding_pubkey_a,
funding_pubkey_b,
keys.clone(),
feerate_per_kw,
&mut included_non_dust_htlcs,
Expand Down
22 changes: 8 additions & 14 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,18 +731,17 @@ fn test_update_fee_that_funder_cannot_afford() {

// Get the TestChannelSigner for each channel, which will be used to (1) get the keys
// needed to sign the new commitment tx and (2) sign the new commitment tx.
let (local_revocation_basepoint, local_htlc_basepoint, local_funding) = {
let (local_revocation_basepoint, local_htlc_basepoint) = {
let per_peer_state = nodes[0].node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap();
let local_chan = chan_lock.channel_by_id.get(&chan.2).map(
|phase| if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None }
).flatten().unwrap();
let chan_signer = local_chan.get_signer();
let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
pubkeys.funding_pubkey)
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint)
};
let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = {
let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point) = {
let per_peer_state = nodes[1].node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap();
let remote_chan = chan_lock.channel_by_id.get(&chan.2).map(
Expand All @@ -751,8 +750,7 @@ fn test_update_fee_that_funder_cannot_afford() {
let chan_signer = remote_chan.get_signer();
let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap(),
pubkeys.funding_pubkey)
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap())
};

// Assemble the set of keys we can use for signatures for our commitment_signed message.
Expand All @@ -771,7 +769,6 @@ fn test_update_fee_that_funder_cannot_afford() {
INITIAL_COMMITMENT_NUMBER - 1,
push_sats,
channel_value - push_sats - commit_tx_fee_msat(non_buffer_feerate + 4, 0, &channel_type_features) / 1000,
local_funding, remote_funding,
commit_tx_keys.clone(),
non_buffer_feerate + 4,
&mut htlcs,
Expand Down Expand Up @@ -1470,7 +1467,7 @@ fn test_fee_spike_violation_fails_htlc() {

// Get the TestChannelSigner for each channel, which will be used to (1) get the keys
// needed to sign the new commitment tx and (2) sign the new commitment tx.
let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point, local_funding) = {
let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point) = {
let per_peer_state = nodes[0].node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap();
let local_chan = chan_lock.channel_by_id.get(&chan.2).map(
Expand All @@ -1483,10 +1480,9 @@ fn test_fee_spike_violation_fails_htlc() {
let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
chan_signer.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER).unwrap(),
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx).unwrap(),
chan_signer.as_ref().pubkeys().funding_pubkey)
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx).unwrap())
};
let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = {
let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point) = {
let per_peer_state = nodes[1].node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap();
let remote_chan = chan_lock.channel_by_id.get(&chan.2).map(
Expand All @@ -1495,8 +1491,7 @@ fn test_fee_spike_violation_fails_htlc() {
let chan_signer = remote_chan.get_signer();
let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap(),
chan_signer.as_ref().pubkeys().funding_pubkey)
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap())
};

// Assemble the set of keys we can use for signatures for our commitment_signed message.
Expand Down Expand Up @@ -1528,7 +1523,6 @@ fn test_fee_spike_violation_fails_htlc() {
commitment_number,
95000,
local_chan_balance,
local_funding, remote_funding,
commit_tx_keys.clone(),
feerate_per_kw,
&mut vec![(accepted_htlc_info, ())],
Expand Down
60 changes: 57 additions & 3 deletions lightning/src/sign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ 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_counterparty_payment_script, get_revokeable_redeemscript, make_funding_redeemscript,
ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
HTLCOutputInCommitment, HolderCommitmentTransaction,
get_anchor_redeemscript, get_counterparty_payment_script, get_revokeable_redeemscript,
make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction,
CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
};
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use crate::ln::channel_keys::{
Expand Down Expand Up @@ -958,6 +958,12 @@ pub trait ChannelSigner {
&self, htlc: &HTLCOutputInCommitment, is_holder_tx: bool, per_commitment_point: &PublicKey,
secp_ctx: &Secp256k1<secp256k1::All>,
) -> ScriptBuf;

/// Get the broadcaster anchor output of a commit tx
fn get_broadcaster_anchor_txout(&self, is_holder_tx: bool) -> Option<TxOut>;

/// Get the counterparty anchor output of a commit tx
fn get_counterparty_anchor_txout(&self, is_holder_tx: bool) -> Option<TxOut>;
}

/// Specifies the recipient of an invoice.
Expand Down Expand Up @@ -1837,6 +1843,54 @@ impl ChannelSigner for InMemorySigner {
let script = chan_utils::get_htlc_redeemscript(htlc, params.channel_type_features(), &keys);
script.to_p2wsh()
}

fn get_broadcaster_anchor_txout(&self, is_holder_tx: bool) -> Option<TxOut> {
if self
.channel_parameters
.as_ref()
.unwrap()
.channel_type_features
.supports_anchors_zero_fee_htlc_tx()
{
let params = if is_holder_tx {
self.channel_parameters.as_ref().unwrap().as_holder_broadcastable()
} else {
self.channel_parameters.as_ref().unwrap().as_counterparty_broadcastable()
};
let broadcaster_funding_key = params.broadcaster_pubkeys().funding_pubkey;
let anchor_script = get_anchor_redeemscript(&broadcaster_funding_key);
Some(TxOut {
script_pubkey: anchor_script.to_p2wsh(),
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
})
} else {
None
}
}

fn get_counterparty_anchor_txout(&self, is_holder_tx: bool) -> Option<TxOut> {
if self
.channel_parameters
.as_ref()
.unwrap()
.channel_type_features
.supports_anchors_zero_fee_htlc_tx()
{
let params = if is_holder_tx {
self.channel_parameters.as_ref().unwrap().as_holder_broadcastable()
} else {
self.channel_parameters.as_ref().unwrap().as_counterparty_broadcastable()
};
let counterparty_funding_key = params.countersignatory_pubkeys().funding_pubkey;
let anchor_script = get_anchor_redeemscript(&counterparty_funding_key);
Some(TxOut {
script_pubkey: anchor_script.to_p2wsh(),
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
})
} else {
None
}
}
}

const MISSING_PARAMS_ERR: &'static str =
Expand Down
Loading

0 comments on commit cab22c2

Please sign in to comment.