-
Couldn't load subscription status.
- Fork 420
Use correct to_remote script in counterparty commitments #2605
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
Use correct to_remote script in counterparty commitments #2605
Conversation
37f3469 to
3c1b187
Compare
Codecov ReportAttention:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #2605 +/- ##
==========================================
- Coverage 89.03% 88.99% -0.04%
==========================================
Files 112 112
Lines 86351 86706 +355
Branches 86351 86706 +355
==========================================
+ Hits 76879 77167 +288
- Misses 7235 7308 +73
+ Partials 2237 2231 -6
☔ View full report in Codecov by Sentry. |
f6b21c9 to
1fb2a41
Compare
|
The commit titled |
It is, check again :) We previously had an estimate of 108, now 109. |
1fb2a41 to
9113ea3
Compare
|
Oh lol the extra one I skimmed past. |
| let remotepubkey = self.pubkeys().payment_point; | ||
| let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey(); | ||
| let remotepubkey = bitcoin::PublicKey::new(self.pubkeys().payment_point); | ||
| let witness_script = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { | ||
| chan_utils::get_to_countersignatory_with_anchors_redeemscript(&remotepubkey.inner) | ||
| } else { | ||
| Script::new_p2pkh(&remotepubkey.pubkey_hash()) | ||
| }; | ||
| let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]); | ||
| let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self); | ||
| let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey(); | ||
| let payment_script = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { | ||
| witness_script.to_v0_p2wsh() | ||
| } else { | ||
| Script::new_v0_p2wpkh(&remotepubkey.wpubkey_hash().unwrap()) | ||
| }; | ||
|
|
||
| if payment_script != descriptor.output.script_pubkey { return Err(()); } | ||
|
|
||
| let mut witness = Vec::with_capacity(2); | ||
| witness.push(remotesig.serialize_der().to_vec()); | ||
| witness[0].push(EcdsaSighashType::All as u8); | ||
| witness.push(remotepubkey.serialize().to_vec()); | ||
| if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { | ||
| witness.push(witness_script.to_bytes()); | ||
| } else { | ||
| witness.push(remotepubkey.to_bytes()); | ||
| } |
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.
We condition on this quite a bit. Wonder if it would be worth type parameterizing to avoid missing places in the future. Doesn't need to be done now, of course.
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.
Mind giving a more concrete suggestion?
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 will change even further once we have taproot and we need different sighash and signing methods.
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 rather than checking against features, you initialized the signer with a parameterization for the channel type. Then instead of having an increasingly complex condition for each of these three assignments, each has it's own implementation. The strategy pattern, essentially. Though, in practice this parameterization may need to trickle up to ChannelMonitor and possibly further, so might not be worth it.
Alternatively, could just use an enum as mentioned later in another comment.
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 rather than checking against features, you initialized the signer with a parameterization for the channel type. Then instead of having an increasingly complex condition for each of these three assignments, each has it's own implementation. The strategy pattern, essentially. Though, in practice this parameterization may need to trickle up to ChannelMonitor and possibly further, so might not be worth it.
This would be quite a big change if we intend to apply it to all other signing methods on InMemorySigner, since it wouldn't make sense to have it on some but not others.
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 the idea though, especially as we add taproot and further channel/commitment upgrades.
| let sequence = | ||
| if descriptor.channel_transaction_parameters.as_ref() | ||
| .map(|channel_params| channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx()) | ||
| .unwrap_or(false) | ||
| { | ||
| Sequence::from_consensus(1) | ||
| } else { | ||
| Sequence::ZERO | ||
| }; | ||
| input.push(TxIn { | ||
| previous_output: descriptor.outpoint.into_bitcoin_outpoint(), | ||
| script_sig: Script::new(), | ||
| sequence: Sequence::ZERO, | ||
| sequence, | ||
| witness: Witness::new(), | ||
| }); | ||
| witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH; | ||
| witness_weight += descriptor.max_witness_length(); |
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.
Hmmm... I'd definitely lean towards a separate variant, FWIW. Seems cleaner.
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 thought of adding a new variant came from a confusion between StaticOutput and StaticPaymentOutput descriptors. Once we upgrade to Taproot, we'd also need a new variant because we're going from P2WSH to P2TR. Not sure it's worth having all these different types when it's still the same output (to_remote on a counterparty commitment) we're claiming backed by the same key.
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 see. My thinking was that by explicitly enumerating the cases, we would be forced to consider all of them and avoid bugs. I suppose an alternative would be to have a mapping from ChannelTypeFeatures to an enum with a variant for each type that we could match on.
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'll see if I can come up with something tomorrow, it'll have to wait for when taproot is added here otherwise. In any case, the best way to avoid bugs here would be test coverage via check_spends!() 😛
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.
SGTM. A mapping wouldn't require any serialization changes, too.
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 to expand on this based on offline discussion. We'll keep the variants as they are in this PR. This will give us a ChannelTransactionParameters which has a ChannelTypeFeatures. Later, we can add something like a to_channel_type method to ChannelTypeFeatures for internal use that will give an enum for us to match so as to avoid missing cases.
Using either another variant or a nested enum instead could be error prone as it may go out of sync with ChannelTypeFeatures.
9113ea3 to
163ad99
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.
Code sounds good.
| ).unwrap(); | ||
|
|
||
| check_spends!(spend_tx, revoked_claim_transactions.get(&spend_tx.input[0].previous_output.txid).unwrap()); | ||
| if let SpendableOutputDescriptor::StaticPaymentOutput(_) = &outputs[0] { |
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 think one way to harden the test can be to count the StaticPaymentOutput with num_static_payment_output and check at the end the right number of this type of descriptor has been found.
While our commitment transactions did use the correct `to_remote` script, the `ChannelMonitor`'s was not as it is tracked separately. This would lead to users never receiving an `Event::SpendableOutputs` with a `StaticPaymentOutput` descriptor to claim the funds. Luckily, any users affected which had channel closures confirmed by a counterparty commitment just need to replay the closing transaction to receive the event.
`to_remote` outputs on commitment transactions with anchor outputs have an additional `1 CSV` constraint on its spending condition, transitioning away from the previous P2WPKH script to a P2WSH. Since our `ChannelMonitor` was never updated to track the proper `to_remote` script on anchor outputs channels, we also missed updating our signer to handle the new script changes.
We were not accounting for the extra byte denoting the number of items in the witness stack.
163ad99 to
5607977
Compare
|
Had to rebase to use |
|
Thanks for adding the test! LGTM. Fee free to squash the fixups. |
5607977 to
f464aa9
Compare
While our commitment transactions did use the correct
to_remotescript, theChannelMonitor's was not as it is tracked separately. This would lead to users never receiving anEvent::SpendableOutputswith aStaticPaymentOutputdescriptor to claim the funds.Luckily, any users affected which had channel closures confirmed by a counterparty commitment just need to replay the closing transaction to receive the event.