github.com/decred/dcrlnd@v0.7.6/contractcourt/htlc_outgoing_contest_resolver_test.go (about)

     1  package contractcourt
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/decred/dcrd/wire"
     8  	"github.com/decred/dcrlnd/chainntnfs"
     9  	"github.com/decred/dcrlnd/channeldb"
    10  	"github.com/decred/dcrlnd/input"
    11  	"github.com/decred/dcrlnd/kvdb"
    12  	"github.com/decred/dcrlnd/lntest/mock"
    13  	"github.com/decred/dcrlnd/lntypes"
    14  	"github.com/decred/dcrlnd/lnwallet"
    15  	"github.com/decred/dcrlnd/lnwire"
    16  )
    17  
    18  const (
    19  	outgoingContestHtlcExpiry = 110
    20  )
    21  
    22  // TestHtlcOutgoingResolverTimeout tests resolution of an offered htlc that
    23  // timed out.
    24  func TestHtlcOutgoingResolverTimeout(t *testing.T) {
    25  	t.Parallel()
    26  	defer timeout(t)()
    27  
    28  	// Setup the resolver with our test resolution.
    29  	ctx := newOutgoingResolverTestContext(t)
    30  
    31  	// Start the resolution process in a goroutine.
    32  	ctx.resolve()
    33  
    34  	// Notify arrival of the block after which the timeout path of the htlc
    35  	// unlocks.
    36  	ctx.notifyEpoch(outgoingContestHtlcExpiry - 1)
    37  
    38  	// Assert that the resolver finishes without error and transforms in a
    39  	// timeout resolver.
    40  	ctx.waitForResult(true)
    41  }
    42  
    43  // TestHtlcOutgoingResolverRemoteClaim tests resolution of an offered htlc that
    44  // is claimed by the remote party.
    45  func TestHtlcOutgoingResolverRemoteClaim(t *testing.T) {
    46  	t.Parallel()
    47  	defer timeout(t)()
    48  
    49  	// Setup the resolver with our test resolution and start the resolution
    50  	// process.
    51  	ctx := newOutgoingResolverTestContext(t)
    52  
    53  	// Replace our mocked checkpoint function with one which will push
    54  	// reports into a channel for us to consume. We do so on the resolver
    55  	// level because our test context has already created the resolver.
    56  	reportChan := make(chan *channeldb.ResolverReport)
    57  	ctx.resolver.Checkpoint = func(_ ContractResolver,
    58  		reports ...*channeldb.ResolverReport) error {
    59  
    60  		// Send all of our reports into the channel.
    61  		for _, report := range reports {
    62  			reportChan <- report
    63  		}
    64  
    65  		return nil
    66  	}
    67  
    68  	ctx.resolve()
    69  
    70  	// The remote party sweeps the htlc. Notify our resolver of this event.
    71  	preimage := lntypes.Preimage{}
    72  	sigScript := [35]byte{
    73  		0: 0x51,
    74  		1: 0x52,
    75  		2: 0x20,
    76  	}
    77  	copy(sigScript[3:], preimage[:])
    78  	spendTx := &wire.MsgTx{
    79  		TxIn: []*wire.TxIn{
    80  			{
    81  				SignatureScript: sigScript[:],
    82  			},
    83  		},
    84  	}
    85  
    86  	spendHash := spendTx.TxHash()
    87  
    88  	ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{
    89  		SpendingTx:    spendTx,
    90  		SpenderTxHash: &spendHash,
    91  	}
    92  
    93  	// We expect the extracted preimage to be added to the witness beacon.
    94  	<-ctx.preimageDB.newPreimages
    95  
    96  	// We also expect a resolution message to the incoming side of the
    97  	// circuit.
    98  	<-ctx.resolutionChan
    99  
   100  	// Finally, check that we have a report as expected.
   101  	expectedReport := &channeldb.ResolverReport{
   102  		OutPoint:        wire.OutPoint{},
   103  		Amount:          0,
   104  		ResolverType:    channeldb.ResolverTypeOutgoingHtlc,
   105  		ResolverOutcome: channeldb.ResolverOutcomeClaimed,
   106  		SpendTxID:       &spendHash,
   107  	}
   108  
   109  	assertResolverReport(t, reportChan, expectedReport)
   110  
   111  	// Assert that the resolver finishes without error.
   112  	ctx.waitForResult(false)
   113  }
   114  
   115  type resolveResult struct {
   116  	err          error
   117  	nextResolver ContractResolver
   118  }
   119  
   120  type outgoingResolverTestContext struct {
   121  	resolver           *htlcOutgoingContestResolver
   122  	notifier           *mock.ChainNotifier
   123  	preimageDB         *mockWitnessBeacon
   124  	resolverResultChan chan resolveResult
   125  	resolutionChan     chan ResolutionMsg
   126  	t                  *testing.T
   127  }
   128  
   129  func newOutgoingResolverTestContext(t *testing.T) *outgoingResolverTestContext {
   130  	notifier := &mock.ChainNotifier{
   131  		EpochChan: make(chan *chainntnfs.BlockEpoch),
   132  		SpendChan: make(chan *chainntnfs.SpendDetail),
   133  		ConfChan:  make(chan *chainntnfs.TxConfirmation),
   134  	}
   135  
   136  	checkPointChan := make(chan struct{}, 1)
   137  	resolutionChan := make(chan ResolutionMsg, 1)
   138  
   139  	preimageDB := newMockWitnessBeacon()
   140  
   141  	onionProcessor := &mockOnionProcessor{}
   142  
   143  	chainCfg := ChannelArbitratorConfig{
   144  		ChainArbitratorConfig: ChainArbitratorConfig{
   145  			Notifier:   notifier,
   146  			PreimageDB: preimageDB,
   147  			DeliverResolutionMsg: func(msgs ...ResolutionMsg) error {
   148  				if len(msgs) != 1 {
   149  					return fmt.Errorf("expected 1 "+
   150  						"resolution msg, instead got %v",
   151  						len(msgs))
   152  				}
   153  
   154  				resolutionChan <- msgs[0]
   155  				return nil
   156  			},
   157  			OnionProcessor: onionProcessor,
   158  		},
   159  		PutResolverReport: func(_ kvdb.RwTx,
   160  			_ *channeldb.ResolverReport) error {
   161  
   162  			return nil
   163  		},
   164  	}
   165  
   166  	outgoingRes := lnwallet.OutgoingHtlcResolution{
   167  		Expiry: outgoingContestHtlcExpiry,
   168  		SweepSignDesc: input.SignDescriptor{
   169  			Output: &wire.TxOut{},
   170  		},
   171  	}
   172  
   173  	cfg := ResolverConfig{
   174  		ChannelArbitratorConfig: chainCfg,
   175  		Checkpoint: func(_ ContractResolver,
   176  			_ ...*channeldb.ResolverReport) error {
   177  
   178  			checkPointChan <- struct{}{}
   179  			return nil
   180  		},
   181  	}
   182  
   183  	resolver := &htlcOutgoingContestResolver{
   184  		htlcTimeoutResolver: &htlcTimeoutResolver{
   185  			contractResolverKit: *newContractResolverKit(cfg),
   186  			htlcResolution:      outgoingRes,
   187  			htlc: channeldb.HTLC{
   188  				Amt:       lnwire.MilliAtom(testHtlcAmount),
   189  				RHash:     testResHash,
   190  				OnionBlob: testOnionBlob,
   191  			},
   192  		},
   193  	}
   194  
   195  	return &outgoingResolverTestContext{
   196  		resolver:       resolver,
   197  		notifier:       notifier,
   198  		preimageDB:     preimageDB,
   199  		resolutionChan: resolutionChan,
   200  		t:              t,
   201  	}
   202  }
   203  
   204  func (i *outgoingResolverTestContext) resolve() {
   205  	// Start resolver.
   206  	i.resolverResultChan = make(chan resolveResult, 1)
   207  	go func() {
   208  		nextResolver, err := i.resolver.Resolve()
   209  		i.resolverResultChan <- resolveResult{
   210  			nextResolver: nextResolver,
   211  			err:          err,
   212  		}
   213  	}()
   214  
   215  	// Notify initial block height.
   216  	i.notifyEpoch(testInitialBlockHeight)
   217  }
   218  
   219  func (i *outgoingResolverTestContext) notifyEpoch(height int32) {
   220  	i.notifier.EpochChan <- &chainntnfs.BlockEpoch{
   221  		Height: height,
   222  	}
   223  }
   224  
   225  func (i *outgoingResolverTestContext) waitForResult(expectTimeoutRes bool) {
   226  	i.t.Helper()
   227  
   228  	result := <-i.resolverResultChan
   229  	if result.err != nil {
   230  		i.t.Fatal(result.err)
   231  	}
   232  
   233  	if !expectTimeoutRes {
   234  		if result.nextResolver != nil {
   235  			i.t.Fatal("expected no next resolver")
   236  		}
   237  		return
   238  	}
   239  
   240  	_, ok := result.nextResolver.(*htlcTimeoutResolver)
   241  	if !ok {
   242  		i.t.Fatal("expected htlcTimeoutResolver")
   243  	}
   244  }