mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-29 23:21:12 +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:
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
||||
)
|
||||
@@ -37,8 +36,9 @@ import (
|
||||
// necessary components are stripped out and encrypted before being sent to
|
||||
// the tower in a StateUpdate.
|
||||
type backupTask struct {
|
||||
id wtdb.BackupID
|
||||
breachInfo *lnwallet.BreachRetribution
|
||||
id wtdb.BackupID
|
||||
breachInfo *lnwallet.BreachRetribution
|
||||
commitmentType blob.CommitmentType
|
||||
|
||||
// state-dependent variables
|
||||
|
||||
@@ -127,6 +127,11 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody,
|
||||
return err
|
||||
}
|
||||
|
||||
commitType, err := session.Policy.BlobType.CommitmentType(&chanType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse the non-dust outputs from the breach transaction,
|
||||
// simultaneously computing the total amount contained in the inputs
|
||||
// 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
|
||||
// paying to-the-remote party (which is us).
|
||||
if breachInfo.RemoteOutputSignDesc != nil {
|
||||
toLocalInput = input.NewBaseInput(
|
||||
&breachInfo.RemoteOutpoint,
|
||||
input.CommitmentRevoke,
|
||||
breachInfo.RemoteOutputSignDesc,
|
||||
0,
|
||||
)
|
||||
toLocalInput, err = commitType.ToLocalInput(breachInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
totalAmt += breachInfo.RemoteOutputSignDesc.Output.Value
|
||||
}
|
||||
if breachInfo.LocalOutputSignDesc != nil {
|
||||
var witnessType input.WitnessType
|
||||
switch {
|
||||
case chanType.HasAnchors():
|
||||
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,
|
||||
)
|
||||
toRemoteInput, err = commitType.ToRemoteInput(breachInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
||||
}
|
||||
|
||||
t.commitmentType = commitType
|
||||
t.breachInfo = breachInfo
|
||||
t.toLocalInput = toLocalInput
|
||||
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
|
||||
// breach transaction.
|
||||
if t.toLocalInput != nil {
|
||||
// An older ToLocalPenaltyWitnessSize constant used to
|
||||
// underestimate the size by one byte. The diferrence 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. 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,
|
||||
)
|
||||
toLocalWitnessSize, err := commitType.ToLocalWitnessSize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
weightEstimate.AddWitnessInput(toLocalWitnessSize)
|
||||
}
|
||||
if t.toRemoteInput != nil {
|
||||
// Legacy channels (both tweaked and non-tweaked) spend from
|
||||
// P2WKH output. Anchor channels spend a to-remote confirmed
|
||||
// P2WSH output.
|
||||
if chanType.HasAnchors() {
|
||||
weightEstimate.AddWitnessInput(
|
||||
input.ToRemoteConfirmedWitnessSize,
|
||||
)
|
||||
} else {
|
||||
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
||||
toRemoteWitnessSize, err := commitType.ToRemoteWitnessSize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
weightEstimate.AddWitnessInput(toRemoteWitnessSize)
|
||||
}
|
||||
|
||||
// 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
|
||||
// the transaction and store the computed witness within the justice
|
||||
// kit.
|
||||
commitType := t.commitmentType
|
||||
for _, inp := range inputs {
|
||||
// Lookup the input's new post-sort position.
|
||||
i := inputIndex[*inp.OutPoint()]
|
||||
@@ -361,17 +328,17 @@ func (t *backupTask) craftSessionPayload(
|
||||
return hint, nil, err
|
||||
}
|
||||
|
||||
// Parse the DER-encoded signature from the first position of
|
||||
// the resulting witness. We trim an extra byte to remove the
|
||||
// sighash flag.
|
||||
witness := inputScript.Witness
|
||||
rawSignature := witness[0][:len(witness[0])-1]
|
||||
signature, err := commitType.ParseRawSig(inputScript.Witness)
|
||||
if err != nil {
|
||||
return hint, nil, err
|
||||
}
|
||||
|
||||
// Re-encode the DER signature into a fixed-size 64 byte
|
||||
// signature.
|
||||
signature, err := lnwire.NewSigFromECDSARawSignature(
|
||||
rawSignature,
|
||||
)
|
||||
toLocalWitnessType, err := commitType.ToLocalWitnessType()
|
||||
if err != nil {
|
||||
return hint, nil, err
|
||||
}
|
||||
|
||||
toRemoteWitnessType, err := commitType.ToRemoteWitnessType()
|
||||
if err != nil {
|
||||
return hint, nil, err
|
||||
}
|
||||
@@ -380,14 +347,9 @@ func (t *backupTask) craftSessionPayload(
|
||||
// using the input's witness type to select the appropriate
|
||||
// field
|
||||
switch inp.WitnessType() {
|
||||
case input.CommitmentRevoke:
|
||||
case toLocalWitnessType:
|
||||
justiceKit.CommitToLocalSig = signature
|
||||
|
||||
case input.CommitSpendNoDelayTweakless:
|
||||
fallthrough
|
||||
case input.CommitmentNoDelay:
|
||||
fallthrough
|
||||
case input.CommitmentToRemoteConfirmed:
|
||||
case toRemoteWitnessType:
|
||||
justiceKit.CommitToRemoteSig = signature
|
||||
default:
|
||||
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
|
||||
// session.
|
||||
func genTaskTest(
|
||||
t *testing.T,
|
||||
name string,
|
||||
stateNum uint64,
|
||||
toLocalAmt int64,
|
||||
@@ -91,15 +92,12 @@ func genTaskTest(
|
||||
}
|
||||
|
||||
// Parse the key pairs for all keys used in the test.
|
||||
revSK, revPK := btcec.PrivKeyFromBytes(
|
||||
revPrivBytes,
|
||||
)
|
||||
_, toLocalPK := btcec.PrivKeyFromBytes(
|
||||
toLocalPrivBytes,
|
||||
)
|
||||
toRemoteSK, toRemotePK := btcec.PrivKeyFromBytes(
|
||||
toRemotePrivBytes,
|
||||
)
|
||||
revSK, revPK := btcec.PrivKeyFromBytes(revPrivBytes)
|
||||
_, toLocalPK := btcec.PrivKeyFromBytes(toLocalPrivBytes)
|
||||
toRemoteSK, toRemotePK := btcec.PrivKeyFromBytes(toRemotePrivBytes)
|
||||
|
||||
commitType, err := blobType.CommitmentType(&chanType)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create the signer, and add the revocation and to-remote privkeys.
|
||||
signer := wtmock.NewMockSigner()
|
||||
@@ -174,12 +172,9 @@ func genTaskTest(
|
||||
Hash: txid,
|
||||
Index: index,
|
||||
}
|
||||
toLocalInput = input.NewBaseInput(
|
||||
&breachInfo.RemoteOutpoint,
|
||||
input.CommitmentRevoke,
|
||||
breachInfo.RemoteOutputSignDesc,
|
||||
0,
|
||||
)
|
||||
toLocalInput, err = commitType.ToLocalInput(breachInfo)
|
||||
require.NoError(t, err)
|
||||
|
||||
index++
|
||||
}
|
||||
if toRemoteAmt > 0 {
|
||||
@@ -188,31 +183,8 @@ func genTaskTest(
|
||||
Index: index,
|
||||
}
|
||||
|
||||
var witnessType input.WitnessType
|
||||
switch {
|
||||
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,
|
||||
)
|
||||
}
|
||||
toRemoteInput, err = commitType.ToRemoteInput(breachInfo)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
return backupTaskTest{
|
||||
@@ -312,6 +284,7 @@ func TestBackupTask(t *testing.T) {
|
||||
|
||||
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit no-reward, both outputs",
|
||||
100, // stateNum
|
||||
200000, // toLocalAmt
|
||||
@@ -325,6 +298,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit no-reward, to-local output only",
|
||||
1000, // stateNum
|
||||
200000, // toLocalAmt
|
||||
@@ -338,6 +312,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit no-reward, to-remote output only",
|
||||
1, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -351,6 +326,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit no-reward, to-remote output only, creates dust",
|
||||
1, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -364,6 +340,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit no-reward, no outputs, fee rate exceeds inputs",
|
||||
300, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -377,6 +354,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit no-reward, no outputs, fee rate of 0 creates dust",
|
||||
300, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -390,6 +368,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit reward, both outputs",
|
||||
100, // stateNum
|
||||
200000, // toLocalAmt
|
||||
@@ -403,6 +382,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit reward, to-local output only",
|
||||
1000, // stateNum
|
||||
200000, // toLocalAmt
|
||||
@@ -416,6 +396,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit reward, to-remote output only",
|
||||
1, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -429,6 +410,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit reward, to-remote output only, creates dust",
|
||||
1, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -442,6 +424,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit reward, no outputs, fee rate exceeds inputs",
|
||||
300, // stateNum
|
||||
0, // toLocalAmt
|
||||
@@ -455,6 +438,7 @@ func TestBackupTask(t *testing.T) {
|
||||
chanType,
|
||||
),
|
||||
genTaskTest(
|
||||
t,
|
||||
"commit reward, no outputs, fee rate of 0 creates dust",
|
||||
300, // stateNum
|
||||
0, // toLocalAmt
|
||||
|
Reference in New Issue
Block a user