watchtower/wtclient: add anchor backup tasks
This commit is contained in:
parent
97cb030854
commit
eb00e496bf
@ -39,6 +39,7 @@ import (
|
|||||||
type backupTask struct {
|
type backupTask struct {
|
||||||
id wtdb.BackupID
|
id wtdb.BackupID
|
||||||
breachInfo *lnwallet.BreachRetribution
|
breachInfo *lnwallet.BreachRetribution
|
||||||
|
chanType channeldb.ChannelType
|
||||||
|
|
||||||
// state-dependent variables
|
// state-dependent variables
|
||||||
|
|
||||||
@ -90,18 +91,33 @@ func newBackupTask(chanID *lnwire.ChannelID,
|
|||||||
if breachInfo.LocalOutputSignDesc != nil {
|
if breachInfo.LocalOutputSignDesc != nil {
|
||||||
var witnessType input.WitnessType
|
var witnessType input.WitnessType
|
||||||
switch {
|
switch {
|
||||||
|
case chanType.HasAnchors():
|
||||||
|
witnessType = input.CommitmentToRemoteConfirmed
|
||||||
case chanType.IsTweakless():
|
case chanType.IsTweakless():
|
||||||
witnessType = input.CommitSpendNoDelayTweakless
|
witnessType = input.CommitSpendNoDelayTweakless
|
||||||
default:
|
default:
|
||||||
witnessType = input.CommitmentNoDelay
|
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(
|
toRemoteInput = input.NewBaseInput(
|
||||||
&breachInfo.LocalOutpoint,
|
&breachInfo.LocalOutpoint,
|
||||||
witnessType,
|
witnessType,
|
||||||
breachInfo.LocalOutputSignDesc,
|
breachInfo.LocalOutputSignDesc,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
||||||
}
|
}
|
||||||
@ -112,6 +128,7 @@ func newBackupTask(chanID *lnwire.ChannelID,
|
|||||||
CommitHeight: breachInfo.RevokedStateNum,
|
CommitHeight: breachInfo.RevokedStateNum,
|
||||||
},
|
},
|
||||||
breachInfo: breachInfo,
|
breachInfo: breachInfo,
|
||||||
|
chanType: chanType,
|
||||||
toLocalInput: toLocalInput,
|
toLocalInput: toLocalInput,
|
||||||
toRemoteInput: toRemoteInput,
|
toRemoteInput: toRemoteInput,
|
||||||
totalAmt: btcutil.Amount(totalAmt),
|
totalAmt: btcutil.Amount(totalAmt),
|
||||||
@ -151,14 +168,29 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody) error {
|
|||||||
// underestimate the size by one byte. The diferrence in weight
|
// underestimate the size by one byte. The diferrence in weight
|
||||||
// can cause different output values on the sweep transaction,
|
// can cause different output values on the sweep transaction,
|
||||||
// so we mimic the original bug and create signatures using the
|
// so we mimic the original bug and create signatures using the
|
||||||
// original weight estimate.
|
// original weight estimate. For anchor channels we'll go ahead
|
||||||
|
// an use the correct penalty witness when signing our justice
|
||||||
|
// transactions.
|
||||||
|
if t.chanType.HasAnchors() {
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
input.ToLocalPenaltyWitnessSize,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
weightEstimate.AddWitnessInput(
|
weightEstimate.AddWitnessInput(
|
||||||
input.ToLocalPenaltyWitnessSize - 1,
|
input.ToLocalPenaltyWitnessSize - 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if t.toRemoteInput != nil {
|
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 t.chanType.HasAnchors() {
|
||||||
|
weightEstimate.AddWitnessInput(input.ToRemoteConfirmedWitnessSize)
|
||||||
|
} else {
|
||||||
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// All justice transactions have a p2wkh output paying to the victim.
|
// All justice transactions have a p2wkh output paying to the victim.
|
||||||
weightEstimate.AddP2WKHOutput()
|
weightEstimate.AddP2WKHOutput()
|
||||||
@ -169,6 +201,12 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody) error {
|
|||||||
weightEstimate.AddP2WKHOutput()
|
weightEstimate.AddP2WKHOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.chanType.HasAnchors() != session.Policy.IsAnchorChannel() {
|
||||||
|
log.Criticalf("Invalid task (has_anchors=%t) for session "+
|
||||||
|
"(has_anchors=%t)", t.chanType.HasAnchors(),
|
||||||
|
session.Policy.IsAnchorChannel())
|
||||||
|
}
|
||||||
|
|
||||||
// Now, compute the output values depending on whether FlagReward is set
|
// Now, compute the output values depending on whether FlagReward is set
|
||||||
// in the current session's policy.
|
// in the current session's policy.
|
||||||
outputs, err := session.Policy.ComputeJusticeTxOuts(
|
outputs, err := session.Policy.ComputeJusticeTxOuts(
|
||||||
@ -225,9 +263,10 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
// information. This will either be contain both the to-local and
|
// information. This will either be contain both the to-local and
|
||||||
// to-remote outputs, or only be the to-local output.
|
// to-remote outputs, or only be the to-local output.
|
||||||
inputs := t.inputs()
|
inputs := t.inputs()
|
||||||
for prevOutPoint := range inputs {
|
for prevOutPoint, input := range inputs {
|
||||||
justiceTxn.AddTxIn(&wire.TxIn{
|
justiceTxn.AddTxIn(&wire.TxIn{
|
||||||
PreviousOutPoint: prevOutPoint,
|
PreviousOutPoint: prevOutPoint,
|
||||||
|
Sequence: input.BlocksToMaturity(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +333,8 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
case input.CommitSpendNoDelayTweakless:
|
case input.CommitSpendNoDelayTweakless:
|
||||||
fallthrough
|
fallthrough
|
||||||
case input.CommitmentNoDelay:
|
case input.CommitmentNoDelay:
|
||||||
|
fallthrough
|
||||||
|
case input.CommitmentToRemoteConfirmed:
|
||||||
copy(justiceKit.CommitToRemoteSig[:], signature[:])
|
copy(justiceKit.CommitToRemoteSig[:], signature[:])
|
||||||
default:
|
default:
|
||||||
return hint, nil, fmt.Errorf("invalid witness type: %v",
|
return hint, nil, fmt.Errorf("invalid witness type: %v",
|
||||||
|
@ -95,6 +95,12 @@ func genTaskTest(
|
|||||||
bindErr error,
|
bindErr error,
|
||||||
chanType channeldb.ChannelType) backupTaskTest {
|
chanType channeldb.ChannelType) backupTaskTest {
|
||||||
|
|
||||||
|
// Set the anchor flag in the blob type if the session needs to support
|
||||||
|
// anchor channels.
|
||||||
|
if chanType.HasAnchors() {
|
||||||
|
blobType |= blob.Type(blob.FlagAnchorChannel)
|
||||||
|
}
|
||||||
|
|
||||||
// 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(
|
||||||
btcec.S256(), revPrivBytes,
|
btcec.S256(), revPrivBytes,
|
||||||
@ -195,12 +201,22 @@ func genTaskTest(
|
|||||||
|
|
||||||
var witnessType input.WitnessType
|
var witnessType input.WitnessType
|
||||||
switch {
|
switch {
|
||||||
|
case chanType.HasAnchors():
|
||||||
|
witnessType = input.CommitmentToRemoteConfirmed
|
||||||
case chanType.IsTweakless():
|
case chanType.IsTweakless():
|
||||||
witnessType = input.CommitSpendNoDelayTweakless
|
witnessType = input.CommitSpendNoDelayTweakless
|
||||||
default:
|
default:
|
||||||
witnessType = input.CommitmentNoDelay
|
witnessType = input.CommitmentNoDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if chanType.HasAnchors() {
|
||||||
|
toRemoteInput = input.NewCsvInput(
|
||||||
|
&breachInfo.LocalOutpoint,
|
||||||
|
witnessType,
|
||||||
|
breachInfo.LocalOutputSignDesc,
|
||||||
|
0, 1,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
toRemoteInput = input.NewBaseInput(
|
toRemoteInput = input.NewBaseInput(
|
||||||
&breachInfo.LocalOutpoint,
|
&breachInfo.LocalOutpoint,
|
||||||
witnessType,
|
witnessType,
|
||||||
@ -208,6 +224,7 @@ func genTaskTest(
|
|||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return backupTaskTest{
|
return backupTaskTest{
|
||||||
name: name,
|
name: name,
|
||||||
@ -260,10 +277,42 @@ func TestBackupTask(t *testing.T) {
|
|||||||
chanTypes := []channeldb.ChannelType{
|
chanTypes := []channeldb.ChannelType{
|
||||||
channeldb.SingleFunderBit,
|
channeldb.SingleFunderBit,
|
||||||
channeldb.SingleFunderTweaklessBit,
|
channeldb.SingleFunderTweaklessBit,
|
||||||
|
channeldb.AnchorOutputsBit,
|
||||||
}
|
}
|
||||||
|
|
||||||
var backupTaskTests []backupTaskTest
|
var backupTaskTests []backupTaskTest
|
||||||
for _, chanType := range chanTypes {
|
for _, chanType := range chanTypes {
|
||||||
|
// Depending on whether the test is for anchor channels or
|
||||||
|
// legacy (tweaked and non-tweaked) channels, adjust the
|
||||||
|
// expected sweep amount to accommodate. These are different for
|
||||||
|
// several reasons:
|
||||||
|
// - anchor to-remote outputs require a P2WSH sweep rather
|
||||||
|
// than a P2WKH sweep.
|
||||||
|
// - the to-local weight estimate fixes an off-by-one.
|
||||||
|
// In tests related to the dust threshold, the size difference
|
||||||
|
// between the channel types makes it so that the threshold fee
|
||||||
|
// rate is slightly lower (since the transactions are heavier).
|
||||||
|
var (
|
||||||
|
expSweepCommitNoRewardBoth int64 = 299241
|
||||||
|
expSweepCommitNoRewardLocal int64 = 199514
|
||||||
|
expSweepCommitNoRewardRemote int64 = 99561
|
||||||
|
expSweepCommitRewardBoth int64 = 296117
|
||||||
|
expSweepCommitRewardLocal int64 = 197390
|
||||||
|
expSweepCommitRewardRemote int64 = 98437
|
||||||
|
sweepFeeRateNoRewardRemoteDust chainfee.SatPerKWeight = 227500
|
||||||
|
sweepFeeRateRewardRemoteDust chainfee.SatPerKWeight = 175000
|
||||||
|
)
|
||||||
|
if chanType.HasAnchors() {
|
||||||
|
expSweepCommitNoRewardBoth = 299236
|
||||||
|
expSweepCommitNoRewardLocal = 199513
|
||||||
|
expSweepCommitNoRewardRemote = 99557
|
||||||
|
expSweepCommitRewardBoth = 296112
|
||||||
|
expSweepCommitRewardLocal = 197389
|
||||||
|
expSweepCommitRewardRemote = 98433
|
||||||
|
sweepFeeRateNoRewardRemoteDust = 225000
|
||||||
|
sweepFeeRateRewardRemoteDust = 173750
|
||||||
|
}
|
||||||
|
|
||||||
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, both outputs",
|
"commit no-reward, both outputs",
|
||||||
@ -273,7 +322,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
299241, // expSweepAmt
|
expSweepCommitNoRewardBoth, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
chanType,
|
chanType,
|
||||||
@ -286,7 +335,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
199514, // expSweepAmt
|
expSweepCommitNoRewardLocal, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
chanType,
|
chanType,
|
||||||
@ -299,7 +348,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
99561, // expSweepAmt
|
expSweepCommitNoRewardRemote, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
chanType,
|
chanType,
|
||||||
@ -310,7 +359,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
227500, // sweepFeeRate
|
sweepFeeRateNoRewardRemoteDust, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
@ -351,7 +400,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
296117, // expSweepAmt
|
expSweepCommitRewardBoth, // expSweepAmt
|
||||||
3000, // expRewardAmt
|
3000, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
chanType,
|
chanType,
|
||||||
@ -364,7 +413,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
197390, // expSweepAmt
|
expSweepCommitRewardLocal, // expSweepAmt
|
||||||
2000, // expRewardAmt
|
2000, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
chanType,
|
chanType,
|
||||||
@ -377,7 +426,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
98437, // expSweepAmt
|
expSweepCommitRewardRemote, // expSweepAmt
|
||||||
1000, // expRewardAmt
|
1000, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
chanType,
|
chanType,
|
||||||
@ -388,7 +437,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
175000, // sweepFeeRate
|
sweepFeeRateRewardRemoteDust, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
|
Loading…
Reference in New Issue
Block a user