Add HTLC support to BOLT 3 commitment transaction - #181
Add HTLC support to BOLT 3 commitment transaction#181NishantBansal2003 wants to merge 14 commits into
Conversation
e930116 to
a0cc8a9
Compare
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
a0cc8a9 to
06279db
Compare
erickcestari
left a comment
There was a problem hiding this comment.
Nice PR! I've added some notes:
| /// | ||
| /// Returns [`CommitmentError::HtlcNotFound`] if no in-flight HTLC matches | ||
| /// `id` and `offerer`. | ||
| pub fn fulfill_htlc(&mut self, id: u64, offerer: Side) -> Result<(), CommitmentError> { |
There was a problem hiding this comment.
fulfill_htlc, fail_htlc, update_fee, update_per_commitment_point and advance_commitment_number are missing unit tests.
| #[derive(Clone, Copy)] | ||
| pub struct Htlc { | ||
| /// HTLC ID, unique per channel and offering direction. | ||
| id: u64, |
There was a problem hiding this comment.
The Htlc cannot be built outside the commitment module.
| id: u64, | |
| pub id: u64, |
| let htlcs = bolt3_htlc_list(); | ||
| commitment_params.add_htlc(htlcs[1]).unwrap(); | ||
| commitment_params.add_htlc(htlcs[2]).unwrap(); | ||
| commitment_params.add_htlc(htlcs[3]).unwrap(); | ||
| commitment_params.add_htlc(htlcs[4]).unwrap(); |
There was a problem hiding this comment.
Shouldn't all of these minimum_feerate tests add the 5 HTLCs to actually verify that the HTLCs are being trimmed correctly?
| let htlcs = bolt3_htlc_list(); | |
| commitment_params.add_htlc(htlcs[1]).unwrap(); | |
| commitment_params.add_htlc(htlcs[2]).unwrap(); | |
| commitment_params.add_htlc(htlcs[3]).unwrap(); | |
| commitment_params.add_htlc(htlcs[4]).unwrap(); | |
| let htlcs = bolt3_htlc_list(); | |
| commitment_params.add_htlc(htlcs[0]).unwrap(); | |
| commitment_params.add_htlc(htlcs[1]).unwrap(); | |
| commitment_params.add_htlc(htlcs[2]).unwrap(); | |
| commitment_params.add_htlc(htlcs[3]).unwrap(); | |
| commitment_params.add_htlc(htlcs[4]).unwrap(); |
| let opener_htlc_basepoint_privkey = | ||
| SecretKey::from_slice(&opener_htlc_basepoint_privkey_bytes).expect("valid private key"); | ||
| let opener_htlc_basepoint = PublicKey::from_secret_key(&secp, &opener_htlc_basepoint_privkey); |
There was a problem hiding this comment.
Shouldn't we use the same htlc_basepoint that we sent to the target in the open_channel message?
There was a problem hiding this comment.
Yes, but with this, we can also execute invalid signature cases via opener_htlc_basepoint_privkey, and invalid script cases via opener_htlc_basepoint, since the latter was used in build_htlc_witness_script
But I don't think it helps us much, as opener_funding_pubkey used to, since the signature is already invalid due to the invalid private key. So I think we can use open_channel.htlc_basepoint instead
| /// | ||
| /// Returns [`CommitmentError::HtlcExceedsBalance`] if the HTLC amount | ||
| /// would underflow the offerer's balance. | ||
| pub fn add_htlc(&mut self, htlc: Htlc) -> Result<(), CommitmentError> { |
There was a problem hiding this comment.
We could either return an error or assert when an attempt is made to add a duplicate Htlc.
There was a problem hiding this comment.
Currently, we are the initiator here (In the future, when the target might be the initiator, we should definitely sanitize the result in the oracle itself before coming here), If we prohibit the duplicate HTLC case, we won't be able to test that case with the target. So I think we should just add it (it's not an issue in the commitment tx anyway), and if the peer's response is not an error/warning, that should be handled in the oracle.
I was also thinking of removing the HtlcExceedsBalance case as well, similar to how we handled the commitment fee case in the commitment tx with saturating_sub (Will also add a comment discussing the edge cases that are not handled here and should be handled only by the oracle)
| let (signature, htlc_signature) = config.sign_counterparty_commitment(&state, &holder); | ||
| assert!(htlc_signature.is_empty()); // There are no HTLCs in the initial commitment transaction. |
There was a problem hiding this comment.
nit:
| let (signature, htlc_signature) = config.sign_counterparty_commitment(&state, &holder); | |
| assert!(htlc_signature.is_empty()); // There are no HTLCs in the initial commitment transaction. | |
| let (signature, htlc_signatures) = config.sign_counterparty_commitment(&state, &holder); | |
| assert!(htlc_signatures.is_empty()); // There are no HTLCs in the initial commitment transaction. |
|
|
||
| // Opener signs own commitment. | ||
| let (local_signature, local_htlc_signsignature) = |
There was a problem hiding this comment.
nit:
| let (local_signature, local_htlc_signsignature) = | |
| let (local_signature, local_htlc_signatures) = |
| let pos = self | ||
| .htlcs | ||
| .iter() | ||
| .position(|h| h.id == id && h.offerer == offerer) | ||
| .ok_or(CommitmentError::HtlcNotFound)?; |
There was a problem hiding this comment.
I think, based on the discussion in #181 (comment), there might be a comment suggesting that we should remove the HtlcNotFound error and make it a hard assert, since this will also be checked by the oracle before getting here. But I think we should keep this until we have those oracles
ref: #111
Bolts ref:
Verification
Anchor test vectors (local signatures): https://github.com/ACINQ/eclair/blob/master/eclair-core/src/test/resources/bolt3-tx-test-vectors-anchor-outputs-zero-fee-htlc-tx-format.txt
Custom tests on top of commit ACINQ/eclair@26d035070 (after this commit, eclair removed support for non-anchor channels), with the diff below:
Eclair diff
Note: I split this into smaller commits, so some functions and fields temporarily use
#[allow(dead_code)]so that each commit is clippy clean. These annotations are removed in later commits