mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-12 09:52:14 +02:00
watchtower: introduce CommitmentType
In this commit a new enum, CommitmentType, is introduced and initially there are 3 CommitmentTypes: Legacy, LegacyTweakless and Anchor. Then, various methods are added to `CommitmentType`. This allows us to remove a bunch of "if-else" chains from the `wtclient` and `lookout` code. This will also make things easier to extend when a new commitment type (like Taproot) is added.
This commit is contained in:
parent
4d8fa349ca
commit
204ca6cb0f
174
watchtower/blob/commitments.go
Normal file
174
watchtower/blob/commitments.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ package blob
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Flag represents a specify option that can be present in a Type.
|
// Flag represents a specify option that can be present in a Type.
|
||||||
@ -81,6 +83,27 @@ func (t Type) Identifier() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitmentType returns the appropriate CommitmentType for the given blob Type
|
||||||
|
// and channel type.
|
||||||
|
func (t Type) CommitmentType(chanType *channeldb.ChannelType) (CommitmentType,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case t.Has(FlagAnchorChannel):
|
||||||
|
return AnchorCommitment, nil
|
||||||
|
|
||||||
|
case t.Has(FlagCommitOutputs):
|
||||||
|
if chanType != nil && chanType.IsTweakless() {
|
||||||
|
return LegacyTweaklessCommitment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return LegacyCommitment, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0, ErrUnknownBlobType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Has returns true if the Type has the passed flag enabled.
|
// Has returns true if the Type has the passed flag enabled.
|
||||||
func (t Type) Has(flag Flag) bool {
|
func (t Type) Has(flag Flag) bool {
|
||||||
return Flag(t)&flag == flag
|
return Flag(t)&flag == flag
|
||||||
|
@ -264,6 +264,11 @@ func (p *JusticeDescriptor) CreateJusticeTxn() (*wire.MsgTx, error) {
|
|||||||
weightEstimate input.TxWeightEstimator
|
weightEstimate input.TxWeightEstimator
|
||||||
)
|
)
|
||||||
|
|
||||||
|
commitmentType, err := p.SessionInfo.Policy.BlobType.CommitmentType(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Add the sweep address's contribution, depending on whether it is a
|
// Add the sweep address's contribution, depending on whether it is a
|
||||||
// p2wkh or p2wsh output.
|
// p2wkh or p2wsh output.
|
||||||
switch len(p.JusticeKit.SweepAddress) {
|
switch len(p.JusticeKit.SweepAddress) {
|
||||||
@ -290,16 +295,13 @@ func (p *JusticeDescriptor) CreateJusticeTxn() (*wire.MsgTx, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// An older ToLocalPenaltyWitnessSize constant used to underestimate the
|
// Get the weight for the to-local witness and add that to the
|
||||||
// size by one byte. The diferrence in weight can cause different output
|
// estimator.
|
||||||
// values on the sweep transaction, so we mimic the original bug to
|
toLocalWitnessSize, err := commitmentType.ToLocalWitnessSize()
|
||||||
// avoid invalidating signatures by older clients. For anchor channels
|
if err != nil {
|
||||||
// we correct this and use the correct witness size.
|
return nil, err
|
||||||
if p.JusticeKit.BlobType.IsAnchorChannel() {
|
|
||||||
weightEstimate.AddWitnessInput(input.ToLocalPenaltyWitnessSize)
|
|
||||||
} else {
|
|
||||||
weightEstimate.AddWitnessInput(input.ToLocalPenaltyWitnessSize - 1)
|
|
||||||
}
|
}
|
||||||
|
weightEstimate.AddWitnessInput(toLocalWitnessSize)
|
||||||
|
|
||||||
sweepInputs = append(sweepInputs, toLocalInput)
|
sweepInputs = append(sweepInputs, toLocalInput)
|
||||||
|
|
||||||
@ -319,11 +321,14 @@ func (p *JusticeDescriptor) CreateJusticeTxn() (*wire.MsgTx, error) {
|
|||||||
log.Debugf("Found to remote witness output=%#v, stack=%v",
|
log.Debugf("Found to remote witness output=%#v, stack=%v",
|
||||||
toRemoteInput.txOut, toRemoteInput.witness)
|
toRemoteInput.txOut, toRemoteInput.witness)
|
||||||
|
|
||||||
if p.JusticeKit.BlobType.IsAnchorChannel() {
|
// Get the weight for the to-remote witness and add that to the
|
||||||
weightEstimate.AddWitnessInput(input.ToRemoteConfirmedWitnessSize)
|
// estimator.
|
||||||
} else {
|
toRemoteWitnessSize, err := commitmentType.ToRemoteWitnessSize()
|
||||||
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weightEstimate.AddWitnessInput(toRemoteWitnessSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(conner): sweep htlc outputs
|
// TODO(conner): sweep htlc outputs
|
||||||
|
@ -92,15 +92,13 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Parse the key pairs for all keys used in the test.
|
// Parse the key pairs for all keys used in the test.
|
||||||
revSK, revPK := btcec.PrivKeyFromBytes(
|
revSK, revPK := btcec.PrivKeyFromBytes(revPrivBytes)
|
||||||
revPrivBytes,
|
_, toLocalPK := btcec.PrivKeyFromBytes(toLocalPrivBytes)
|
||||||
)
|
toRemoteSK, toRemotePK := btcec.PrivKeyFromBytes(toRemotePrivBytes)
|
||||||
_, toLocalPK := btcec.PrivKeyFromBytes(
|
|
||||||
toLocalPrivBytes,
|
// Get the commitment type.
|
||||||
)
|
commitType, err := blobType.CommitmentType(nil)
|
||||||
toRemoteSK, toRemotePK := btcec.PrivKeyFromBytes(
|
require.NoError(t, err)
|
||||||
toRemotePrivBytes,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create the signer, and add the revocation and to-remote privkeys.
|
// Create the signer, and add the revocation and to-remote privkeys.
|
||||||
signer := wtmock.NewMockSigner()
|
signer := wtmock.NewMockSigner()
|
||||||
@ -113,11 +111,11 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
|
|||||||
toLocalScript, err := input.CommitScriptToSelf(
|
toLocalScript, err := input.CommitScriptToSelf(
|
||||||
csvDelay, toLocalPK, revPK,
|
csvDelay, toLocalPK, revPK,
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Compute the to-local witness script hash.
|
// Compute the to-local witness script hash.
|
||||||
toLocalScriptHash, err := input.WitnessScriptHash(toLocalScript)
|
toLocalScriptHash, err := input.WitnessScriptHash(toLocalScript)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Compute the to-remote redeem script, witness script hash, and
|
// Compute the to-remote redeem script, witness script hash, and
|
||||||
// sequence numbers.
|
// sequence numbers.
|
||||||
@ -147,12 +145,12 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
|
|||||||
toRemoteRedeemScript, err = input.CommitScriptToRemoteConfirmed(
|
toRemoteRedeemScript, err = input.CommitScriptToRemoteConfirmed(
|
||||||
toRemotePK,
|
toRemotePK,
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
toRemoteScriptHash, err = input.WitnessScriptHash(
|
toRemoteScriptHash, err = input.WitnessScriptHash(
|
||||||
toRemoteRedeemScript,
|
toRemoteRedeemScript,
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// As it should be.
|
// As it should be.
|
||||||
toRemoteSigningScript = toRemoteRedeemScript
|
toRemoteSigningScript = toRemoteRedeemScript
|
||||||
@ -161,7 +159,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
|
|||||||
toRemoteScriptHash, err = input.CommitScriptUnencumbered(
|
toRemoteScriptHash, err = input.CommitScriptUnencumbered(
|
||||||
toRemotePK,
|
toRemotePK,
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// NOTE: This is the _pkscript_.
|
// NOTE: This is the _pkscript_.
|
||||||
toRemoteSigningScript = toRemoteScriptHash
|
toRemoteSigningScript = toRemoteScriptHash
|
||||||
@ -188,26 +186,24 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
|
|||||||
// Compute the weight estimate for our justice transaction.
|
// Compute the weight estimate for our justice transaction.
|
||||||
var weightEstimate input.TxWeightEstimator
|
var weightEstimate input.TxWeightEstimator
|
||||||
|
|
||||||
// An older ToLocalPenaltyWitnessSize constant used to underestimate the
|
// Add the local witness size to the weight estimator.
|
||||||
// size by one byte. The diferrence in weight can cause different output
|
toLocalWitnessSize, err := commitType.ToLocalWitnessSize()
|
||||||
// values on the sweep transaction, so we mimic the original bug and
|
require.NoError(t, err)
|
||||||
// create signatures using the original weight estimate. For anchor
|
weightEstimate.AddWitnessInput(toLocalWitnessSize)
|
||||||
// channels we fix this and use the correct witness size.
|
|
||||||
if isAnchorChannel {
|
|
||||||
weightEstimate.AddWitnessInput(input.ToLocalPenaltyWitnessSize)
|
|
||||||
} else {
|
|
||||||
weightEstimate.AddWitnessInput(input.ToLocalPenaltyWitnessSize - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isAnchorChannel {
|
// Add the remote witness size to the weight estimator.
|
||||||
weightEstimate.AddWitnessInput(input.ToRemoteConfirmedWitnessSize)
|
toRemoteWitnessSize, err := commitType.ToRemoteWitnessSize()
|
||||||
} else {
|
require.NoError(t, err)
|
||||||
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
weightEstimate.AddWitnessInput(toRemoteWitnessSize)
|
||||||
}
|
|
||||||
|
// Add the sweep output to the weight estimator.
|
||||||
weightEstimate.AddP2WKHOutput()
|
weightEstimate.AddP2WKHOutput()
|
||||||
|
|
||||||
|
// Add the reward output to the weight estimator.
|
||||||
if blobType.Has(blob.FlagReward) {
|
if blobType.Has(blob.FlagReward) {
|
||||||
weightEstimate.AddP2WKHOutput()
|
weightEstimate.AddP2WKHOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
txWeight := weightEstimate.Weight()
|
txWeight := weightEstimate.Weight()
|
||||||
|
|
||||||
// Create a session info so that simulate agreement of the sweep
|
// Create a session info so that simulate agreement of the sweep
|
||||||
@ -263,7 +259,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
|
|||||||
totalAmount, int64(txWeight), justiceKit.SweepAddress,
|
totalAmount, int64(txWeight), justiceKit.SweepAddress,
|
||||||
sessionInfo.RewardAddress,
|
sessionInfo.RewardAddress,
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Attach the txouts and BIP69 sort the resulting transaction.
|
// Attach the txouts and BIP69 sort the resulting transaction.
|
||||||
justiceTxn.TxOut = outputs
|
justiceTxn.TxOut = outputs
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
|
||||||
"github.com/lightningnetwork/lnd/watchtower/blob"
|
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||||
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
||||||
)
|
)
|
||||||
@ -39,6 +38,7 @@ import (
|
|||||||
type backupTask struct {
|
type backupTask struct {
|
||||||
id wtdb.BackupID
|
id wtdb.BackupID
|
||||||
breachInfo *lnwallet.BreachRetribution
|
breachInfo *lnwallet.BreachRetribution
|
||||||
|
commitmentType blob.CommitmentType
|
||||||
|
|
||||||
// state-dependent variables
|
// state-dependent variables
|
||||||
|
|
||||||
@ -127,6 +127,11 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commitType, err := session.Policy.BlobType.CommitmentType(&chanType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the non-dust outputs from the breach transaction,
|
// Parse the non-dust outputs from the breach transaction,
|
||||||
// simultaneously computing the total amount contained in the inputs
|
// simultaneously computing the total amount contained in the inputs
|
||||||
// present. We can't compute the exact output values at this time
|
// present. We can't compute the exact output values at this time
|
||||||
@ -147,48 +152,23 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody,
|
|||||||
// to that output as local, though relative to their commitment, it is
|
// to that output as local, though relative to their commitment, it is
|
||||||
// paying to-the-remote party (which is us).
|
// paying to-the-remote party (which is us).
|
||||||
if breachInfo.RemoteOutputSignDesc != nil {
|
if breachInfo.RemoteOutputSignDesc != nil {
|
||||||
toLocalInput = input.NewBaseInput(
|
toLocalInput, err = commitType.ToLocalInput(breachInfo)
|
||||||
&breachInfo.RemoteOutpoint,
|
if err != nil {
|
||||||
input.CommitmentRevoke,
|
return err
|
||||||
breachInfo.RemoteOutputSignDesc,
|
}
|
||||||
0,
|
|
||||||
)
|
|
||||||
totalAmt += breachInfo.RemoteOutputSignDesc.Output.Value
|
totalAmt += breachInfo.RemoteOutputSignDesc.Output.Value
|
||||||
}
|
}
|
||||||
if breachInfo.LocalOutputSignDesc != nil {
|
if breachInfo.LocalOutputSignDesc != nil {
|
||||||
var witnessType input.WitnessType
|
toRemoteInput, err = commitType.ToRemoteInput(breachInfo)
|
||||||
switch {
|
if err != nil {
|
||||||
case chanType.HasAnchors():
|
return err
|
||||||
witnessType = input.CommitmentToRemoteConfirmed
|
|
||||||
case chanType.IsTweakless():
|
|
||||||
witnessType = input.CommitSpendNoDelayTweakless
|
|
||||||
default:
|
|
||||||
witnessType = input.CommitmentNoDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anchor channels have a CSV-encumbered to-remote output. We'll
|
|
||||||
// construct a CSV input in that case and assign the proper CSV
|
|
||||||
// delay of 1, otherwise we fallback to the a regular P2WKH
|
|
||||||
// to-remote output for tweaked or tweakless channels.
|
|
||||||
if chanType.HasAnchors() {
|
|
||||||
toRemoteInput = input.NewCsvInput(
|
|
||||||
&breachInfo.LocalOutpoint,
|
|
||||||
witnessType,
|
|
||||||
breachInfo.LocalOutputSignDesc,
|
|
||||||
0, 1,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toRemoteInput = input.NewBaseInput(
|
|
||||||
&breachInfo.LocalOutpoint,
|
|
||||||
witnessType,
|
|
||||||
breachInfo.LocalOutputSignDesc,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.commitmentType = commitType
|
||||||
t.breachInfo = breachInfo
|
t.breachInfo = breachInfo
|
||||||
t.toLocalInput = toLocalInput
|
t.toLocalInput = toLocalInput
|
||||||
t.toRemoteInput = toRemoteInput
|
t.toRemoteInput = toRemoteInput
|
||||||
@ -202,34 +182,20 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody,
|
|||||||
// Next, add the contribution from the inputs that are present on this
|
// Next, add the contribution from the inputs that are present on this
|
||||||
// breach transaction.
|
// breach transaction.
|
||||||
if t.toLocalInput != nil {
|
if t.toLocalInput != nil {
|
||||||
// An older ToLocalPenaltyWitnessSize constant used to
|
toLocalWitnessSize, err := commitType.ToLocalWitnessSize()
|
||||||
// underestimate the size by one byte. The diferrence in weight
|
if err != nil {
|
||||||
// can cause different output values on the sweep transaction,
|
return err
|
||||||
// so we mimic the original bug and create signatures using the
|
|
||||||
// original weight estimate. For anchor channels we'll go ahead
|
|
||||||
// an use the correct penalty witness when signing our justice
|
|
||||||
// transactions.
|
|
||||||
if chanType.HasAnchors() {
|
|
||||||
weightEstimate.AddWitnessInput(
|
|
||||||
input.ToLocalPenaltyWitnessSize,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
weightEstimate.AddWitnessInput(
|
|
||||||
input.ToLocalPenaltyWitnessSize - 1,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weightEstimate.AddWitnessInput(toLocalWitnessSize)
|
||||||
}
|
}
|
||||||
if t.toRemoteInput != nil {
|
if t.toRemoteInput != nil {
|
||||||
// Legacy channels (both tweaked and non-tweaked) spend from
|
toRemoteWitnessSize, err := commitType.ToRemoteWitnessSize()
|
||||||
// P2WKH output. Anchor channels spend a to-remote confirmed
|
if err != nil {
|
||||||
// P2WSH output.
|
return err
|
||||||
if chanType.HasAnchors() {
|
|
||||||
weightEstimate.AddWitnessInput(
|
|
||||||
input.ToRemoteConfirmedWitnessSize,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weightEstimate.AddWitnessInput(toRemoteWitnessSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// All justice transactions will either use segwit v0 (p2wkh + p2wsh)
|
// All justice transactions will either use segwit v0 (p2wkh + p2wsh)
|
||||||
@ -349,6 +315,7 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
// Now, iterate through the list of inputs that were initially added to
|
// Now, iterate through the list of inputs that were initially added to
|
||||||
// the transaction and store the computed witness within the justice
|
// the transaction and store the computed witness within the justice
|
||||||
// kit.
|
// kit.
|
||||||
|
commitType := t.commitmentType
|
||||||
for _, inp := range inputs {
|
for _, inp := range inputs {
|
||||||
// Lookup the input's new post-sort position.
|
// Lookup the input's new post-sort position.
|
||||||
i := inputIndex[*inp.OutPoint()]
|
i := inputIndex[*inp.OutPoint()]
|
||||||
@ -361,17 +328,17 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
return hint, nil, err
|
return hint, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the DER-encoded signature from the first position of
|
signature, err := commitType.ParseRawSig(inputScript.Witness)
|
||||||
// the resulting witness. We trim an extra byte to remove the
|
if err != nil {
|
||||||
// sighash flag.
|
return hint, nil, err
|
||||||
witness := inputScript.Witness
|
}
|
||||||
rawSignature := witness[0][:len(witness[0])-1]
|
|
||||||
|
|
||||||
// Re-encode the DER signature into a fixed-size 64 byte
|
toLocalWitnessType, err := commitType.ToLocalWitnessType()
|
||||||
// signature.
|
if err != nil {
|
||||||
signature, err := lnwire.NewSigFromECDSARawSignature(
|
return hint, nil, err
|
||||||
rawSignature,
|
}
|
||||||
)
|
|
||||||
|
toRemoteWitnessType, err := commitType.ToRemoteWitnessType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hint, nil, err
|
return hint, nil, err
|
||||||
}
|
}
|
||||||
@ -380,14 +347,9 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
// using the input's witness type to select the appropriate
|
// using the input's witness type to select the appropriate
|
||||||
// field
|
// field
|
||||||
switch inp.WitnessType() {
|
switch inp.WitnessType() {
|
||||||
case input.CommitmentRevoke:
|
case toLocalWitnessType:
|
||||||
justiceKit.CommitToLocalSig = signature
|
justiceKit.CommitToLocalSig = signature
|
||||||
|
case toRemoteWitnessType:
|
||||||
case input.CommitSpendNoDelayTweakless:
|
|
||||||
fallthrough
|
|
||||||
case input.CommitmentNoDelay:
|
|
||||||
fallthrough
|
|
||||||
case input.CommitmentToRemoteConfirmed:
|
|
||||||
justiceKit.CommitToRemoteSig = signature
|
justiceKit.CommitToRemoteSig = signature
|
||||||
default:
|
default:
|
||||||
return hint, nil, fmt.Errorf("invalid witness type: %v",
|
return hint, nil, fmt.Errorf("invalid witness type: %v",
|
||||||
|
@ -72,6 +72,7 @@ type backupTaskTest struct {
|
|||||||
// corresponding BreachInfo, as well as setting the wtpolicy.Policy of the given
|
// corresponding BreachInfo, as well as setting the wtpolicy.Policy of the given
|
||||||
// session.
|
// session.
|
||||||
func genTaskTest(
|
func genTaskTest(
|
||||||
|
t *testing.T,
|
||||||
name string,
|
name string,
|
||||||
stateNum uint64,
|
stateNum uint64,
|
||||||
toLocalAmt int64,
|
toLocalAmt int64,
|
||||||
@ -91,15 +92,12 @@ func genTaskTest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the key pairs for all keys used in the test.
|
// Parse the key pairs for all keys used in the test.
|
||||||
revSK, revPK := btcec.PrivKeyFromBytes(
|
revSK, revPK := btcec.PrivKeyFromBytes(revPrivBytes)
|
||||||
revPrivBytes,
|
_, toLocalPK := btcec.PrivKeyFromBytes(toLocalPrivBytes)
|
||||||
)
|
toRemoteSK, toRemotePK := btcec.PrivKeyFromBytes(toRemotePrivBytes)
|
||||||
_, toLocalPK := btcec.PrivKeyFromBytes(
|
|
||||||
toLocalPrivBytes,
|
commitType, err := blobType.CommitmentType(&chanType)
|
||||||
)
|
require.NoError(t, err)
|
||||||
toRemoteSK, toRemotePK := btcec.PrivKeyFromBytes(
|
|
||||||
toRemotePrivBytes,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create the signer, and add the revocation and to-remote privkeys.
|
// Create the signer, and add the revocation and to-remote privkeys.
|
||||||
signer := wtmock.NewMockSigner()
|
signer := wtmock.NewMockSigner()
|
||||||
@ -174,12 +172,9 @@ func genTaskTest(
|
|||||||
Hash: txid,
|
Hash: txid,
|
||||||
Index: index,
|
Index: index,
|
||||||
}
|
}
|
||||||
toLocalInput = input.NewBaseInput(
|
toLocalInput, err = commitType.ToLocalInput(breachInfo)
|
||||||
&breachInfo.RemoteOutpoint,
|
require.NoError(t, err)
|
||||||
input.CommitmentRevoke,
|
|
||||||
breachInfo.RemoteOutputSignDesc,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
if toRemoteAmt > 0 {
|
if toRemoteAmt > 0 {
|
||||||
@ -188,31 +183,8 @@ func genTaskTest(
|
|||||||
Index: index,
|
Index: index,
|
||||||
}
|
}
|
||||||
|
|
||||||
var witnessType input.WitnessType
|
toRemoteInput, err = commitType.ToRemoteInput(breachInfo)
|
||||||
switch {
|
require.NoError(t, err)
|
||||||
case chanType.HasAnchors():
|
|
||||||
witnessType = input.CommitmentToRemoteConfirmed
|
|
||||||
case chanType.IsTweakless():
|
|
||||||
witnessType = input.CommitSpendNoDelayTweakless
|
|
||||||
default:
|
|
||||||
witnessType = input.CommitmentNoDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
if chanType.HasAnchors() {
|
|
||||||
toRemoteInput = input.NewCsvInput(
|
|
||||||
&breachInfo.LocalOutpoint,
|
|
||||||
witnessType,
|
|
||||||
breachInfo.LocalOutputSignDesc,
|
|
||||||
0, 1,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toRemoteInput = input.NewBaseInput(
|
|
||||||
&breachInfo.LocalOutpoint,
|
|
||||||
witnessType,
|
|
||||||
breachInfo.LocalOutputSignDesc,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return backupTaskTest{
|
return backupTaskTest{
|
||||||
@ -312,6 +284,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
|
|
||||||
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit no-reward, both outputs",
|
"commit no-reward, both outputs",
|
||||||
100, // stateNum
|
100, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
@ -325,6 +298,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit no-reward, to-local output only",
|
"commit no-reward, to-local output only",
|
||||||
1000, // stateNum
|
1000, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
@ -338,6 +312,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit no-reward, to-remote output only",
|
"commit no-reward, to-remote output only",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -351,6 +326,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit no-reward, to-remote output only, creates dust",
|
"commit no-reward, to-remote output only, creates dust",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -364,6 +340,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit no-reward, no outputs, fee rate exceeds inputs",
|
"commit no-reward, no outputs, fee rate exceeds inputs",
|
||||||
300, // stateNum
|
300, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -377,6 +354,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit no-reward, no outputs, fee rate of 0 creates dust",
|
"commit no-reward, no outputs, fee rate of 0 creates dust",
|
||||||
300, // stateNum
|
300, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -390,6 +368,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit reward, both outputs",
|
"commit reward, both outputs",
|
||||||
100, // stateNum
|
100, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
@ -403,6 +382,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit reward, to-local output only",
|
"commit reward, to-local output only",
|
||||||
1000, // stateNum
|
1000, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
@ -416,6 +396,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit reward, to-remote output only",
|
"commit reward, to-remote output only",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -429,6 +410,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit reward, to-remote output only, creates dust",
|
"commit reward, to-remote output only, creates dust",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -442,6 +424,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit reward, no outputs, fee rate exceeds inputs",
|
"commit reward, no outputs, fee rate exceeds inputs",
|
||||||
300, // stateNum
|
300, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
@ -455,6 +438,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanType,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
|
t,
|
||||||
"commit reward, no outputs, fee rate of 0 creates dust",
|
"commit reward, no outputs, fee rate of 0 creates dust",
|
||||||
300, // stateNum
|
300, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
|
Loading…
x
Reference in New Issue
Block a user