cnct: use sweeper in commit resolver

Now that the sweeper is available, it isn't necessary anymore for the
commit resolver to craft its own sweep tx. Instead it can offer its
input to the sweeper and wait for the outcome.
This commit is contained in:
Joost Jager 2019-01-17 13:11:04 +01:00
parent b7387a5972
commit e486340106
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
2 changed files with 77 additions and 124 deletions

@ -294,7 +294,6 @@ func TestContractInsertionRetrieval(t *testing.T) {
resolved: false, resolved: false,
broadcastHeight: 109, broadcastHeight: 109,
chanPoint: testChanPoint1, chanPoint: testChanPoint1,
sweepTx: nil,
}, },
} }

@ -1,17 +1,14 @@
package contractcourt package contractcourt
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"github.com/lightningnetwork/lnd/input"
"io" "io"
"io/ioutil"
"github.com/lightningnetwork/lnd/input"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep"
) )
// commitSweepResolver is a resolver that will attempt to sweep the commitment // commitSweepResolver is a resolver that will attempt to sweep the commitment
@ -34,11 +31,6 @@ type commitSweepResolver struct {
// chanPoint is the channel point of the original contract. // chanPoint is the channel point of the original contract.
chanPoint wire.OutPoint chanPoint wire.OutPoint
// sweepTx is the fully signed transaction which when broadcast, will
// sweep the commitment output into an output under control by the
// source wallet.
sweepTx *wire.MsgTx
ResolverKit ResolverKit
} }
@ -87,20 +79,14 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
return nil, fmt.Errorf("quitting") return nil, fmt.Errorf("quitting")
} }
// TODO(roasbeef): checkpoint tx confirmed?
// We're dealing with our commitment transaction if the delay on the // We're dealing with our commitment transaction if the delay on the
// resolution isn't zero. // resolution isn't zero.
isLocalCommitTx := c.commitResolution.MaturityDelay != 0 isLocalCommitTx := c.commitResolution.MaturityDelay != 0
switch { if !isLocalCommitTx {
// If the sweep transaction isn't already generated, and the remote // We'll craft an input with all the information required for
// party broadcast the commitment transaction then we'll create it now. // the sweeper to create a fully valid sweeping transaction to
case c.sweepTx == nil && !isLocalCommitTx: // recover these coins.
// As we haven't already generated the sweeping transaction,
// we'll now craft an input with all the information required
// to create a fully valid sweeping transaction to recover
// these coins.
inp := input.MakeBaseInput( inp := input.MakeBaseInput(
&c.commitResolution.SelfOutPoint, &c.commitResolution.SelfOutPoint,
input.CommitmentNoDelay, input.CommitmentNoDelay,
@ -108,106 +94,86 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
c.broadcastHeight, c.broadcastHeight,
) )
// With out input constructed, we'll now request that the // With our input constructed, we'll now offer it to the
// sweeper construct a valid sweeping transaction for this // sweeper.
// input. log.Infof("%T(%v): sweeping commit output", c, c.chanPoint)
//
// TODO: Set tx lock time to current block height instead of resultChan, err := c.Sweeper.SweepInput(&inp)
// zero. Will be taken care of once sweeper implementation is
// complete.
//
// TODO: Use time-based sweeper and result chan.
c.sweepTx, err = c.Sweeper.CreateSweepTx(
[]input.Input{&inp},
sweep.FeePreference{
ConfTarget: sweepConfTarget,
}, 0,
)
if err != nil { if err != nil {
log.Errorf("%T(%v): unable to sweep input: %v",
c, c.chanPoint, err)
return nil, err return nil, err
} }
log.Infof("%T(%v): sweeping commit output with tx=%v", c, // Sweeper is going to join this input with other inputs if
c.chanPoint, spew.Sdump(c.sweepTx)) // 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.
select {
case sweepResult := <-resultChan:
if sweepResult.Err != nil {
log.Errorf("%T(%v): unable to sweep input: %v",
c, c.chanPoint, sweepResult.Err)
return nil, err
}
log.Infof("ChannelPoint(%v) commit tx is fully resolved by "+
"sweep tx: %v", c.chanPoint, sweepResult.Tx.TxHash())
case <-c.Quit:
return nil, fmt.Errorf("quitting")
}
c.resolved = true
return nil, c.Checkpoint(c)
}
// Otherwise we are dealing with a local commitment transaction and the
// output we need to sweep has been sent to the nursery for incubation.
// In this case, we'll wait until the commitment output has been spent.
spendNtfn, err := c.Notifier.RegisterSpendNtfn(
&c.commitResolution.SelfOutPoint,
c.commitResolution.SelfOutputSignDesc.Output.PkScript,
c.broadcastHeight,
)
if err != nil {
return nil, err
}
log.Infof("%T(%v): waiting for commit output to be swept", c,
c.chanPoint)
var sweepTx *wire.MsgTx
select {
case commitSpend, ok := <-spendNtfn.Spend:
if !ok {
return nil, fmt.Errorf("quitting")
}
// Once we detect the commitment output has been spent,
// we'll extract the spending transaction itself, as we
// now consider this to be our sweep transaction.
sweepTx = commitSpend.SpendingTx
log.Infof("%T(%v): commit output swept by txid=%v",
c, c.chanPoint, sweepTx.TxHash())
// With the sweep transaction constructed, we'll now Checkpoint
// our state.
if err := c.Checkpoint(c); err != nil { if err := c.Checkpoint(c); err != nil {
log.Errorf("unable to Checkpoint: %v", err) log.Errorf("unable to Checkpoint: %v", err)
return nil, err return nil, err
} }
case <-c.Quit:
// With the sweep transaction checkpointed, we'll now publish return nil, fmt.Errorf("quitting")
// the transaction. Upon restart, the resolver will immediately
// take the case below since the sweep tx is checkpointed.
err := c.PublishTx(c.sweepTx)
if err != nil && err != lnwallet.ErrDoubleSpend {
log.Errorf("%T(%v): unable to publish sweep tx: %v",
c, c.chanPoint, err)
return nil, err
}
// If the sweep transaction has been generated, and the remote party
// broadcast the commit transaction, we'll republish it for reliability
// to ensure it confirms. The resolver will enter this case after
// checkpointing in the case above, ensuring we reliably on restarts.
case c.sweepTx != nil && !isLocalCommitTx:
err := c.PublishTx(c.sweepTx)
if err != nil && err != lnwallet.ErrDoubleSpend {
log.Errorf("%T(%v): unable to publish sweep tx: %v",
c, c.chanPoint, err)
return nil, err
}
// Otherwise, this is our commitment transaction, So we'll obtain the
// sweep transaction once the commitment output has been spent.
case c.sweepTx == nil && isLocalCommitTx:
// Otherwise, if we're dealing with our local commitment
// transaction, then the output we need to sweep has been sent
// to the nursery for incubation. In this case, we'll wait
// until the commitment output has been spent.
spendNtfn, err := c.Notifier.RegisterSpendNtfn(
&c.commitResolution.SelfOutPoint,
c.commitResolution.SelfOutputSignDesc.Output.PkScript,
c.broadcastHeight,
)
if err != nil {
return nil, err
}
log.Infof("%T(%v): waiting for commit output to be swept", c,
c.chanPoint)
select {
case commitSpend, ok := <-spendNtfn.Spend:
if !ok {
return nil, fmt.Errorf("quitting")
}
// Once we detect the commitment output has been spent,
// we'll extract the spending transaction itself, as we
// now consider this to be our sweep transaction.
c.sweepTx = commitSpend.SpendingTx
log.Infof("%T(%v): commit output swept by txid=%v",
c, c.chanPoint, c.sweepTx.TxHash())
if err := c.Checkpoint(c); err != nil {
log.Errorf("unable to Checkpoint: %v", err)
return nil, err
}
case <-c.Quit:
return nil, fmt.Errorf("quitting")
}
} }
log.Infof("%T(%v): waiting for commit sweep txid=%v conf", c, c.chanPoint, log.Infof("%T(%v): waiting for commit sweep txid=%v conf", c, c.chanPoint,
c.sweepTx.TxHash()) sweepTx.TxHash())
// Now we'll wait until the sweeping transaction has been fully // Now we'll wait until the sweeping transaction has been fully
// confirmed. Once it's confirmed, we can mark this contract resolved. // confirmed. Once it's confirmed, we can mark this contract resolved.
sweepTXID := c.sweepTx.TxHash() sweepTXID := sweepTx.TxHash()
sweepingScript := c.sweepTx.TxOut[0].PkScript sweepingScript := sweepTx.TxOut[0].PkScript
confNtfn, err = c.Notifier.RegisterConfirmationsNtfn( confNtfn, err = c.Notifier.RegisterConfirmationsNtfn(
&sweepTXID, sweepingScript, 1, c.broadcastHeight, &sweepTXID, sweepingScript, 1, c.broadcastHeight,
) )
@ -272,9 +238,9 @@ func (c *commitSweepResolver) Encode(w io.Writer) error {
return err return err
} }
if c.sweepTx != nil { // Previously a sweep tx was serialized at this point. Refactoring
return c.sweepTx.Serialize(w) // removed this, but keep in mind that this data may still be present in
} // the database.
return nil return nil
} }
@ -303,22 +269,10 @@ func (c *commitSweepResolver) Decode(r io.Reader) error {
return err return err
} }
txBytes, err := ioutil.ReadAll(r) // Previously a sweep tx was deserialized at this point. Refactoring
if err != nil { // removed this, but keep in mind that this data may still be present in
return err // the database.
}
if len(txBytes) == 0 {
return nil
}
txReader := bytes.NewReader(txBytes)
tx := &wire.MsgTx{}
if err := tx.Deserialize(txReader); err != nil {
return nil
}
c.sweepTx = tx
return nil return nil
} }