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

View File

@ -55,6 +55,10 @@ const (
// ResolverTypeOutgoingHtlc represents resolution of an outgoing htlc.
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

View File

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

View File

@ -169,13 +169,44 @@ func TestCommitSweepResolverNoDelay(t *testing.T) {
}
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.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.
<-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()
}
@ -203,6 +234,21 @@ func testCommitSweepResolverDelay(t *testing.T, sweepErr error) {
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
// test case.
ctx.sweeper.sweepErr = sweepErr
@ -250,6 +296,22 @@ func testCommitSweepResolverDelay(t *testing.T, sweepErr error) {
<-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()
// If this test case generates a sweep error, we don't expect to be