mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
697 lines
22 KiB
Go
697 lines
22 KiB
Go
package wtclient
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/watchtower/blob"
|
|
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
|
"github.com/lightningnetwork/lnd/watchtower/wtmock"
|
|
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const csvDelay uint32 = 144
|
|
|
|
var (
|
|
zeroSig = makeSig(0)
|
|
|
|
revPrivBytes = []byte{
|
|
0x8f, 0x4b, 0x51, 0x83, 0xa9, 0x34, 0xbd, 0x5f,
|
|
0x74, 0x6c, 0x9d, 0x5c, 0xae, 0x88, 0x2d, 0x31,
|
|
0x06, 0x90, 0xdd, 0x8c, 0x9b, 0x31, 0xbc, 0xd1,
|
|
0x78, 0x91, 0x88, 0x2a, 0xf9, 0x74, 0xa0, 0xef,
|
|
}
|
|
|
|
toLocalPrivBytes = []byte{
|
|
0xde, 0x17, 0xc1, 0x2f, 0xdc, 0x1b, 0xc0, 0xc6,
|
|
0x59, 0x5d, 0xf9, 0xc1, 0x3e, 0x89, 0xbc, 0x6f,
|
|
0x01, 0x85, 0x45, 0x76, 0x26, 0xce, 0x9c, 0x55,
|
|
0x3b, 0xc9, 0xec, 0x3d, 0xd8, 0x8b, 0xac, 0xa8,
|
|
}
|
|
|
|
toRemotePrivBytes = []byte{
|
|
0x28, 0x59, 0x6f, 0x36, 0xb8, 0x9f, 0x19, 0x5d,
|
|
0xcb, 0x07, 0x48, 0x8a, 0xe5, 0x89, 0x71, 0x74,
|
|
0x70, 0x4c, 0xff, 0x1e, 0x9c, 0x00, 0x93, 0xbe,
|
|
0xe2, 0x2e, 0x68, 0x08, 0x4c, 0xb4, 0x0f, 0x4f,
|
|
}
|
|
)
|
|
|
|
type backupTaskTest struct {
|
|
name string
|
|
chanID lnwire.ChannelID
|
|
breachInfo *lnwallet.BreachRetribution
|
|
expToLocalInput input.Input
|
|
expToRemoteInput input.Input
|
|
expTotalAmt btcutil.Amount
|
|
expSweepAmt int64
|
|
expRewardAmt int64
|
|
expRewardScript []byte
|
|
session *wtdb.ClientSessionBody
|
|
bindErr error
|
|
expSweepScript []byte
|
|
signer input.Signer
|
|
chanType channeldb.ChannelType
|
|
commitType blob.CommitmentType
|
|
}
|
|
|
|
// genTaskTest creates a instance of a backupTaskTest using the passed
|
|
// parameters. This method handles generating a breach transaction and its
|
|
// corresponding BreachInfo, as well as setting the wtpolicy.Policy of the given
|
|
// session.
|
|
func genTaskTest(
|
|
t *testing.T,
|
|
name string,
|
|
stateNum uint64,
|
|
toLocalAmt int64,
|
|
toRemoteAmt int64,
|
|
blobType blob.Type,
|
|
sweepFeeRate chainfee.SatPerKWeight,
|
|
rewardScript []byte,
|
|
expSweepAmt int64,
|
|
expRewardAmt int64,
|
|
bindErr error,
|
|
chanType channeldb.ChannelType) backupTaskTest {
|
|
|
|
// Set the anchor or taproot flag in the blob type if the session needs
|
|
// to support anchor or taproot channels.
|
|
if chanType.IsTaproot() {
|
|
blobType |= blob.Type(blob.FlagTaprootChannel)
|
|
} else if chanType.HasAnchors() {
|
|
blobType |= blob.Type(blob.FlagAnchorChannel)
|
|
}
|
|
|
|
// 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)
|
|
|
|
commitType, err := blobType.CommitmentType(&chanType)
|
|
require.NoError(t, err)
|
|
|
|
// Create the signer, and add the revocation and to-remote privkeys.
|
|
signer := wtmock.NewMockSigner()
|
|
var (
|
|
revKeyLoc = signer.AddPrivKey(revSK)
|
|
toRemoteKeyLoc = signer.AddPrivKey(toRemoteSK)
|
|
)
|
|
|
|
// First, we'll initialize a new breach transaction and the
|
|
// corresponding breach retribution. The retribution stores a pointer to
|
|
// the breach transaction, which we will continue to modify.
|
|
breachTxn := wire.NewMsgTx(2)
|
|
breachInfo := &lnwallet.BreachRetribution{
|
|
RevokedStateNum: stateNum,
|
|
BreachTxHash: breachTxn.TxHash(),
|
|
KeyRing: &lnwallet.CommitmentKeyRing{
|
|
RevocationKey: revPK,
|
|
ToLocalKey: toLocalPK,
|
|
ToRemoteKey: toRemotePK,
|
|
},
|
|
RemoteDelay: csvDelay,
|
|
}
|
|
|
|
// Add the sign descriptors and outputs corresponding to the to-local
|
|
// and to-remote outputs, respectively, if either input amount is
|
|
// non-zero. Note that the naming here seems reversed, but both are
|
|
// correct. For example, the to-remote output on the remote party's
|
|
// commitment is an output that pays to us. Hence the retribution refers
|
|
// to that output as local, though relative to their commitment, it is
|
|
// paying to-the-remote party (which is us).
|
|
if toLocalAmt > 0 {
|
|
var toLocalSignDesc *input.SignDescriptor
|
|
|
|
if chanType.IsTaproot() {
|
|
scriptTree, _ := input.NewLocalCommitScriptTree(
|
|
csvDelay, toLocalPK, revPK,
|
|
)
|
|
|
|
pkScript, _ := input.PayToTaprootScript(
|
|
scriptTree.TaprootKey,
|
|
)
|
|
|
|
revokeTapleafHash := txscript.NewBaseTapLeaf(
|
|
scriptTree.RevocationLeaf.Script,
|
|
).TapHash()
|
|
|
|
tapTree := scriptTree.TapscriptTree
|
|
revokeIdx := tapTree.LeafProofIndex[revokeTapleafHash]
|
|
revokeMerkleProof := tapTree.LeafMerkleProofs[revokeIdx]
|
|
revokeControlBlock := revokeMerkleProof.ToControlBlock(
|
|
&input.TaprootNUMSKey,
|
|
)
|
|
ctrlBytes, _ := revokeControlBlock.ToBytes()
|
|
|
|
toLocalSignDesc = &input.SignDescriptor{
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
KeyLocator: revKeyLoc,
|
|
PubKey: revPK,
|
|
},
|
|
Output: &wire.TxOut{
|
|
Value: toLocalAmt,
|
|
PkScript: pkScript,
|
|
},
|
|
WitnessScript: scriptTree.RevocationLeaf.Script,
|
|
SignMethod: input.TaprootScriptSpendSignMethod, //nolint:lll
|
|
HashType: txscript.SigHashDefault,
|
|
ControlBlock: ctrlBytes,
|
|
}
|
|
} else {
|
|
toLocalSignDesc = &input.SignDescriptor{
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
KeyLocator: revKeyLoc,
|
|
PubKey: revPK,
|
|
},
|
|
Output: &wire.TxOut{
|
|
Value: toLocalAmt,
|
|
},
|
|
HashType: txscript.SigHashAll,
|
|
}
|
|
}
|
|
|
|
breachInfo.RemoteOutputSignDesc = toLocalSignDesc
|
|
breachTxn.AddTxOut(toLocalSignDesc.Output)
|
|
}
|
|
if toRemoteAmt > 0 {
|
|
var toRemoteSignDesc *input.SignDescriptor
|
|
|
|
if chanType.IsTaproot() {
|
|
scriptTree, _ := input.NewRemoteCommitScriptTree(
|
|
toRemotePK,
|
|
)
|
|
|
|
pkScript, _ := input.PayToTaprootScript(
|
|
scriptTree.TaprootKey,
|
|
)
|
|
|
|
revokeTapleafHash := txscript.NewBaseTapLeaf(
|
|
scriptTree.SettleLeaf.Script,
|
|
).TapHash()
|
|
|
|
tapTree := scriptTree.TapscriptTree
|
|
revokeIdx := tapTree.LeafProofIndex[revokeTapleafHash]
|
|
revokeMerkleProof := tapTree.LeafMerkleProofs[revokeIdx]
|
|
revokeControlBlock := revokeMerkleProof.ToControlBlock(
|
|
&input.TaprootNUMSKey,
|
|
)
|
|
|
|
ctrlBytes, _ := revokeControlBlock.ToBytes()
|
|
|
|
toRemoteSignDesc = &input.SignDescriptor{
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
KeyLocator: toRemoteKeyLoc,
|
|
PubKey: toRemotePK,
|
|
},
|
|
WitnessScript: scriptTree.SettleLeaf.Script,
|
|
SignMethod: input.TaprootScriptSpendSignMethod, //nolint:lll
|
|
Output: &wire.TxOut{
|
|
Value: toRemoteAmt,
|
|
PkScript: pkScript,
|
|
},
|
|
HashType: txscript.SigHashDefault,
|
|
InputIndex: 1,
|
|
ControlBlock: ctrlBytes,
|
|
}
|
|
} else {
|
|
toRemoteSignDesc = &input.SignDescriptor{
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
KeyLocator: toRemoteKeyLoc,
|
|
PubKey: toRemotePK,
|
|
},
|
|
Output: &wire.TxOut{
|
|
Value: toRemoteAmt,
|
|
},
|
|
HashType: txscript.SigHashAll,
|
|
}
|
|
}
|
|
|
|
breachInfo.LocalOutputSignDesc = toRemoteSignDesc
|
|
breachTxn.AddTxOut(toRemoteSignDesc.Output)
|
|
}
|
|
|
|
var (
|
|
toLocalInput input.Input
|
|
toRemoteInput input.Input
|
|
)
|
|
|
|
// Now that the breach transaction has all its outputs, we can compute
|
|
// its txid and inputs spending from it. We also generate the
|
|
// input.Inputs that should be derived by the backup task.
|
|
txid := breachTxn.TxHash()
|
|
var index uint32
|
|
if toLocalAmt > 0 {
|
|
breachInfo.RemoteOutpoint = wire.OutPoint{
|
|
Hash: txid,
|
|
Index: index,
|
|
}
|
|
toLocalInput, err = commitType.ToLocalInput(breachInfo)
|
|
require.NoError(t, err)
|
|
|
|
index++
|
|
}
|
|
if toRemoteAmt > 0 {
|
|
breachInfo.LocalOutpoint = wire.OutPoint{
|
|
Hash: txid,
|
|
Index: index,
|
|
}
|
|
|
|
toRemoteInput, err = commitType.ToRemoteInput(breachInfo)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
return backupTaskTest{
|
|
name: name,
|
|
breachInfo: breachInfo,
|
|
expToLocalInput: toLocalInput,
|
|
expToRemoteInput: toRemoteInput,
|
|
expTotalAmt: btcutil.Amount(toLocalAmt + toRemoteAmt),
|
|
expSweepAmt: expSweepAmt,
|
|
expRewardAmt: expRewardAmt,
|
|
expRewardScript: rewardScript,
|
|
session: &wtdb.ClientSessionBody{
|
|
Policy: wtpolicy.Policy{
|
|
TxPolicy: wtpolicy.TxPolicy{
|
|
BlobType: blobType,
|
|
SweepFeeRate: sweepFeeRate,
|
|
RewardRate: 10000,
|
|
},
|
|
},
|
|
RewardPkScript: rewardScript,
|
|
},
|
|
bindErr: bindErr,
|
|
expSweepScript: sweepAddr,
|
|
signer: signer,
|
|
chanType: chanType,
|
|
commitType: commitType,
|
|
}
|
|
}
|
|
|
|
var (
|
|
blobTypeCommitNoReward = blob.FlagCommitOutputs.Type()
|
|
|
|
blobTypeCommitReward = (blob.FlagCommitOutputs | blob.FlagReward).Type()
|
|
|
|
addr, _ = btcutil.DecodeAddress(
|
|
"tb1pw8gzj8clt3v5lxykpgacpju5n8xteskt7gxhmudu6pa70nwfhe6s3unsyk",
|
|
&chaincfg.TestNet3Params,
|
|
)
|
|
|
|
addrScript, _ = txscript.PayToAddrScript(addr)
|
|
|
|
sweepAddrScript, _ = btcutil.DecodeAddress(
|
|
"tb1qs3jyc9sf5kak3x0w99cav9u605aeu3t600xxx0",
|
|
&chaincfg.TestNet3Params,
|
|
)
|
|
|
|
sweepAddr, _ = txscript.PayToAddrScript(sweepAddrScript)
|
|
)
|
|
|
|
// TestBackupTaskBind tests the initialization and binding of a backupTask to a
|
|
// ClientSession. After a successful bind, all parameters of the justice
|
|
// transaction should be solidified, so we assert there correctness. In an
|
|
// unsuccessful bind, the session-dependent parameters should be unmodified so
|
|
// that the backup task can be rescheduled if necessary. Finally, we assert that
|
|
// the backup task is able to encrypt a valid justice kit, and that we can
|
|
// decrypt it using the breach txid.
|
|
func TestBackupTask(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
chanTypes := []channeldb.ChannelType{
|
|
channeldb.SingleFunderBit,
|
|
channeldb.SingleFunderTweaklessBit,
|
|
channeldb.AnchorOutputsBit,
|
|
channeldb.SimpleTaprootFeatureBit,
|
|
}
|
|
|
|
var backupTaskTests []backupTaskTest
|
|
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 = 296069
|
|
expSweepCommitRewardLocal int64 = 197342
|
|
expSweepCommitRewardRemote int64 = 98389
|
|
sweepFeeRateNoRewardRemoteDust chainfee.SatPerKWeight = 227500
|
|
sweepFeeRateRewardRemoteDust chainfee.SatPerKWeight = 175350
|
|
)
|
|
if chanType.IsTaproot() {
|
|
expSweepCommitNoRewardBoth = 299165
|
|
expSweepCommitNoRewardLocal = 199468
|
|
expSweepCommitNoRewardRemote = 99531
|
|
sweepFeeRateNoRewardRemoteDust = 213200
|
|
expSweepCommitRewardBoth = 295993
|
|
expSweepCommitRewardLocal = 197296
|
|
expSweepCommitRewardRemote = 98359
|
|
sweepFeeRateRewardRemoteDust = 167000
|
|
} else if chanType.HasAnchors() {
|
|
expSweepCommitNoRewardBoth = 299236
|
|
expSweepCommitNoRewardLocal = 199513
|
|
expSweepCommitNoRewardRemote = 99557
|
|
expSweepCommitRewardBoth = 296064
|
|
expSweepCommitRewardLocal = 197341
|
|
expSweepCommitRewardRemote = 98385
|
|
sweepFeeRateNoRewardRemoteDust = 225400
|
|
sweepFeeRateRewardRemoteDust = 174100
|
|
}
|
|
|
|
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
|
genTaskTest(
|
|
t,
|
|
"commit no-reward, both outputs",
|
|
100, // stateNum
|
|
200000, // toLocalAmt
|
|
100000, // toRemoteAmt
|
|
blobTypeCommitNoReward, // blobType
|
|
1000, // sweepFeeRate
|
|
nil, // rewardScript
|
|
expSweepCommitNoRewardBoth, // expSweepAmt
|
|
0, // expRewardAmt
|
|
nil, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit no-reward, to-local output only",
|
|
1000, // stateNum
|
|
200000, // toLocalAmt
|
|
0, // toRemoteAmt
|
|
blobTypeCommitNoReward, // blobType
|
|
1000, // sweepFeeRate
|
|
nil, // rewardScript
|
|
expSweepCommitNoRewardLocal, // expSweepAmt
|
|
0, // expRewardAmt
|
|
nil, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit no-reward, to-remote output only",
|
|
1, // stateNum
|
|
0, // toLocalAmt
|
|
100000, // toRemoteAmt
|
|
blobTypeCommitNoReward, // blobType
|
|
1000, // sweepFeeRate
|
|
nil, // rewardScript
|
|
expSweepCommitNoRewardRemote, // expSweepAmt
|
|
0, // expRewardAmt
|
|
nil, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit no-reward, to-remote output only, creates dust",
|
|
1, // stateNum
|
|
0, // toLocalAmt
|
|
100000, // toRemoteAmt
|
|
blobTypeCommitNoReward, // blobType
|
|
sweepFeeRateNoRewardRemoteDust, // sweepFeeRate
|
|
nil, // rewardScript
|
|
0, // expSweepAmt
|
|
0, // expRewardAmt
|
|
wtpolicy.ErrCreatesDust, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit no-reward, no outputs, fee rate exceeds inputs",
|
|
300, // stateNum
|
|
0, // toLocalAmt
|
|
0, // toRemoteAmt
|
|
blobTypeCommitNoReward, // blobType
|
|
1000, // sweepFeeRate
|
|
nil, // rewardScript
|
|
0, // expSweepAmt
|
|
0, // expRewardAmt
|
|
wtpolicy.ErrFeeExceedsInputs, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit no-reward, no outputs, fee rate of 0 creates dust",
|
|
300, // stateNum
|
|
0, // toLocalAmt
|
|
0, // toRemoteAmt
|
|
blobTypeCommitNoReward, // blobType
|
|
0, // sweepFeeRate
|
|
nil, // rewardScript
|
|
0, // expSweepAmt
|
|
0, // expRewardAmt
|
|
wtpolicy.ErrCreatesDust, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit reward, both outputs",
|
|
100, // stateNum
|
|
200000, // toLocalAmt
|
|
100000, // toRemoteAmt
|
|
blobTypeCommitReward, // blobType
|
|
1000, // sweepFeeRate
|
|
addrScript, // rewardScript
|
|
expSweepCommitRewardBoth, // expSweepAmt
|
|
3000, // expRewardAmt
|
|
nil, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit reward, to-local output only",
|
|
1000, // stateNum
|
|
200000, // toLocalAmt
|
|
0, // toRemoteAmt
|
|
blobTypeCommitReward, // blobType
|
|
1000, // sweepFeeRate
|
|
addrScript, // rewardScript
|
|
expSweepCommitRewardLocal, // expSweepAmt
|
|
2000, // expRewardAmt
|
|
nil, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit reward, to-remote output only",
|
|
1, // stateNum
|
|
0, // toLocalAmt
|
|
100000, // toRemoteAmt
|
|
blobTypeCommitReward, // blobType
|
|
1000, // sweepFeeRate
|
|
addrScript, // rewardScript
|
|
expSweepCommitRewardRemote, // expSweepAmt
|
|
1000, // expRewardAmt
|
|
nil, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit reward, to-remote output only, creates dust",
|
|
1, // stateNum
|
|
0, // toLocalAmt
|
|
108221, // toRemoteAmt
|
|
blobTypeCommitReward, // blobType
|
|
sweepFeeRateRewardRemoteDust, // sweepFeeRate
|
|
addrScript, // rewardScript
|
|
0, // expSweepAmt
|
|
0, // expRewardAmt
|
|
wtpolicy.ErrCreatesDust, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit reward, no outputs, fee rate exceeds inputs",
|
|
300, // stateNum
|
|
0, // toLocalAmt
|
|
0, // toRemoteAmt
|
|
blobTypeCommitReward, // blobType
|
|
1000, // sweepFeeRate
|
|
addrScript, // rewardScript
|
|
0, // expSweepAmt
|
|
0, // expRewardAmt
|
|
wtpolicy.ErrFeeExceedsInputs, // bindErr
|
|
chanType,
|
|
),
|
|
genTaskTest(
|
|
t,
|
|
"commit reward, no outputs, fee rate of 0 creates dust",
|
|
300, // stateNum
|
|
0, // toLocalAmt
|
|
0, // toRemoteAmt
|
|
blobTypeCommitReward, // blobType
|
|
0, // sweepFeeRate
|
|
addrScript, // rewardScript
|
|
0, // expSweepAmt
|
|
0, // expRewardAmt
|
|
wtpolicy.ErrCreatesDust, // bindErr
|
|
chanType,
|
|
),
|
|
}...)
|
|
}
|
|
|
|
for _, test := range backupTaskTests {
|
|
test := test
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testBackupTask(t, test)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testBackupTask(t *testing.T, test backupTaskTest) {
|
|
// Create a new backupTask from the channel id and breach info.
|
|
id := wtdb.BackupID{
|
|
ChanID: test.chanID,
|
|
CommitHeight: test.breachInfo.RevokedStateNum,
|
|
}
|
|
task := newBackupTask(id, test.expSweepScript)
|
|
|
|
// getBreachInfo is a helper closure that returns the breach retribution
|
|
// info and channel type for the given channel and commit height.
|
|
getBreachInfo := func(id lnwire.ChannelID, commitHeight uint64) (
|
|
*lnwallet.BreachRetribution, channeldb.ChannelType, error) {
|
|
|
|
return test.breachInfo, test.chanType, nil
|
|
}
|
|
|
|
// Reconstruct the expected input.Inputs that will be returned by the
|
|
// task's inputs() method.
|
|
expInputs := make(map[wire.OutPoint]input.Input)
|
|
if task.toLocalInput != nil {
|
|
expInputs[task.toLocalInput.OutPoint()] = task.toLocalInput
|
|
}
|
|
if task.toRemoteInput != nil {
|
|
expInputs[task.toRemoteInput.OutPoint()] = task.toRemoteInput
|
|
}
|
|
|
|
// Assert that the inputs method returns the correct slice of
|
|
// input.Inputs.
|
|
inputs := task.inputs()
|
|
require.Equal(t, expInputs, inputs)
|
|
|
|
// Now, bind the session to the task. If successful, this locks in the
|
|
// session's negotiated parameters and allows the backup task to derive
|
|
// the final free variables in the justice transaction.
|
|
err := task.bindSession(test.session, getBreachInfo)
|
|
require.ErrorIs(t, err, test.bindErr)
|
|
|
|
// Assert that all parameters set during after binding the backup task
|
|
// are properly populated.
|
|
require.Equal(t, test.chanID, task.id.ChanID)
|
|
require.Equal(t, test.breachInfo.RevokedStateNum, task.id.CommitHeight)
|
|
require.Equal(t, test.expTotalAmt, task.totalAmt)
|
|
require.Equal(t, test.breachInfo, task.breachInfo)
|
|
require.Equal(t, test.expToLocalInput, task.toLocalInput)
|
|
require.Equal(t, test.expToRemoteInput, task.toRemoteInput)
|
|
|
|
// Exit early if the bind was supposed to fail. But first, we check that
|
|
// all fields set during a bind are still unset. This ensure that a
|
|
// failed bind doesn't have side-effects if the task is retried with a
|
|
// different session.
|
|
if test.bindErr != nil {
|
|
require.Zerof(t, task.blobType, "blob type should not be set "+
|
|
"on failed bind, found: %s", task.blobType)
|
|
|
|
require.Nilf(t, task.outputs, "justice outputs should not be "+
|
|
" set on failed bind, found: %v", task.outputs)
|
|
|
|
return
|
|
}
|
|
|
|
// Otherwise, the binding succeeded. Assert that all values set during
|
|
// the bind are properly populated.
|
|
policy := test.session.Policy
|
|
require.Equal(t, policy.BlobType, task.blobType)
|
|
|
|
// Compute the expected outputs on the justice transaction.
|
|
var expOutputs = []*wire.TxOut{
|
|
{
|
|
PkScript: test.expSweepScript,
|
|
Value: test.expSweepAmt,
|
|
},
|
|
}
|
|
|
|
// If the policy specifies a reward output, add it to the expected list
|
|
// of outputs.
|
|
if test.session.Policy.BlobType.Has(blob.FlagReward) {
|
|
expOutputs = append(expOutputs, &wire.TxOut{
|
|
PkScript: test.expRewardScript,
|
|
Value: test.expRewardAmt,
|
|
})
|
|
}
|
|
|
|
// Assert that the computed outputs match our expected outputs.
|
|
require.Equal(t, expOutputs, task.outputs)
|
|
|
|
// Now, we'll construct, sign, and encrypt the blob containing the parts
|
|
// needed to reconstruct the justice transaction.
|
|
hint, encBlob, err := task.craftSessionPayload(test.signer)
|
|
require.NoError(t, err, "unable to craft session payload")
|
|
|
|
// Verify that the breach hint matches the breach txid's prefix.
|
|
breachTxID := test.breachInfo.BreachTxHash
|
|
expHint := blob.NewBreachHintFromHash(&breachTxID)
|
|
require.Equal(t, expHint, hint)
|
|
|
|
// Decrypt the return blob to obtain the JusticeKit containing its
|
|
// contents.
|
|
key := blob.NewBreachKeyFromHash(&breachTxID)
|
|
jKit, err := blob.Decrypt(key, encBlob, policy.BlobType)
|
|
require.NoError(t, err, "unable to decrypt blob")
|
|
|
|
keyRing := test.breachInfo.KeyRing
|
|
expToLocalPK := keyRing.ToLocalKey
|
|
expRevPK := keyRing.RevocationKey
|
|
expToRemotePK := keyRing.ToRemoteKey
|
|
|
|
breachInfo := &lnwallet.BreachRetribution{
|
|
RemoteDelay: csvDelay,
|
|
KeyRing: &lnwallet.CommitmentKeyRing{
|
|
ToLocalKey: expToLocalPK,
|
|
RevocationKey: expRevPK,
|
|
ToRemoteKey: expToRemotePK,
|
|
},
|
|
}
|
|
|
|
expectedKit, err := test.commitType.NewJusticeKit(
|
|
test.expSweepScript, breachInfo, test.expToRemoteInput != nil,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
jKit.AddToLocalSig(zeroSig)
|
|
jKit.AddToRemoteSig(zeroSig)
|
|
|
|
require.Equal(t, expectedKit, jKit)
|
|
}
|
|
|
|
func makeSig(i int) lnwire.Sig {
|
|
var sigBytes [64]byte
|
|
binary.BigEndian.PutUint64(sigBytes[:8], uint64(i))
|
|
|
|
sig, _ := lnwire.NewSigFromWireECDSA(sigBytes[:])
|
|
|
|
return sig
|
|
}
|