mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
819c15fa0b
In this commit, we break the old `launchResolvers` into two steps - step one is to launch the resolvers synchronously, and step two is to actually waiting for the resolvers to be resolved. This is critical as in the following commit we will require the resolvers to be launched at the same blockbeat when a force close event is sent by the chain watcher.
186 lines
6.2 KiB
Go
186 lines
6.2 KiB
Go
package contractcourt
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"sync/atomic"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btclog/v2"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/fn/v2"
|
|
"github.com/lightningnetwork/lnd/sweep"
|
|
)
|
|
|
|
var (
|
|
endian = binary.BigEndian
|
|
)
|
|
|
|
const (
|
|
// sweepConfTarget is the default number of blocks that we'll use as a
|
|
// confirmation target when sweeping.
|
|
sweepConfTarget = 6
|
|
)
|
|
|
|
// ContractResolver is an interface which packages a state machine which is
|
|
// able to carry out the necessary steps required to fully resolve a Bitcoin
|
|
// contract on-chain. Resolvers are fully encodable to ensure callers are able
|
|
// to persist them properly. A resolver may produce another resolver in the
|
|
// case that claiming an HTLC is a multi-stage process. In this case, we may
|
|
// partially resolve the contract, then persist, and set up for an additional
|
|
// resolution.
|
|
type ContractResolver interface {
|
|
// ResolverKey returns an identifier which should be globally unique
|
|
// for this particular resolver within the chain the original contract
|
|
// resides within.
|
|
ResolverKey() []byte
|
|
|
|
// Launch starts the resolver by constructing an input and offering it
|
|
// to the sweeper. Once offered, it's expected to monitor the sweeping
|
|
// result in a goroutine invoked by calling Resolve.
|
|
//
|
|
// NOTE: We can call `Resolve` inside a goroutine at the end of this
|
|
// method to avoid calling it in the ChannelArbitrator. However, there
|
|
// are some DB-related operations such as SwapContract/ResolveContract
|
|
// which need to be done inside the resolvers instead, which needs a
|
|
// deeper refactoring.
|
|
Launch() error
|
|
|
|
// Resolve instructs the contract resolver to resolve the output
|
|
// on-chain. Once the output has been *fully* resolved, the function
|
|
// should return immediately with a nil ContractResolver value for the
|
|
// first return value. In the case that the contract requires further
|
|
// resolution, then another resolve is returned.
|
|
//
|
|
// NOTE: This function MUST be run as a goroutine.
|
|
Resolve() (ContractResolver, error)
|
|
|
|
// SupplementState allows the user of a ContractResolver to supplement
|
|
// it with state required for the proper resolution of a contract.
|
|
SupplementState(*channeldb.OpenChannel)
|
|
|
|
// IsResolved returns true if the stored state in the resolve is fully
|
|
// resolved. In this case the target output can be forgotten.
|
|
IsResolved() bool
|
|
|
|
// Encode writes an encoded version of the ContractResolver into the
|
|
// passed Writer.
|
|
Encode(w io.Writer) error
|
|
|
|
// Stop signals the resolver to cancel any current resolution
|
|
// processes, and suspend.
|
|
Stop()
|
|
}
|
|
|
|
// htlcContractResolver is the required interface for htlc resolvers.
|
|
type htlcContractResolver interface {
|
|
ContractResolver
|
|
|
|
// HtlcPoint returns the htlc's outpoint on the commitment tx.
|
|
HtlcPoint() wire.OutPoint
|
|
|
|
// Supplement adds additional information to the resolver that is
|
|
// required before Resolve() is called.
|
|
Supplement(htlc channeldb.HTLC)
|
|
|
|
// SupplementDeadline gives the deadline height for the HTLC output.
|
|
// This is only useful for outgoing HTLCs.
|
|
SupplementDeadline(deadlineHeight fn.Option[int32])
|
|
}
|
|
|
|
// reportingContractResolver is a ContractResolver that also exposes a report on
|
|
// the resolution state of the contract.
|
|
type reportingContractResolver interface {
|
|
ContractResolver
|
|
|
|
report() *ContractReport
|
|
}
|
|
|
|
// ResolverConfig contains the externally supplied configuration items that are
|
|
// required by a ContractResolver implementation.
|
|
type ResolverConfig struct {
|
|
// ChannelArbitratorConfig contains all the interfaces and closures
|
|
// required for the resolver to interact with outside sub-systems.
|
|
ChannelArbitratorConfig
|
|
|
|
// Checkpoint allows a resolver to check point its state. This function
|
|
// should write the state of the resolver to persistent storage, and
|
|
// return a non-nil error upon success. It takes a resolver report,
|
|
// which contains information about the outcome and should be written
|
|
// to disk if non-nil.
|
|
Checkpoint func(ContractResolver, ...*channeldb.ResolverReport) error
|
|
}
|
|
|
|
// contractResolverKit is meant to be used as a mix-in struct to be embedded within a
|
|
// given ContractResolver implementation. It contains all the common items that
|
|
// a resolver requires to carry out its duties.
|
|
type contractResolverKit struct {
|
|
ResolverConfig
|
|
|
|
log btclog.Logger
|
|
|
|
quit chan struct{}
|
|
|
|
// sweepResultChan is the result chan returned from calling
|
|
// `SweepInput`. It should be mounted to the specific resolver once the
|
|
// input has been offered to the sweeper.
|
|
sweepResultChan chan sweep.Result
|
|
|
|
// launched specifies whether the resolver has been launched. Calling
|
|
// `Launch` will be a no-op if this is true. This value is not saved to
|
|
// db, as it's fine to relaunch a resolver after a restart. It's only
|
|
// used to avoid resending requests to the sweeper when a new blockbeat
|
|
// is received.
|
|
launched atomic.Bool
|
|
|
|
// resolved reflects if the contract has been fully resolved or not.
|
|
resolved atomic.Bool
|
|
}
|
|
|
|
// newContractResolverKit instantiates the mix-in struct.
|
|
func newContractResolverKit(cfg ResolverConfig) *contractResolverKit {
|
|
return &contractResolverKit{
|
|
ResolverConfig: cfg,
|
|
quit: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// initLogger initializes the resolver-specific logger.
|
|
func (r *contractResolverKit) initLogger(prefix string) {
|
|
logPrefix := fmt.Sprintf("ChannelArbitrator(%v): %s:", r.ChanPoint,
|
|
prefix)
|
|
|
|
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)
|
|
}
|
|
|
|
// isLaunched returns true if the resolver has been launched.
|
|
func (r *contractResolverKit) isLaunched() bool {
|
|
return r.launched.Load()
|
|
}
|
|
|
|
// markLaunched marks the resolver as launched.
|
|
func (r *contractResolverKit) markLaunched() {
|
|
r.launched.Store(true)
|
|
}
|
|
|
|
var (
|
|
// errResolverShuttingDown is returned when the resolver stops
|
|
// progressing because it received the quit signal.
|
|
errResolverShuttingDown = errors.New("resolver shutting down")
|
|
)
|