github.com/decred/dcrlnd@v0.7.6/channeldb/migration24/migration_test.go (about)

     1  package migration24
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math/rand"
    10  	"testing"
    11  
    12  	"github.com/decred/dcrd/chaincfg/chainhash"
    13  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    14  	"github.com/decred/dcrd/dcrutil/v4"
    15  	"github.com/decred/dcrd/wire"
    16  	lnwire "github.com/decred/dcrlnd/channeldb/migration/lnwire21"
    17  	mig "github.com/decred/dcrlnd/channeldb/migration_01_to_11"
    18  	"github.com/decred/dcrlnd/channeldb/migtest"
    19  	"github.com/decred/dcrlnd/kvdb"
    20  )
    21  
    22  var (
    23  	key = [chainhash.HashSize]byte{
    24  		0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
    25  		0x68, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
    26  		0xd, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
    27  		0x1e, 0xb, 0x4c, 0xf9, 0x9e, 0xc5, 0x8c, 0xe9,
    28  	}
    29  	testTx = &wire.MsgTx{
    30  		Version: 1,
    31  		TxIn: []*wire.TxIn{
    32  			{
    33  				PreviousOutPoint: wire.OutPoint{
    34  					Hash:  chainhash.Hash{},
    35  					Index: 0xffffffff,
    36  				},
    37  				SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00,
    38  					0x1b, 0x01, 0x62},
    39  				Sequence: 0xffffffff,
    40  			},
    41  		},
    42  		TxOut: []*wire.TxOut{
    43  			{
    44  				Value: 5000000000,
    45  				PkScript: []byte{
    46  					0x41, // OP_DATA_65
    47  					0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e,
    48  					0xb1, 0xc5, 0xfe, 0x29, 0x5a, 0xbd,
    49  					0xeb, 0x1d, 0xca, 0x42, 0x81, 0xbe,
    50  					0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
    51  					0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2,
    52  					0x86, 0x24, 0xe1, 0x81, 0x75, 0xe8,
    53  					0x51, 0xc9, 0x6b, 0x97, 0x3d, 0x81,
    54  					0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
    55  					0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed,
    56  					0xf6, 0x20, 0xd1, 0x84, 0x24, 0x1a,
    57  					0x6a, 0xed, 0x8b, 0x63,
    58  					0xa6, // 65-byte signature
    59  					0xac, // OP_CHECKSIG
    60  				},
    61  			},
    62  		},
    63  		LockTime: 5,
    64  	}
    65  	pubKey = secp256k1.PrivKeyFromBytes(key[:]).PubKey()
    66  )
    67  
    68  // TestMigrateFwdPkgCleanup asserts that the migration will delete all the
    69  // forwarding packages for close channels, and leave the rest untouched.
    70  func TestMigrateFwdPkgCleanup(t *testing.T) {
    71  	migrationTests := []struct {
    72  		name                string
    73  		beforeMigrationFunc func(kvdb.RwTx) error
    74  		afterMigrationFunc  func(kvdb.RwTx) error
    75  	}{
    76  		{
    77  			// No closed channels summeries in the db.
    78  			// This leaves the fwdpkg untouched.
    79  			name: "no closed channel summeries",
    80  			beforeMigrationFunc: genBeforeMigration(
    81  				false, []int{}, []int{1},
    82  			),
    83  			afterMigrationFunc: genAfterMigration(
    84  				[]int{}, []int{1},
    85  			),
    86  		},
    87  		{
    88  			// One closed summery found, and the forwarding package
    89  			// shares the same channel ID.
    90  			// This makes the fwdpkg removed.
    91  			name: "remove one fwdpkg",
    92  			beforeMigrationFunc: genBeforeMigration(
    93  				false, []int{1}, []int{1},
    94  			),
    95  			afterMigrationFunc: genAfterMigration(
    96  				[]int{1}, []int{},
    97  			),
    98  		},
    99  		{
   100  			// One closed summery with pending status found, and the
   101  			// forwarding package shares the same channel ID.
   102  			// This leaves the fwdpkg untouched.
   103  			name: "no action if closed status is pending",
   104  			beforeMigrationFunc: genBeforeMigration(
   105  				true, []int{1}, []int{1},
   106  			),
   107  			afterMigrationFunc: genAfterMigration(
   108  				[]int{}, []int{1},
   109  			),
   110  		},
   111  		{
   112  			// One closed summery found, while the forwarding
   113  			// package has a different channel ID.
   114  			// This leaves the fwdpkg untouched.
   115  			name: "no fwdpkg removed",
   116  			beforeMigrationFunc: genBeforeMigration(
   117  				false, []int{1}, []int{2},
   118  			),
   119  			afterMigrationFunc: genAfterMigration(
   120  				[]int{}, []int{2},
   121  			),
   122  		},
   123  		{
   124  			// Multiple closed summeries and fwdpkg nested buckets
   125  			// found. Only the matched fwdPkg nested buckets are
   126  			// removed.
   127  			name: "only matching fwdpkg removed",
   128  			beforeMigrationFunc: genBeforeMigration(false,
   129  				[]int{1, 2, 3},
   130  				[]int{1, 2, 4},
   131  			),
   132  			afterMigrationFunc: genAfterMigration(
   133  				[]int{1, 2},
   134  				[]int{4},
   135  			),
   136  		},
   137  	}
   138  
   139  	for _, tt := range migrationTests {
   140  		test := tt
   141  		t.Run(test.name, func(t *testing.T) {
   142  			migtest.ApplyMigration(
   143  				t,
   144  				test.beforeMigrationFunc,
   145  				test.afterMigrationFunc,
   146  				MigrateFwdPkgCleanup,
   147  				false,
   148  			)
   149  		})
   150  	}
   151  }
   152  
   153  func genBeforeMigration(isPending bool,
   154  	closeSummeryChanIDs, fwdPkgChanIDs []int) func(kvdb.RwTx) error {
   155  
   156  	return func(tx kvdb.RwTx) error {
   157  		// Create closed channel summeries
   158  		for _, id := range closeSummeryChanIDs {
   159  			chanID := lnwire.NewShortChanIDFromInt(uint64(id))
   160  			if err := createTestCloseChannelSummery(
   161  				tx, isPending, chanID,
   162  			); err != nil {
   163  				return err
   164  			}
   165  		}
   166  
   167  		// Create fwdPkg nested buckets
   168  		for _, id := range fwdPkgChanIDs {
   169  			chanID := lnwire.NewShortChanIDFromInt(uint64(id))
   170  			err := createTestFwdPkgBucket(tx, chanID)
   171  			if err != nil {
   172  				return err
   173  			}
   174  		}
   175  
   176  		return nil
   177  	}
   178  }
   179  
   180  func genAfterMigration(deleted, untouched []int) func(kvdb.RwTx) error {
   181  	return func(tx kvdb.RwTx) error {
   182  		fwdPkgBkt := tx.ReadBucket(fwdPackagesKey)
   183  		if fwdPkgBkt == nil {
   184  			return errors.New("unable to find bucket")
   185  		}
   186  
   187  		// Reading deleted buckets should return nil
   188  		for _, id := range deleted {
   189  			chanID := lnwire.NewShortChanIDFromInt(uint64(id))
   190  			sourceKey := makeLogKey(chanID.ToUint64())
   191  			sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:])
   192  			if sourceBkt != nil {
   193  				return fmt.Errorf(
   194  					"expected bucket to be deleted: %v",
   195  					id,
   196  				)
   197  			}
   198  		}
   199  
   200  		// Reading untouched buckets should return not nil
   201  		for _, id := range untouched {
   202  			chanID := lnwire.NewShortChanIDFromInt(uint64(id))
   203  			sourceKey := makeLogKey(chanID.ToUint64())
   204  			sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:])
   205  			if sourceBkt == nil {
   206  				return fmt.Errorf(
   207  					"expected bucket to not be deleted: %v",
   208  					id,
   209  				)
   210  			}
   211  		}
   212  
   213  		return nil
   214  	}
   215  }
   216  
   217  // createTestCloseChannelSummery creates a CloseChannelSummery for testing.
   218  func createTestCloseChannelSummery(tx kvdb.RwTx, isPending bool,
   219  	chanID lnwire.ShortChannelID) error {
   220  
   221  	closedChanBucket, err := tx.CreateTopLevelBucket(closedChannelBucket)
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	outputPoint := wire.OutPoint{Hash: key, Index: rand.Uint32()}
   227  
   228  	ccs := &mig.ChannelCloseSummary{
   229  		ChanPoint:      outputPoint,
   230  		ShortChanID:    chanID,
   231  		ChainHash:      key,
   232  		ClosingTXID:    testTx.TxHash(),
   233  		CloseHeight:    100,
   234  		RemotePub:      pubKey,
   235  		Capacity:       dcrutil.Amount(10000),
   236  		SettledBalance: dcrutil.Amount(50000),
   237  		CloseType:      mig.RemoteForceClose,
   238  		IsPending:      isPending,
   239  		// Optional fields
   240  		RemoteCurrentRevocation: pubKey,
   241  		RemoteNextRevocation:    pubKey,
   242  	}
   243  	var b bytes.Buffer
   244  	if err := serializeChannelCloseSummary(&b, ccs); err != nil {
   245  		return err
   246  	}
   247  
   248  	var chanPointBuf bytes.Buffer
   249  	if err := writeOutpoint(&chanPointBuf, &outputPoint); err != nil {
   250  		return err
   251  	}
   252  
   253  	return closedChanBucket.Put(chanPointBuf.Bytes(), b.Bytes())
   254  }
   255  
   256  func createTestFwdPkgBucket(tx kvdb.RwTx, chanID lnwire.ShortChannelID) error {
   257  	fwdPkgBkt, err := tx.CreateTopLevelBucket(fwdPackagesKey)
   258  	if err != nil {
   259  		return err
   260  	}
   261  
   262  	source := makeLogKey(chanID.ToUint64())
   263  	if _, err := fwdPkgBkt.CreateBucketIfNotExists(source[:]); err != nil {
   264  		return err
   265  	}
   266  
   267  	return nil
   268  }
   269  
   270  func serializeChannelCloseSummary(
   271  	w io.Writer, cs *mig.ChannelCloseSummary) error {
   272  
   273  	err := mig.WriteElements(
   274  		w,
   275  		cs.ChanPoint, cs.ShortChanID, cs.ChainHash, cs.ClosingTXID,
   276  		cs.CloseHeight, cs.RemotePub, cs.Capacity, cs.SettledBalance,
   277  		cs.TimeLockedBalance, cs.CloseType, cs.IsPending,
   278  	)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	return nil
   283  }
   284  
   285  // writeOutpoint writes an outpoint to the passed writer using the minimal
   286  // amount of bytes possible.
   287  func writeOutpoint(w io.Writer, o *wire.OutPoint) error {
   288  	if _, err := w.Write(o.Hash[:]); err != nil {
   289  		return err
   290  	}
   291  	if err := binary.Write(w, binary.BigEndian, o.Index); err != nil {
   292  		return err
   293  	}
   294  
   295  	return nil
   296  }