lnd/channeldb/migration25/migration_test.go
2022-08-23 22:10:24 +08:00

355 lines
9.2 KiB
Go

package migration25
import (
"bytes"
"fmt"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
mig24 "github.com/lightningnetwork/lnd/channeldb/migration24"
mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
"github.com/lightningnetwork/lnd/channeldb/migtest"
"github.com/lightningnetwork/lnd/kvdb"
)
var (
// Create dummy values to be stored in db.
dummyPrivKey, _ = btcec.NewPrivateKey()
dummyPubKey = dummyPrivKey.PubKey()
dummySig = []byte{1, 2, 3}
dummyTx = &wire.MsgTx{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: chainhash.Hash{},
Index: 0xffffffff,
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 5000000000,
},
},
LockTime: 5,
}
dummyOp = wire.OutPoint{
Hash: chainhash.Hash{},
Index: 9,
}
dummyHTLC = mig.HTLC{
Signature: dummySig,
RHash: [32]byte{},
Amt: 100_000,
RefundTimeout: 731583,
OutputIndex: 1,
Incoming: true,
HtlcIndex: 1,
LogIndex: 1,
}
// ourAmt and theirAmt are the initial balances found in the local
// channel commitment at height 0.
ourAmt = lnwire.MilliSatoshi(500_000)
theirAmt = lnwire.MilliSatoshi(1000_000)
// ourAmtRevoke and theirAmtRevoke are the initial balances found in
// the revocation log at height 0.
//
// NOTE: they are made differently such that we can easily check the
// source when patching the balances.
ourAmtRevoke = lnwire.MilliSatoshi(501_000)
theirAmtRevoke = lnwire.MilliSatoshi(1001_000)
// remoteCommit0 is the channel commitment at commit height 0. This is
// also the revocation log we will use to patch the initial balances.
remoteCommit0 = mig.ChannelCommitment{
LocalBalance: ourAmtRevoke,
RemoteBalance: theirAmtRevoke,
CommitFee: btcutil.Amount(1),
FeePerKw: btcutil.Amount(1),
CommitTx: dummyTx,
CommitSig: dummySig,
Htlcs: []mig.HTLC{},
}
// localCommit0 is the channel commitment at commit height 0. This is
// the channel commitment we will use to patch the initial balances
// when there are no revocation logs.
localCommit0 = mig.ChannelCommitment{
LocalBalance: ourAmt,
RemoteBalance: theirAmt,
CommitFee: btcutil.Amount(1),
FeePerKw: btcutil.Amount(1),
CommitTx: dummyTx,
CommitSig: dummySig,
Htlcs: []mig.HTLC{},
}
// remoteCommit1 and localCommit1 are the channel commitment at commit
// height 1.
remoteCommit1 = mig.ChannelCommitment{
CommitHeight: 1,
LocalLogIndex: 1,
LocalHtlcIndex: 1,
RemoteLogIndex: 1,
RemoteHtlcIndex: 1,
LocalBalance: ourAmt - dummyHTLC.Amt,
RemoteBalance: theirAmt + dummyHTLC.Amt,
CommitFee: btcutil.Amount(1),
FeePerKw: btcutil.Amount(1),
CommitTx: dummyTx,
CommitSig: dummySig,
Htlcs: []mig.HTLC{dummyHTLC},
}
localCommit1 = mig.ChannelCommitment{
CommitHeight: 1,
LocalLogIndex: 1,
LocalHtlcIndex: 1,
RemoteLogIndex: 1,
RemoteHtlcIndex: 1,
LocalBalance: ourAmt - dummyHTLC.Amt,
RemoteBalance: theirAmt + dummyHTLC.Amt,
CommitFee: btcutil.Amount(1),
FeePerKw: btcutil.Amount(1),
CommitTx: dummyTx,
CommitSig: dummySig,
Htlcs: []mig.HTLC{dummyHTLC},
}
// openChannel0 is the OpenChannel at commit height 0. When this
// variable is used, we expect to patch the initial balances from its
// commitments.
openChannel0 = &OpenChannel{
OpenChannel: mig.OpenChannel{
IdentityPub: dummyPubKey,
FundingOutpoint: dummyOp,
LocalCommitment: localCommit0,
RemoteCommitment: remoteCommit0,
},
}
// openChannel1 is the OpenChannel at commit height 1. When this
// variable is used, we expect to patch the initial balances from the
// remote commitment at height 0.
openChannel1 = &OpenChannel{
OpenChannel: mig.OpenChannel{
IdentityPub: dummyPubKey,
FundingOutpoint: dummyOp,
LocalCommitment: localCommit1,
RemoteCommitment: remoteCommit1,
},
}
)
// TestMigrateInitialBalances checks that the proper initial balances are
// patched to the channel info.
func TestMigrateInitialBalances(t *testing.T) {
testCases := []struct {
name string
beforeMigrationFunc func(kvdb.RwTx) error
afterMigrationFunc func(kvdb.RwTx) error
shouldFail bool
}{
{
// Test that we patch the initial balances using the
// revocation log.
name: "patch balance from revoke log",
beforeMigrationFunc: genBeforeMigration(
openChannel1, &remoteCommit0,
),
afterMigrationFunc: genAfterMigration(
ourAmtRevoke, theirAmtRevoke, openChannel1,
),
},
{
// Test that we patch the initial balances using the
// channel's local commitment since at height 0,
// balances found in LocalCommitment reflect the
// initial balances.
name: "patch balance from local commit",
beforeMigrationFunc: genBeforeMigration(
openChannel0, nil,
),
afterMigrationFunc: genAfterMigration(
ourAmt, theirAmt, openChannel0,
),
},
{
// Test that we patch the initial balances using the
// channel's local commitment even when there is a
// revocation log available.
name: "patch balance from local commit only",
beforeMigrationFunc: genBeforeMigration(
openChannel0, &remoteCommit0,
),
afterMigrationFunc: genAfterMigration(
ourAmt, theirAmt, openChannel0,
),
},
{
// Test that when there is no revocation log the
// migration would fail.
name: "patch balance error on no revoke log",
beforeMigrationFunc: genBeforeMigration(
// Use nil to specify no revocation log will be
// created.
openChannel1, nil,
),
afterMigrationFunc: genAfterMigration(
// Use nil to specify skipping the
// afterMigrationFunc.
0, 0, nil,
),
shouldFail: true,
},
{
// Test that when the saved revocation log is not what
// we want the migration would fail.
name: "patch balance error on wrong revoke log",
beforeMigrationFunc: genBeforeMigration(
// Use the revocation log with the wrong
// height.
openChannel1, &remoteCommit1,
),
afterMigrationFunc: genAfterMigration(
// Use nil to specify skipping the
// afterMigrationFunc.
0, 0, nil,
),
shouldFail: true,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
migtest.ApplyMigration(
t,
tc.beforeMigrationFunc,
tc.afterMigrationFunc,
MigrateInitialBalances,
tc.shouldFail,
)
})
}
}
func genBeforeMigration(c *OpenChannel,
commit *mig.ChannelCommitment) func(kvdb.RwTx) error {
return func(tx kvdb.RwTx) error {
if c.InitialLocalBalance != 0 {
return fmt.Errorf("non zero initial local amount")
}
if c.InitialRemoteBalance != 0 {
return fmt.Errorf("non zero initial local amount")
}
// Create the channel bucket.
chanBucket, err := CreateChanBucket(tx, c)
if err != nil {
return err
}
// Save the channel info using legacy format.
if err := putChanInfo(chanBucket, c, true); err != nil {
return err
}
// Save the channel commitments.
if err := PutChanCommitments(chanBucket, c); err != nil {
return err
}
// If we have a remote commitment, save it as our revocation
// log.
if commit != nil {
err := putChannelLogEntryLegacy(chanBucket, commit)
if err != nil {
return err
}
}
return nil
}
}
func genAfterMigration(ourAmt, theirAmt lnwire.MilliSatoshi,
c *OpenChannel) func(kvdb.RwTx) error {
return func(tx kvdb.RwTx) error {
// If the passed OpenChannel is nil, we will skip the
// afterMigrationFunc as it indicates an error is expected
// during the migration.
if c == nil {
return nil
}
chanBucket, err := FetchChanBucket(tx, c)
if err != nil {
return err
}
newChan := &OpenChannel{}
// Fetch the channel info using the new format.
err = fetchChanInfo(chanBucket, newChan, false)
if err != nil {
return err
}
// Check our initial amount is correct.
if newChan.InitialLocalBalance != ourAmt {
return fmt.Errorf("wrong local balance, got %d, "+
"want %d", newChan.InitialLocalBalance, ourAmt)
}
// Check their initial amount is correct.
if newChan.InitialRemoteBalance != theirAmt {
return fmt.Errorf("wrong remote balance, got %d, "+
"want %d", newChan.InitialRemoteBalance,
theirAmt)
}
// We also check the relevant channel info fields stay the
// same.
if !newChan.IdentityPub.IsEqual(c.IdentityPub) {
return fmt.Errorf("wrong IdentityPub")
}
if newChan.FundingOutpoint != c.FundingOutpoint {
return fmt.Errorf("wrong FundingOutpoint")
}
return nil
}
}
// putChannelLogEntryLegacy saves an old format revocation log to the bucket.
func putChannelLogEntryLegacy(chanBucket kvdb.RwBucket,
commit *mig.ChannelCommitment) error {
logBucket, err := chanBucket.CreateBucketIfNotExists(
revocationLogBucketLegacy,
)
if err != nil {
return err
}
var b bytes.Buffer
if err := mig.SerializeChanCommit(&b, commit); err != nil {
return err
}
logEntrykey := mig24.MakeLogKey(commit.CommitHeight)
return logBucket.Put(logEntrykey[:], b.Bytes())
}