mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
251 lines
7.0 KiB
Go
251 lines
7.0 KiB
Go
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
|
|
|
|
// TaprootCommitment represents the commitment transaction of a simple
|
|
// taproot channel.
|
|
TaprootCommitment
|
|
)
|
|
|
|
// 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, TaprootCommitment:
|
|
// Anchor and Taproot 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
|
|
|
|
case TaprootCommitment:
|
|
return input.TaprootCommitmentRevoke, 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
|
|
|
|
case TaprootCommitment:
|
|
return input.TaprootRemoteCommitSpend, 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
|
|
|
|
// Taproot channels spend a confirmed P2SH output.
|
|
case TaprootCommitment:
|
|
return input.TaprootToRemoteWitnessSize, 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
|
|
|
|
case TaprootCommitment:
|
|
return input.TaprootToLocalRevokeWitnessSize, 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) {
|
|
|
|
// Check that the witness has at least one item since this is required
|
|
// for all commitment types to follow.
|
|
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 checks below do not panic.
|
|
if witness[0] == nil {
|
|
return lnwire.Sig{}, fmt.Errorf("the first witness element " +
|
|
"should not be nil")
|
|
}
|
|
|
|
switch c {
|
|
case LegacyCommitment, LegacyTweaklessCommitment, AnchorCommitment:
|
|
// 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)
|
|
|
|
case TaprootCommitment:
|
|
rawSignature := witness[0]
|
|
if len(rawSignature) > 64 {
|
|
rawSignature = witness[0][:len(witness[0])-1]
|
|
}
|
|
|
|
// Re-encode the schnorr signature into a fixed-size 64 byte
|
|
// signature.
|
|
return lnwire.NewSigFromSchnorrRawSignature(rawSignature)
|
|
|
|
default:
|
|
return lnwire.Sig{}, fmt.Errorf("unknown commitment type: %v",
|
|
c)
|
|
}
|
|
}
|
|
|
|
// NewJusticeKit can be used to construct a new JusticeKit depending on the
|
|
// CommitmentType.
|
|
func (c CommitmentType) NewJusticeKit(sweepScript []byte,
|
|
breachInfo *lnwallet.BreachRetribution, withToRemote bool) (JusticeKit,
|
|
error) {
|
|
|
|
switch c {
|
|
case LegacyCommitment, LegacyTweaklessCommitment:
|
|
return newLegacyJusticeKit(
|
|
sweepScript, breachInfo, withToRemote,
|
|
), nil
|
|
|
|
case AnchorCommitment:
|
|
return newAnchorJusticeKit(
|
|
sweepScript, breachInfo, withToRemote,
|
|
), nil
|
|
|
|
case TaprootCommitment:
|
|
return newTaprootJusticeKit(
|
|
sweepScript, breachInfo, withToRemote,
|
|
)
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown commitment type: %v", c)
|
|
}
|
|
}
|
|
|
|
// EmptyJusticeKit returns the appropriate empty justice kit for the given
|
|
// CommitmentType.
|
|
func (c CommitmentType) EmptyJusticeKit() (JusticeKit, error) {
|
|
switch c {
|
|
case LegacyTweaklessCommitment, LegacyCommitment:
|
|
return &legacyJusticeKit{}, nil
|
|
|
|
case AnchorCommitment:
|
|
return &anchorJusticeKit{
|
|
legacyJusticeKit: legacyJusticeKit{},
|
|
}, nil
|
|
|
|
case TaprootCommitment:
|
|
return &taprootJusticeKit{}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown commitment type: %v", c)
|
|
}
|
|
}
|