contractcourt: persist commit sweep resolutions

This commit is contained in:
carla 2020-07-07 19:49:58 +02:00
parent d0ec872ef3
commit d8a4b37c0e
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91
3 changed files with 84 additions and 6 deletions

@ -55,6 +55,10 @@ const (
// ResolverTypeOutgoingHtlc represents resolution of an outgoing htlc. // ResolverTypeOutgoingHtlc represents resolution of an outgoing htlc.
ResolverTypeOutgoingHtlc ResolverType = 2 ResolverTypeOutgoingHtlc ResolverType = 2
// ResolverTypeCommit represents resolution of our time locked commit
// when we force close.
ResolverTypeCommit ResolverType = 3
) )
// ResolverOutcome indicates the outcome for the resolver that that the contract // ResolverOutcome indicates the outcome for the resolver that that the contract

@ -6,9 +6,11 @@ import (
"io" "io"
"sync" "sync"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
@ -235,11 +237,13 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
return nil, err return nil, err
} }
var sweepTxID chainhash.Hash
// Sweeper is going to join this input with other inputs if // Sweeper is going to join this input with other inputs if
// possible and publish the sweep tx. When the sweep tx // possible and publish the sweep tx. When the sweep tx
// confirms, it signals us through the result channel with the // confirms, it signals us through the result channel with the
// outcome. Wait for this to happen. // outcome. Wait for this to happen.
recovered := true outcome := channeldb.ResolverOutcomeClaimed
select { select {
case sweepResult := <-resultChan: case sweepResult := <-resultChan:
switch sweepResult.Err { switch sweepResult.Err {
@ -250,7 +254,7 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
// the contract. // the contract.
c.log.Warnf("local commitment output was swept by "+ c.log.Warnf("local commitment output was swept by "+
"remote party via %v", sweepResult.Tx.TxHash()) "remote party via %v", sweepResult.Tx.TxHash())
recovered = false outcome = channeldb.ResolverOutcomeUnclaimed
case nil: case nil:
// No errors, therefore continue processing. // No errors, therefore continue processing.
c.log.Infof("local commitment output fully resolved by "+ c.log.Infof("local commitment output fully resolved by "+
@ -262,22 +266,30 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
return nil, sweepResult.Err return nil, sweepResult.Err
} }
sweepTxID = sweepResult.Tx.TxHash()
case <-c.quit: case <-c.quit:
return nil, errResolverShuttingDown return nil, errResolverShuttingDown
} }
// Funds have been swept and balance is no longer in limbo. // Funds have been swept and balance is no longer in limbo.
c.reportLock.Lock() c.reportLock.Lock()
if recovered { if outcome == channeldb.ResolverOutcomeClaimed {
// We only record the balance as recovered if it actually came // We only record the balance as recovered if it actually came
// back to us. // back to us.
c.currentReport.RecoveredBalance = c.currentReport.LimboBalance c.currentReport.RecoveredBalance = c.currentReport.LimboBalance
} }
c.currentReport.LimboBalance = 0 c.currentReport.LimboBalance = 0
c.reportLock.Unlock() c.reportLock.Unlock()
report := c.currentReport.resolverReport(
&sweepTxID, channeldb.ResolverTypeCommit, outcome,
)
c.resolved = true c.resolved = true
return nil, c.Checkpoint(c, nil)
// Checkpoint the resolver with a closure that will write the outcome
// of the resolver and its sweep transaction to disk.
return nil, c.Checkpoint(c, report)
} }
// Stop signals the resolver to cancel any current resolution processes, and // Stop signals the resolver to cancel any current resolution processes, and

@ -169,13 +169,44 @@ func TestCommitSweepResolverNoDelay(t *testing.T) {
} }
ctx := newCommitSweepResolverTestContext(t, &res) ctx := newCommitSweepResolverTestContext(t, &res)
// Replace our checkpoint with one which will push reports into a
// channel for us to consume. We replace this function on the resolver
// itself because it is created by the test context.
reportChan := make(chan *channeldb.ResolverReport)
ctx.resolver.Checkpoint = func(_ ContractResolver,
reports ...*channeldb.ResolverReport) error {
// Send all of our reports into the channel.
for _, report := range reports {
reportChan <- report
}
return nil
}
ctx.resolve() ctx.resolve()
ctx.notifier.confChan <- &chainntnfs.TxConfirmation{} spendTx := &wire.MsgTx{}
spendHash := spendTx.TxHash()
ctx.notifier.confChan <- &chainntnfs.TxConfirmation{
Tx: spendTx,
}
// No csv delay, so the input should be swept immediately. // No csv delay, so the input should be swept immediately.
<-ctx.sweeper.sweptInputs <-ctx.sweeper.sweptInputs
amt := btcutil.Amount(res.SelfOutputSignDesc.Output.Value)
expectedReport := &channeldb.ResolverReport{
OutPoint: wire.OutPoint{},
Amount: amt,
ResolverType: channeldb.ResolverTypeCommit,
ResolverOutcome: channeldb.ResolverOutcomeClaimed,
SpendTxID: &spendHash,
}
assertResolverReport(t, reportChan, expectedReport)
ctx.waitForResult() ctx.waitForResult()
} }
@ -203,6 +234,21 @@ func testCommitSweepResolverDelay(t *testing.T, sweepErr error) {
ctx := newCommitSweepResolverTestContext(t, &res) ctx := newCommitSweepResolverTestContext(t, &res)
// Replace our checkpoint with one which will push reports into a
// channel for us to consume. We replace this function on the resolver
// itself because it is created by the test context.
reportChan := make(chan *channeldb.ResolverReport)
ctx.resolver.Checkpoint = func(_ ContractResolver,
reports ...*channeldb.ResolverReport) error {
// Send all of our reports into the channel.
for _, report := range reports {
reportChan <- report
}
return nil
}
// Setup whether we expect the sweeper to receive a sweep error in this // Setup whether we expect the sweeper to receive a sweep error in this
// test case. // test case.
ctx.sweeper.sweepErr = sweepErr ctx.sweeper.sweepErr = sweepErr
@ -250,6 +296,22 @@ func testCommitSweepResolverDelay(t *testing.T, sweepErr error) {
<-ctx.sweeper.sweptInputs <-ctx.sweeper.sweptInputs
// Set the resolution report outcome based on whether our sweep
// succeeded.
outcome := channeldb.ResolverOutcomeClaimed
if sweepErr != nil {
outcome = channeldb.ResolverOutcomeUnclaimed
}
sweepTx := ctx.sweeper.sweepTx.TxHash()
assertResolverReport(t, reportChan, &channeldb.ResolverReport{
OutPoint: outpoint,
ResolverType: channeldb.ResolverTypeCommit,
ResolverOutcome: outcome,
Amount: btcutil.Amount(amt),
SpendTxID: &sweepTx,
})
ctx.waitForResult() ctx.waitForResult()
// If this test case generates a sweep error, we don't expect to be // If this test case generates a sweep error, we don't expect to be