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  }