contractcourt: Fix heuristic for identifying STC commit broadcaster.

This commit fixes the heuristic we use for identifying the party
that broadcast a Simple Taproot Channel commitment transaction.

Prior to this change we checked if the last script element was an
OP_DROP. However, both the local and remote commitment outputs
have an OP_DROP at the end.

The new approach checks the resolver's SignDescriptor and compares
that key to the keys in the channel's local ChannelConfig. If the
key is the delay key, we know that it is our commitment transaction.
This commit is contained in:
Keagan McClelland 2024-06-28 12:34:58 -07:00
parent 31d45757a4
commit 09f5e08d32
No known key found for this signature in database
GPG Key ID: FA7E65C951F12439

View File

@ -24,6 +24,11 @@ import (
// version of the commitment transaction. We can sweep this output immediately, // version of the commitment transaction. We can sweep this output immediately,
// as it doesn't have a time-lock delay. // as it doesn't have a time-lock delay.
type commitSweepResolver struct { type commitSweepResolver struct {
// localChanCfg is used to provide the resolver with the keys required
// to identify whether the commitment transaction was broadcast by the
// local or remote party.
localChanCfg channeldb.ChannelConfig
// commitResolution contains all data required to successfully sweep // commitResolution contains all data required to successfully sweep
// this HTLC on-chain. // this HTLC on-chain.
commitResolution lnwallet.CommitOutputResolution commitResolution lnwallet.CommitOutputResolution
@ -252,18 +257,26 @@ func (c *commitSweepResolver) Resolve(_ bool) (ContractResolver, error) {
signDesc = c.commitResolution.SelfOutputSignDesc signDesc = c.commitResolution.SelfOutputSignDesc
) )
switch { switch {
// For taproot channels, we'll know if this is the local commit based // For taproot channels, we'll know if this is the local commit based
// on the witness script. For local channels, the witness script has an // on the timelock value. For remote commitment transactions, the
// OP_DROP value. // witness script has a timelock of 1.
//
// TODO(roasbeef): revisit this after the script changes
// * otherwise need to base off the key in script or the CSV value
// (script num encode)
case c.chanType.IsTaproot(): case c.chanType.IsTaproot():
scriptLen := len(signDesc.WitnessScript) delayKey := c.localChanCfg.DelayBasePoint.PubKey
isLocalCommitTx = signDesc.WitnessScript[scriptLen-1] == nonDelayKey := c.localChanCfg.PaymentBasePoint.PubKey
txscript.OP_DROP
signKey := c.commitResolution.SelfOutputSignDesc.KeyDesc.PubKey
// If the key in the script is neither of these, we shouldn't
// proceed. This should be impossible.
if !signKey.IsEqual(delayKey) && !signKey.IsEqual(nonDelayKey) {
return nil, fmt.Errorf("unknown sign key %v", signKey)
}
// The commitment transaction is ours iff the signing key is
// the delay key.
isLocalCommitTx = signKey.IsEqual(delayKey)
// The output is on our local commitment if the script starts with // The output is on our local commitment if the script starts with
// OP_IF for the revocation clause. On the remote commitment it will // OP_IF for the revocation clause. On the remote commitment it will
@ -446,6 +459,7 @@ func (c *commitSweepResolver) SupplementState(state *channeldb.OpenChannel) {
if state.ChanType.HasLeaseExpiration() { if state.ChanType.HasLeaseExpiration() {
c.leaseExpiry = state.ThawHeight c.leaseExpiry = state.ThawHeight
} }
c.localChanCfg = state.LocalChanCfg
c.channelInitiator = state.IsInitiator c.channelInitiator = state.IsInitiator
c.chanType = state.ChanType c.chanType = state.ChanType
} }