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 }