github.com/decred/dcrlnd@v0.7.6/contractcourt/htlc_success_resolver_test.go (about) 1 package contractcourt 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "testing" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/decred/dcrd/chaincfg/chainhash" 11 "github.com/decred/dcrd/dcrutil/v4" 12 "github.com/decred/dcrd/wire" 13 "github.com/decred/dcrlnd/chainntnfs" 14 "github.com/decred/dcrlnd/channeldb" 15 "github.com/decred/dcrlnd/input" 16 "github.com/decred/dcrlnd/kvdb" 17 "github.com/decred/dcrlnd/lntest/mock" 18 "github.com/decred/dcrlnd/lnwallet" 19 "github.com/decred/dcrlnd/lnwire" 20 ) 21 22 var testHtlcAmt = lnwire.MilliAtom(200000) 23 24 type htlcResolverTestContext struct { 25 resolver ContractResolver 26 27 checkpoint func(_ ContractResolver, 28 _ ...*channeldb.ResolverReport) error 29 30 notifier *mock.ChainNotifier 31 resolverResultChan chan resolveResult 32 resolutionChan chan ResolutionMsg 33 34 t *testing.T 35 } 36 37 func newHtlcResolverTestContext(t *testing.T, 38 newResolver func(htlc channeldb.HTLC, 39 cfg ResolverConfig) ContractResolver) *htlcResolverTestContext { 40 41 notifier := &mock.ChainNotifier{ 42 EpochChan: make(chan *chainntnfs.BlockEpoch, 1), 43 SpendChan: make(chan *chainntnfs.SpendDetail, 1), 44 ConfChan: make(chan *chainntnfs.TxConfirmation, 1), 45 } 46 47 testCtx := &htlcResolverTestContext{ 48 checkpoint: nil, 49 notifier: notifier, 50 resolutionChan: make(chan ResolutionMsg, 1), 51 t: t, 52 } 53 54 witnessBeacon := newMockWitnessBeacon() 55 chainCfg := ChannelArbitratorConfig{ 56 ChainArbitratorConfig: ChainArbitratorConfig{ 57 Notifier: notifier, 58 PreimageDB: witnessBeacon, 59 PublishTx: func(_ *wire.MsgTx, _ string) error { 60 return nil 61 }, 62 Sweeper: newMockSweeper(), 63 IncubateOutputs: func(wire.OutPoint, *lnwallet.OutgoingHtlcResolution, 64 *lnwallet.IncomingHtlcResolution, uint32) error { 65 return nil 66 }, 67 DeliverResolutionMsg: func(msgs ...ResolutionMsg) error { 68 if len(msgs) != 1 { 69 return fmt.Errorf("expected 1 "+ 70 "resolution msg, instead got %v", 71 len(msgs)) 72 } 73 74 testCtx.resolutionChan <- msgs[0] 75 return nil 76 }, 77 }, 78 PutResolverReport: func(_ kvdb.RwTx, 79 report *channeldb.ResolverReport) error { 80 81 return nil 82 }, 83 } 84 // Since we want to replace this checkpoint method later in the test, 85 // we wrap the call to it in a closure. The linter will complain about 86 // this so set nolint directive. 87 checkpointFunc := func(c ContractResolver, // nolint 88 r ...*channeldb.ResolverReport) error { 89 return testCtx.checkpoint(c, r...) 90 } 91 92 cfg := ResolverConfig{ 93 ChannelArbitratorConfig: chainCfg, 94 Checkpoint: checkpointFunc, 95 } 96 97 htlc := channeldb.HTLC{ 98 RHash: testResHash, 99 OnionBlob: testOnionBlob, 100 Amt: testHtlcAmt, 101 } 102 103 testCtx.resolver = newResolver(htlc, cfg) 104 105 return testCtx 106 } 107 108 func (i *htlcResolverTestContext) resolve() { 109 // Start resolver. 110 i.resolverResultChan = make(chan resolveResult, 1) 111 go func() { 112 nextResolver, err := i.resolver.Resolve() 113 i.resolverResultChan <- resolveResult{ 114 nextResolver: nextResolver, 115 err: err, 116 } 117 }() 118 } 119 120 func (i *htlcResolverTestContext) waitForResult() { 121 i.t.Helper() 122 123 result := <-i.resolverResultChan 124 if result.err != nil { 125 i.t.Fatal(result.err) 126 } 127 128 if result.nextResolver != nil { 129 i.t.Fatal("expected no next resolver") 130 } 131 } 132 133 // TestHtlcSuccessSingleStage tests successful sweep of a single stage htlc 134 // claim. 135 func TestHtlcSuccessSingleStage(t *testing.T) { 136 htlcOutpoint := wire.OutPoint{Index: 3} 137 138 sweepTx := &wire.MsgTx{ 139 TxIn: []*wire.TxIn{{}}, 140 TxOut: []*wire.TxOut{{}}, 141 } 142 143 // singleStageResolution is a resolution for a htlc on the remote 144 // party's commitment. 145 singleStageResolution := lnwallet.IncomingHtlcResolution{ 146 SweepSignDesc: testSignDesc, 147 ClaimOutpoint: htlcOutpoint, 148 } 149 150 sweepTxid := sweepTx.TxHash() 151 claim := &channeldb.ResolverReport{ 152 OutPoint: htlcOutpoint, 153 Amount: dcrutil.Amount(testSignDesc.Output.Value), 154 ResolverType: channeldb.ResolverTypeIncomingHtlc, 155 ResolverOutcome: channeldb.ResolverOutcomeClaimed, 156 SpendTxID: &sweepTxid, 157 } 158 159 checkpoints := []checkpoint{ 160 { 161 // We send a confirmation for our sweep tx to indicate 162 // that our sweep succeeded. 163 preCheckpoint: func(ctx *htlcResolverTestContext, 164 _ bool) error { 165 // The resolver will create and publish a sweep 166 // tx. 167 resolver := ctx.resolver.(*htlcSuccessResolver) 168 resolver.Sweeper.(*mockSweeper). 169 createSweepTxChan <- sweepTx 170 171 // Confirm the sweep, which should resolve it. 172 ctx.notifier.ConfChan <- &chainntnfs.TxConfirmation{ 173 Tx: sweepTx, 174 BlockHeight: testInitialBlockHeight - 1, 175 } 176 177 return nil 178 }, 179 180 // After the sweep has confirmed, we expect the 181 // checkpoint to be resolved, and with the above 182 // report. 183 resolved: true, 184 reports: []*channeldb.ResolverReport{ 185 claim, 186 }, 187 }, 188 } 189 190 testHtlcSuccess( 191 t, singleStageResolution, checkpoints, 192 ) 193 } 194 195 // TestSecondStageResolution tests successful sweep of a second stage htlc 196 // claim, going through the Nursery. 197 func TestHtlcSuccessSecondStageResolution(t *testing.T) { 198 commitOutpoint := wire.OutPoint{Index: 2} 199 htlcOutpoint := wire.OutPoint{Index: 3} 200 201 sweepTx := &wire.MsgTx{ 202 TxIn: []*wire.TxIn{{}}, 203 TxOut: []*wire.TxOut{{}}, 204 } 205 sweepHash := sweepTx.TxHash() 206 207 // twoStageResolution is a resolution for htlc on our own commitment 208 // which is spent from the signed success tx. 209 twoStageResolution := lnwallet.IncomingHtlcResolution{ 210 Preimage: [32]byte{}, 211 SignedSuccessTx: &wire.MsgTx{ 212 TxIn: []*wire.TxIn{ 213 { 214 PreviousOutPoint: commitOutpoint, 215 }, 216 }, 217 TxOut: []*wire.TxOut{ 218 { 219 Value: 111, 220 PkScript: []byte{0xaa, 0xaa}, 221 }, 222 }, 223 }, 224 ClaimOutpoint: htlcOutpoint, 225 SweepSignDesc: testSignDesc, 226 } 227 228 successTx := twoStageResolution.SignedSuccessTx.TxHash() 229 firstStage := &channeldb.ResolverReport{ 230 OutPoint: commitOutpoint, 231 Amount: testHtlcAmt.ToAtoms(), 232 ResolverType: channeldb.ResolverTypeIncomingHtlc, 233 ResolverOutcome: channeldb.ResolverOutcomeFirstStage, 234 SpendTxID: &successTx, 235 } 236 237 secondStage := &channeldb.ResolverReport{ 238 OutPoint: htlcOutpoint, 239 Amount: dcrutil.Amount(testSignDesc.Output.Value), 240 ResolverType: channeldb.ResolverTypeIncomingHtlc, 241 ResolverOutcome: channeldb.ResolverOutcomeClaimed, 242 SpendTxID: &sweepHash, 243 } 244 245 checkpoints := []checkpoint{ 246 { 247 // The resolver will send the output to the Nursery. 248 incubating: true, 249 }, 250 { 251 // It will then wait for the Nursery to spend the 252 // output. We send a spend notification for our output 253 // to resolve our htlc. 254 preCheckpoint: func(ctx *htlcResolverTestContext, 255 _ bool) error { 256 ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ 257 SpendingTx: sweepTx, 258 SpenderTxHash: &sweepHash, 259 } 260 261 return nil 262 }, 263 incubating: true, 264 resolved: true, 265 reports: []*channeldb.ResolverReport{ 266 secondStage, 267 firstStage, 268 }, 269 }, 270 } 271 272 testHtlcSuccess( 273 t, twoStageResolution, checkpoints, 274 ) 275 } 276 277 // TestHtlcSuccessSecondStageResolutionSweeper test that a resolver with 278 // non-nil SignDetails will offer the second-level transaction to the sweeper 279 // for re-signing. 280 func TestHtlcSuccessSecondStageResolutionSweeper(t *testing.T) { 281 commitOutpoint := wire.OutPoint{Index: 2} 282 htlcOutpoint := wire.OutPoint{Index: 3} 283 284 successTx := &wire.MsgTx{ 285 TxIn: []*wire.TxIn{ 286 { 287 PreviousOutPoint: commitOutpoint, 288 }, 289 }, 290 TxOut: []*wire.TxOut{ 291 { 292 Value: 123, 293 PkScript: []byte{0xff, 0xff}, 294 }, 295 }, 296 } 297 298 reSignedSuccessTx := &wire.MsgTx{ 299 TxIn: []*wire.TxIn{ 300 { 301 PreviousOutPoint: wire.OutPoint{ 302 Hash: chainhash.Hash{0xaa, 0xbb}, 303 Index: 0, 304 }, 305 }, 306 successTx.TxIn[0], 307 { 308 PreviousOutPoint: wire.OutPoint{ 309 Hash: chainhash.Hash{0xaa, 0xbb}, 310 Index: 2, 311 }, 312 }, 313 }, 314 315 TxOut: []*wire.TxOut{ 316 { 317 Value: 111, 318 PkScript: []byte{0xaa, 0xaa}, 319 }, 320 successTx.TxOut[0], 321 }, 322 } 323 reSignedHash := successTx.TxHash() 324 325 sweepTx := &wire.MsgTx{ 326 TxIn: []*wire.TxIn{ 327 328 { 329 PreviousOutPoint: wire.OutPoint{ 330 Hash: reSignedHash, 331 Index: 1, 332 }, 333 }, 334 }, 335 TxOut: []*wire.TxOut{{}}, 336 } 337 sweepHash := sweepTx.TxHash() 338 339 // twoStageResolution is a resolution for htlc on our own commitment 340 // which is spent from the signed success tx. 341 twoStageResolution := lnwallet.IncomingHtlcResolution{ 342 Preimage: [32]byte{}, 343 CsvDelay: 4, 344 SignedSuccessTx: successTx, 345 SignDetails: &input.SignDetails{ 346 SignDesc: testSignDesc, 347 PeerSig: testSig, 348 }, 349 ClaimOutpoint: htlcOutpoint, 350 SweepSignDesc: testSignDesc, 351 } 352 353 firstStage := &channeldb.ResolverReport{ 354 OutPoint: commitOutpoint, 355 Amount: testHtlcAmt.ToAtoms(), 356 ResolverType: channeldb.ResolverTypeIncomingHtlc, 357 ResolverOutcome: channeldb.ResolverOutcomeFirstStage, 358 SpendTxID: &reSignedHash, 359 } 360 361 secondStage := &channeldb.ResolverReport{ 362 OutPoint: htlcOutpoint, 363 Amount: dcrutil.Amount(testSignDesc.Output.Value), 364 ResolverType: channeldb.ResolverTypeIncomingHtlc, 365 ResolverOutcome: channeldb.ResolverOutcomeClaimed, 366 SpendTxID: &sweepHash, 367 } 368 369 checkpoints := []checkpoint{ 370 { 371 // The HTLC output on the commitment should be offered 372 // to the sweeper. We'll notify that it gets spent. 373 preCheckpoint: func(ctx *htlcResolverTestContext, 374 _ bool) error { 375 376 resolver := ctx.resolver.(*htlcSuccessResolver) 377 inp := <-resolver.Sweeper.(*mockSweeper).sweptInputs 378 op := inp.OutPoint() 379 if *op != commitOutpoint { 380 return fmt.Errorf("outpoint %v swept, "+ 381 "expected %v", op, 382 commitOutpoint) 383 } 384 385 ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ 386 SpendingTx: reSignedSuccessTx, 387 SpenderTxHash: &reSignedHash, 388 SpenderInputIndex: 1, 389 SpendingHeight: 10, 390 } 391 return nil 392 393 }, 394 // incubating=true is used to signal that the 395 // second-level transaction was confirmed. 396 incubating: true, 397 }, 398 { 399 // The resolver will wait for the second-level's CSV 400 // lock to expire. 401 preCheckpoint: func(ctx *htlcResolverTestContext, 402 resumed bool) error { 403 404 // If we are resuming from a checkpoint, we 405 // expect the resolver to re-subscribe to a 406 // spend, hence we must resend it. 407 if resumed { 408 ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ 409 SpendingTx: reSignedSuccessTx, 410 SpenderTxHash: &reSignedHash, 411 SpenderInputIndex: 1, 412 SpendingHeight: 10, 413 } 414 } 415 416 ctx.notifier.EpochChan <- &chainntnfs.BlockEpoch{ 417 Height: 13, 418 } 419 420 // We expect it to sweep the second-level 421 // transaction we notfied about above. 422 resolver := ctx.resolver.(*htlcSuccessResolver) 423 inp := <-resolver.Sweeper.(*mockSweeper).sweptInputs 424 op := inp.OutPoint() 425 exp := wire.OutPoint{ 426 Hash: reSignedHash, 427 Index: 1, 428 } 429 if *op != exp { 430 return fmt.Errorf("swept outpoint %v, expected %v", 431 op, exp) 432 } 433 434 // Notify about the spend, which should resolve 435 // the resolver. 436 ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ 437 SpendingTx: sweepTx, 438 SpenderTxHash: &sweepHash, 439 SpendingHeight: 14, 440 } 441 442 return nil 443 }, 444 445 incubating: true, 446 resolved: true, 447 reports: []*channeldb.ResolverReport{ 448 secondStage, 449 firstStage, 450 }, 451 }, 452 } 453 454 testHtlcSuccess(t, twoStageResolution, checkpoints) 455 } 456 457 // checkpoint holds expected data we expect the resolver to checkpoint itself 458 // to the DB next. 459 type checkpoint struct { 460 // preCheckpoint is a method that will be called before we reach the 461 // checkpoint, to carry out any needed operations to drive the resolver 462 // in this stage. 463 preCheckpoint func(*htlcResolverTestContext, bool) error 464 465 // data we expect the resolver to be checkpointed with next. 466 incubating bool 467 resolved bool 468 reports []*channeldb.ResolverReport 469 } 470 471 // testHtlcSuccess tests resolution of a success resolver. It takes a a list of 472 // checkpoints that it expects the resolver to go through. And will run the 473 // resolver all the way through these checkpoints, and also attempt to resume 474 // the resolver from every checkpoint. 475 func testHtlcSuccess(t *testing.T, resolution lnwallet.IncomingHtlcResolution, 476 checkpoints []checkpoint) { 477 478 defer timeout(t)() 479 480 // We first run the resolver from start to finish, ensuring it gets 481 // checkpointed at every expected stage. We store the checkpointed data 482 // for the next portion of the test. 483 ctx := newHtlcResolverTestContext(t, 484 func(htlc channeldb.HTLC, cfg ResolverConfig) ContractResolver { 485 return &htlcSuccessResolver{ 486 contractResolverKit: *newContractResolverKit(cfg), 487 htlc: htlc, 488 htlcResolution: resolution, 489 } 490 }, 491 ) 492 493 checkpointedState := runFromCheckpoint(t, ctx, checkpoints) 494 495 // Now, from every checkpoint created, we re-create the resolver, and 496 // run the test from that checkpoint. 497 for i := range checkpointedState { 498 cp := bytes.NewReader(checkpointedState[i]) 499 ctx := newHtlcResolverTestContext(t, 500 func(htlc channeldb.HTLC, cfg ResolverConfig) ContractResolver { 501 resolver, err := newSuccessResolverFromReader(cp, cfg) 502 if err != nil { 503 t.Fatal(err) 504 } 505 506 resolver.Supplement(htlc) 507 resolver.htlcResolution = resolution 508 return resolver 509 }, 510 ) 511 512 // Run from the given checkpoint, ensuring we'll hit the rest. 513 _ = runFromCheckpoint(t, ctx, checkpoints[i+1:]) 514 } 515 } 516 517 // runFromCheckpoint executes the Resolve method on the success resolver, and 518 // asserts that it checkpoints itself according to the expected checkpoints. 519 func runFromCheckpoint(t *testing.T, ctx *htlcResolverTestContext, 520 expectedCheckpoints []checkpoint) [][]byte { 521 522 defer timeout(t)() 523 524 var checkpointedState [][]byte 525 526 // Replace our checkpoint method with one which we'll use to assert the 527 // checkpointed state and reports are equal to what we expect. 528 nextCheckpoint := 0 529 checkpointChan := make(chan struct{}) 530 ctx.checkpoint = func(resolver ContractResolver, 531 reports ...*channeldb.ResolverReport) error { 532 533 if nextCheckpoint >= len(expectedCheckpoints) { 534 t.Fatal("did not expect more checkpoints") 535 } 536 537 var resolved, incubating bool 538 if h, ok := resolver.(*htlcSuccessResolver); ok { 539 resolved = h.resolved 540 incubating = h.outputIncubating 541 } 542 if h, ok := resolver.(*htlcTimeoutResolver); ok { 543 resolved = h.resolved 544 incubating = h.outputIncubating 545 } 546 547 cp := expectedCheckpoints[nextCheckpoint] 548 549 if resolved != cp.resolved { 550 t.Fatalf("expected checkpoint to be resolve=%v, had %v", 551 cp.resolved, resolved) 552 } 553 554 if !reflect.DeepEqual(incubating, cp.incubating) { 555 t.Fatalf("expected checkpoint to be have "+ 556 "incubating=%v, had %v", cp.incubating, 557 incubating) 558 559 } 560 561 // Check we go the expected reports. 562 if len(reports) != len(cp.reports) { 563 t.Fatalf("unexpected number of reports. Expected %v "+ 564 "got %v", len(cp.reports), len(reports)) 565 } 566 567 for i, report := range reports { 568 if !reflect.DeepEqual(report, cp.reports[i]) { 569 t.Fatalf("expected: %v, got: %v", 570 spew.Sdump(cp.reports[i]), 571 spew.Sdump(report)) 572 } 573 } 574 575 // Finally encode the resolver, and store it for later use. 576 b := bytes.Buffer{} 577 if err := resolver.Encode(&b); err != nil { 578 t.Fatal(err) 579 } 580 581 checkpointedState = append(checkpointedState, b.Bytes()) 582 nextCheckpoint++ 583 checkpointChan <- struct{}{} 584 return nil 585 } 586 587 // Start the htlc success resolver. 588 ctx.resolve() 589 590 // Go through our list of expected checkpoints, so we can run the 591 // preCheckpoint logic if needed. 592 resumed := true 593 for i, cp := range expectedCheckpoints { 594 if cp.preCheckpoint != nil { 595 if err := cp.preCheckpoint(ctx, resumed); err != nil { 596 t.Fatalf("failure at stage %d: %v", i, err) 597 } 598 599 } 600 resumed = false 601 602 // Wait for the resolver to have checkpointed its state. 603 <-checkpointChan 604 } 605 606 // Wait for the resolver to fully complete. 607 ctx.waitForResult() 608 609 if nextCheckpoint < len(expectedCheckpoints) { 610 t.Fatalf("not all checkpoints hit") 611 } 612 613 return checkpointedState 614 }