lnd/channeldb/migration24/migration_test.go
yyforyongyu 8b289e79f5
channeldb+migration: export commonly used methods
This commit exports several commonly used methods that can be used by
later migrations. It also adds a channel commit deserializer.
2022-04-20 01:18:12 +08:00

297 lines
7.4 KiB
Go

package migration24
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math/rand"
"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"
mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
"github.com/lightningnetwork/lnd/channeldb/migtest"
"github.com/lightningnetwork/lnd/kvdb"
)
var (
key = [chainhash.HashSize]byte{
0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
0x68, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
0xd, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
0x1e, 0xb, 0x4c, 0xf9, 0x9e, 0xc5, 0x8c, 0xe9,
}
testTx = &wire.MsgTx{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: chainhash.Hash{},
Index: 0xffffffff,
},
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00,
0x1b, 0x01, 0x62},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 5000000000,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e,
0xb1, 0xc5, 0xfe, 0x29, 0x5a, 0xbd,
0xeb, 0x1d, 0xca, 0x42, 0x81, 0xbe,
0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2,
0x86, 0x24, 0xe1, 0x81, 0x75, 0xe8,
0x51, 0xc9, 0x6b, 0x97, 0x3d, 0x81,
0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed,
0xf6, 0x20, 0xd1, 0x84, 0x24, 0x1a,
0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
},
LockTime: 5,
}
_, pubKey = btcec.PrivKeyFromBytes(key[:])
)
// TestMigrateFwdPkgCleanup asserts that the migration will delete all the
// forwarding packages for close channels, and leave the rest untouched.
func TestMigrateFwdPkgCleanup(t *testing.T) {
migrationTests := []struct {
name string
beforeMigrationFunc func(kvdb.RwTx) error
afterMigrationFunc func(kvdb.RwTx) error
}{
{
// No closed channels summeries in the db.
// This leaves the fwdpkg untouched.
name: "no closed channel summeries",
beforeMigrationFunc: genBeforeMigration(
false, []int{}, []int{1},
),
afterMigrationFunc: genAfterMigration(
[]int{}, []int{1},
),
},
{
// One closed summery found, and the forwarding package
// shares the same channel ID.
// This makes the fwdpkg removed.
name: "remove one fwdpkg",
beforeMigrationFunc: genBeforeMigration(
false, []int{1}, []int{1},
),
afterMigrationFunc: genAfterMigration(
[]int{1}, []int{},
),
},
{
// One closed summery with pending status found, and the
// forwarding package shares the same channel ID.
// This leaves the fwdpkg untouched.
name: "no action if closed status is pending",
beforeMigrationFunc: genBeforeMigration(
true, []int{1}, []int{1},
),
afterMigrationFunc: genAfterMigration(
[]int{}, []int{1},
),
},
{
// One closed summery found, while the forwarding
// package has a different channel ID.
// This leaves the fwdpkg untouched.
name: "no fwdpkg removed",
beforeMigrationFunc: genBeforeMigration(
false, []int{1}, []int{2},
),
afterMigrationFunc: genAfterMigration(
[]int{}, []int{2},
),
},
{
// Multiple closed summeries and fwdpkg nested buckets
// found. Only the matched fwdPkg nested buckets are
// removed.
name: "only matching fwdpkg removed",
beforeMigrationFunc: genBeforeMigration(false,
[]int{1, 2, 3},
[]int{1, 2, 4},
),
afterMigrationFunc: genAfterMigration(
[]int{1, 2},
[]int{4},
),
},
}
for _, tt := range migrationTests {
test := tt
t.Run(test.name, func(t *testing.T) {
migtest.ApplyMigration(
t,
test.beforeMigrationFunc,
test.afterMigrationFunc,
MigrateFwdPkgCleanup,
false,
)
})
}
}
func genBeforeMigration(isPending bool,
closeSummeryChanIDs, fwdPkgChanIDs []int) func(kvdb.RwTx) error {
return func(tx kvdb.RwTx) error {
// Create closed channel summeries
for _, id := range closeSummeryChanIDs {
chanID := lnwire.NewShortChanIDFromInt(uint64(id))
if err := createTestCloseChannelSummery(
tx, isPending, chanID,
); err != nil {
return err
}
}
// Create fwdPkg nested buckets
for _, id := range fwdPkgChanIDs {
chanID := lnwire.NewShortChanIDFromInt(uint64(id))
err := createTestFwdPkgBucket(tx, chanID)
if err != nil {
return err
}
}
return nil
}
}
func genAfterMigration(deleted, untouched []int) func(kvdb.RwTx) error {
return func(tx kvdb.RwTx) error {
fwdPkgBkt := tx.ReadBucket(fwdPackagesKey)
if fwdPkgBkt == nil {
return errors.New("unable to find bucket")
}
// Reading deleted buckets should return nil
for _, id := range deleted {
chanID := lnwire.NewShortChanIDFromInt(uint64(id))
sourceKey := MakeLogKey(chanID.ToUint64())
sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:])
if sourceBkt != nil {
return fmt.Errorf(
"expected bucket to be deleted: %v",
id,
)
}
}
// Reading untouched buckets should return not nil
for _, id := range untouched {
chanID := lnwire.NewShortChanIDFromInt(uint64(id))
sourceKey := MakeLogKey(chanID.ToUint64())
sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:])
if sourceBkt == nil {
return fmt.Errorf(
"expected bucket to not be deleted: %v",
id,
)
}
}
return nil
}
}
// createTestCloseChannelSummery creates a CloseChannelSummery for testing.
func createTestCloseChannelSummery(tx kvdb.RwTx, isPending bool,
chanID lnwire.ShortChannelID) error {
closedChanBucket, err := tx.CreateTopLevelBucket(closedChannelBucket)
if err != nil {
return err
}
outputPoint := wire.OutPoint{Hash: key, Index: rand.Uint32()}
ccs := &mig.ChannelCloseSummary{
ChanPoint: outputPoint,
ShortChanID: chanID,
ChainHash: key,
ClosingTXID: testTx.TxHash(),
CloseHeight: 100,
RemotePub: pubKey,
Capacity: btcutil.Amount(10000),
SettledBalance: btcutil.Amount(50000),
CloseType: mig.RemoteForceClose,
IsPending: isPending,
// Optional fields
RemoteCurrentRevocation: pubKey,
RemoteNextRevocation: pubKey,
}
var b bytes.Buffer
if err := serializeChannelCloseSummary(&b, ccs); err != nil {
return err
}
var chanPointBuf bytes.Buffer
if err := writeOutpoint(&chanPointBuf, &outputPoint); err != nil {
return err
}
return closedChanBucket.Put(chanPointBuf.Bytes(), b.Bytes())
}
func createTestFwdPkgBucket(tx kvdb.RwTx, chanID lnwire.ShortChannelID) error {
fwdPkgBkt, err := tx.CreateTopLevelBucket(fwdPackagesKey)
if err != nil {
return err
}
source := MakeLogKey(chanID.ToUint64())
if _, err := fwdPkgBkt.CreateBucketIfNotExists(source[:]); err != nil {
return err
}
return nil
}
func serializeChannelCloseSummary(
w io.Writer, cs *mig.ChannelCloseSummary) error {
err := mig.WriteElements(
w,
cs.ChanPoint, cs.ShortChanID, cs.ChainHash, cs.ClosingTXID,
cs.CloseHeight, cs.RemotePub, cs.Capacity, cs.SettledBalance,
cs.TimeLockedBalance, cs.CloseType, cs.IsPending,
)
if err != nil {
return err
}
return nil
}
// writeOutpoint writes an outpoint to the passed writer using the minimal
// amount of bytes possible.
func writeOutpoint(w io.Writer, o *wire.OutPoint) error {
if _, err := w.Write(o.Hash[:]); err != nil {
return err
}
if err := binary.Write(w, binary.BigEndian, o.Index); err != nil {
return err
}
return nil
}