lnd/channeldb/migration25/migration_test.go
yyforyongyu de2bcbf925
migration25: export methods to be used for following migrations
This commit exports several private methods to be used in later
migrations. It's safe to do so as no actual logic or migration scheme is
changed.
2022-05-19 03:34:13 +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())
}