-
Notifications
You must be signed in to change notification settings - Fork 407
Introduce CommitmentTransactionInfo #742
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
Conversation
Codecov Report
@@ Coverage Diff @@
## main #742 +/- ##
==========================================
+ Coverage 91.23% 91.29% +0.06%
==========================================
Files 37 37
Lines 22623 22791 +168
==========================================
+ Hits 20639 20807 +168
Misses 1984 1984
Continue to review full report at Codecov.
|
sketched a replacement to |
ec93084
to
c669158
Compare
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.
Thanks for working on this, there is a general point about binding to the broadcaster/countersignatory nomenclature for HolderCommitmentTxInfo
//ChannelStaticInfo
as these data structures are used to generate holder/counterparty transaction and the direction is caller-dependant.
Overall, I like the change to have a clean transaction store, independent of direction.
|
||
destination_script: Script, | ||
broadcasted_holder_revokable_script: Option<(Script, PublicKey, PublicKey)>, | ||
counterparty_payment_script: Script, | ||
shutdown_script: Script, | ||
|
||
keys: ChanSigner, | ||
counterparty_pubkeys: ChannelPublicKeys, |
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.
I don't follow this change at reviewing ec16cf2, is it consumed somewhere ?
Further, I even think we could drop any counterparty pubkeys by extending ChannelKeys with counterparty_pubkeys
as they're stored their post channel acceptance. ChannelMonitor already have access to key signer, so it's not an issue calling at counterparty script generation.
It might be a low latency hit in case of external signer out-of-memory but onchain txns should be sufficiently limited to not care about it.
Note, we can't directly store the scripts as they're function of the per_commitment_point we only derive at counterparty commitment tx detection.
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.
I don't follow this change at reviewing ec16cf2, is it consumed somewhere ?
I removed this field, my mistake.
Further, I even think we could drop any counterparty pubkeys by extending ChannelKeys with
counterparty_pubkeys
as they're stored their post channel acceptance. ChannelMonitor already have access to key signer, so it's not an issue calling at counterparty script generation.
@ksedgwic and I were thinking about that, but I'm a bit concerned about making the node dependent on the signer for transaction construction. I wonder if it may make it harder to perform some system-administration operations when the signer is unavailable for some reason. But otherwise, that's something that can be done in the followup PR.
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.
Just fyi, you have access to counterparty keys in the following contexts:
- To claim a revoked
to_local
output on counterparty tx, L1318 insrc/chain/channelmonitor.rs
- To claim a revoked HTLC output on counterparty tx, L1331 in
src/chain/channelmonitor.rs
- To claim a valid HTLC output on counterparty tx, L1470 in
src/chain/channelmonitor.rs
- To claim a revoked output on counterparty HTLC tx, L1502 in
src/chain/channelmonitor.rs
All those cases assume your monitor is feeded with blocks (block_connected
) but I guess in system-administration you will turn off chain watching to avoid a state update ? That said I agree it's hard to scope yet and orthogonal to this PR.
lightning/src/ln/chan_utils.rs
Outdated
/// This class tracks the information needed to build a holder's commitment transaction and to actually | ||
/// build and sign. It is used for holder transactions that we sign only when needed. | ||
#[derive(Clone)] | ||
pub struct HolderCommitmentTransactionInfo { |
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.
As a name suggestion what about HolderRawChannelTxn
, what we want to convey is a) this is scoping both commitment and htlc txn b) this is "raw" in opposition to signer finalized.
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.
I renamed this to HolderCommitmentTransaction
(and renamed the old one to something else). I feel that "Raw" isn't that descriptive.
Let me know what you think.
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.
I like dropping the Info
on new names.
I would like to convey better the different commitment package states : channel_parameters (step 1) --> channel_parameters + CommitmentTransaction (dynamic) (step 2) --> channel_parameters + CommitmentTransaction + counterparty_sigs (step 3) --> channel_parameters + CommitmentTransaction + counterparty_sigs + holder_sigs (step 4).
Note that you have a bifurcation at (step 2) if it's for counterparty, you will produce the holder sigs and send them through commitment_signed
. Otherwise, if it's for holder, you will pass the state to our chain monitor.
That's the gross graph I hope we move towards to make it clearer for future reviewers, but not need to stick to it rn.
0a18bd3
to
12f90c5
Compare
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.
Thanks for update, I think this comment still needs resolution, afterwards SGTM.
8bed28b
to
2c2ad88
Compare
I reworded the commits and added a rename as follows:
Curious how people feel about the naming. |
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.
See the small nits on new DirectedChannelTransactionParameters
, otherwise SGTM
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.
Generally looks good, thanks! I think there's some opportunities for cleanup and optimization yet, but the structure seems good.
broadcaster_is_holder: bool, | ||
} | ||
|
||
impl<'a> DirectedChannelTransactionParameters<'a> { |
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.
Since this basically just exists to switch between different fields in ChannelTransactionParameters
can it be replaced with a few functions in impl ChannelTransactionParameters
that take either a direction bool or a CommitmentTransaction
(or functions in CommitmentTransaction
that take ChannelTransactionParameters
instead?)? It just seems like quite a bit of code for a few if is_outbound { a } else { b }
blocks.
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.
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.
Note if you do this and consume directly ChannelTransactionParameters in CommitmentTransaction builder you will re-introduce directionality about code path which are exercised for building commitments for both sides, which is confusing.
Your direction bool is likely to be something like holder_is_broadcaster
?
lightning/src/ln/chan_utils.rs
Outdated
/// The backwards-counting commitment number | ||
pub commitment_number: u64, | ||
/// The value to be sent to the broadcaster | ||
pub to_broadcaster_value_sat: u64, |
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.
Shouldn't the explicit values be replaced with the set of dust HTLCs so that an external signer can at least see the dust HTLCs, even if they aren't included?
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.
the only interesting policy on dust HTLCs that I came up with is a limit on their aggregate value, which can be enforced without knowing their details. any other policies you can think of?
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.
Hmm, maybe, I was thinking also that its better to force the external signer to re-check the values against dust instead of letting them accidentally accept a commitment transaction that burns their entire value to dust unreasonably - just in general its more explicit and we communicate the data needed to build the transaction, not the conclusions the non-trusted signer made from that data.
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.
I still don't see how the signer would use this information. Here are my thoughts:
- the info in the dust
HTLCOutputInCommitment
cannot be verified by the signer. It could have been invented by a compromised node. - as opposed to the non-dust HTLCs, the info is not going on-chain and is not signed by the counterparty, so doesn't describe any recourse we have
So it seems like the only thing the signer can do with this is sum the individual values and see everything adds up, but that doesn't seem to buy us any additional security, since we already know the result.
The one policy I can see related to this is a limit on the sum of dust HTLCs, which can be computed from the existing fields already.
Am I missing something?
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.
I was thinking that a signer may use information about dust HTLCs in one channel to correlate with non-dust HTLCs in another channel (with a different dust limit) that represent forwarding of the same payment. Or possibly correlate HTLCs in the inbound/outbound commitment transactions in the same channel, since IIRC both could have different dust limits.
I admit I don't in any way have a coherent threat model for how using this data would be helpful, only that it seems strange to not pass it to the signer.
lightning/src/ln/chan_utils.rs
Outdated
} | ||
|
||
fn build_outputs<T: secp256k1::Signing + secp256k1::Verification>(&self, channel_parameters: &DirectedChannelTransactionParameters, secp_ctx: &Secp256k1<T>) -> Result<Vec<(TxOut, (Script, Option<HTLCOutputInCommitment>))>, ()> { | ||
let htlcs = self.htlcs.iter().map(|h| (h.clone(), ())).collect(); |
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.
This seems pretty redundant - are there callers where we really need clones of HTLCOutputInCommitment
or can we just let clients access the already-newly-pub htlcs
field in the struct?
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.
5c91c1b
to
d09ea33
Compare
bc0fba0
to
49a2fc6
Compare
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.
Maybe squash commits for final review ?
Squashed. |
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.
Basically happy with this. A few comments, but they're all simple doc/API/one-liners, so should be trivial. Need one more ACK, but I'm happy.
/// Set the counterparty static channel data, including basepoints, | ||
/// counterparty_selected/holder_selected_contest_delay and funding outpoint. | ||
/// This is done as soon as the funding outpoint is known. Since these are static channel data, | ||
/// they MUST NOT be allowed to change to different values once set. |
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.
Maybe note in the docs that channel_parameters.is_populated()
will be true. In a future change it may make sense to have a PendingOutboundChannel
object that gets converted to a Channel
when we receive our counterparty data to remove a ton of the Option
al stuff in Channel
, including makeing counterparty_parameters
in here non-Option
al
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.
done
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.
The phrasing here is awkward - its not that is_populated()
MUST be true (implying some kind of requirement on the user), but that it WILL be true (as it is a requirement for our own internal code, thus an API guarantee provided to the user).
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.
The ChannelKeys
API is also used internally in an external signer. i.e. it's called directly by user code.
lightning/src/ln/chan_utils.rs
Outdated
// - 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: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> { |
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.
nit: this could probably use an #[inline]
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.
Does this actually do anything? isn't the compiler smart enough to inline if it's appropriate? also, isn't the gain pretty minimal, given everything that this function does internally?
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.
Probably nothing, indeed. The function is probably big enough that it may not qualify for normal inlining, but also does work which certain callers don't care about (and the callers are generally tiny functions) - making it a decent candidate for "LLVM, you should try inlining here, even if you think this function is big enough that you wouldn't otherwise". That said, a bunch of that extra work is probably complicated enough that LLVM wouldn't be able to figure out how to skip it, so...whatever.
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.
Got it. Well, do you think LLVM knows to skip populating a vec if the inlining makes it obvious the vec isn't used?
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 the contained object didn't have a Drop
and you gave it enough CPU budget...maybe? I don't think this is worth discussing in this much depth either way, its just one of those things I always figure doing as the "utility function that is big but only called from tiny exposed functions" pattern can be pessimal for the inliner if code locality isn't critical to the specific use-case :).
f39873d
to
4ef6a3f
Compare
Needs another ACK, but I'm happy modulo resolution of #742 (comment) |
CommitmentTransaction maintains the per-commitment transaction fields needed to construct the associated bitcoin transactions (commitment, HTLC). It replaces passing around of Bitcoin transactions. The ChannelKeys API is modified accordingly. By regenerating the transaction when implementing a validating external signer, this allows a higher level of assurance that all relevant aspects of the transactions were checked for policy violations. ChannelTransactionParameters replaces passing around of individual per-channel fields that are needed to construct Bitcoin transactions. Eliminate ChannelStaticData in favor of ChannelTransactionParameters. Use counterparty txid instead of tx in channelmonitor update.
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.
Code Review ACK 9291a38, comments are nits.
#[derive(Clone)] | ||
/// A simple implementation of ChannelKeys that just keeps the private keys in memory. | ||
/// | ||
/// This implementation performs no policy checks and is insufficient by itself as |
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.
Would be cool to describe what kind of policy checks can be implemented for a custom signer. Maybe you can link your already-written set of policy controls examples (https://gitlab.com/lightning-signer/docs/-/blob/master/policy-controls.md) somewhere :) ?
/// This is done as soon as the funding outpoint is known. Since these are static channel data, | ||
/// they MUST NOT be allowed to change to different values once set. | ||
/// | ||
/// channel_parameters.is_populated() MUST be true. |
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.
"...otherwise it will panic".
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.
There's no guarantees of this - there are no requirements around panics placed on the clients of the API.
} | ||
} | ||
} | ||
|
||
//TODO: getting lastest holder transactions should be infaillible and result in us "force-closing the channel", but we may | ||
fn extract_holder_sigs(holder_commitment: &HolderCommitmentTransaction, sigs: Vec<Signature>) -> Vec<Option<(usize, Signature)>> { |
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.
Could be called "extract_indexed_htlc_sigs"? It will be clearer.
|
This is part of the revamp of the signer API (AKA "phase 2", also see #408) where we want to pass around the semantic transaction rather than the Bitcoin transaction.
CommitmentTransaction
(the semantic transaction) maintains the per-commitment transaction fields needed to construct the associated bitcoin transactions (commitment, HTLC). This will allow a higher level of assurance that all relevant aspects of the transactions were checked for policy violations.ChannelTransactionParameters
replaces passing around of individual per-channel fields that are needed to construct Bitcoin transactions.This change affects
Channel
,ChannelMonitor
andChannelKeys
.Steps for a future PR: