mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-29 02:00:54 +02:00
contractcourt: fix concurrent access to resolved
This commit makes `resolved` an atomic bool to avoid data race. This field is now defined in `contractResolverKit` to avoid code duplication.
This commit is contained in:
@ -24,9 +24,6 @@ type anchorResolver struct {
|
|||||||
// anchor is the outpoint on the commitment transaction.
|
// anchor is the outpoint on the commitment transaction.
|
||||||
anchor wire.OutPoint
|
anchor wire.OutPoint
|
||||||
|
|
||||||
// resolved reflects if the contract has been fully resolved or not.
|
|
||||||
resolved bool
|
|
||||||
|
|
||||||
// broadcastHeight is the height that the original contract was
|
// broadcastHeight is the height that the original contract was
|
||||||
// broadcast to the main-chain at. We'll use this value to bound any
|
// broadcast to the main-chain at. We'll use this value to bound any
|
||||||
// historical queries to the chain for spends/confirmations.
|
// historical queries to the chain for spends/confirmations.
|
||||||
@ -89,7 +86,7 @@ func (c *anchorResolver) ResolverKey() []byte {
|
|||||||
// NOTE: Part of the ContractResolver interface.
|
// NOTE: Part of the ContractResolver interface.
|
||||||
func (c *anchorResolver) Resolve() (ContractResolver, error) {
|
func (c *anchorResolver) Resolve() (ContractResolver, error) {
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
if c.resolved {
|
if c.IsResolved() {
|
||||||
c.log.Errorf("already resolved")
|
c.log.Errorf("already resolved")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -139,7 +136,7 @@ func (c *anchorResolver) Resolve() (ContractResolver, error) {
|
|||||||
)
|
)
|
||||||
c.reportLock.Unlock()
|
c.reportLock.Unlock()
|
||||||
|
|
||||||
c.resolved = true
|
c.markResolved()
|
||||||
return nil, c.PutResolverReport(nil, report)
|
return nil, c.PutResolverReport(nil, report)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,14 +151,6 @@ func (c *anchorResolver) Stop() {
|
|||||||
close(c.quit)
|
close(c.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the stored state in the resolve is fully
|
|
||||||
// resolved. In this case the target output can be forgotten.
|
|
||||||
//
|
|
||||||
// NOTE: Part of the ContractResolver interface.
|
|
||||||
func (c *anchorResolver) IsResolved() bool {
|
|
||||||
return c.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupplementState allows the user of a ContractResolver to supplement it with
|
// SupplementState allows the user of a ContractResolver to supplement it with
|
||||||
// state required for the proper resolution of a contract.
|
// state required for the proper resolution of a contract.
|
||||||
//
|
//
|
||||||
@ -198,7 +187,7 @@ func (c *anchorResolver) Launch() error {
|
|||||||
c.launched = true
|
c.launched = true
|
||||||
|
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
if c.resolved {
|
if c.IsResolved() {
|
||||||
c.log.Errorf("already resolved")
|
c.log.Errorf("already resolved")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,6 @@ import (
|
|||||||
// future, this will likely take over the duties the current BreachArbitrator
|
// future, this will likely take over the duties the current BreachArbitrator
|
||||||
// has.
|
// has.
|
||||||
type breachResolver struct {
|
type breachResolver struct {
|
||||||
// resolved reflects if the contract has been fully resolved or not.
|
|
||||||
resolved bool
|
|
||||||
|
|
||||||
// subscribed denotes whether or not the breach resolver has subscribed
|
// subscribed denotes whether or not the breach resolver has subscribed
|
||||||
// to the BreachArbitrator for breach resolution.
|
// to the BreachArbitrator for breach resolution.
|
||||||
subscribed bool
|
subscribed bool
|
||||||
@ -62,7 +59,7 @@ func (b *breachResolver) Resolve() (ContractResolver, error) {
|
|||||||
// If the breach resolution process is already complete, then
|
// If the breach resolution process is already complete, then
|
||||||
// we can cleanup and checkpoint the resolved state.
|
// we can cleanup and checkpoint the resolved state.
|
||||||
if complete {
|
if complete {
|
||||||
b.resolved = true
|
b.markResolved()
|
||||||
return nil, b.Checkpoint(b)
|
return nil, b.Checkpoint(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +72,9 @@ func (b *breachResolver) Resolve() (ContractResolver, error) {
|
|||||||
// The replyChan has been closed, signalling that the breach
|
// The replyChan has been closed, signalling that the breach
|
||||||
// has been fully resolved. Checkpoint the resolved state and
|
// has been fully resolved. Checkpoint the resolved state and
|
||||||
// exit.
|
// exit.
|
||||||
b.resolved = true
|
b.markResolved()
|
||||||
return nil, b.Checkpoint(b)
|
return nil, b.Checkpoint(b)
|
||||||
|
|
||||||
case <-b.quit:
|
case <-b.quit:
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,19 +87,13 @@ func (b *breachResolver) Stop() {
|
|||||||
close(b.quit)
|
close(b.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the breachResolver is fully resolved and cleanup
|
|
||||||
// can occur.
|
|
||||||
func (b *breachResolver) IsResolved() bool {
|
|
||||||
return b.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupplementState adds additional state to the breachResolver.
|
// SupplementState adds additional state to the breachResolver.
|
||||||
func (b *breachResolver) SupplementState(_ *channeldb.OpenChannel) {
|
func (b *breachResolver) SupplementState(_ *channeldb.OpenChannel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes the breachResolver to the passed writer.
|
// Encode encodes the breachResolver to the passed writer.
|
||||||
func (b *breachResolver) Encode(w io.Writer) error {
|
func (b *breachResolver) Encode(w io.Writer) error {
|
||||||
return binary.Write(w, endian, b.resolved)
|
return binary.Write(w, endian, b.IsResolved())
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBreachResolverFromReader attempts to decode an encoded breachResolver
|
// newBreachResolverFromReader attempts to decode an encoded breachResolver
|
||||||
@ -114,9 +106,13 @@ func newBreachResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
replyChan: make(chan struct{}),
|
replyChan: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Read(r, endian, &b.resolved); err != nil {
|
var resolved bool
|
||||||
|
if err := binary.Read(r, endian, &resolved); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resolved {
|
||||||
|
b.markResolved()
|
||||||
|
}
|
||||||
|
|
||||||
b.initLogger(fmt.Sprintf("%T(%v)", b, b.ChanPoint))
|
b.initLogger(fmt.Sprintf("%T(%v)", b, b.ChanPoint))
|
||||||
|
|
||||||
|
@ -206,8 +206,8 @@ func assertResolversEqual(t *testing.T, originalResolver ContractResolver,
|
|||||||
ogRes.outputIncubating, diskRes.outputIncubating)
|
ogRes.outputIncubating, diskRes.outputIncubating)
|
||||||
}
|
}
|
||||||
if ogRes.resolved != diskRes.resolved {
|
if ogRes.resolved != diskRes.resolved {
|
||||||
t.Fatalf("expected %v, got %v", ogRes.resolved,
|
t.Fatalf("expected %v, got %v", ogRes.resolved.Load(),
|
||||||
diskRes.resolved)
|
diskRes.resolved.Load())
|
||||||
}
|
}
|
||||||
if ogRes.broadcastHeight != diskRes.broadcastHeight {
|
if ogRes.broadcastHeight != diskRes.broadcastHeight {
|
||||||
t.Fatalf("expected %v, got %v",
|
t.Fatalf("expected %v, got %v",
|
||||||
@ -229,8 +229,8 @@ func assertResolversEqual(t *testing.T, originalResolver ContractResolver,
|
|||||||
ogRes.outputIncubating, diskRes.outputIncubating)
|
ogRes.outputIncubating, diskRes.outputIncubating)
|
||||||
}
|
}
|
||||||
if ogRes.resolved != diskRes.resolved {
|
if ogRes.resolved != diskRes.resolved {
|
||||||
t.Fatalf("expected %v, got %v", ogRes.resolved,
|
t.Fatalf("expected %v, got %v", ogRes.resolved.Load(),
|
||||||
diskRes.resolved)
|
diskRes.resolved.Load())
|
||||||
}
|
}
|
||||||
if ogRes.broadcastHeight != diskRes.broadcastHeight {
|
if ogRes.broadcastHeight != diskRes.broadcastHeight {
|
||||||
t.Fatalf("expected %v, got %v",
|
t.Fatalf("expected %v, got %v",
|
||||||
@ -275,8 +275,8 @@ func assertResolversEqual(t *testing.T, originalResolver ContractResolver,
|
|||||||
ogRes.commitResolution, diskRes.commitResolution)
|
ogRes.commitResolution, diskRes.commitResolution)
|
||||||
}
|
}
|
||||||
if ogRes.resolved != diskRes.resolved {
|
if ogRes.resolved != diskRes.resolved {
|
||||||
t.Fatalf("expected %v, got %v", ogRes.resolved,
|
t.Fatalf("expected %v, got %v", ogRes.resolved.Load(),
|
||||||
diskRes.resolved)
|
diskRes.resolved.Load())
|
||||||
}
|
}
|
||||||
if ogRes.broadcastHeight != diskRes.broadcastHeight {
|
if ogRes.broadcastHeight != diskRes.broadcastHeight {
|
||||||
t.Fatalf("expected %v, got %v",
|
t.Fatalf("expected %v, got %v",
|
||||||
@ -312,13 +312,14 @@ func TestContractInsertionRetrieval(t *testing.T) {
|
|||||||
SweepSignDesc: testSignDesc,
|
SweepSignDesc: testSignDesc,
|
||||||
},
|
},
|
||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
|
||||||
broadcastHeight: 102,
|
broadcastHeight: 102,
|
||||||
htlc: channeldb.HTLC{
|
htlc: channeldb.HTLC{
|
||||||
HtlcIndex: 12,
|
HtlcIndex: 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
successResolver := htlcSuccessResolver{
|
timeoutResolver.resolved.Store(true)
|
||||||
|
|
||||||
|
successResolver := &htlcSuccessResolver{
|
||||||
htlcResolution: lnwallet.IncomingHtlcResolution{
|
htlcResolution: lnwallet.IncomingHtlcResolution{
|
||||||
Preimage: testPreimage,
|
Preimage: testPreimage,
|
||||||
SignedSuccessTx: nil,
|
SignedSuccessTx: nil,
|
||||||
@ -327,40 +328,49 @@ func TestContractInsertionRetrieval(t *testing.T) {
|
|||||||
SweepSignDesc: testSignDesc,
|
SweepSignDesc: testSignDesc,
|
||||||
},
|
},
|
||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
|
||||||
broadcastHeight: 109,
|
broadcastHeight: 109,
|
||||||
htlc: channeldb.HTLC{
|
htlc: channeldb.HTLC{
|
||||||
RHash: testPreimage,
|
RHash: testPreimage,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
resolvers := []ContractResolver{
|
successResolver.resolved.Store(true)
|
||||||
&timeoutResolver,
|
|
||||||
&successResolver,
|
commitResolver := &commitSweepResolver{
|
||||||
&commitSweepResolver{
|
commitResolution: lnwallet.CommitOutputResolution{
|
||||||
commitResolution: lnwallet.CommitOutputResolution{
|
SelfOutPoint: testChanPoint2,
|
||||||
SelfOutPoint: testChanPoint2,
|
SelfOutputSignDesc: testSignDesc,
|
||||||
SelfOutputSignDesc: testSignDesc,
|
MaturityDelay: 99,
|
||||||
MaturityDelay: 99,
|
|
||||||
},
|
|
||||||
resolved: false,
|
|
||||||
broadcastHeight: 109,
|
|
||||||
chanPoint: testChanPoint1,
|
|
||||||
},
|
},
|
||||||
|
broadcastHeight: 109,
|
||||||
|
chanPoint: testChanPoint1,
|
||||||
|
}
|
||||||
|
commitResolver.resolved.Store(false)
|
||||||
|
|
||||||
|
resolvers := []ContractResolver{
|
||||||
|
&timeoutResolver, successResolver, commitResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
// All resolvers require a unique ResolverKey() output. To achieve this
|
// All resolvers require a unique ResolverKey() output. To achieve this
|
||||||
// for the composite resolvers, we'll mutate the underlying resolver
|
// for the composite resolvers, we'll mutate the underlying resolver
|
||||||
// with a new outpoint.
|
// with a new outpoint.
|
||||||
contestTimeout := timeoutResolver
|
contestTimeout := htlcTimeoutResolver{
|
||||||
contestTimeout.htlcResolution.ClaimOutpoint = randOutPoint()
|
htlcResolution: lnwallet.OutgoingHtlcResolution{
|
||||||
|
ClaimOutpoint: randOutPoint(),
|
||||||
|
SweepSignDesc: testSignDesc,
|
||||||
|
},
|
||||||
|
}
|
||||||
resolvers = append(resolvers, &htlcOutgoingContestResolver{
|
resolvers = append(resolvers, &htlcOutgoingContestResolver{
|
||||||
htlcTimeoutResolver: &contestTimeout,
|
htlcTimeoutResolver: &contestTimeout,
|
||||||
})
|
})
|
||||||
contestSuccess := successResolver
|
contestSuccess := &htlcSuccessResolver{
|
||||||
contestSuccess.htlcResolution.ClaimOutpoint = randOutPoint()
|
htlcResolution: lnwallet.IncomingHtlcResolution{
|
||||||
|
ClaimOutpoint: randOutPoint(),
|
||||||
|
SweepSignDesc: testSignDesc,
|
||||||
|
},
|
||||||
|
}
|
||||||
resolvers = append(resolvers, &htlcIncomingContestResolver{
|
resolvers = append(resolvers, &htlcIncomingContestResolver{
|
||||||
htlcExpiry: 100,
|
htlcExpiry: 100,
|
||||||
htlcSuccessResolver: &contestSuccess,
|
htlcSuccessResolver: contestSuccess,
|
||||||
})
|
})
|
||||||
|
|
||||||
// For quick lookup during the test, we'll create this map which allow
|
// For quick lookup during the test, we'll create this map which allow
|
||||||
@ -438,12 +448,12 @@ func TestContractResolution(t *testing.T) {
|
|||||||
SweepSignDesc: testSignDesc,
|
SweepSignDesc: testSignDesc,
|
||||||
},
|
},
|
||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
|
||||||
broadcastHeight: 192,
|
broadcastHeight: 192,
|
||||||
htlc: channeldb.HTLC{
|
htlc: channeldb.HTLC{
|
||||||
HtlcIndex: 9912,
|
HtlcIndex: 9912,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
timeoutResolver.resolved.Store(true)
|
||||||
|
|
||||||
// First, we'll insert the resolver into the database and ensure that
|
// First, we'll insert the resolver into the database and ensure that
|
||||||
// we get the same resolver out the other side. We do not need to apply
|
// we get the same resolver out the other side. We do not need to apply
|
||||||
@ -491,12 +501,13 @@ func TestContractSwapping(t *testing.T) {
|
|||||||
SweepSignDesc: testSignDesc,
|
SweepSignDesc: testSignDesc,
|
||||||
},
|
},
|
||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
|
||||||
broadcastHeight: 102,
|
broadcastHeight: 102,
|
||||||
htlc: channeldb.HTLC{
|
htlc: channeldb.HTLC{
|
||||||
HtlcIndex: 12,
|
HtlcIndex: 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
timeoutResolver.resolved.Store(true)
|
||||||
|
|
||||||
contestResolver := &htlcOutgoingContestResolver{
|
contestResolver := &htlcOutgoingContestResolver{
|
||||||
htlcTimeoutResolver: timeoutResolver,
|
htlcTimeoutResolver: timeoutResolver,
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,6 @@ type commitSweepResolver struct {
|
|||||||
// this HTLC on-chain.
|
// this HTLC on-chain.
|
||||||
commitResolution lnwallet.CommitOutputResolution
|
commitResolution lnwallet.CommitOutputResolution
|
||||||
|
|
||||||
// resolved reflects if the contract has been fully resolved or not.
|
|
||||||
resolved bool
|
|
||||||
|
|
||||||
// broadcastHeight is the height that the original contract was
|
// broadcastHeight is the height that the original contract was
|
||||||
// broadcast to the main-chain at. We'll use this value to bound any
|
// broadcast to the main-chain at. We'll use this value to bound any
|
||||||
// historical queries to the chain for spends/confirmations.
|
// historical queries to the chain for spends/confirmations.
|
||||||
@ -171,7 +168,7 @@ func (c *commitSweepResolver) getCommitTxConfHeight() (uint32, error) {
|
|||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
|
func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
if c.resolved {
|
if c.IsResolved() {
|
||||||
c.log.Errorf("already resolved")
|
c.log.Errorf("already resolved")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -224,7 +221,7 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
|
|||||||
report := c.currentReport.resolverReport(
|
report := c.currentReport.resolverReport(
|
||||||
&sweepTxID, channeldb.ResolverTypeCommit, outcome,
|
&sweepTxID, channeldb.ResolverTypeCommit, outcome,
|
||||||
)
|
)
|
||||||
c.resolved = true
|
c.markResolved()
|
||||||
|
|
||||||
// Checkpoint the resolver with a closure that will write the outcome
|
// Checkpoint the resolver with a closure that will write the outcome
|
||||||
// of the resolver and its sweep transaction to disk.
|
// of the resolver and its sweep transaction to disk.
|
||||||
@ -241,14 +238,6 @@ func (c *commitSweepResolver) Stop() {
|
|||||||
close(c.quit)
|
close(c.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the stored state in the resolve is fully
|
|
||||||
// resolved. In this case the target output can be forgotten.
|
|
||||||
//
|
|
||||||
// NOTE: Part of the ContractResolver interface.
|
|
||||||
func (c *commitSweepResolver) IsResolved() bool {
|
|
||||||
return c.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupplementState allows the user of a ContractResolver to supplement it with
|
// SupplementState allows the user of a ContractResolver to supplement it with
|
||||||
// state required for the proper resolution of a contract.
|
// state required for the proper resolution of a contract.
|
||||||
//
|
//
|
||||||
@ -277,7 +266,7 @@ func (c *commitSweepResolver) Encode(w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(w, endian, c.resolved); err != nil {
|
if err := binary.Write(w, endian, c.IsResolved()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(w, endian, c.broadcastHeight); err != nil {
|
if err := binary.Write(w, endian, c.broadcastHeight); err != nil {
|
||||||
@ -312,9 +301,14 @@ func newCommitSweepResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Read(r, endian, &c.resolved); err != nil {
|
var resolved bool
|
||||||
|
if err := binary.Read(r, endian, &resolved); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resolved {
|
||||||
|
c.markResolved()
|
||||||
|
}
|
||||||
|
|
||||||
if err := binary.Read(r, endian, &c.broadcastHeight); err != nil {
|
if err := binary.Read(r, endian, &c.broadcastHeight); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -383,7 +377,7 @@ func (c *commitSweepResolver) Launch() error {
|
|||||||
c.launched = true
|
c.launched = true
|
||||||
|
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
if c.resolved {
|
if c.IsResolved() {
|
||||||
c.log.Errorf("already resolved")
|
c.log.Errorf("already resolved")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btclog/v2"
|
"github.com/btcsuite/btclog/v2"
|
||||||
@ -119,6 +120,9 @@ type contractResolverKit struct {
|
|||||||
// launched specifies whether the resolver has been launched. Calling
|
// launched specifies whether the resolver has been launched. Calling
|
||||||
// `Launch` will be a no-op if this is true.
|
// `Launch` will be a no-op if this is true.
|
||||||
launched bool
|
launched bool
|
||||||
|
|
||||||
|
// resolved reflects if the contract has been fully resolved or not.
|
||||||
|
resolved atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newContractResolverKit instantiates the mix-in struct.
|
// newContractResolverKit instantiates the mix-in struct.
|
||||||
@ -137,6 +141,19 @@ func (r *contractResolverKit) initLogger(prefix string) {
|
|||||||
r.log = log.WithPrefix(logPrefix)
|
r.log = log.WithPrefix(logPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsResolved returns true if the stored state in the resolve is fully
|
||||||
|
// resolved. In this case the target output can be forgotten.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the ContractResolver interface.
|
||||||
|
func (r *contractResolverKit) IsResolved() bool {
|
||||||
|
return r.resolved.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// markResolved marks the resolver as resolved.
|
||||||
|
func (r *contractResolverKit) markResolved() {
|
||||||
|
r.resolved.Store(true)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// errResolverShuttingDown is returned when the resolver stops
|
// errResolverShuttingDown is returned when the resolver stops
|
||||||
// progressing because it received the quit signal.
|
// progressing because it received the quit signal.
|
||||||
|
@ -124,7 +124,7 @@ func (h *htlcIncomingContestResolver) Launch() error {
|
|||||||
func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||||
// If we're already full resolved, then we don't have anything further
|
// If we're already full resolved, then we don't have anything further
|
||||||
// to do.
|
// to do.
|
||||||
if h.resolved {
|
if h.IsResolved() {
|
||||||
h.log.Errorf("already resolved")
|
h.log.Errorf("already resolved")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
// will time it out and get their funds back. This situation
|
// will time it out and get their funds back. This situation
|
||||||
// can present itself when we crash before processRemoteAdds in
|
// can present itself when we crash before processRemoteAdds in
|
||||||
// the link has ran.
|
// the link has ran.
|
||||||
h.resolved = true
|
h.markResolved()
|
||||||
|
|
||||||
if err := h.processFinalHtlcFail(); err != nil {
|
if err := h.processFinalHtlcFail(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -193,7 +193,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
log.Infof("%T(%v): HTLC has timed out (expiry=%v, height=%v), "+
|
log.Infof("%T(%v): HTLC has timed out (expiry=%v, height=%v), "+
|
||||||
"abandoning", h, h.htlcResolution.ClaimOutpoint,
|
"abandoning", h, h.htlcResolution.ClaimOutpoint,
|
||||||
h.htlcExpiry, currentHeight)
|
h.htlcExpiry, currentHeight)
|
||||||
h.resolved = true
|
h.markResolved()
|
||||||
|
|
||||||
if err := h.processFinalHtlcFail(); err != nil {
|
if err := h.processFinalHtlcFail(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -234,7 +234,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
h.htlcResolution.ClaimOutpoint,
|
h.htlcResolution.ClaimOutpoint,
|
||||||
h.htlcExpiry, currentHeight)
|
h.htlcExpiry, currentHeight)
|
||||||
|
|
||||||
h.resolved = true
|
h.markResolved()
|
||||||
|
|
||||||
if err := h.processFinalHtlcFail(); err != nil {
|
if err := h.processFinalHtlcFail(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -396,7 +396,8 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
"(expiry=%v, height=%v), abandoning", h,
|
"(expiry=%v, height=%v), abandoning", h,
|
||||||
h.htlcResolution.ClaimOutpoint,
|
h.htlcResolution.ClaimOutpoint,
|
||||||
h.htlcExpiry, currentHeight)
|
h.htlcExpiry, currentHeight)
|
||||||
h.resolved = true
|
|
||||||
|
h.markResolved()
|
||||||
|
|
||||||
if err := h.processFinalHtlcFail(); err != nil {
|
if err := h.processFinalHtlcFail(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -517,14 +518,6 @@ func (h *htlcIncomingContestResolver) Stop() {
|
|||||||
close(h.quit)
|
close(h.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the stored state in the resolve is fully
|
|
||||||
// resolved. In this case the target output can be forgotten.
|
|
||||||
//
|
|
||||||
// NOTE: Part of the ContractResolver interface.
|
|
||||||
func (h *htlcIncomingContestResolver) IsResolved() bool {
|
|
||||||
return h.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes an encoded version of the ContractResolver into the passed
|
// Encode writes an encoded version of the ContractResolver into the passed
|
||||||
// Writer.
|
// Writer.
|
||||||
//
|
//
|
||||||
|
@ -82,7 +82,7 @@ func (h *htlcOutgoingContestResolver) Launch() error {
|
|||||||
func (h *htlcOutgoingContestResolver) Resolve() (ContractResolver, error) {
|
func (h *htlcOutgoingContestResolver) Resolve() (ContractResolver, error) {
|
||||||
// If we're already full resolved, then we don't have anything further
|
// If we're already full resolved, then we don't have anything further
|
||||||
// to do.
|
// to do.
|
||||||
if h.resolved {
|
if h.IsResolved() {
|
||||||
h.log.Errorf("already resolved")
|
h.log.Errorf("already resolved")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -215,14 +215,6 @@ func (h *htlcOutgoingContestResolver) Stop() {
|
|||||||
close(h.quit)
|
close(h.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the stored state in the resolve is fully
|
|
||||||
// resolved. In this case the target output can be forgotten.
|
|
||||||
//
|
|
||||||
// NOTE: Part of the ContractResolver interface.
|
|
||||||
func (h *htlcOutgoingContestResolver) IsResolved() bool {
|
|
||||||
return h.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes an encoded version of the ContractResolver into the passed
|
// Encode writes an encoded version of the ContractResolver into the passed
|
||||||
// Writer.
|
// Writer.
|
||||||
//
|
//
|
||||||
|
@ -42,9 +42,6 @@ type htlcSuccessResolver struct {
|
|||||||
// second-level output (true).
|
// second-level output (true).
|
||||||
outputIncubating bool
|
outputIncubating bool
|
||||||
|
|
||||||
// resolved reflects if the contract has been fully resolved or not.
|
|
||||||
resolved bool
|
|
||||||
|
|
||||||
// broadcastHeight is the height that the original contract was
|
// broadcastHeight is the height that the original contract was
|
||||||
// broadcast to the main-chain at. We'll use this value to bound any
|
// broadcast to the main-chain at. We'll use this value to bound any
|
||||||
// historical queries to the chain for spends/confirmations.
|
// historical queries to the chain for spends/confirmations.
|
||||||
@ -122,7 +119,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
case h.resolved:
|
case h.IsResolved():
|
||||||
h.log.Errorf("already resolved")
|
h.log.Errorf("already resolved")
|
||||||
|
|
||||||
// If this is an output on the remote party's commitment transaction,
|
// If this is an output on the remote party's commitment transaction,
|
||||||
@ -226,7 +223,7 @@ func (h *htlcSuccessResolver) checkpointClaim(spendTx *chainhash.Hash) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we checkpoint the resolver with our report(s).
|
// Finally, we checkpoint the resolver with our report(s).
|
||||||
h.resolved = true
|
h.markResolved()
|
||||||
return h.Checkpoint(h, reports...)
|
return h.Checkpoint(h, reports...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,14 +238,6 @@ func (h *htlcSuccessResolver) Stop() {
|
|||||||
close(h.quit)
|
close(h.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the stored state in the resolve is fully
|
|
||||||
// resolved. In this case the target output can be forgotten.
|
|
||||||
//
|
|
||||||
// NOTE: Part of the ContractResolver interface.
|
|
||||||
func (h *htlcSuccessResolver) IsResolved() bool {
|
|
||||||
return h.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// report returns a report on the resolution state of the contract.
|
// report returns a report on the resolution state of the contract.
|
||||||
func (h *htlcSuccessResolver) report() *ContractReport {
|
func (h *htlcSuccessResolver) report() *ContractReport {
|
||||||
// If the sign details are nil, the report will be created by handled
|
// If the sign details are nil, the report will be created by handled
|
||||||
@ -298,7 +287,7 @@ func (h *htlcSuccessResolver) Encode(w io.Writer) error {
|
|||||||
if err := binary.Write(w, endian, h.outputIncubating); err != nil {
|
if err := binary.Write(w, endian, h.outputIncubating); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(w, endian, h.resolved); err != nil {
|
if err := binary.Write(w, endian, h.IsResolved()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
|
if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
|
||||||
@ -337,9 +326,15 @@ func newSuccessResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
if err := binary.Read(r, endian, &h.outputIncubating); err != nil {
|
if err := binary.Read(r, endian, &h.outputIncubating); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := binary.Read(r, endian, &h.resolved); err != nil {
|
|
||||||
|
var resolved bool
|
||||||
|
if err := binary.Read(r, endian, &resolved); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resolved {
|
||||||
|
h.markResolved()
|
||||||
|
}
|
||||||
|
|
||||||
if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
|
if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -745,7 +740,7 @@ func (h *htlcSuccessResolver) Launch() error {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
case h.resolved:
|
case h.IsResolved():
|
||||||
h.log.Errorf("already resolved")
|
h.log.Errorf("already resolved")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -616,11 +616,11 @@ func runFromCheckpoint(t *testing.T, ctx *htlcResolverTestContext,
|
|||||||
|
|
||||||
var resolved, incubating bool
|
var resolved, incubating bool
|
||||||
if h, ok := resolver.(*htlcSuccessResolver); ok {
|
if h, ok := resolver.(*htlcSuccessResolver); ok {
|
||||||
resolved = h.resolved
|
resolved = h.resolved.Load()
|
||||||
incubating = h.outputIncubating
|
incubating = h.outputIncubating
|
||||||
}
|
}
|
||||||
if h, ok := resolver.(*htlcTimeoutResolver); ok {
|
if h, ok := resolver.(*htlcTimeoutResolver); ok {
|
||||||
resolved = h.resolved
|
resolved = h.resolved.Load()
|
||||||
incubating = h.outputIncubating
|
incubating = h.outputIncubating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,6 @@ type htlcTimeoutResolver struct {
|
|||||||
// incubator (utxo nursery).
|
// incubator (utxo nursery).
|
||||||
outputIncubating bool
|
outputIncubating bool
|
||||||
|
|
||||||
// resolved reflects if the contract has been fully resolved or not.
|
|
||||||
resolved bool
|
|
||||||
|
|
||||||
// broadcastHeight is the height that the original contract was
|
// broadcastHeight is the height that the original contract was
|
||||||
// broadcast to the main-chain at. We'll use this value to bound any
|
// broadcast to the main-chain at. We'll use this value to bound any
|
||||||
// historical queries to the chain for spends/confirmations.
|
// historical queries to the chain for spends/confirmations.
|
||||||
@ -238,7 +235,7 @@ func (h *htlcTimeoutResolver) claimCleanUp(
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
h.resolved = true
|
h.markResolved()
|
||||||
|
|
||||||
// Checkpoint our resolver with a report which reflects the preimage
|
// Checkpoint our resolver with a report which reflects the preimage
|
||||||
// claim by the remote party.
|
// claim by the remote party.
|
||||||
@ -424,7 +421,7 @@ func checkSizeAndIndex(witness wire.TxWitness, size, index int) bool {
|
|||||||
// NOTE: Part of the ContractResolver interface.
|
// NOTE: Part of the ContractResolver interface.
|
||||||
func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) {
|
func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) {
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
if h.resolved {
|
if h.IsResolved() {
|
||||||
h.log.Errorf("already resolved")
|
h.log.Errorf("already resolved")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -622,14 +619,6 @@ func (h *htlcTimeoutResolver) Stop() {
|
|||||||
close(h.quit)
|
close(h.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResolved returns true if the stored state in the resolve is fully
|
|
||||||
// resolved. In this case the target output can be forgotten.
|
|
||||||
//
|
|
||||||
// NOTE: Part of the ContractResolver interface.
|
|
||||||
func (h *htlcTimeoutResolver) IsResolved() bool {
|
|
||||||
return h.resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// report returns a report on the resolution state of the contract.
|
// report returns a report on the resolution state of the contract.
|
||||||
func (h *htlcTimeoutResolver) report() *ContractReport {
|
func (h *htlcTimeoutResolver) report() *ContractReport {
|
||||||
// If we have a SignedTimeoutTx but no SignDetails, this is a local
|
// If we have a SignedTimeoutTx but no SignDetails, this is a local
|
||||||
@ -689,7 +678,7 @@ func (h *htlcTimeoutResolver) Encode(w io.Writer) error {
|
|||||||
if err := binary.Write(w, endian, h.outputIncubating); err != nil {
|
if err := binary.Write(w, endian, h.outputIncubating); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(w, endian, h.resolved); err != nil {
|
if err := binary.Write(w, endian, h.IsResolved()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
|
if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
|
||||||
@ -730,9 +719,15 @@ func newTimeoutResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
if err := binary.Read(r, endian, &h.outputIncubating); err != nil {
|
if err := binary.Read(r, endian, &h.outputIncubating); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := binary.Read(r, endian, &h.resolved); err != nil {
|
|
||||||
|
var resolved bool
|
||||||
|
if err := binary.Read(r, endian, &resolved); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resolved {
|
||||||
|
h.markResolved()
|
||||||
|
}
|
||||||
|
|
||||||
if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
|
if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1149,7 +1144,7 @@ func (h *htlcTimeoutResolver) checkpointClaim(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we checkpoint the resolver with our report(s).
|
// Finally, we checkpoint the resolver with our report(s).
|
||||||
h.resolved = true
|
h.markResolved()
|
||||||
|
|
||||||
return h.Checkpoint(h, report)
|
return h.Checkpoint(h, report)
|
||||||
}
|
}
|
||||||
@ -1285,7 +1280,7 @@ func (h *htlcTimeoutResolver) Launch() error {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
// If we're already resolved, then we can exit early.
|
// If we're already resolved, then we can exit early.
|
||||||
case h.resolved:
|
case h.IsResolved():
|
||||||
h.log.Errorf("already resolved")
|
h.log.Errorf("already resolved")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
// Finally, the resolver should be marked as resolved.
|
// Finally, the resolver should be marked as resolved.
|
||||||
if !resolver.resolved {
|
if !resolver.resolved.Load() {
|
||||||
t.Fatalf("resolver should be marked as resolved")
|
t.Fatalf("resolver should be marked as resolved")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user