-
Notifications
You must be signed in to change notification settings - Fork 384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Custom Transactions] Define the TxBuilder
trait
#3606
Open
tankyleo
wants to merge
18
commits into
lightningdevkit:main
Choose a base branch
from
tankyleo:25-02-tx-builder
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+446
−395
Open
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
ca8c728
Provide built commitment transactions to `ChannelMonitor`
tankyleo 3dcf40a
Relax the requirements of building a `CommitmentTransaction`
tankyleo 2a82a1f
Remove redundant parameters from the `CommitmentTransaction` constructor
tankyleo a50d32d
Let `CommitmentTransaction` build the `TxCreationKeys` it will cache
tankyleo d60cfda
Begin removing per-commitment key derivations from channel objects
tankyleo df9a561
Remove `i64` casts in `ChannelContext::build_commitment_transaction`
tankyleo ca3fa4e
Clean up tautologies in `ChannelContext::build_commitment_transaction`
tankyleo 570ca77
Reorder the operations in `ChannelContext::build_commitment_transaction`
tankyleo 82b40fc
Remove redundant fields in `CommitmentStats`
tankyleo 5c97de0
Tweak return values of `ChannelContext::build_commitment_transaction`
tankyleo 42d5a79
[Custom Transactions] Define the `TxBuilder` trait
tankyleo 300b396
f: Just handle new updates for now, but don't set them
tankyleo 3318e4a
f: Remove parameter from `provide_initial_counterparty_commitment_tx`
tankyleo 7c2d605
f: Remove all explicit `TxCreationKeys` in channel
tankyleo ad05a2c
f: Break up a long line
tankyleo 338532f
f: Correct comments describing commitment structs
tankyleo 87a35dd
f: Delete a big branch in non-dust htlc scan
tankyleo dcfb0f9
Remove unnecessary allocation in `send_commitment_no_state_update`
tankyleo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1968,8 +1968,7 @@ trait InitialRemoteCommitmentReceiver<SP: Deref> where SP::Target: SignerProvide | |
) -> Result<CommitmentTransaction, ChannelError> where L::Target: Logger { | ||
let funding_script = self.context().get_funding_redeemscript(); | ||
|
||
let keys = self.context().build_holder_transaction_keys(holder_commitment_point.current_point()); | ||
let initial_commitment_tx = self.context().build_commitment_transaction(self.funding(), holder_commitment_point.transaction_number(), &keys, true, false, logger).tx; | ||
let initial_commitment_tx = self.context().build_commitment_transaction(self.funding(), holder_commitment_point.transaction_number(), &holder_commitment_point.current_point(), true, false, logger).tx; | ||
let trusted_tx = initial_commitment_tx.trust(); | ||
let initial_commitment_bitcoin_tx = trusted_tx.built_transaction(); | ||
let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.funding().channel_value_satoshis); | ||
|
@@ -2006,8 +2005,7 @@ trait InitialRemoteCommitmentReceiver<SP: Deref> where SP::Target: SignerProvide | |
} | ||
}; | ||
let context = self.context(); | ||
let counterparty_keys = context.build_remote_transaction_keys(); | ||
let counterparty_initial_commitment_tx = context.build_commitment_transaction(self.funding(), context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; | ||
let counterparty_initial_commitment_tx = context.build_commitment_transaction(self.funding(), context.cur_counterparty_commitment_transaction_number, &context.counterparty_cur_commitment_point.unwrap(), false, false, logger).tx; | ||
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust(); | ||
let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction(); | ||
|
||
|
@@ -3412,7 +3410,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider { | |
/// generated by the peer which proposed adding the HTLCs, and thus we need to understand both | ||
/// which peer generated this transaction and "to whom" this transaction flows. | ||
#[inline] | ||
fn build_commitment_transaction<L: Deref>(&self, funding: &FundingScope, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, logger: &L) -> CommitmentStats | ||
fn build_commitment_transaction<L: Deref>(&self, funding: &FundingScope, commitment_number: u64, per_commitment_point: &PublicKey, local: bool, generated_by_local: bool, logger: &L) -> CommitmentStats | ||
where L::Target: Logger | ||
{ | ||
let mut included_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::new(); | ||
|
@@ -3614,7 +3612,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider { | |
if local { self.channel_transaction_parameters.as_holder_broadcastable() } | ||
else { self.channel_transaction_parameters.as_counterparty_broadcastable() }; | ||
let tx = CommitmentTransaction::new(commitment_number, | ||
&keys.per_commitment_point, | ||
&per_commitment_point, | ||
value_to_a as u64, | ||
value_to_b as u64, | ||
feerate_per_kw, | ||
|
@@ -3640,32 +3638,6 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider { | |
} | ||
} | ||
|
||
#[inline] | ||
/// Creates a set of keys for build_commitment_transaction to generate a transaction which our | ||
/// counterparty will sign (ie DO NOT send signatures over a transaction created by this to | ||
/// our counterparty!) | ||
/// The result is a transaction which we can revoke broadcastership of (ie a "local" transaction) | ||
/// TODO Some magic rust shit to compile-time check this? | ||
fn build_holder_transaction_keys(&self, per_commitment_point: PublicKey) -> TxCreationKeys { | ||
let delayed_payment_base = &self.get_holder_pubkeys().delayed_payment_basepoint; | ||
let htlc_basepoint = &self.get_holder_pubkeys().htlc_basepoint; | ||
let counterparty_pubkeys = self.get_counterparty_pubkeys(); | ||
|
||
TxCreationKeys::derive_new(&self.secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint) | ||
} | ||
|
||
#[inline] | ||
/// Creates a set of keys for build_commitment_transaction to generate a transaction which we | ||
/// will sign and send to our counterparty. | ||
/// If an Err is returned, it is a ChannelError::Close (for get_funding_created) | ||
fn build_remote_transaction_keys(&self) -> TxCreationKeys { | ||
let revocation_basepoint = &self.get_holder_pubkeys().revocation_basepoint; | ||
let htlc_basepoint = &self.get_holder_pubkeys().htlc_basepoint; | ||
let counterparty_pubkeys = self.get_counterparty_pubkeys(); | ||
|
||
TxCreationKeys::derive_new(&self.secp_ctx, &self.counterparty_cur_commitment_point.unwrap(), &counterparty_pubkeys.delayed_payment_basepoint, &counterparty_pubkeys.htlc_basepoint, revocation_basepoint, htlc_basepoint) | ||
} | ||
|
||
/// Gets the redeemscript for the funding transaction output (ie the funding transaction output | ||
/// pays to get_funding_redeemscript().to_p2wsh()). | ||
/// Panics if called before accept_channel/InboundV1Channel::new | ||
|
@@ -4459,9 +4431,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider { | |
SP::Target: SignerProvider, | ||
L::Target: Logger | ||
{ | ||
let counterparty_keys = self.build_remote_transaction_keys(); | ||
let counterparty_initial_commitment_tx = self.build_commitment_transaction( | ||
funding, self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; | ||
funding, self.cur_counterparty_commitment_transaction_number, &self.counterparty_cur_commitment_point.unwrap(), false, false, logger).tx; | ||
match self.holder_signer { | ||
// TODO (taproot|arik): move match into calling method for Taproot | ||
ChannelSignerType::Ecdsa(ref ecdsa) => { | ||
|
@@ -5454,9 +5425,7 @@ impl<SP: Deref> FundedChannel<SP> where | |
|
||
let funding_script = self.context.get_funding_redeemscript(); | ||
|
||
let keys = self.context.build_holder_transaction_keys(self.holder_commitment_point.current_point()); | ||
|
||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &keys, true, false, logger); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &self.holder_commitment_point.current_point(), true, false, logger); | ||
let commitment_txid = { | ||
let trusted_tx = commitment_stats.tx.trust(); | ||
let bitcoin_tx = trusted_tx.built_transaction(); | ||
|
@@ -5524,19 +5493,20 @@ impl<SP: Deref> FundedChannel<SP> where | |
|
||
let mut nondust_htlc_sources = Vec::with_capacity(htlcs_cloned.len()); | ||
let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len()); | ||
let holder_keys = TxCreationKeys::from_channel_static_keys(&self.holder_commitment_point.current_point(), self.context.get_holder_pubkeys(), self.context.get_counterparty_pubkeys(), &self.context.secp_ctx); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be able to get these from the built commitment transaction above? |
||
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() { | ||
if let Some(_) = htlc.transaction_output_index { | ||
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw, | ||
self.context.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.context.channel_type, | ||
&keys.broadcaster_delayed_payment_key, &keys.revocation_key); | ||
&holder_keys.broadcaster_delayed_payment_key, &holder_keys.revocation_key); | ||
|
||
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &keys); | ||
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &holder_keys); | ||
let htlc_sighashtype = if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All }; | ||
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap()[..]); | ||
log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.", | ||
log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.to_public_key().serialize()), | ||
log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(holder_keys.countersignatory_htlc_key.to_public_key().serialize()), | ||
encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript), &self.context.channel_id()); | ||
if let Err(_) = self.context.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key.to_public_key()) { | ||
if let Err(_) = self.context.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &holder_keys.countersignatory_htlc_key.to_public_key()) { | ||
return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned())); | ||
} | ||
if !separate_nondust_htlc_sources { | ||
|
@@ -6218,8 +6188,7 @@ impl<SP: Deref> FundedChannel<SP> where | |
// Before proposing a feerate update, check that we can actually afford the new fee. | ||
let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator); | ||
let htlc_stats = self.context.get_pending_htlc_stats(Some(feerate_per_kw), dust_exposure_limiting_feerate); | ||
let keys = self.context.build_holder_transaction_keys(self.holder_commitment_point.current_point()); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &keys, true, true, logger); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &self.holder_commitment_point.current_point(), true, true, logger); | ||
let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + htlc_stats.on_holder_tx_outbound_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.context.get_channel_type()) * 1000; | ||
let holder_balance_msat = commitment_stats.local_balance_msat - htlc_stats.outbound_holding_cell_msat; | ||
if holder_balance_msat < buffer_fee_msat + self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 { | ||
|
@@ -6532,8 +6501,7 @@ impl<SP: Deref> FundedChannel<SP> where | |
self.holder_commitment_point.try_resolve_pending(&self.context.holder_signer, &self.context.secp_ctx, logger); | ||
} | ||
let funding_signed = if self.context.signer_pending_funding && !self.context.is_outbound() { | ||
let counterparty_keys = self.context.build_remote_transaction_keys(); | ||
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number + 1, &counterparty_keys, false, false, logger).tx; | ||
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number + 1, &self.context.counterparty_cur_commitment_point.unwrap(), false, false, logger).tx; | ||
self.context.get_funding_signed_msg(logger, counterparty_initial_commitment_tx) | ||
} else { None }; | ||
// Provide a `channel_ready` message if we need to, but only if we're _not_ still pending | ||
|
@@ -8469,8 +8437,7 @@ impl<SP: Deref> FundedChannel<SP> where | |
-> (Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>, CommitmentTransaction) | ||
where L::Target: Logger | ||
{ | ||
let counterparty_keys = self.context.build_remote_transaction_keys(); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &self.context.counterparty_cur_commitment_point.unwrap(), false, true, logger); | ||
let counterparty_commitment_tx = commitment_stats.tx; | ||
|
||
#[cfg(any(test, fuzzing))] | ||
|
@@ -8501,8 +8468,7 @@ impl<SP: Deref> FundedChannel<SP> where | |
#[cfg(any(test, fuzzing))] | ||
self.build_commitment_no_state_update(logger); | ||
|
||
let counterparty_keys = self.context.build_remote_transaction_keys(); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger); | ||
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &self.context.counterparty_cur_commitment_point.unwrap(), false, true, logger); | ||
let counterparty_commitment_txid = commitment_stats.tx.trust().txid(); | ||
|
||
match &self.context.holder_signer { | ||
|
@@ -8528,7 +8494,8 @@ impl<SP: Deref> FundedChannel<SP> where | |
encode::serialize_hex(&commitment_stats.tx.trust().built_transaction().transaction), | ||
&counterparty_commitment_txid, encode::serialize_hex(&self.context.get_funding_redeemscript()), | ||
log_bytes!(signature.serialize_compact()[..]), &self.context.channel_id()); | ||
|
||
|
||
let counterparty_keys = TxCreationKeys::from_channel_static_keys(&self.context.counterparty_cur_commitment_point.unwrap(), self.context.get_counterparty_pubkeys(), self.context.get_holder_pubkeys(), &self.context.secp_ctx); | ||
for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) { | ||
log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}", | ||
encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.context.get_holder_selected_contest_delay(), htlc, &self.context.channel_type, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)), | ||
|
@@ -8980,8 +8947,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider { | |
|
||
/// Only allowed after [`ChannelContext::channel_transaction_parameters`] is set. | ||
fn get_funding_created_msg<L: Deref>(&mut self, logger: &L) -> Option<msgs::FundingCreated> where L::Target: Logger { | ||
let counterparty_keys = self.context.build_remote_transaction_keys(); | ||
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; | ||
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(&self.funding, self.context.cur_counterparty_commitment_transaction_number, &self.context.counterparty_cur_commitment_point.unwrap(), false, false, logger).tx; | ||
let signature = match &self.context.holder_signer { | ||
// TODO (taproot|arik): move match into calling method for Taproot | ||
ChannelSignerType::Ecdsa(ecdsa) => { | ||
|
@@ -11633,7 +11599,7 @@ mod tests { | |
$( { $htlc_idx: expr, $counterparty_htlc_sig_hex: expr, $htlc_sig_hex: expr, $htlc_tx_hex: expr } ), * | ||
} ) => { { | ||
let (commitment_tx, htlcs): (_, Vec<HTLCOutputInCommitment>) = { | ||
let mut commitment_stats = chan.context.build_commitment_transaction(&chan.funding, 0xffffffffffff - 42, &keys, true, false, &logger); | ||
let mut commitment_stats = chan.context.build_commitment_transaction(&chan.funding, 0xffffffffffff - 42, &per_commitment_point, true, false, &logger); | ||
|
||
let htlcs = commitment_stats.htlcs_included.drain(..) | ||
.filter_map(|(htlc, _)| if htlc.transaction_output_index.is_some() { Some(htlc) } else { None }) | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're already touching some of these long lines, let's try to break them up as we go to reduce the changes when we rustfmt this file.