package blob import ( "fmt" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" ) // CommitmentType characterises the various properties of the breach commitment // transaction. type CommitmentType uint8 const ( // LegacyCommitment represents a legacy commitment transaction where // anchor outputs are not yet used and so the to_remote output is just // a regular but tweaked P2WKH. LegacyCommitment CommitmentType = iota // LegacyTweaklessCommitment is similar to the LegacyCommitment with the // added detail of the to_remote output not being tweaked. LegacyTweaklessCommitment // AnchorCommitment represents the commitment transaction of an // anchor channel. The key differences are that the to_remote is // encumbered by a 1 block CSV and so is thus a P2WSH output. AnchorCommitment ) // ToLocalInput constructs the input that will be used to spend the to_local // output. func (c CommitmentType) ToLocalInput(info *lnwallet.BreachRetribution) ( input.Input, error) { witnessType, err := c.ToLocalWitnessType() if err != nil { return nil, err } return input.NewBaseInput( &info.RemoteOutpoint, witnessType, info.RemoteOutputSignDesc, 0, ), nil } // ToRemoteInput constructs the input that will be used to spend the to_remote // output. func (c CommitmentType) ToRemoteInput(info *lnwallet.BreachRetribution) ( input.Input, error) { witnessType, err := c.ToRemoteWitnessType() if err != nil { return nil, err } switch c { case LegacyCommitment, LegacyTweaklessCommitment: return input.NewBaseInput( &info.LocalOutpoint, witnessType, info.LocalOutputSignDesc, 0, ), nil case AnchorCommitment: // Anchor channels have a CSV-encumbered to-remote output. We'll // construct a CSV input and assign the proper CSV delay of 1. return input.NewCsvInput( &info.LocalOutpoint, witnessType, info.LocalOutputSignDesc, 0, 1, ), nil default: return nil, fmt.Errorf("unknown commitment type: %v", c) } } // ToLocalWitnessType is the input type of the to_local output. func (c CommitmentType) ToLocalWitnessType() (input.WitnessType, error) { switch c { case LegacyTweaklessCommitment, LegacyCommitment, AnchorCommitment: return input.CommitmentRevoke, nil default: return nil, fmt.Errorf("unknown commitment type: %v", c) } } // ToRemoteWitnessType is the input type of the to_remote output. func (c CommitmentType) ToRemoteWitnessType() (input.WitnessType, error) { switch c { case LegacyTweaklessCommitment: return input.CommitSpendNoDelayTweakless, nil case LegacyCommitment: return input.CommitmentNoDelay, nil case AnchorCommitment: return input.CommitmentToRemoteConfirmed, nil default: return nil, fmt.Errorf("unknown commitment type: %v", c) } } // ToRemoteWitnessSize is the size of the witness that will be required to spend // the to_remote output. func (c CommitmentType) ToRemoteWitnessSize() (int, error) { switch c { // Legacy channels (both tweaked and non-tweaked) spend from P2WKH // output. case LegacyTweaklessCommitment, LegacyCommitment: return input.P2WKHWitnessSize, nil // Anchor channels spend a to-remote confirmed P2WSH output. case AnchorCommitment: return input.ToRemoteConfirmedWitnessSize, nil default: return 0, fmt.Errorf("unknown commitment type: %v", c) } } // ToLocalWitnessSize is the size of the witness that will be required to spend // the to_local output. func (c CommitmentType) ToLocalWitnessSize() (int, error) { switch c { // An older ToLocalPenaltyWitnessSize constant used to underestimate the // size by one byte. The difference in weight can cause different output // values on the sweep transaction, so we mimic the original bug and // create signatures using the original weight estimate. case LegacyTweaklessCommitment, LegacyCommitment: return input.ToLocalPenaltyWitnessSize - 1, nil case AnchorCommitment: return input.ToLocalPenaltyWitnessSize, nil default: return 0, fmt.Errorf("unknown commitment type: %v", c) } } // ParseRawSig parses a wire.TxWitness and creates an lnwire.Sig. func (c CommitmentType) ParseRawSig(witness wire.TxWitness) (lnwire.Sig, error) { switch c { case LegacyCommitment, LegacyTweaklessCommitment, AnchorCommitment: // Check that the witness has at least one item. if len(witness) < 1 { return lnwire.Sig{}, fmt.Errorf("the witness should " + "have at least one element") } // Check that the first witness element is non-nil. This is to // ensure that the witness length check below does not panic. if witness[0] == nil { return lnwire.Sig{}, fmt.Errorf("the first witness " + "element should not be nil") } // Parse the DER-encoded signature from the first position of // the resulting witness. We trim an extra byte to remove the // sighash flag. rawSignature := witness[0][:len(witness[0])-1] // Re-encode the DER signature into a fixed-size 64 byte // signature. return lnwire.NewSigFromECDSARawSignature(rawSignature) default: return lnwire.Sig{}, fmt.Errorf("unknown commitment type: %v", c) } }