contractcourt: persist commit sweep resolutions
This commit is contained in:
parent
d0ec872ef3
commit
d8a4b37c0e
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user