mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-01 18:27:43 +02:00
contractcourt: add Launch
method to htlc success resolver
This commit breaks the `Resolve` into two parts - the first part is moved into a `Launch` method that handles sending sweep requests, and the second part remains in `Resolve` which handles waiting for the spend. Since we are using both utxo nursery and sweeper at the same time, to make sure this change doesn't break the existing behavior, we implement the `Launch` as following, - zero-fee htlc - handled by the sweeper - direct output from the remote commit - handled by the sweeper - legacy htlc - handled by the utxo nursery
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lntest/mock"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testHtlcAmt = lnwire.MilliSatoshi(200000)
|
||||
@@ -39,6 +41,15 @@ type htlcResolverTestContext struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func newHtlcResolverTestContextFromReader(t *testing.T,
|
||||
newResolver func(htlc channeldb.HTLC,
|
||||
cfg ResolverConfig) ContractResolver) *htlcResolverTestContext {
|
||||
|
||||
ctx := newHtlcResolverTestContext(t, newResolver)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func newHtlcResolverTestContext(t *testing.T,
|
||||
newResolver func(htlc channeldb.HTLC,
|
||||
cfg ResolverConfig) ContractResolver) *htlcResolverTestContext {
|
||||
@@ -133,6 +144,7 @@ func newHtlcResolverTestContext(t *testing.T,
|
||||
func (i *htlcResolverTestContext) resolve() {
|
||||
// Start resolver.
|
||||
i.resolverResultChan = make(chan resolveResult, 1)
|
||||
|
||||
go func() {
|
||||
nextResolver, err := i.resolver.Resolve()
|
||||
i.resolverResultChan <- resolveResult{
|
||||
@@ -192,6 +204,7 @@ func TestHtlcSuccessSingleStage(t *testing.T) {
|
||||
// sweeper.
|
||||
details := &chainntnfs.SpendDetail{
|
||||
SpendingTx: sweepTx,
|
||||
SpentOutPoint: &htlcOutpoint,
|
||||
SpenderTxHash: &sweepTxid,
|
||||
}
|
||||
ctx.notifier.SpendChan <- details
|
||||
@@ -215,8 +228,8 @@ func TestHtlcSuccessSingleStage(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// TestSecondStageResolution tests successful sweep of a second stage htlc
|
||||
// claim, going through the Nursery.
|
||||
// TestHtlcSuccessSecondStageResolution tests successful sweep of a second
|
||||
// stage htlc claim, going through the Nursery.
|
||||
func TestHtlcSuccessSecondStageResolution(t *testing.T) {
|
||||
commitOutpoint := wire.OutPoint{Index: 2}
|
||||
htlcOutpoint := wire.OutPoint{Index: 3}
|
||||
@@ -279,6 +292,7 @@ func TestHtlcSuccessSecondStageResolution(t *testing.T) {
|
||||
|
||||
ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{
|
||||
SpendingTx: sweepTx,
|
||||
SpentOutPoint: &htlcOutpoint,
|
||||
SpenderTxHash: &sweepHash,
|
||||
}
|
||||
|
||||
@@ -302,6 +316,8 @@ func TestHtlcSuccessSecondStageResolution(t *testing.T) {
|
||||
// TestHtlcSuccessSecondStageResolutionSweeper test that a resolver with
|
||||
// non-nil SignDetails will offer the second-level transaction to the sweeper
|
||||
// for re-signing.
|
||||
//
|
||||
//nolint:ll
|
||||
func TestHtlcSuccessSecondStageResolutionSweeper(t *testing.T) {
|
||||
commitOutpoint := wire.OutPoint{Index: 2}
|
||||
htlcOutpoint := wire.OutPoint{Index: 3}
|
||||
@@ -399,7 +415,20 @@ func TestHtlcSuccessSecondStageResolutionSweeper(t *testing.T) {
|
||||
_ bool) error {
|
||||
|
||||
resolver := ctx.resolver.(*htlcSuccessResolver)
|
||||
inp := <-resolver.Sweeper.(*mockSweeper).sweptInputs
|
||||
|
||||
var (
|
||||
inp input.Input
|
||||
ok bool
|
||||
)
|
||||
|
||||
select {
|
||||
case inp, ok = <-resolver.Sweeper.(*mockSweeper).sweptInputs:
|
||||
require.True(t, ok)
|
||||
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("expected input to be swept")
|
||||
}
|
||||
|
||||
op := inp.OutPoint()
|
||||
if op != commitOutpoint {
|
||||
return fmt.Errorf("outpoint %v swept, "+
|
||||
@@ -412,6 +441,7 @@ func TestHtlcSuccessSecondStageResolutionSweeper(t *testing.T) {
|
||||
SpenderTxHash: &reSignedHash,
|
||||
SpenderInputIndex: 1,
|
||||
SpendingHeight: 10,
|
||||
SpentOutPoint: &commitOutpoint,
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -434,13 +464,37 @@ func TestHtlcSuccessSecondStageResolutionSweeper(t *testing.T) {
|
||||
SpenderTxHash: &reSignedHash,
|
||||
SpenderInputIndex: 1,
|
||||
SpendingHeight: 10,
|
||||
SpentOutPoint: &commitOutpoint,
|
||||
}
|
||||
}
|
||||
|
||||
// We expect it to sweep the second-level
|
||||
// transaction we notfied about above.
|
||||
resolver := ctx.resolver.(*htlcSuccessResolver)
|
||||
inp := <-resolver.Sweeper.(*mockSweeper).sweptInputs
|
||||
|
||||
// Mock `waitForSpend` to return the commit
|
||||
// spend.
|
||||
ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{
|
||||
SpendingTx: reSignedSuccessTx,
|
||||
SpenderTxHash: &reSignedHash,
|
||||
SpenderInputIndex: 1,
|
||||
SpendingHeight: 10,
|
||||
SpentOutPoint: &commitOutpoint,
|
||||
}
|
||||
|
||||
var (
|
||||
inp input.Input
|
||||
ok bool
|
||||
)
|
||||
|
||||
select {
|
||||
case inp, ok = <-resolver.Sweeper.(*mockSweeper).sweptInputs:
|
||||
require.True(t, ok)
|
||||
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("expected input to be swept")
|
||||
}
|
||||
|
||||
op := inp.OutPoint()
|
||||
exp := wire.OutPoint{
|
||||
Hash: reSignedHash,
|
||||
@@ -457,6 +511,7 @@ func TestHtlcSuccessSecondStageResolutionSweeper(t *testing.T) {
|
||||
SpendingTx: sweepTx,
|
||||
SpenderTxHash: &sweepHash,
|
||||
SpendingHeight: 14,
|
||||
SpentOutPoint: &op,
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -504,11 +559,14 @@ func testHtlcSuccess(t *testing.T, resolution lnwallet.IncomingHtlcResolution,
|
||||
// for the next portion of the test.
|
||||
ctx := newHtlcResolverTestContext(t,
|
||||
func(htlc channeldb.HTLC, cfg ResolverConfig) ContractResolver {
|
||||
return &htlcSuccessResolver{
|
||||
r := &htlcSuccessResolver{
|
||||
contractResolverKit: *newContractResolverKit(cfg),
|
||||
htlc: htlc,
|
||||
htlcResolution: resolution,
|
||||
}
|
||||
r.initLogger("htlcSuccessResolver")
|
||||
|
||||
return r
|
||||
},
|
||||
)
|
||||
|
||||
@@ -606,7 +664,12 @@ func runFromCheckpoint(t *testing.T, ctx *htlcResolverTestContext,
|
||||
|
||||
checkpointedState = append(checkpointedState, b.Bytes())
|
||||
nextCheckpoint++
|
||||
checkpointChan <- struct{}{}
|
||||
select {
|
||||
case checkpointChan <- struct{}{}:
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("checkpoint timeout")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -617,6 +680,8 @@ func runFromCheckpoint(t *testing.T, ctx *htlcResolverTestContext,
|
||||
// preCheckpoint logic if needed.
|
||||
resumed := true
|
||||
for i, cp := range expectedCheckpoints {
|
||||
t.Logf("Running checkpoint %d", i)
|
||||
|
||||
if cp.preCheckpoint != nil {
|
||||
if err := cp.preCheckpoint(ctx, resumed); err != nil {
|
||||
t.Fatalf("failure at stage %d: %v", i, err)
|
||||
@@ -625,15 +690,15 @@ func runFromCheckpoint(t *testing.T, ctx *htlcResolverTestContext,
|
||||
resumed = false
|
||||
|
||||
// Wait for the resolver to have checkpointed its state.
|
||||
<-checkpointChan
|
||||
select {
|
||||
case <-checkpointChan:
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatalf("resolver did not checkpoint at stage %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the resolver to fully complete.
|
||||
ctx.waitForResult()
|
||||
|
||||
if nextCheckpoint < len(expectedCheckpoints) {
|
||||
t.Fatalf("not all checkpoints hit")
|
||||
}
|
||||
|
||||
return checkpointedState
|
||||
}
|
||||
|
Reference in New Issue
Block a user