github.com/decred/dcrlnd@v0.7.6/contractcourt/utxonursery_test.go (about) 1 //go:build !rpctest 2 // +build !rpctest 3 4 package contractcourt 5 6 import ( 7 "bytes" 8 "fmt" 9 "math" 10 "os" 11 "reflect" 12 "runtime/pprof" 13 "sync" 14 "testing" 15 "time" 16 17 "github.com/decred/dcrd/chaincfg/chainhash" 18 "github.com/decred/dcrd/dcrec/secp256k1/v4" 19 "github.com/decred/dcrd/dcrutil/v4" 20 "github.com/decred/dcrd/txscript/v4" 21 "github.com/decred/dcrd/wire" 22 "github.com/decred/dcrlnd/channeldb" 23 "github.com/decred/dcrlnd/input" 24 "github.com/decred/dcrlnd/lntest/mock" 25 "github.com/decred/dcrlnd/lnwallet" 26 "github.com/decred/dcrlnd/sweep" 27 ) 28 29 var ( 30 outPoints = []wire.OutPoint{ 31 { 32 Hash: [chainhash.HashSize]byte{ 33 0x51, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 34 0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 35 0x2d, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 36 0x1f, 0xb, 0x4c, 0xf9, 0x9e, 0xc5, 0x8c, 0xe9, 37 }, 38 Index: 9, 39 }, 40 { 41 Hash: [chainhash.HashSize]byte{ 42 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 43 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 44 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, 45 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 46 }, 47 Index: 49, 48 }, 49 { 50 Hash: [chainhash.HashSize]byte{ 51 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 52 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 53 0x0d, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 54 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, 55 }, 56 Index: 23, 57 }, 58 { 59 Hash: [chainhash.HashSize]byte{ 60 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, 61 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 62 0x0d, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 63 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 64 }, 65 Index: 30, 66 }, 67 { 68 Hash: [chainhash.HashSize]byte{ 69 0x0d, 0xe7, 0x95, 0xe4, 0xfc, 0xd2, 0xc6, 0xda, 70 0xb7, 0x25, 0xb8, 0x4d, 0x63, 0x59, 0xe6, 0x96, 71 0x31, 0x13, 0xa1, 0x17, 0x81, 0xb6, 0x37, 0xd8, 72 0x1e, 0x0b, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, 73 }, 74 Index: 2, 75 }, 76 { 77 Hash: [chainhash.HashSize]byte{ 78 0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 79 0x51, 0xb6, 0x37, 0xd8, 0x1f, 0x0b, 0x4c, 0xf9, 80 0x9e, 0xc5, 0x8c, 0xe9, 0xfc, 0xd2, 0xc6, 0xda, 81 0x2d, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 82 }, 83 Index: 9, 84 }, 85 } 86 87 keys = [][]byte{ 88 {0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 89 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 90 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 91 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 92 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 93 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 94 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 95 0xb4, 0x12, 0xa3, 96 }, 97 {0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 98 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 99 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 100 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 101 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 102 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 103 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 104 0xb4, 0x12, 0xa3, 105 }, 106 {0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 107 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, 108 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 109 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, 110 }, 111 } 112 113 signDescriptors = []input.SignDescriptor{ 114 { 115 SingleTweak: []byte{ 116 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 117 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 118 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 119 0x02, 0x02, 0x02, 0x02, 0x02, 120 }, 121 WitnessScript: []byte{ 122 0x00, 0x14, 0xee, 0x91, 0x41, 0x7e, 0x85, 0x6c, 0xde, 123 0x10, 0xa2, 0x91, 0x1e, 0xdc, 0xbd, 0xbd, 0x69, 0xe2, 124 0xef, 0xb5, 0x71, 0x48, 125 }, 126 Output: &wire.TxOut{ 127 Value: 5000000000, 128 PkScript: []byte{ 129 0x41, // OP_DATA_65 130 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, 131 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, 132 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, 133 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, 134 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 135 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 136 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 137 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 138 0xa6, // 65-byte signature 139 0xac, // OP_CHECKSIG 140 }, 141 }, 142 HashType: txscript.SigHashAll, 143 }, 144 { 145 SingleTweak: []byte{ 146 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 147 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 148 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 149 0x02, 0x02, 0x02, 0x02, 0x02, 150 }, 151 WitnessScript: []byte{ 152 0x00, 0x14, 0xee, 0x91, 0x41, 0x7e, 0x85, 0x6c, 0xde, 153 0x10, 0xa2, 0x91, 0x1e, 0xdc, 0xbd, 0xbd, 0x69, 0xe2, 154 0xef, 0xb5, 0x71, 0x48, 155 }, 156 Output: &wire.TxOut{ 157 Value: 5000000000, 158 PkScript: []byte{ 159 0x41, // OP_DATA_65 160 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, 161 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, 162 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, 163 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, 164 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 165 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 166 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 167 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 168 0xa6, // 65-byte signature 169 0xac, // OP_CHECKSIG 170 }, 171 }, 172 HashType: txscript.SigHashAll, 173 }, 174 { 175 SingleTweak: []byte{ 176 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 177 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 178 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 179 0x02, 0x02, 0x02, 0x02, 0x02, 180 }, 181 WitnessScript: []byte{ 182 0x00, 0x14, 0xee, 0x91, 0x41, 0x7e, 0x85, 0x6c, 0xde, 183 0x10, 0xa2, 0x91, 0x1e, 0xdc, 0xbd, 0xbd, 0x69, 0xe2, 184 0xef, 0xb5, 0x71, 0x48, 185 }, 186 Output: &wire.TxOut{ 187 Value: 5000000000, 188 PkScript: []byte{ 189 0x41, // OP_DATA_65 190 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, 191 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, 192 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, 193 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, 194 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 195 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 196 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 197 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 198 0xa6, // 65-byte signature 199 0xac, // OP_CHECKSIG 200 }, 201 }, 202 HashType: txscript.SigHashAll, 203 }, 204 } 205 206 kidOutputs = []kidOutput{ 207 { 208 breachedOutput: breachedOutput{ 209 amt: dcrutil.Amount(13e7), 210 outpoint: outPoints[1], 211 witnessType: input.CommitmentTimeLock, 212 confHeight: uint32(1000), 213 }, 214 originChanPoint: outPoints[0], 215 blocksToMaturity: uint32(42), 216 }, 217 218 { 219 breachedOutput: breachedOutput{ 220 amt: dcrutil.Amount(24e7), 221 outpoint: outPoints[2], 222 witnessType: input.CommitmentTimeLock, 223 confHeight: uint32(1000), 224 }, 225 originChanPoint: outPoints[0], 226 blocksToMaturity: uint32(42), 227 }, 228 229 { 230 breachedOutput: breachedOutput{ 231 amt: dcrutil.Amount(2e5), 232 outpoint: outPoints[3], 233 witnessType: input.CommitmentTimeLock, 234 confHeight: uint32(500), 235 }, 236 originChanPoint: outPoints[0], 237 blocksToMaturity: uint32(28), 238 }, 239 240 { 241 breachedOutput: breachedOutput{ 242 amt: dcrutil.Amount(10e6), 243 outpoint: outPoints[4], 244 witnessType: input.CommitmentTimeLock, 245 confHeight: uint32(500), 246 }, 247 originChanPoint: outPoints[0], 248 blocksToMaturity: uint32(28), 249 }, 250 } 251 252 babyOutputs = []babyOutput{ 253 { 254 kidOutput: kidOutputs[1], 255 expiry: 3829, 256 timeoutTx: timeoutTx, 257 }, 258 { 259 kidOutput: kidOutputs[2], 260 expiry: 4, 261 timeoutTx: timeoutTx, 262 }, 263 { 264 kidOutput: kidOutputs[3], 265 expiry: 4, 266 timeoutTx: timeoutTx, 267 }, 268 } 269 270 // Dummy timeout tx used to test serialization, borrowed from dcrd 271 // msgtx_test 272 timeoutTx = &wire.MsgTx{ 273 Version: 1, 274 TxIn: []*wire.TxIn{ 275 { 276 PreviousOutPoint: wire.OutPoint{ 277 Hash: chainhash.Hash{ 278 0xa5, 0x33, 0x52, 0xd5, 0x13, 0x57, 0x66, 0xf0, 279 0x30, 0x76, 0x59, 0x74, 0x18, 0x26, 0x3d, 0xa2, 280 0xd9, 0xc9, 0x58, 0x31, 0x59, 0x68, 0xfe, 0xa8, 281 0x23, 0x52, 0x94, 0x67, 0x48, 0x1f, 0xf9, 0xcd, 282 }, 283 Index: 19, 284 }, 285 SignatureScript: []byte{ 286 // OP_DATA_70 287 0x46, 288 289 // 70-byte signature 290 0x30, 0x43, 0x02, 0x1f, 0x4d, 0x23, 0x81, 0xdc, 291 0x97, 0xf1, 0x82, 0xab, 0xd8, 0x18, 0x5f, 0x51, 292 0x75, 0x30, 0x18, 0x52, 0x32, 0x12, 0xf5, 0xdd, 293 0xc0, 0x7c, 0xc4, 0xe6, 0x3a, 0x8d, 0xc0, 0x36, 294 0x58, 0xda, 0x19, 0x02, 0x20, 0x60, 0x8b, 0x5c, 295 0x4d, 0x92, 0xb8, 0x6b, 0x6d, 0xe7, 0xd7, 0x8e, 296 0xf2, 0x3a, 0x2f, 0xa7, 0x35, 0xbc, 0xb5, 0x9b, 297 0x91, 0x4a, 0x48, 0xb0, 0xe1, 0x87, 0xc5, 0xe7, 298 0x56, 0x9a, 0x18, 0x19, 0x70, 0x01, 299 300 // OP_DATA_33 301 0x21, 302 303 // 33-byte serialize pub key 304 0x03, 0x07, 0xea, 0xd0, 0x84, 0x80, 0x7e, 0xb7, 305 0x63, 0x46, 0xdf, 0x69, 0x77, 0x00, 0x0c, 0x89, 306 0x39, 0x2f, 0x45, 0xc7, 0x64, 0x25, 0xb2, 0x61, 307 0x81, 0xf5, 0x21, 0xd7, 0xf3, 0x70, 0x06, 0x6a, 308 0x8f, 309 }, 310 Sequence: 0xffffffff, 311 }, 312 }, 313 TxOut: []*wire.TxOut{ 314 { 315 Value: 395019, 316 PkScript: []byte{ // p2wkh output 317 0x00, // Version 0 witness program 318 0x14, // OP_DATA_20 319 0x9d, 0xda, 0xc6, 0xf3, 0x9d, 0x51, 0xe0, 0x39, 320 0x8e, 0x53, 0x2a, 0x22, 0xc4, 0x1b, 0xa1, 0x89, 321 0x40, 0x6a, 0x85, 0x23, // 20-byte pub key hash 322 }, 323 }, 324 }, 325 } 326 327 testChanPoint = wire.OutPoint{} 328 defaultTestTimeout = 5 * time.Second 329 ) 330 331 func init() { 332 // Finish initializing our test vectors by parsing the desired public keys and 333 // properly populating the sign descriptors of all baby and kid outputs. 334 for i := range signDescriptors { 335 pk, err := secp256k1.ParsePubKey(keys[i]) 336 if err != nil { 337 panic(fmt.Sprintf("unable to parse pub key during init: %v", err)) 338 } 339 signDescriptors[i].KeyDesc.PubKey = pk 340 341 } 342 for i := range kidOutputs { 343 isd := i % len(signDescriptors) 344 kidOutputs[i].signDesc = signDescriptors[isd] 345 } 346 347 for i := range babyOutputs { 348 isd := i % len(signDescriptors) 349 babyOutputs[i].kidOutput.signDesc = signDescriptors[isd] 350 } 351 352 initIncubateTests() 353 } 354 355 func TestKidOutputSerialization(t *testing.T) { 356 t.Parallel() 357 358 for i, kid := range kidOutputs { 359 var b bytes.Buffer 360 if err := kid.Encode(&b); err != nil { 361 t.Fatalf("Encode #%d: unable to serialize "+ 362 "kid output: %v", i, err) 363 } 364 365 var deserializedKid kidOutput 366 if err := deserializedKid.Decode(&b); err != nil { 367 t.Fatalf("Decode #%d: unable to deserialize "+ 368 "kid output: %v", i, err) 369 } 370 371 if !reflect.DeepEqual(kid, deserializedKid) { 372 t.Fatalf("DeepEqual #%d: unexpected kidOutput, "+ 373 "want %+v, got %+v", 374 i, kid, deserializedKid) 375 } 376 } 377 } 378 379 func TestBabyOutputSerialization(t *testing.T) { 380 t.Parallel() 381 382 for i, baby := range babyOutputs { 383 var b bytes.Buffer 384 if err := baby.Encode(&b); err != nil { 385 t.Fatalf("Encode #%d: unable to serialize "+ 386 "baby output: %v", i, err) 387 } 388 389 var deserializedBaby babyOutput 390 if err := deserializedBaby.Decode(&b); err != nil { 391 t.Fatalf("Decode #%d: unable to deserialize "+ 392 "baby output: %v", i, err) 393 } 394 395 if !reflect.DeepEqual(baby, deserializedBaby) { 396 t.Fatalf("DeepEqual #%d: unexpected babyOutput, "+ 397 "want %+v, got %+v", 398 i, baby, deserializedBaby) 399 } 400 401 } 402 } 403 404 type nurseryTestContext struct { 405 nursery *UtxoNursery 406 notifier *sweep.MockNotifier 407 chainIO *mock.ChainIO 408 publishChan chan wire.MsgTx 409 store *nurseryStoreInterceptor 410 restart func() bool 411 receiveTx func() wire.MsgTx 412 sweeper *mockSweeperFull 413 timeoutChan chan chan time.Time 414 t *testing.T 415 dbCleanup func() 416 } 417 418 func createNurseryTestContext(t *testing.T, 419 checkStartStop func(func()) bool) *nurseryTestContext { 420 421 // Create a temporary database and connect nurseryStore to it. The 422 // alternative, mocking nurseryStore, is not chosen because there is 423 // still considerable logic in the store. 424 425 cdb, cleanup, err := channeldb.MakeTestDB() 426 if err != nil { 427 t.Fatalf("unable to open channeldb: %v", err) 428 } 429 430 store, err := NewNurseryStore(&chainhash.Hash{}, cdb) 431 if err != nil { 432 t.Fatal(err) 433 } 434 435 // Wrap the store in an inceptor to be able to wait for events in this 436 // test. 437 storeIntercepter := newNurseryStoreInterceptor(store) 438 439 notifier := sweep.NewMockNotifier(t) 440 441 publishChan := make(chan wire.MsgTx, 1) 442 publishFunc := func(tx *wire.MsgTx, source string) error { 443 log.Tracef("Publishing tx %v by %v", tx.TxHash(), source) 444 publishChan <- *tx 445 return nil 446 } 447 448 timeoutChan := make(chan chan time.Time) 449 450 chainIO := &mock.ChainIO{ 451 BestHeight: 0, 452 } 453 454 sweeper := newMockSweeperFull(t) 455 456 nurseryCfg := NurseryConfig{ 457 Notifier: notifier, 458 FetchClosedChannels: func(pendingOnly bool) ( 459 []*channeldb.ChannelCloseSummary, error) { 460 return []*channeldb.ChannelCloseSummary{}, nil 461 }, 462 FetchClosedChannel: func(chanID *wire.OutPoint) ( 463 *channeldb.ChannelCloseSummary, error) { 464 return &channeldb.ChannelCloseSummary{ 465 CloseHeight: 0, 466 }, nil 467 }, 468 Store: storeIntercepter, 469 ChainIO: chainIO, 470 SweepInput: sweeper.sweepInput, 471 PublishTransaction: func(tx *wire.MsgTx, _ string) error { 472 return publishFunc(tx, "nursery") 473 }, 474 } 475 476 nursery := NewUtxoNursery(&nurseryCfg) 477 nursery.Start() 478 479 ctx := &nurseryTestContext{ 480 nursery: nursery, 481 notifier: notifier, 482 chainIO: chainIO, 483 store: storeIntercepter, 484 publishChan: publishChan, 485 sweeper: sweeper, 486 timeoutChan: timeoutChan, 487 t: t, 488 dbCleanup: cleanup, 489 } 490 491 ctx.receiveTx = func() wire.MsgTx { 492 var tx wire.MsgTx 493 select { 494 case tx = <-ctx.publishChan: 495 log.Debugf("Published tx %v", tx.TxHash()) 496 return tx 497 case <-time.After(defaultTestTimeout): 498 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 499 500 t.Fatalf("tx not published") 501 } 502 return tx 503 } 504 505 ctx.restart = func() bool { 506 return checkStartStop(func() { 507 log.Tracef("Restart sweeper and nursery") 508 // Simulate lnd restart. 509 ctx.nursery.Stop() 510 511 // Restart sweeper. 512 ctx.sweeper = newMockSweeperFull(t) 513 514 /// Restart nursery. 515 nurseryCfg.SweepInput = ctx.sweeper.sweepInput 516 ctx.nursery = NewUtxoNursery(&nurseryCfg) 517 ctx.nursery.Start() 518 519 }) 520 } 521 522 // Start with testing an immediate restart. 523 ctx.restart() 524 525 return ctx 526 } 527 528 func (ctx *nurseryTestContext) notifyEpoch(height int32) { 529 ctx.t.Helper() 530 531 ctx.chainIO.BestHeight = height 532 ctx.notifier.NotifyEpoch(height) 533 } 534 535 func (ctx *nurseryTestContext) finish() { 536 defer ctx.dbCleanup() 537 538 // Add a final restart point in this state 539 ctx.restart() 540 541 // We assume that when finish is called, nursery has finished all its 542 // goroutines. This implies that the waitgroup is empty. 543 signalChan := make(chan struct{}) 544 go func() { 545 ctx.nursery.wg.Wait() 546 close(signalChan) 547 }() 548 549 // The only goroutine that is still expected to be running is 550 // incubator(). Simulate exit of this goroutine. 551 ctx.nursery.wg.Done() 552 553 // We now expect the Wait to succeed. 554 select { 555 case <-signalChan: 556 case <-time.After(time.Second): 557 ctx.t.Fatalf("lingering goroutines detected after test " + 558 "is finished") 559 } 560 561 // Restore waitgroup state to what it was before. 562 ctx.nursery.wg.Add(1) 563 564 ctx.nursery.Stop() 565 566 // We should have consumed and asserted all published transactions in 567 // our unit tests. 568 select { 569 case <-ctx.publishChan: 570 ctx.t.Fatalf("unexpected transactions published") 571 default: 572 } 573 574 // Assert that the database is empty. All channels removed and height 575 // index cleared. 576 nurseryChannels, err := ctx.nursery.cfg.Store.ListChannels() 577 if err != nil { 578 ctx.t.Fatal(err) 579 } 580 if len(nurseryChannels) > 0 { 581 ctx.t.Fatalf("Expected all channels to be removed from store") 582 } 583 584 activeHeights, err := ctx.nursery.cfg.Store.HeightsBelowOrEqual( 585 math.MaxUint32) 586 if err != nil { 587 ctx.t.Fatal(err) 588 } 589 if len(activeHeights) > 0 { 590 ctx.t.Fatalf("Expected height index to be empty") 591 } 592 } 593 594 func createOutgoingRes(onLocalCommitment bool) *lnwallet.OutgoingHtlcResolution { 595 // Set up an outgoing htlc resolution to hand off to nursery. 596 closeTx := &wire.MsgTx{} 597 598 htlcOp := wire.OutPoint{ 599 Hash: closeTx.TxHash(), 600 Index: 0, 601 } 602 603 outgoingRes := lnwallet.OutgoingHtlcResolution{ 604 Expiry: 125, 605 SweepSignDesc: input.SignDescriptor{ 606 Output: &wire.TxOut{ 607 Value: 10000, 608 }, 609 }, 610 } 611 612 if onLocalCommitment { 613 timeoutTx := &wire.MsgTx{ 614 TxIn: []*wire.TxIn{ 615 { 616 PreviousOutPoint: htlcOp, 617 SignatureScript: []byte{}, 618 }, 619 }, 620 TxOut: []*wire.TxOut{ 621 {}, 622 }, 623 } 624 625 outgoingRes.SignedTimeoutTx = timeoutTx 626 outgoingRes.CsvDelay = 2 627 } else { 628 outgoingRes.ClaimOutpoint = htlcOp 629 outgoingRes.CsvDelay = 0 630 } 631 632 return &outgoingRes 633 } 634 635 func incubateTestOutput(t *testing.T, nursery *UtxoNursery, 636 onLocalCommitment bool) *lnwallet.OutgoingHtlcResolution { 637 638 outgoingRes := createOutgoingRes(onLocalCommitment) 639 640 // Hand off to nursery. 641 err := nursery.IncubateOutputs( 642 testChanPoint, 643 []lnwallet.OutgoingHtlcResolution{*outgoingRes}, 644 nil, 0, 645 ) 646 if err != nil { 647 t.Fatal(err) 648 } 649 650 // IncubateOutputs is executing synchronously and we expect the output 651 // to immediately show up in the report. 652 expectedStage := uint32(2) 653 if onLocalCommitment { 654 expectedStage = 1 655 } 656 assertNurseryReport(t, nursery, 1, expectedStage, 10000) 657 658 return outgoingRes 659 } 660 661 func assertNurseryReport(t *testing.T, nursery *UtxoNursery, 662 expectedNofHtlcs int, expectedStage uint32, 663 expectedLimboBalance dcrutil.Amount) { 664 report, err := nursery.NurseryReport(&testChanPoint) 665 if err != nil { 666 t.Fatal(err) 667 } 668 669 if len(report.Htlcs) != expectedNofHtlcs { 670 t.Fatalf("expected %v outputs to be reported, but report "+ 671 "only contains %v", expectedNofHtlcs, len(report.Htlcs)) 672 } 673 674 if expectedNofHtlcs != 0 { 675 htlcReport := report.Htlcs[0] 676 if htlcReport.Stage != expectedStage { 677 t.Fatalf("expected htlc be advanced to stage %v, but "+ 678 "it is reported in stage %v", 679 expectedStage, htlcReport.Stage) 680 } 681 } 682 683 if report.LimboBalance != expectedLimboBalance { 684 t.Fatalf("expected limbo balance to be %v, but it is %v instead", 685 expectedLimboBalance, report.LimboBalance) 686 } 687 } 688 689 func assertNurseryReportUnavailable(t *testing.T, nursery *UtxoNursery) { 690 _, err := nursery.NurseryReport(&testChanPoint) 691 if err != ErrContractNotFound { 692 t.Fatal("expected report to be unavailable") 693 } 694 } 695 696 // testRestartLoop runs the specified test multiple times and in every run it 697 // will attempt to execute a restart action in a different location. This is to 698 // assert that the unit under test is recovering correctly from restarts. 699 func testRestartLoop(t *testing.T, test func(*testing.T, 700 func(func()) bool)) { 701 702 // Start with running the test without any restarts (index zero) 703 restartIdx := 0 704 705 for { 706 currentStartStopIdx := 0 707 708 // checkStartStop is called at every point in the test where a 709 // restart should be exercised. When this function is called as 710 // many times as the current value of currentStartStopIdx, it 711 // will execute startStopFunc. 712 checkStartStop := func(startStopFunc func()) bool { 713 currentStartStopIdx++ 714 if restartIdx == currentStartStopIdx { 715 startStopFunc() 716 717 return true 718 } 719 log.Debugf("Skipping restart point %v", 720 currentStartStopIdx) 721 return false 722 } 723 724 var subTestName string 725 if restartIdx == 0 { 726 subTestName = "no_restart" 727 } else { 728 subTestName = fmt.Sprintf("restart_%v", restartIdx) 729 } 730 t.Run(subTestName, 731 func(t *testing.T) { 732 test(t, checkStartStop) 733 }) 734 735 // Exit the loop when all restart points have been tested. 736 if currentStartStopIdx == restartIdx { 737 return 738 } 739 restartIdx++ 740 } 741 } 742 743 func TestNurseryOutgoingHtlcSuccessOnLocal(t *testing.T) { 744 testRestartLoop(t, testNurseryOutgoingHtlcSuccessOnLocal) 745 } 746 747 func testNurseryOutgoingHtlcSuccessOnLocal(t *testing.T, 748 checkStartStop func(func()) bool) { 749 750 ctx := createNurseryTestContext(t, checkStartStop) 751 752 outgoingRes := incubateTestOutput(t, ctx.nursery, true) 753 754 ctx.restart() 755 756 // Notify arrival of block where HTLC CLTV expires. 757 ctx.notifyEpoch(125) 758 759 // This should trigger nursery to publish the timeout tx. 760 ctx.receiveTx() 761 762 if ctx.restart() { 763 // Restart should retrigger broadcast of timeout tx. 764 ctx.receiveTx() 765 } 766 767 // Confirm the timeout tx. This should promote the HTLC to KNDR state. 768 timeoutTxHash := outgoingRes.SignedTimeoutTx.TxHash() 769 if err := ctx.notifier.ConfirmTx(&timeoutTxHash, 126); err != nil { 770 t.Fatal(err) 771 } 772 773 // Wait for output to be promoted in store to KNDR. 774 select { 775 case <-ctx.store.cribToKinderChan: 776 case <-time.After(defaultTestTimeout): 777 t.Fatalf("output not promoted to KNDR") 778 } 779 780 ctx.restart() 781 782 // Notify arrival of block where second level HTLC unlocks. 783 ctx.notifyEpoch(128) 784 785 // Check final sweep into wallet. 786 testSweepHtlc(t, ctx) 787 788 ctx.finish() 789 } 790 791 func TestNurseryOutgoingHtlcSuccessOnRemote(t *testing.T) { 792 testRestartLoop(t, testNurseryOutgoingHtlcSuccessOnRemote) 793 } 794 795 func testNurseryOutgoingHtlcSuccessOnRemote(t *testing.T, 796 checkStartStop func(func()) bool) { 797 798 ctx := createNurseryTestContext(t, checkStartStop) 799 800 outgoingRes := incubateTestOutput(t, ctx.nursery, false) 801 802 ctx.restart() 803 804 // Notify confirmation of the commitment tx. Is only listened to when 805 // resolving remote commitment tx. 806 // 807 // TODO(joostjager): This is probably not correct? 808 err := ctx.notifier.ConfirmTx(&outgoingRes.ClaimOutpoint.Hash, 124) 809 if err != nil { 810 t.Fatal(err) 811 } 812 813 // Wait for output to be promoted from PSCL to KNDR. 814 select { 815 case <-ctx.store.preschoolToKinderChan: 816 case <-time.After(defaultTestTimeout): 817 t.Fatalf("output not promoted to KNDR") 818 } 819 820 ctx.restart() 821 822 // Notify arrival of block where HTLC CLTV expires. 823 ctx.notifyEpoch(125) 824 825 // Check final sweep into wallet. 826 testSweepHtlc(t, ctx) 827 828 ctx.finish() 829 } 830 831 func testSweepHtlc(t *testing.T, ctx *nurseryTestContext) { 832 testSweep(t, ctx, func() { 833 // Verify stage in nursery report. HTLCs should now both still 834 // be in stage two. 835 assertNurseryReport(t, ctx.nursery, 1, 2, 10000) 836 }) 837 } 838 839 func testSweep(t *testing.T, ctx *nurseryTestContext, 840 afterPublishAssert func()) { 841 842 // Wait for nursery to publish the sweep tx. 843 ctx.sweeper.expectSweep() 844 845 if ctx.restart() { 846 // Nursery reoffers its input after a restart. 847 ctx.sweeper.expectSweep() 848 } 849 850 afterPublishAssert() 851 852 // Confirm the sweep tx. 853 ctx.sweeper.sweepAll() 854 855 // Wait for output to be promoted in store to GRAD. 856 select { 857 case <-ctx.store.graduateKinderChan: 858 case <-time.After(defaultTestTimeout): 859 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 860 t.Fatalf("output not graduated") 861 } 862 863 ctx.restart() 864 865 // As there only was one output to graduate, we expect the channel to be 866 // closed and no report available anymore. 867 assertNurseryReportUnavailable(t, ctx.nursery) 868 } 869 870 type nurseryStoreInterceptor struct { 871 ns NurseryStorer 872 873 // TODO(joostjager): put more useful info through these channels. 874 cribToKinderChan chan struct{} 875 cribToRemoteSpendChan chan struct{} 876 graduateKinderChan chan struct{} 877 preschoolToKinderChan chan struct{} 878 } 879 880 func newNurseryStoreInterceptor(ns NurseryStorer) *nurseryStoreInterceptor { 881 return &nurseryStoreInterceptor{ 882 ns: ns, 883 cribToKinderChan: make(chan struct{}), 884 cribToRemoteSpendChan: make(chan struct{}), 885 graduateKinderChan: make(chan struct{}), 886 preschoolToKinderChan: make(chan struct{}), 887 } 888 } 889 890 func (i *nurseryStoreInterceptor) Incubate(kidOutputs []kidOutput, 891 babyOutputs []babyOutput) error { 892 893 return i.ns.Incubate(kidOutputs, babyOutputs) 894 } 895 896 func (i *nurseryStoreInterceptor) CribToKinder(babyOutput *babyOutput) error { 897 err := i.ns.CribToKinder(babyOutput) 898 899 i.cribToKinderChan <- struct{}{} 900 901 return err 902 } 903 904 func (i *nurseryStoreInterceptor) PreschoolToKinder(kidOutput *kidOutput, 905 lastGradHeight uint32) error { 906 907 err := i.ns.PreschoolToKinder(kidOutput, lastGradHeight) 908 909 i.preschoolToKinderChan <- struct{}{} 910 911 return err 912 } 913 914 func (i *nurseryStoreInterceptor) GraduateKinder(height uint32, kid *kidOutput) error { 915 err := i.ns.GraduateKinder(height, kid) 916 917 i.graduateKinderChan <- struct{}{} 918 919 return err 920 } 921 922 func (i *nurseryStoreInterceptor) FetchPreschools() ([]kidOutput, error) { 923 return i.ns.FetchPreschools() 924 } 925 926 func (i *nurseryStoreInterceptor) FetchClass(height uint32) ( 927 []kidOutput, []babyOutput, error) { 928 929 return i.ns.FetchClass(height) 930 } 931 932 func (i *nurseryStoreInterceptor) HeightsBelowOrEqual(height uint32) ( 933 []uint32, error) { 934 935 return i.ns.HeightsBelowOrEqual(height) 936 } 937 938 func (i *nurseryStoreInterceptor) ForChanOutputs(chanPoint *wire.OutPoint, 939 callback func([]byte, []byte) error, reset func()) error { 940 941 return i.ns.ForChanOutputs(chanPoint, callback, reset) 942 } 943 944 func (i *nurseryStoreInterceptor) ListChannels() ([]wire.OutPoint, error) { 945 return i.ns.ListChannels() 946 } 947 948 func (i *nurseryStoreInterceptor) IsMatureChannel(chanPoint *wire.OutPoint) ( 949 bool, error) { 950 951 return i.ns.IsMatureChannel(chanPoint) 952 } 953 954 func (i *nurseryStoreInterceptor) RemoveChannel(chanPoint *wire.OutPoint) error { 955 return i.ns.RemoveChannel(chanPoint) 956 } 957 958 type mockSweeperFull struct { 959 lock sync.Mutex 960 961 resultChans map[wire.OutPoint]chan sweep.Result 962 t *testing.T 963 964 sweepChan chan input.Input 965 } 966 967 func newMockSweeperFull(t *testing.T) *mockSweeperFull { 968 return &mockSweeperFull{ 969 resultChans: make(map[wire.OutPoint]chan sweep.Result), 970 sweepChan: make(chan input.Input, 1), 971 t: t, 972 } 973 } 974 975 func (s *mockSweeperFull) sweepInput(input input.Input, 976 _ sweep.Params) (chan sweep.Result, error) { 977 978 log.Debugf("mockSweeperFull sweepInput called for %v", *input.OutPoint()) 979 980 select { 981 case s.sweepChan <- input: 982 case <-time.After(defaultTestTimeout): 983 s.t.Fatal("signal result timeout") 984 } 985 986 s.lock.Lock() 987 defer s.lock.Unlock() 988 989 c := make(chan sweep.Result, 1) 990 s.resultChans[*input.OutPoint()] = c 991 992 return c, nil 993 } 994 995 func (s *mockSweeperFull) expectSweep() { 996 s.t.Helper() 997 998 select { 999 case <-s.sweepChan: 1000 case <-time.After(defaultTestTimeout): 1001 s.t.Fatal("signal result timeout") 1002 } 1003 } 1004 1005 func (s *mockSweeperFull) sweepAll() { 1006 s.t.Helper() 1007 1008 s.lock.Lock() 1009 currentChans := s.resultChans 1010 s.resultChans = make(map[wire.OutPoint]chan sweep.Result) 1011 s.lock.Unlock() 1012 1013 for o, c := range currentChans { 1014 log.Debugf("mockSweeperFull signal swept for %v", o) 1015 1016 select { 1017 case c <- sweep.Result{}: 1018 case <-time.After(defaultTestTimeout): 1019 s.t.Fatal("signal result timeout") 1020 } 1021 } 1022 }