mirror of
https://github.com/aljazceru/breez-lnd.git
synced 2025-12-17 14:14:21 +01:00
multi: move breach arbiter and utxo nursery into contractcourt package
In this commit, we take an initial step towards converting the existing breach arbiter and utxo nursery logic into contract resolvers by moving the files as is, into the `contractcourt` pacakge. This commit is primarily move only, though we had to massage some interfaces and config names along the way to make things compile and the tests run properly.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
package lnd
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
|
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/labels"
|
"github.com/lightningnetwork/lnd/labels"
|
||||||
@@ -59,7 +58,7 @@ var (
|
|||||||
errBrarShuttingDown = errors.New("breacharbiter shutting down")
|
errBrarShuttingDown = errors.New("breacharbiter shutting down")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContractBreachEvent is an event the breachArbiter will receive in case a
|
// ContractBreachEvent is an event the BreachArbiter will receive in case a
|
||||||
// contract breach is observed on-chain. It contains the necessary information
|
// contract breach is observed on-chain. It contains the necessary information
|
||||||
// to handle the breach, and a ProcessACK closure we will use to ACK the event
|
// to handle the breach, and a ProcessACK closure we will use to ACK the event
|
||||||
// when we have safely stored all the necessary information.
|
// when we have safely stored all the necessary information.
|
||||||
@@ -80,6 +79,51 @@ type ContractBreachEvent struct {
|
|||||||
BreachRetribution *lnwallet.BreachRetribution
|
BreachRetribution *lnwallet.BreachRetribution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelCloseType is an enum which signals the type of channel closure the
|
||||||
|
// peer should execute.
|
||||||
|
type ChannelCloseType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CloseRegular indicates a regular cooperative channel closure
|
||||||
|
// should be attempted.
|
||||||
|
CloseRegular ChannelCloseType = iota
|
||||||
|
|
||||||
|
// CloseBreach indicates that a channel breach has been detected, and
|
||||||
|
// the link should immediately be marked as unavailable.
|
||||||
|
CloseBreach
|
||||||
|
)
|
||||||
|
|
||||||
|
// RetributionStorer provides an interface for managing a persistent map from
|
||||||
|
// wire.OutPoint -> retributionInfo. Upon learning of a breach, a BreachArbiter
|
||||||
|
// should record the retributionInfo for the breached channel, which serves a
|
||||||
|
// checkpoint in the event that retribution needs to be resumed after failure.
|
||||||
|
// A RetributionStore provides an interface for managing the persisted set, as
|
||||||
|
// well as mapping user defined functions over the entire on-disk contents.
|
||||||
|
//
|
||||||
|
// Calls to RetributionStore may occur concurrently. A concrete instance of
|
||||||
|
// RetributionStore should use appropriate synchronization primitives, or
|
||||||
|
// be otherwise safe for concurrent access.
|
||||||
|
type RetributionStorer interface {
|
||||||
|
// Add persists the retributionInfo to disk, using the information's
|
||||||
|
// chanPoint as the key. This method should overwrite any existing
|
||||||
|
// entries found under the same key, and an error should be raised if
|
||||||
|
// the addition fails.
|
||||||
|
Add(retInfo *retributionInfo) error
|
||||||
|
|
||||||
|
// IsBreached queries the retribution store to see if the breach arbiter
|
||||||
|
// is aware of any breaches for the provided channel point.
|
||||||
|
IsBreached(chanPoint *wire.OutPoint) (bool, error)
|
||||||
|
|
||||||
|
// Remove deletes the retributionInfo from disk, if any exists, under
|
||||||
|
// the given key. An error should be re raised if the removal fails.
|
||||||
|
Remove(key *wire.OutPoint) error
|
||||||
|
|
||||||
|
// ForAll iterates over the existing on-disk contents and applies a
|
||||||
|
// chosen, read-only callback to each. This method should ensure that it
|
||||||
|
// immediately propagate any errors generated by the callback.
|
||||||
|
ForAll(cb func(*retributionInfo) error, reset func()) error
|
||||||
|
}
|
||||||
|
|
||||||
// BreachConfig bundles the required subsystems used by the breach arbiter. An
|
// BreachConfig bundles the required subsystems used by the breach arbiter. An
|
||||||
// instance of BreachConfig is passed to newBreachArbiter during instantiation.
|
// instance of BreachConfig is passed to newBreachArbiter during instantiation.
|
||||||
type BreachConfig struct {
|
type BreachConfig struct {
|
||||||
@@ -87,7 +131,7 @@ type BreachConfig struct {
|
|||||||
// which it detects a breach, ensuring now further activity will
|
// which it detects a breach, ensuring now further activity will
|
||||||
// continue across the link. The method accepts link's channel point and
|
// continue across the link. The method accepts link's channel point and
|
||||||
// a close type to be included in the channel close summary.
|
// a close type to be included in the channel close summary.
|
||||||
CloseLink func(*wire.OutPoint, htlcswitch.ChannelCloseType)
|
CloseLink func(*wire.OutPoint, ChannelCloseType)
|
||||||
|
|
||||||
// DB provides access to the user's channels, allowing the breach
|
// DB provides access to the user's channels, allowing the breach
|
||||||
// arbiter to determine the current state of a user's channels, and how
|
// arbiter to determine the current state of a user's channels, and how
|
||||||
@@ -110,9 +154,9 @@ type BreachConfig struct {
|
|||||||
// transaction to the network.
|
// transaction to the network.
|
||||||
PublishTransaction func(*wire.MsgTx, string) error
|
PublishTransaction func(*wire.MsgTx, string) error
|
||||||
|
|
||||||
// ContractBreaches is a channel where the breachArbiter will receive
|
// ContractBreaches is a channel where the BreachArbiter will receive
|
||||||
// notifications in the event of a contract breach being observed. A
|
// notifications in the event of a contract breach being observed. A
|
||||||
// ContractBreachEvent must be ACKed by the breachArbiter, such that
|
// ContractBreachEvent must be ACKed by the BreachArbiter, such that
|
||||||
// the sending subsystem knows that the event is properly handed off.
|
// the sending subsystem knows that the event is properly handed off.
|
||||||
ContractBreaches <-chan *ContractBreachEvent
|
ContractBreaches <-chan *ContractBreachEvent
|
||||||
|
|
||||||
@@ -124,10 +168,10 @@ type BreachConfig struct {
|
|||||||
// Store is a persistent resource that maintains information regarding
|
// Store is a persistent resource that maintains information regarding
|
||||||
// breached channels. This is used in conjunction with DB to recover
|
// breached channels. This is used in conjunction with DB to recover
|
||||||
// from crashes, restarts, or other failures.
|
// from crashes, restarts, or other failures.
|
||||||
Store RetributionStore
|
Store RetributionStorer
|
||||||
}
|
}
|
||||||
|
|
||||||
// breachArbiter is a special subsystem which is responsible for watching and
|
// BreachArbiter is a special subsystem which is responsible for watching and
|
||||||
// acting on the detection of any attempted uncooperative channel breaches by
|
// acting on the detection of any attempted uncooperative channel breaches by
|
||||||
// channel counterparties. This file essentially acts as deterrence code for
|
// channel counterparties. This file essentially acts as deterrence code for
|
||||||
// those attempting to launch attacks against the daemon. In practice it's
|
// those attempting to launch attacks against the daemon. In practice it's
|
||||||
@@ -135,7 +179,7 @@ type BreachConfig struct {
|
|||||||
// important to have it in place just in case we encounter cheating channel
|
// important to have it in place just in case we encounter cheating channel
|
||||||
// counterparties.
|
// counterparties.
|
||||||
// TODO(roasbeef): closures in config for subsystem pointers to decouple?
|
// TODO(roasbeef): closures in config for subsystem pointers to decouple?
|
||||||
type breachArbiter struct {
|
type BreachArbiter struct {
|
||||||
started sync.Once
|
started sync.Once
|
||||||
stopped sync.Once
|
stopped sync.Once
|
||||||
|
|
||||||
@@ -146,18 +190,18 @@ type breachArbiter struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBreachArbiter creates a new instance of a breachArbiter initialized with
|
// NewBreachArbiter creates a new instance of a BreachArbiter initialized with
|
||||||
// its dependent objects.
|
// its dependent objects.
|
||||||
func newBreachArbiter(cfg *BreachConfig) *breachArbiter {
|
func NewBreachArbiter(cfg *BreachConfig) *BreachArbiter {
|
||||||
return &breachArbiter{
|
return &BreachArbiter{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start is an idempotent method that officially starts the breachArbiter along
|
// Start is an idempotent method that officially starts the BreachArbiter along
|
||||||
// with all other goroutines it needs to perform its functions.
|
// with all other goroutines it needs to perform its functions.
|
||||||
func (b *breachArbiter) Start() error {
|
func (b *BreachArbiter) Start() error {
|
||||||
var err error
|
var err error
|
||||||
b.started.Do(func() {
|
b.started.Do(func() {
|
||||||
err = b.start()
|
err = b.start()
|
||||||
@@ -165,7 +209,7 @@ func (b *breachArbiter) Start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *breachArbiter) start() error {
|
func (b *BreachArbiter) start() error {
|
||||||
brarLog.Tracef("Starting breach arbiter")
|
brarLog.Tracef("Starting breach arbiter")
|
||||||
|
|
||||||
// Load all retributions currently persisted in the retribution store.
|
// Load all retributions currently persisted in the retribution store.
|
||||||
@@ -259,10 +303,10 @@ func (b *breachArbiter) start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop is an idempotent method that signals the breachArbiter to execute a
|
// Stop is an idempotent method that signals the BreachArbiter to execute a
|
||||||
// graceful shutdown. This function will block until all goroutines spawned by
|
// graceful shutdown. This function will block until all goroutines spawned by
|
||||||
// the breachArbiter have gracefully exited.
|
// the BreachArbiter have gracefully exited.
|
||||||
func (b *breachArbiter) Stop() error {
|
func (b *BreachArbiter) Stop() error {
|
||||||
b.stopped.Do(func() {
|
b.stopped.Do(func() {
|
||||||
brarLog.Infof("Breach arbiter shutting down")
|
brarLog.Infof("Breach arbiter shutting down")
|
||||||
|
|
||||||
@@ -274,11 +318,11 @@ func (b *breachArbiter) Stop() error {
|
|||||||
|
|
||||||
// IsBreached queries the breach arbiter's retribution store to see if it is
|
// IsBreached queries the breach arbiter's retribution store to see if it is
|
||||||
// aware of any channel breaches for a particular channel point.
|
// aware of any channel breaches for a particular channel point.
|
||||||
func (b *breachArbiter) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
func (b *BreachArbiter) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
||||||
return b.cfg.Store.IsBreached(chanPoint)
|
return b.cfg.Store.IsBreached(chanPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractObserver is the primary goroutine for the breachArbiter. This
|
// contractObserver is the primary goroutine for the BreachArbiter. This
|
||||||
// goroutine is responsible for handling breach events coming from the
|
// goroutine is responsible for handling breach events coming from the
|
||||||
// contractcourt on the ContractBreaches channel. If a channel breach is
|
// contractcourt on the ContractBreaches channel. If a channel breach is
|
||||||
// detected, then the contractObserver will execute the retribution logic
|
// detected, then the contractObserver will execute the retribution logic
|
||||||
@@ -286,7 +330,7 @@ func (b *breachArbiter) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
|||||||
// wallet.
|
// wallet.
|
||||||
//
|
//
|
||||||
// NOTE: This MUST be run as a goroutine.
|
// NOTE: This MUST be run as a goroutine.
|
||||||
func (b *breachArbiter) contractObserver() {
|
func (b *BreachArbiter) contractObserver() {
|
||||||
defer b.wg.Done()
|
defer b.wg.Done()
|
||||||
|
|
||||||
brarLog.Infof("Starting contract observer, watching for breaches.")
|
brarLog.Infof("Starting contract observer, watching for breaches.")
|
||||||
@@ -307,45 +351,6 @@ func (b *breachArbiter) contractObserver() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertToSecondLevelRevoke takes a breached output, and a transaction that
|
|
||||||
// spends it to the second level, and mutates the breach output into one that
|
|
||||||
// is able to properly sweep that second level output. We'll use this function
|
|
||||||
// when we go to sweep a breached commitment transaction, but the cheating
|
|
||||||
// party has already attempted to take it to the second level
|
|
||||||
func convertToSecondLevelRevoke(bo *breachedOutput, breachInfo *retributionInfo,
|
|
||||||
spendDetails *chainntnfs.SpendDetail) {
|
|
||||||
|
|
||||||
// In this case, we'll modify the witness type of this output to
|
|
||||||
// actually prepare for a second level revoke.
|
|
||||||
bo.witnessType = input.HtlcSecondLevelRevoke
|
|
||||||
|
|
||||||
// We'll also redirect the outpoint to this second level output, so the
|
|
||||||
// spending transaction updates it inputs accordingly.
|
|
||||||
spendingTx := spendDetails.SpendingTx
|
|
||||||
oldOp := bo.outpoint
|
|
||||||
bo.outpoint = wire.OutPoint{
|
|
||||||
Hash: spendingTx.TxHash(),
|
|
||||||
Index: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, we need to update the amount so we can do fee estimation
|
|
||||||
// properly, and also so we can generate a valid signature as we need
|
|
||||||
// to know the new input value (the second level transactions shaves
|
|
||||||
// off some funds to fees).
|
|
||||||
newAmt := spendingTx.TxOut[0].Value
|
|
||||||
bo.amt = btcutil.Amount(newAmt)
|
|
||||||
bo.signDesc.Output.Value = newAmt
|
|
||||||
bo.signDesc.Output.PkScript = spendingTx.TxOut[0].PkScript
|
|
||||||
|
|
||||||
// Finally, we'll need to adjust the witness program in the
|
|
||||||
// SignDescriptor.
|
|
||||||
bo.signDesc.WitnessScript = bo.secondLevelWitnessScript
|
|
||||||
|
|
||||||
brarLog.Warnf("HTLC(%v) for ChannelPoint(%v) has been spent to the "+
|
|
||||||
"second-level, adjusting -> %v", oldOp, breachInfo.chanPoint,
|
|
||||||
bo.outpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
// spend is used to wrap the index of the retributionInfo output that gets
|
// spend is used to wrap the index of the retributionInfo output that gets
|
||||||
// spent together with the spend details.
|
// spent together with the spend details.
|
||||||
type spend struct {
|
type spend struct {
|
||||||
@@ -357,7 +362,7 @@ type spend struct {
|
|||||||
// returns the spend details for those outputs. The spendNtfns map is a cache
|
// returns the spend details for those outputs. The spendNtfns map is a cache
|
||||||
// used to store registered spend subscriptions, in case we must call this
|
// used to store registered spend subscriptions, in case we must call this
|
||||||
// method multiple times.
|
// method multiple times.
|
||||||
func (b *breachArbiter) waitForSpendEvent(breachInfo *retributionInfo,
|
func (b *BreachArbiter) waitForSpendEvent(breachInfo *retributionInfo,
|
||||||
spendNtfns map[wire.OutPoint]*chainntnfs.SpendEvent) ([]spend, error) {
|
spendNtfns map[wire.OutPoint]*chainntnfs.SpendEvent) ([]spend, error) {
|
||||||
|
|
||||||
inputs := breachInfo.breachedOutputs
|
inputs := breachInfo.breachedOutputs
|
||||||
@@ -480,6 +485,46 @@ func (b *breachArbiter) waitForSpendEvent(breachInfo *retributionInfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertToSecondLevelRevoke takes a breached output, and a transaction that
|
||||||
|
// spends it to the second level, and mutates the breach output into one that
|
||||||
|
// is able to properly sweep that second level output. We'll use this function
|
||||||
|
// when we go to sweep a breached commitment transaction, but the cheating
|
||||||
|
// party has already attempted to take it to the second level
|
||||||
|
func convertToSecondLevelRevoke(bo *breachedOutput, breachInfo *retributionInfo,
|
||||||
|
spendDetails *chainntnfs.SpendDetail) {
|
||||||
|
|
||||||
|
// In this case, we'll modify the witness type of this output to
|
||||||
|
// actually prepare for a second level revoke.
|
||||||
|
bo.witnessType = input.HtlcSecondLevelRevoke
|
||||||
|
|
||||||
|
// We'll also redirect the outpoint to this second level output, so the
|
||||||
|
// spending transaction updates it inputs accordingly.
|
||||||
|
spendingTx := spendDetails.SpendingTx
|
||||||
|
spendInputIndex := spendDetails.SpenderInputIndex
|
||||||
|
oldOp := bo.outpoint
|
||||||
|
bo.outpoint = wire.OutPoint{
|
||||||
|
Hash: spendingTx.TxHash(),
|
||||||
|
Index: spendInputIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, we need to update the amount so we can do fee estimation
|
||||||
|
// properly, and also so we can generate a valid signature as we need
|
||||||
|
// to know the new input value (the second level transactions shaves
|
||||||
|
// off some funds to fees).
|
||||||
|
newAmt := spendingTx.TxOut[spendInputIndex].Value
|
||||||
|
bo.amt = btcutil.Amount(newAmt)
|
||||||
|
bo.signDesc.Output.Value = newAmt
|
||||||
|
bo.signDesc.Output.PkScript = spendingTx.TxOut[spendInputIndex].PkScript
|
||||||
|
|
||||||
|
// Finally, we'll need to adjust the witness program in the
|
||||||
|
// SignDescriptor.
|
||||||
|
bo.signDesc.WitnessScript = bo.secondLevelWitnessScript
|
||||||
|
|
||||||
|
brarLog.Warnf("HTLC(%v) for ChannelPoint(%v) has been spent to the "+
|
||||||
|
"second-level, adjusting -> %v", oldOp, breachInfo.chanPoint,
|
||||||
|
bo.outpoint)
|
||||||
|
}
|
||||||
|
|
||||||
// updateBreachInfo mutates the passed breachInfo by removing or converting any
|
// updateBreachInfo mutates the passed breachInfo by removing or converting any
|
||||||
// outputs among the spends. It also counts the total and revoked funds swept
|
// outputs among the spends. It also counts the total and revoked funds swept
|
||||||
// by our justice spends.
|
// by our justice spends.
|
||||||
@@ -581,7 +626,7 @@ func updateBreachInfo(breachInfo *retributionInfo, spends []spend) (
|
|||||||
// the lingering funds within the channel into the daemon's wallet.
|
// the lingering funds within the channel into the daemon's wallet.
|
||||||
//
|
//
|
||||||
// NOTE: This MUST be run as a goroutine.
|
// NOTE: This MUST be run as a goroutine.
|
||||||
func (b *breachArbiter) exactRetribution(confChan *chainntnfs.ConfirmationEvent,
|
func (b *BreachArbiter) exactRetribution(confChan *chainntnfs.ConfirmationEvent,
|
||||||
breachInfo *retributionInfo) {
|
breachInfo *retributionInfo) {
|
||||||
|
|
||||||
defer b.wg.Done()
|
defer b.wg.Done()
|
||||||
@@ -797,7 +842,7 @@ Loop:
|
|||||||
|
|
||||||
// cleanupBreach marks the given channel point as fully resolved and removes the
|
// cleanupBreach marks the given channel point as fully resolved and removes the
|
||||||
// retribution for that the channel from the retribution store.
|
// retribution for that the channel from the retribution store.
|
||||||
func (b *breachArbiter) cleanupBreach(chanPoint *wire.OutPoint) error {
|
func (b *BreachArbiter) cleanupBreach(chanPoint *wire.OutPoint) error {
|
||||||
// With the channel closed, mark it in the database as such.
|
// With the channel closed, mark it in the database as such.
|
||||||
err := b.cfg.DB.MarkChanFullyClosed(chanPoint)
|
err := b.cfg.DB.MarkChanFullyClosed(chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -816,15 +861,15 @@ func (b *breachArbiter) cleanupBreach(chanPoint *wire.OutPoint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleBreachHandoff handles a new breach event, by writing it to disk, then
|
// handleBreachHandoff handles a new breach event, by writing it to disk, then
|
||||||
// notifies the breachArbiter contract observer goroutine that a channel's
|
// notifies the BreachArbiter contract observer goroutine that a channel's
|
||||||
// contract has been breached by the prior counterparty. Once notified the
|
// contract has been breached by the prior counterparty. Once notified the
|
||||||
// breachArbiter will attempt to sweep ALL funds within the channel using the
|
// BreachArbiter will attempt to sweep ALL funds within the channel using the
|
||||||
// information provided within the BreachRetribution generated due to the
|
// information provided within the BreachRetribution generated due to the
|
||||||
// breach of channel contract. The funds will be swept only after the breaching
|
// breach of channel contract. The funds will be swept only after the breaching
|
||||||
// transaction receives a necessary number of confirmations.
|
// transaction receives a necessary number of confirmations.
|
||||||
//
|
//
|
||||||
// NOTE: This MUST be run as a goroutine.
|
// NOTE: This MUST be run as a goroutine.
|
||||||
func (b *breachArbiter) handleBreachHandoff(breachEvent *ContractBreachEvent) {
|
func (b *BreachArbiter) handleBreachHandoff(breachEvent *ContractBreachEvent) {
|
||||||
defer b.wg.Done()
|
defer b.wg.Done()
|
||||||
|
|
||||||
chanPoint := breachEvent.ChanPoint
|
chanPoint := breachEvent.ChanPoint
|
||||||
@@ -844,7 +889,7 @@ func (b *breachArbiter) handleBreachHandoff(breachEvent *ContractBreachEvent) {
|
|||||||
// breached in order to ensure any incoming or outgoing
|
// breached in order to ensure any incoming or outgoing
|
||||||
// multi-hop HTLCs aren't sent over this link, nor any other
|
// multi-hop HTLCs aren't sent over this link, nor any other
|
||||||
// links associated with this peer.
|
// links associated with this peer.
|
||||||
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
|
b.cfg.CloseLink(&chanPoint, CloseBreach)
|
||||||
|
|
||||||
// TODO(roasbeef): need to handle case of remote broadcast
|
// TODO(roasbeef): need to handle case of remote broadcast
|
||||||
// mid-local initiated state-transition, possible
|
// mid-local initiated state-transition, possible
|
||||||
@@ -1185,7 +1230,7 @@ type justiceTxVariants struct {
|
|||||||
// the funds within the channel which we are now entitled to due to a breach of
|
// the funds within the channel which we are now entitled to due to a breach of
|
||||||
// the channel's contract by the counterparty. This function returns a *fully*
|
// the channel's contract by the counterparty. This function returns a *fully*
|
||||||
// signed transaction with the witness for each input fully in place.
|
// signed transaction with the witness for each input fully in place.
|
||||||
func (b *breachArbiter) createJusticeTx(
|
func (b *BreachArbiter) createJusticeTx(
|
||||||
breachedOutputs []breachedOutput) (*justiceTxVariants, error) {
|
breachedOutputs []breachedOutput) (*justiceTxVariants, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -1235,7 +1280,7 @@ func (b *breachArbiter) createJusticeTx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
|
// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
|
||||||
func (b *breachArbiter) createSweepTx(inputs []input.Input) (*wire.MsgTx,
|
func (b *BreachArbiter) createSweepTx(inputs []input.Input) (*wire.MsgTx,
|
||||||
error) {
|
error) {
|
||||||
if len(inputs) == 0 {
|
if len(inputs) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -1289,7 +1334,7 @@ func (b *breachArbiter) createSweepTx(inputs []input.Input) (*wire.MsgTx,
|
|||||||
|
|
||||||
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
|
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
|
||||||
// spendable outputs by sweeping the funds into a single p2wkh output.
|
// spendable outputs by sweeping the funds into a single p2wkh output.
|
||||||
func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
||||||
inputs ...input.Input) (*wire.MsgTx, error) {
|
inputs ...input.Input) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
// First, we obtain a new public key script from the wallet which we'll
|
// First, we obtain a new public key script from the wallet which we'll
|
||||||
@@ -1382,55 +1427,24 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
|||||||
return txn, nil
|
return txn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetributionStore provides an interface for managing a persistent map from
|
// RetributionStore handles persistence of retribution states to disk and is
|
||||||
// wire.OutPoint -> retributionInfo. Upon learning of a breach, a BreachArbiter
|
|
||||||
// should record the retributionInfo for the breached channel, which serves a
|
|
||||||
// checkpoint in the event that retribution needs to be resumed after failure.
|
|
||||||
// A RetributionStore provides an interface for managing the persisted set, as
|
|
||||||
// well as mapping user defined functions over the entire on-disk contents.
|
|
||||||
//
|
|
||||||
// Calls to RetributionStore may occur concurrently. A concrete instance of
|
|
||||||
// RetributionStore should use appropriate synchronization primitives, or
|
|
||||||
// be otherwise safe for concurrent access.
|
|
||||||
type RetributionStore interface {
|
|
||||||
// Add persists the retributionInfo to disk, using the information's
|
|
||||||
// chanPoint as the key. This method should overwrite any existing
|
|
||||||
// entries found under the same key, and an error should be raised if
|
|
||||||
// the addition fails.
|
|
||||||
Add(retInfo *retributionInfo) error
|
|
||||||
|
|
||||||
// IsBreached queries the retribution store to see if the breach arbiter
|
|
||||||
// is aware of any breaches for the provided channel point.
|
|
||||||
IsBreached(chanPoint *wire.OutPoint) (bool, error)
|
|
||||||
|
|
||||||
// Remove deletes the retributionInfo from disk, if any exists, under
|
|
||||||
// the given key. An error should be re raised if the removal fails.
|
|
||||||
Remove(key *wire.OutPoint) error
|
|
||||||
|
|
||||||
// ForAll iterates over the existing on-disk contents and applies a
|
|
||||||
// chosen, read-only callback to each. This method should ensure that it
|
|
||||||
// immediately propagate any errors generated by the callback.
|
|
||||||
ForAll(cb func(*retributionInfo) error, reset func()) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// retributionStore handles persistence of retribution states to disk and is
|
|
||||||
// backed by a boltdb bucket. The primary responsibility of the retribution
|
// backed by a boltdb bucket. The primary responsibility of the retribution
|
||||||
// store is to ensure that we can recover from a restart in the middle of a
|
// store is to ensure that we can recover from a restart in the middle of a
|
||||||
// breached contract retribution.
|
// breached contract retribution.
|
||||||
type retributionStore struct {
|
type RetributionStore struct {
|
||||||
db *channeldb.DB
|
db *channeldb.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRetributionStore creates a new instance of a retributionStore.
|
// NewRetributionStore creates a new instance of a RetributionStore.
|
||||||
func newRetributionStore(db *channeldb.DB) *retributionStore {
|
func NewRetributionStore(db *channeldb.DB) *RetributionStore {
|
||||||
return &retributionStore{
|
return &RetributionStore{
|
||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a retribution state to the retributionStore, which is then persisted
|
// Add adds a retribution state to the RetributionStore, which is then persisted
|
||||||
// to disk.
|
// to disk.
|
||||||
func (rs *retributionStore) Add(ret *retributionInfo) error {
|
func (rs *RetributionStore) Add(ret *retributionInfo) error {
|
||||||
return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
|
||||||
// If this is our first contract breach, the retributionBucket
|
// If this is our first contract breach, the retributionBucket
|
||||||
// won't exist, in which case, we just create a new bucket.
|
// won't exist, in which case, we just create a new bucket.
|
||||||
@@ -1457,7 +1471,7 @@ func (rs *retributionStore) Add(ret *retributionInfo) error {
|
|||||||
// previously breached. This is used when connecting to a peer to determine if
|
// previously breached. This is used when connecting to a peer to determine if
|
||||||
// it is safe to add a link to the htlcswitch, as we should never add a channel
|
// it is safe to add a link to the htlcswitch, as we should never add a channel
|
||||||
// that has already been breached.
|
// that has already been breached.
|
||||||
func (rs *retributionStore) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
func (rs *RetributionStore) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
||||||
var found bool
|
var found bool
|
||||||
err := kvdb.View(rs.db, func(tx kvdb.RTx) error {
|
err := kvdb.View(rs.db, func(tx kvdb.RTx) error {
|
||||||
retBucket := tx.ReadBucket(retributionBucket)
|
retBucket := tx.ReadBucket(retributionBucket)
|
||||||
@@ -1485,7 +1499,7 @@ func (rs *retributionStore) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
|||||||
|
|
||||||
// Remove removes a retribution state and finalized justice transaction by
|
// Remove removes a retribution state and finalized justice transaction by
|
||||||
// channel point from the retribution store.
|
// channel point from the retribution store.
|
||||||
func (rs *retributionStore) Remove(chanPoint *wire.OutPoint) error {
|
func (rs *RetributionStore) Remove(chanPoint *wire.OutPoint) error {
|
||||||
return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
|
||||||
retBucket := tx.ReadWriteBucket(retributionBucket)
|
retBucket := tx.ReadWriteBucket(retributionBucket)
|
||||||
|
|
||||||
@@ -1524,7 +1538,7 @@ func (rs *retributionStore) Remove(chanPoint *wire.OutPoint) error {
|
|||||||
|
|
||||||
// ForAll iterates through all stored retributions and executes the passed
|
// ForAll iterates through all stored retributions and executes the passed
|
||||||
// callback function on each retribution.
|
// callback function on each retribution.
|
||||||
func (rs *retributionStore) ForAll(cb func(*retributionInfo) error,
|
func (rs *RetributionStore) ForAll(cb func(*retributionInfo) error,
|
||||||
reset func()) error {
|
reset func()) error {
|
||||||
|
|
||||||
return kvdb.View(rs.db, func(tx kvdb.RTx) error {
|
return kvdb.View(rs.db, func(tx kvdb.RTx) error {
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
//go:build !rpctest
|
||||||
// +build !rpctest
|
// +build !rpctest
|
||||||
|
|
||||||
package lnd
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -26,7 +27,6 @@ import (
|
|||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lntest/channels"
|
"github.com/lightningnetwork/lnd/lntest/channels"
|
||||||
@@ -338,7 +338,7 @@ func init() {
|
|||||||
// modifications to the entries are made between calls or through side effects,
|
// modifications to the entries are made between calls or through side effects,
|
||||||
// and (2) that the database is actually being persisted between actions.
|
// and (2) that the database is actually being persisted between actions.
|
||||||
type FailingRetributionStore interface {
|
type FailingRetributionStore interface {
|
||||||
RetributionStore
|
RetributionStorer
|
||||||
|
|
||||||
Restart()
|
Restart()
|
||||||
}
|
}
|
||||||
@@ -350,18 +350,18 @@ type FailingRetributionStore interface {
|
|||||||
type failingRetributionStore struct {
|
type failingRetributionStore struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
rs RetributionStore
|
rs RetributionStorer
|
||||||
|
|
||||||
nextAddErr error
|
nextAddErr error
|
||||||
|
|
||||||
restart func() RetributionStore
|
restart func() RetributionStorer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFailingRetributionStore creates a new failing retribution store. The given
|
// newFailingRetributionStore creates a new failing retribution store. The given
|
||||||
// restart closure should ensure that it is reloading its contents from the
|
// restart closure should ensure that it is reloading its contents from the
|
||||||
// persistent source.
|
// persistent source.
|
||||||
func newFailingRetributionStore(
|
func newFailingRetributionStore(
|
||||||
restart func() RetributionStore) *failingRetributionStore {
|
restart func() RetributionStorer) *failingRetributionStore {
|
||||||
|
|
||||||
return &failingRetributionStore{
|
return &failingRetributionStore{
|
||||||
mu: sync.Mutex{},
|
mu: sync.Mutex{},
|
||||||
@@ -631,7 +631,7 @@ func TestMockRetributionStore(t *testing.T) {
|
|||||||
func(tt *testing.T) {
|
func(tt *testing.T) {
|
||||||
mrs := newMockRetributionStore()
|
mrs := newMockRetributionStore()
|
||||||
frs := newFailingRetributionStore(
|
frs := newFailingRetributionStore(
|
||||||
func() RetributionStore { return mrs },
|
func() RetributionStorer { return mrs },
|
||||||
)
|
)
|
||||||
test.test(frs, tt)
|
test.test(frs, tt)
|
||||||
},
|
},
|
||||||
@@ -677,7 +677,7 @@ func TestChannelDBRetributionStore(t *testing.T) {
|
|||||||
defer db.Close()
|
defer db.Close()
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
restartDb := func() RetributionStore {
|
restartDb := func() RetributionStorer {
|
||||||
// Close and reopen channeldb
|
// Close and reopen channeldb
|
||||||
if err = db.Close(); err != nil {
|
if err = db.Close(); err != nil {
|
||||||
t.Fatalf("unable to close "+
|
t.Fatalf("unable to close "+
|
||||||
@@ -691,7 +691,7 @@ func TestChannelDBRetributionStore(t *testing.T) {
|
|||||||
"channeldb: %v", err)
|
"channeldb: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newRetributionStore(db)
|
return NewRetributionStore(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
frs := newFailingRetributionStore(restartDb)
|
frs := newFailingRetributionStore(restartDb)
|
||||||
@@ -703,7 +703,7 @@ func TestChannelDBRetributionStore(t *testing.T) {
|
|||||||
|
|
||||||
// countRetributions uses a retribution store's ForAll to count the number of
|
// countRetributions uses a retribution store's ForAll to count the number of
|
||||||
// elements emitted from the store.
|
// elements emitted from the store.
|
||||||
func countRetributions(t *testing.T, rs RetributionStore) int {
|
func countRetributions(t *testing.T, rs RetributionStorer) int {
|
||||||
count := 0
|
count := 0
|
||||||
err := rs.ForAll(func(_ *retributionInfo) error {
|
err := rs.ForAll(func(_ *retributionInfo) error {
|
||||||
count++
|
count++
|
||||||
@@ -971,7 +971,7 @@ restartCheck:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initBreachedState(t *testing.T) (*breachArbiter,
|
func initBreachedState(t *testing.T) (*BreachArbiter,
|
||||||
*lnwallet.LightningChannel, *lnwallet.LightningChannel,
|
*lnwallet.LightningChannel, *lnwallet.LightningChannel,
|
||||||
*lnwallet.LocalForceCloseSummary, chan *ContractBreachEvent,
|
*lnwallet.LocalForceCloseSummary, chan *ContractBreachEvent,
|
||||||
func(), func()) {
|
func(), func()) {
|
||||||
@@ -2035,7 +2035,7 @@ func findInputIndex(t *testing.T, op wire.OutPoint, tx *wire.MsgTx) int {
|
|||||||
|
|
||||||
// assertArbiterBreach checks that the breach arbiter has persisted the breach
|
// assertArbiterBreach checks that the breach arbiter has persisted the breach
|
||||||
// information for a particular channel.
|
// information for a particular channel.
|
||||||
func assertArbiterBreach(t *testing.T, brar *breachArbiter,
|
func assertArbiterBreach(t *testing.T, brar *BreachArbiter,
|
||||||
chanPoint *wire.OutPoint) {
|
chanPoint *wire.OutPoint) {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -2055,7 +2055,7 @@ func assertArbiterBreach(t *testing.T, brar *breachArbiter,
|
|||||||
|
|
||||||
// assertNoArbiterBreach checks that the breach arbiter has not persisted the
|
// assertNoArbiterBreach checks that the breach arbiter has not persisted the
|
||||||
// breach information for a particular channel.
|
// breach information for a particular channel.
|
||||||
func assertNoArbiterBreach(t *testing.T, brar *breachArbiter,
|
func assertNoArbiterBreach(t *testing.T, brar *BreachArbiter,
|
||||||
chanPoint *wire.OutPoint) {
|
chanPoint *wire.OutPoint) {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -2074,7 +2074,7 @@ func assertNoArbiterBreach(t *testing.T, brar *breachArbiter,
|
|||||||
|
|
||||||
// assertBrarCleanup blocks until the given channel point has been removed the
|
// assertBrarCleanup blocks until the given channel point has been removed the
|
||||||
// retribution store and the channel is fully closed in the database.
|
// retribution store and the channel is fully closed in the database.
|
||||||
func assertBrarCleanup(t *testing.T, brar *breachArbiter,
|
func assertBrarCleanup(t *testing.T, brar *BreachArbiter,
|
||||||
chanPoint *wire.OutPoint, db *channeldb.DB) {
|
chanPoint *wire.OutPoint, db *channeldb.DB) {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -2159,11 +2159,11 @@ func assertNotPendingClosed(t *testing.T, c *lnwallet.LightningChannel) {
|
|||||||
// createTestArbiter instantiates a breach arbiter with a failing retribution
|
// createTestArbiter instantiates a breach arbiter with a failing retribution
|
||||||
// store, so that controlled failures can be tested.
|
// store, so that controlled failures can be tested.
|
||||||
func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent,
|
func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent,
|
||||||
db *channeldb.DB) (*breachArbiter, func(), error) {
|
db *channeldb.DB) (*BreachArbiter, func(), error) {
|
||||||
|
|
||||||
// Create a failing retribution store, that wraps a normal one.
|
// Create a failing retribution store, that wraps a normal one.
|
||||||
store := newFailingRetributionStore(func() RetributionStore {
|
store := newFailingRetributionStore(func() RetributionStorer {
|
||||||
return newRetributionStore(db)
|
return NewRetributionStore(db)
|
||||||
})
|
})
|
||||||
|
|
||||||
aliceKeyPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(),
|
aliceKeyPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
@@ -2172,8 +2172,8 @@ func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent,
|
|||||||
|
|
||||||
// Assemble our test arbiter.
|
// Assemble our test arbiter.
|
||||||
notifier := mock.MakeMockSpendNotifier()
|
notifier := mock.MakeMockSpendNotifier()
|
||||||
ba := newBreachArbiter(&BreachConfig{
|
ba := NewBreachArbiter(&BreachConfig{
|
||||||
CloseLink: func(_ *wire.OutPoint, _ htlcswitch.ChannelCloseType) {},
|
CloseLink: func(_ *wire.OutPoint, _ ChannelCloseType) {},
|
||||||
DB: db,
|
DB: db,
|
||||||
Estimator: chainfee.NewStaticEstimator(12500, 0),
|
Estimator: chainfee.NewStaticEstimator(12500, 0),
|
||||||
GenSweepScript: func() ([]byte, error) { return nil, nil },
|
GenSweepScript: func() ([]byte, error) { return nil, nil },
|
||||||
@@ -28,8 +28,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultTimeout = time.Second * 5
|
|
||||||
|
|
||||||
// stateTimeout is the timeout we allow when waiting for state
|
// stateTimeout is the timeout we allow when waiting for state
|
||||||
// transitions.
|
// transitions.
|
||||||
stateTimeout = time.Second * 15
|
stateTimeout = time.Second * 15
|
||||||
|
|||||||
@@ -5,14 +5,24 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
// log is a logger that is initialized with no output filters. This
|
// log is a logger that is initialized with no output filters. This
|
||||||
// means the package will not perform any logging by default until the caller
|
// means the package will not perform any logging by default until the caller
|
||||||
// requests it.
|
// requests it.
|
||||||
var log btclog.Logger
|
log btclog.Logger
|
||||||
|
|
||||||
|
// brarLog is the logger used by the breach arb.
|
||||||
|
brarLog btclog.Logger
|
||||||
|
|
||||||
|
// utxnLog is the logger used by the utxo nursary.
|
||||||
|
utxnLog btclog.Logger
|
||||||
|
)
|
||||||
|
|
||||||
// The default amount of logging is none.
|
// The default amount of logging is none.
|
||||||
func init() {
|
func init() {
|
||||||
UseLogger(build.NewSubLogger("CNCT", nil))
|
UseLogger(build.NewSubLogger("CNCT", nil))
|
||||||
|
UseBreachLogger(build.NewSubLogger("BRAR", nil))
|
||||||
|
UseNurseryLogger(build.NewSubLogger("UTXN", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableLog disables all library log output. Logging output is disabled
|
// DisableLog disables all library log output. Logging output is disabled
|
||||||
@@ -28,6 +38,20 @@ func UseLogger(logger btclog.Logger) {
|
|||||||
log = logger
|
log = logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseBreachLogger uses a specified Logger to output package logging info.
|
||||||
|
// This should be used in preference to SetLogWriter if the caller is also
|
||||||
|
// using btclog.
|
||||||
|
func UseBreachLogger(logger btclog.Logger) {
|
||||||
|
brarLog = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseNurseryLogger uses a specified Logger to output package logging info.
|
||||||
|
// This should be used in preference to SetLogWriter if the caller is also
|
||||||
|
// using btclog.
|
||||||
|
func UseNurseryLogger(logger btclog.Logger) {
|
||||||
|
utxnLog = logger
|
||||||
|
}
|
||||||
|
|
||||||
// logClosure is used to provide a closure over expensive logging operations so
|
// logClosure is used to provide a closure over expensive logging operations so
|
||||||
// don't have to be performed when the logging level doesn't warrant it.
|
// don't have to be performed when the logging level doesn't warrant it.
|
||||||
type logClosure func() string
|
type logClosure func() string
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package lnd
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -79,7 +79,7 @@ import (
|
|||||||
// Concretely, it stores commitment and htlc outputs until any time-bounded
|
// Concretely, it stores commitment and htlc outputs until any time-bounded
|
||||||
// constraints have fully matured. The store exposes methods for enumerating its
|
// constraints have fully matured. The store exposes methods for enumerating its
|
||||||
// contents, and persisting state transitions detected by the utxo nursery.
|
// contents, and persisting state transitions detected by the utxo nursery.
|
||||||
type NurseryStore interface {
|
type NurseryStorer interface {
|
||||||
// Incubate registers a set of CSV delayed outputs (incoming HTLC's on
|
// Incubate registers a set of CSV delayed outputs (incoming HTLC's on
|
||||||
// our commitment transaction, or a commitment output), and a slice of
|
// our commitment transaction, or a commitment output), and a slice of
|
||||||
// outgoing htlc outputs to be swept back into the user's wallet. The
|
// outgoing htlc outputs to be swept back into the user's wallet. The
|
||||||
@@ -229,20 +229,20 @@ func prefixOutputKey(statePrefix []byte,
|
|||||||
return pfxOutputBuffer.Bytes(), nil
|
return pfxOutputBuffer.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nurseryStore is a concrete instantiation of a NurseryStore that is backed by
|
// NurseryStore is a concrete instantiation of a NurseryStore that is backed by
|
||||||
// a channeldb.DB instance.
|
// a channeldb.DB instance.
|
||||||
type nurseryStore struct {
|
type NurseryStore struct {
|
||||||
chainHash chainhash.Hash
|
chainHash chainhash.Hash
|
||||||
db *channeldb.DB
|
db *channeldb.DB
|
||||||
|
|
||||||
pfxChainKey []byte
|
pfxChainKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNurseryStore accepts a chain hash and a channeldb.DB instance, returning
|
// NewNurseryStore accepts a chain hash and a channeldb.DB instance, returning
|
||||||
// an instance of nurseryStore who's database is properly segmented for the
|
// an instance of NurseryStore who's database is properly segmented for the
|
||||||
// given chain.
|
// given chain.
|
||||||
func newNurseryStore(chainHash *chainhash.Hash,
|
func NewNurseryStore(chainHash *chainhash.Hash,
|
||||||
db *channeldb.DB) (*nurseryStore, error) {
|
db *channeldb.DB) (*NurseryStore, error) {
|
||||||
|
|
||||||
// Prefix the provided chain hash with "utxn" to create the key for the
|
// Prefix the provided chain hash with "utxn" to create the key for the
|
||||||
// nursery store's root bucket, ensuring each one has proper chain
|
// nursery store's root bucket, ensuring each one has proper chain
|
||||||
@@ -252,7 +252,7 @@ func newNurseryStore(chainHash *chainhash.Hash,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &nurseryStore{
|
return &NurseryStore{
|
||||||
chainHash: *chainHash,
|
chainHash: *chainHash,
|
||||||
db: db,
|
db: db,
|
||||||
pfxChainKey: pfxChainKey,
|
pfxChainKey: pfxChainKey,
|
||||||
@@ -262,7 +262,7 @@ func newNurseryStore(chainHash *chainhash.Hash,
|
|||||||
// Incubate persists the beginning of the incubation process for the
|
// Incubate persists the beginning of the incubation process for the
|
||||||
// CSV-delayed outputs (commitment and incoming HTLC's), commitment output and
|
// CSV-delayed outputs (commitment and incoming HTLC's), commitment output and
|
||||||
// a list of outgoing two-stage htlc outputs.
|
// a list of outgoing two-stage htlc outputs.
|
||||||
func (ns *nurseryStore) Incubate(kids []kidOutput, babies []babyOutput) error {
|
func (ns *NurseryStore) Incubate(kids []kidOutput, babies []babyOutput) error {
|
||||||
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
||||||
// If we have any kid outputs to incubate, then we'll attempt
|
// If we have any kid outputs to incubate, then we'll attempt
|
||||||
// to add each of them to the nursery store. Any duplicate
|
// to add each of them to the nursery store. Any duplicate
|
||||||
@@ -289,7 +289,7 @@ func (ns *nurseryStore) Incubate(kids []kidOutput, babies []babyOutput) error {
|
|||||||
// CribToKinder atomically moves a babyOutput in the crib bucket to the
|
// CribToKinder atomically moves a babyOutput in the crib bucket to the
|
||||||
// kindergarten bucket. The now mature kidOutput contained in the babyOutput
|
// kindergarten bucket. The now mature kidOutput contained in the babyOutput
|
||||||
// will be stored as it waits out the kidOutput's CSV delay.
|
// will be stored as it waits out the kidOutput's CSV delay.
|
||||||
func (ns *nurseryStore) CribToKinder(bby *babyOutput) error {
|
func (ns *NurseryStore) CribToKinder(bby *babyOutput) error {
|
||||||
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
||||||
|
|
||||||
// First, retrieve or create the channel bucket corresponding to
|
// First, retrieve or create the channel bucket corresponding to
|
||||||
@@ -371,7 +371,7 @@ func (ns *nurseryStore) CribToKinder(bby *babyOutput) error {
|
|||||||
// PreschoolToKinder atomically moves a kidOutput from the preschool bucket to
|
// PreschoolToKinder atomically moves a kidOutput from the preschool bucket to
|
||||||
// the kindergarten bucket. This transition should be executed after receiving
|
// the kindergarten bucket. This transition should be executed after receiving
|
||||||
// confirmation of the preschool output's commitment transaction.
|
// confirmation of the preschool output's commitment transaction.
|
||||||
func (ns *nurseryStore) PreschoolToKinder(kid *kidOutput,
|
func (ns *NurseryStore) PreschoolToKinder(kid *kidOutput,
|
||||||
lastGradHeight uint32) error {
|
lastGradHeight uint32) error {
|
||||||
|
|
||||||
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
||||||
@@ -470,7 +470,7 @@ func (ns *nurseryStore) PreschoolToKinder(kid *kidOutput,
|
|||||||
// graduated status. This involves removing the kindergarten entries from both
|
// graduated status. This involves removing the kindergarten entries from both
|
||||||
// the height and channel indexes. The height bucket will be opportunistically
|
// the height and channel indexes. The height bucket will be opportunistically
|
||||||
// pruned from the height index as outputs are removed.
|
// pruned from the height index as outputs are removed.
|
||||||
func (ns *nurseryStore) GraduateKinder(height uint32, kid *kidOutput) error {
|
func (ns *NurseryStore) GraduateKinder(height uint32, kid *kidOutput) error {
|
||||||
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
||||||
|
|
||||||
hghtBucket := ns.getHeightBucket(tx, height)
|
hghtBucket := ns.getHeightBucket(tx, height)
|
||||||
@@ -532,8 +532,8 @@ func (ns *nurseryStore) GraduateKinder(height uint32, kid *kidOutput) error {
|
|||||||
// delay expires at the provided block height.
|
// delay expires at the provided block height.
|
||||||
// FetchClass returns a list of the kindergarten and crib outputs whose timeouts
|
// FetchClass returns a list of the kindergarten and crib outputs whose timeouts
|
||||||
// are expiring
|
// are expiring
|
||||||
func (ns *nurseryStore) FetchClass(
|
func (ns *NurseryStore) FetchClass(
|
||||||
height uint32) ([]kidOutput, []babyOutput, error) {
|
height uint32) ([]kidOutput, []babyOutput, error) { // nolint:golint
|
||||||
|
|
||||||
// Construct list of all crib and kindergarten outputs that need to be
|
// Construct list of all crib and kindergarten outputs that need to be
|
||||||
// processed at the provided block height.
|
// processed at the provided block height.
|
||||||
@@ -594,7 +594,7 @@ func (ns *nurseryStore) FetchClass(
|
|||||||
|
|
||||||
// FetchPreschools returns a list of all outputs currently stored in the
|
// FetchPreschools returns a list of all outputs currently stored in the
|
||||||
// preschool bucket.
|
// preschool bucket.
|
||||||
func (ns *nurseryStore) FetchPreschools() ([]kidOutput, error) {
|
func (ns *NurseryStore) FetchPreschools() ([]kidOutput, error) { // nolint:golint
|
||||||
var kids []kidOutput
|
var kids []kidOutput
|
||||||
if err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
if err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
||||||
|
|
||||||
@@ -669,7 +669,7 @@ func (ns *nurseryStore) FetchPreschools() ([]kidOutput, error) {
|
|||||||
|
|
||||||
// HeightsBelowOrEqual returns a slice of all non-empty heights in the height
|
// HeightsBelowOrEqual returns a slice of all non-empty heights in the height
|
||||||
// index at or below the provided upper bound.
|
// index at or below the provided upper bound.
|
||||||
func (ns *nurseryStore) HeightsBelowOrEqual(height uint32) ([]uint32, error) {
|
func (ns *NurseryStore) HeightsBelowOrEqual(height uint32) ([]uint32, error) {
|
||||||
var activeHeights []uint32
|
var activeHeights []uint32
|
||||||
err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
||||||
// Ensure that the chain bucket for this nursery store exists.
|
// Ensure that the chain bucket for this nursery store exists.
|
||||||
@@ -715,7 +715,7 @@ func (ns *nurseryStore) HeightsBelowOrEqual(height uint32) ([]uint32, error) {
|
|||||||
// inferred from the key's prefix.
|
// inferred from the key's prefix.
|
||||||
// NOTE: The callback should not modify the provided byte slices and is
|
// NOTE: The callback should not modify the provided byte slices and is
|
||||||
// preferably non-blocking.
|
// preferably non-blocking.
|
||||||
func (ns *nurseryStore) ForChanOutputs(chanPoint *wire.OutPoint,
|
func (ns *NurseryStore) ForChanOutputs(chanPoint *wire.OutPoint,
|
||||||
callback func([]byte, []byte) error, reset func()) error {
|
callback func([]byte, []byte) error, reset func()) error {
|
||||||
|
|
||||||
return kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
return kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
||||||
@@ -724,7 +724,7 @@ func (ns *nurseryStore) ForChanOutputs(chanPoint *wire.OutPoint,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListChannels returns all channels the nursery is currently tracking.
|
// ListChannels returns all channels the nursery is currently tracking.
|
||||||
func (ns *nurseryStore) ListChannels() ([]wire.OutPoint, error) {
|
func (ns *NurseryStore) ListChannels() ([]wire.OutPoint, error) {
|
||||||
var activeChannels []wire.OutPoint
|
var activeChannels []wire.OutPoint
|
||||||
if err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
if err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
||||||
// Retrieve the existing chain bucket for this nursery store.
|
// Retrieve the existing chain bucket for this nursery store.
|
||||||
@@ -761,7 +761,7 @@ func (ns *nurseryStore) ListChannels() ([]wire.OutPoint, error) {
|
|||||||
|
|
||||||
// IsMatureChannel determines the whether or not all of the outputs in a
|
// IsMatureChannel determines the whether or not all of the outputs in a
|
||||||
// particular channel bucket have been marked as graduated.
|
// particular channel bucket have been marked as graduated.
|
||||||
func (ns *nurseryStore) IsMatureChannel(chanPoint *wire.OutPoint) (bool, error) {
|
func (ns *NurseryStore) IsMatureChannel(chanPoint *wire.OutPoint) (bool, error) {
|
||||||
err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
err := kvdb.View(ns.db, func(tx kvdb.RTx) error {
|
||||||
// Iterate over the contents of the channel bucket, computing
|
// Iterate over the contents of the channel bucket, computing
|
||||||
// both total number of outputs, and those that have the grad
|
// both total number of outputs, and those that have the grad
|
||||||
@@ -790,7 +790,7 @@ var ErrImmatureChannel = errors.New("cannot remove immature channel, " +
|
|||||||
// RemoveChannel channel erases all entries from the channel bucket for the
|
// RemoveChannel channel erases all entries from the channel bucket for the
|
||||||
// provided channel point.
|
// provided channel point.
|
||||||
// NOTE: The channel's entries in the height index are assumed to be removed.
|
// NOTE: The channel's entries in the height index are assumed to be removed.
|
||||||
func (ns *nurseryStore) RemoveChannel(chanPoint *wire.OutPoint) error {
|
func (ns *NurseryStore) RemoveChannel(chanPoint *wire.OutPoint) error {
|
||||||
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(ns.db, func(tx kvdb.RwTx) error {
|
||||||
// Retrieve the existing chain bucket for this nursery store.
|
// Retrieve the existing chain bucket for this nursery store.
|
||||||
chainBucket := tx.ReadWriteBucket(ns.pfxChainKey)
|
chainBucket := tx.ReadWriteBucket(ns.pfxChainKey)
|
||||||
@@ -853,7 +853,7 @@ func (ns *nurseryStore) RemoveChannel(chanPoint *wire.OutPoint) error {
|
|||||||
// its two-stage process of sweeping funds back to the user's wallet. These
|
// its two-stage process of sweeping funds back to the user's wallet. These
|
||||||
// outputs are persisted in the nursery store in the crib state, and will be
|
// outputs are persisted in the nursery store in the crib state, and will be
|
||||||
// revisited after the first-stage output's CLTV has expired.
|
// revisited after the first-stage output's CLTV has expired.
|
||||||
func (ns *nurseryStore) enterCrib(tx kvdb.RwTx, baby *babyOutput) error {
|
func (ns *NurseryStore) enterCrib(tx kvdb.RwTx, baby *babyOutput) error {
|
||||||
// First, retrieve or create the channel bucket corresponding to the
|
// First, retrieve or create the channel bucket corresponding to the
|
||||||
// baby output's origin channel point.
|
// baby output's origin channel point.
|
||||||
chanPoint := baby.OriginChanPoint()
|
chanPoint := baby.OriginChanPoint()
|
||||||
@@ -910,7 +910,7 @@ func (ns *nurseryStore) enterCrib(tx kvdb.RwTx, baby *babyOutput) error {
|
|||||||
// through a single stage before sweeping. Outputs are stored in the preschool
|
// through a single stage before sweeping. Outputs are stored in the preschool
|
||||||
// bucket until the commitment transaction has been confirmed, at which point
|
// bucket until the commitment transaction has been confirmed, at which point
|
||||||
// they will be moved to the kindergarten bucket.
|
// they will be moved to the kindergarten bucket.
|
||||||
func (ns *nurseryStore) enterPreschool(tx kvdb.RwTx, kid *kidOutput) error {
|
func (ns *NurseryStore) enterPreschool(tx kvdb.RwTx, kid *kidOutput) error {
|
||||||
// First, retrieve or create the channel bucket corresponding to the
|
// First, retrieve or create the channel bucket corresponding to the
|
||||||
// baby output's origin channel point.
|
// baby output's origin channel point.
|
||||||
chanPoint := kid.OriginChanPoint()
|
chanPoint := kid.OriginChanPoint()
|
||||||
@@ -943,7 +943,7 @@ func (ns *nurseryStore) enterPreschool(tx kvdb.RwTx, kid *kidOutput) error {
|
|||||||
|
|
||||||
// createChannelBucket creates or retrieves a channel bucket for the provided
|
// createChannelBucket creates or retrieves a channel bucket for the provided
|
||||||
// channel point.
|
// channel point.
|
||||||
func (ns *nurseryStore) createChannelBucket(tx kvdb.RwTx,
|
func (ns *NurseryStore) createChannelBucket(tx kvdb.RwTx,
|
||||||
chanPoint *wire.OutPoint) (kvdb.RwBucket, error) {
|
chanPoint *wire.OutPoint) (kvdb.RwBucket, error) {
|
||||||
|
|
||||||
// Ensure that the chain bucket for this nursery store exists.
|
// Ensure that the chain bucket for this nursery store exists.
|
||||||
@@ -974,7 +974,7 @@ func (ns *nurseryStore) createChannelBucket(tx kvdb.RwTx,
|
|||||||
// getChannelBucket retrieves an existing channel bucket from the nursery store,
|
// getChannelBucket retrieves an existing channel bucket from the nursery store,
|
||||||
// using the given channel point. If the bucket does not exist, or any bucket
|
// using the given channel point. If the bucket does not exist, or any bucket
|
||||||
// along its path does not exist, a nil value is returned.
|
// along its path does not exist, a nil value is returned.
|
||||||
func (ns *nurseryStore) getChannelBucket(tx kvdb.RTx,
|
func (ns *NurseryStore) getChannelBucket(tx kvdb.RTx,
|
||||||
chanPoint *wire.OutPoint) kvdb.RBucket {
|
chanPoint *wire.OutPoint) kvdb.RBucket {
|
||||||
|
|
||||||
// Retrieve the existing chain bucket for this nursery store.
|
// Retrieve the existing chain bucket for this nursery store.
|
||||||
@@ -1002,7 +1002,7 @@ func (ns *nurseryStore) getChannelBucket(tx kvdb.RTx,
|
|||||||
// getChannelBucketWrite retrieves an existing channel bucket from the nursery store,
|
// getChannelBucketWrite retrieves an existing channel bucket from the nursery store,
|
||||||
// using the given channel point. If the bucket does not exist, or any bucket
|
// using the given channel point. If the bucket does not exist, or any bucket
|
||||||
// along its path does not exist, a nil value is returned.
|
// along its path does not exist, a nil value is returned.
|
||||||
func (ns *nurseryStore) getChannelBucketWrite(tx kvdb.RwTx,
|
func (ns *NurseryStore) getChannelBucketWrite(tx kvdb.RwTx,
|
||||||
chanPoint *wire.OutPoint) kvdb.RwBucket {
|
chanPoint *wire.OutPoint) kvdb.RwBucket {
|
||||||
|
|
||||||
// Retrieve the existing chain bucket for this nursery store.
|
// Retrieve the existing chain bucket for this nursery store.
|
||||||
@@ -1029,7 +1029,7 @@ func (ns *nurseryStore) getChannelBucketWrite(tx kvdb.RwTx,
|
|||||||
|
|
||||||
// createHeightBucket creates or retrieves an existing bucket from the height
|
// createHeightBucket creates or retrieves an existing bucket from the height
|
||||||
// index, corresponding to the provided height.
|
// index, corresponding to the provided height.
|
||||||
func (ns *nurseryStore) createHeightBucket(tx kvdb.RwTx,
|
func (ns *NurseryStore) createHeightBucket(tx kvdb.RwTx,
|
||||||
height uint32) (kvdb.RwBucket, error) {
|
height uint32) (kvdb.RwBucket, error) {
|
||||||
|
|
||||||
// Ensure that the chain bucket for this nursery store exists.
|
// Ensure that the chain bucket for this nursery store exists.
|
||||||
@@ -1057,7 +1057,7 @@ func (ns *nurseryStore) createHeightBucket(tx kvdb.RwTx,
|
|||||||
// getHeightBucketPath retrieves an existing height bucket from the nursery
|
// getHeightBucketPath retrieves an existing height bucket from the nursery
|
||||||
// store, using the provided block height. If the bucket does not exist, or any
|
// store, using the provided block height. If the bucket does not exist, or any
|
||||||
// bucket along its path does not exist, a nil value is returned.
|
// bucket along its path does not exist, a nil value is returned.
|
||||||
func (ns *nurseryStore) getHeightBucketPath(tx kvdb.RTx,
|
func (ns *NurseryStore) getHeightBucketPath(tx kvdb.RTx,
|
||||||
height uint32) (kvdb.RBucket, kvdb.RBucket, kvdb.RBucket) {
|
height uint32) (kvdb.RBucket, kvdb.RBucket, kvdb.RBucket) {
|
||||||
|
|
||||||
// Retrieve the existing chain bucket for this nursery store.
|
// Retrieve the existing chain bucket for this nursery store.
|
||||||
@@ -1083,7 +1083,7 @@ func (ns *nurseryStore) getHeightBucketPath(tx kvdb.RTx,
|
|||||||
// getHeightBucketPathWrite retrieves an existing height bucket from the nursery
|
// getHeightBucketPathWrite retrieves an existing height bucket from the nursery
|
||||||
// store, using the provided block height. If the bucket does not exist, or any
|
// store, using the provided block height. If the bucket does not exist, or any
|
||||||
// bucket along its path does not exist, a nil value is returned.
|
// bucket along its path does not exist, a nil value is returned.
|
||||||
func (ns *nurseryStore) getHeightBucketPathWrite(tx kvdb.RwTx,
|
func (ns *NurseryStore) getHeightBucketPathWrite(tx kvdb.RwTx,
|
||||||
height uint32) (kvdb.RwBucket, kvdb.RwBucket, kvdb.RwBucket) {
|
height uint32) (kvdb.RwBucket, kvdb.RwBucket, kvdb.RwBucket) {
|
||||||
|
|
||||||
// Retrieve the existing chain bucket for this nursery store.
|
// Retrieve the existing chain bucket for this nursery store.
|
||||||
@@ -1111,7 +1111,7 @@ func (ns *nurseryStore) getHeightBucketPathWrite(tx kvdb.RwTx,
|
|||||||
// getHeightBucket retrieves an existing height bucket from the nursery store,
|
// getHeightBucket retrieves an existing height bucket from the nursery store,
|
||||||
// using the provided block height. If the bucket does not exist, or any bucket
|
// using the provided block height. If the bucket does not exist, or any bucket
|
||||||
// along its path does not exist, a nil value is returned.
|
// along its path does not exist, a nil value is returned.
|
||||||
func (ns *nurseryStore) getHeightBucket(tx kvdb.RTx,
|
func (ns *NurseryStore) getHeightBucket(tx kvdb.RTx,
|
||||||
height uint32) kvdb.RBucket {
|
height uint32) kvdb.RBucket {
|
||||||
_, _, hghtBucket := ns.getHeightBucketPath(tx, height)
|
_, _, hghtBucket := ns.getHeightBucketPath(tx, height)
|
||||||
|
|
||||||
@@ -1121,7 +1121,7 @@ func (ns *nurseryStore) getHeightBucket(tx kvdb.RTx,
|
|||||||
// getHeightBucketWrite retrieves an existing height bucket from the nursery store,
|
// getHeightBucketWrite retrieves an existing height bucket from the nursery store,
|
||||||
// using the provided block height. If the bucket does not exist, or any bucket
|
// using the provided block height. If the bucket does not exist, or any bucket
|
||||||
// along its path does not exist, a nil value is returned.
|
// along its path does not exist, a nil value is returned.
|
||||||
func (ns *nurseryStore) getHeightBucketWrite(tx kvdb.RwTx,
|
func (ns *NurseryStore) getHeightBucketWrite(tx kvdb.RwTx,
|
||||||
height uint32) kvdb.RwBucket {
|
height uint32) kvdb.RwBucket {
|
||||||
|
|
||||||
_, _, hghtBucket := ns.getHeightBucketPathWrite(tx, height)
|
_, _, hghtBucket := ns.getHeightBucketPathWrite(tx, height)
|
||||||
@@ -1132,7 +1132,7 @@ func (ns *nurseryStore) getHeightBucketWrite(tx kvdb.RwTx,
|
|||||||
// createHeightChanBucket creates or retrieves an existing height-channel bucket
|
// createHeightChanBucket creates or retrieves an existing height-channel bucket
|
||||||
// for the provided block height and channel point. This method will attempt to
|
// for the provided block height and channel point. This method will attempt to
|
||||||
// instantiate all buckets along the path if required.
|
// instantiate all buckets along the path if required.
|
||||||
func (ns *nurseryStore) createHeightChanBucket(tx kvdb.RwTx,
|
func (ns *NurseryStore) createHeightChanBucket(tx kvdb.RwTx,
|
||||||
height uint32, chanPoint *wire.OutPoint) (kvdb.RwBucket, error) {
|
height uint32, chanPoint *wire.OutPoint) (kvdb.RwBucket, error) {
|
||||||
|
|
||||||
// Ensure that the height bucket for this nursery store exists.
|
// Ensure that the height bucket for this nursery store exists.
|
||||||
@@ -1158,7 +1158,7 @@ func (ns *nurseryStore) createHeightChanBucket(tx kvdb.RwTx,
|
|||||||
// nursery store, using the provided block height and channel point. if the
|
// nursery store, using the provided block height and channel point. if the
|
||||||
// bucket does not exist, or any bucket along its path does not exist, a nil
|
// bucket does not exist, or any bucket along its path does not exist, a nil
|
||||||
// value is returned.
|
// value is returned.
|
||||||
func (ns *nurseryStore) getHeightChanBucketWrite(tx kvdb.RwTx,
|
func (ns *NurseryStore) getHeightChanBucketWrite(tx kvdb.RwTx,
|
||||||
height uint32, chanPoint *wire.OutPoint) kvdb.RwBucket {
|
height uint32, chanPoint *wire.OutPoint) kvdb.RwBucket {
|
||||||
|
|
||||||
// Retrieve the existing height bucket from this nursery store.
|
// Retrieve the existing height bucket from this nursery store.
|
||||||
@@ -1185,7 +1185,7 @@ func (ns *nurseryStore) getHeightChanBucketWrite(tx kvdb.RwTx,
|
|||||||
// enumerate crib and kindergarten outputs at a particular height. The callback
|
// enumerate crib and kindergarten outputs at a particular height. The callback
|
||||||
// is invoked with serialized bytes retrieved for each output of interest,
|
// is invoked with serialized bytes retrieved for each output of interest,
|
||||||
// allowing the caller to deserialize them into the appropriate type.
|
// allowing the caller to deserialize them into the appropriate type.
|
||||||
func (ns *nurseryStore) forEachHeightPrefix(tx kvdb.RTx, prefix []byte,
|
func (ns *NurseryStore) forEachHeightPrefix(tx kvdb.RTx, prefix []byte,
|
||||||
height uint32, callback func([]byte) error) error {
|
height uint32, callback func([]byte) error) error {
|
||||||
|
|
||||||
// Start by retrieving the height bucket corresponding to the provided
|
// Start by retrieving the height bucket corresponding to the provided
|
||||||
@@ -1273,7 +1273,7 @@ func (ns *nurseryStore) forEachHeightPrefix(tx kvdb.RTx, prefix []byte,
|
|||||||
// provided callback. The callback accepts a key-value pair of byte slices
|
// provided callback. The callback accepts a key-value pair of byte slices
|
||||||
// corresponding to the prefixed-output key and the serialized output,
|
// corresponding to the prefixed-output key and the serialized output,
|
||||||
// respectively.
|
// respectively.
|
||||||
func (ns *nurseryStore) forChanOutputs(tx kvdb.RTx, chanPoint *wire.OutPoint,
|
func (ns *NurseryStore) forChanOutputs(tx kvdb.RTx, chanPoint *wire.OutPoint,
|
||||||
callback func([]byte, []byte) error) error {
|
callback func([]byte, []byte) error) error {
|
||||||
|
|
||||||
chanBucket := ns.getChannelBucket(tx, chanPoint)
|
chanBucket := ns.getChannelBucket(tx, chanPoint)
|
||||||
@@ -1291,7 +1291,7 @@ var errBucketNotEmpty = errors.New("bucket is not empty, cannot be pruned")
|
|||||||
// removeOutputFromHeight will delete the given output from the specified
|
// removeOutputFromHeight will delete the given output from the specified
|
||||||
// height-channel bucket, and attempt to prune the upstream directories if they
|
// height-channel bucket, and attempt to prune the upstream directories if they
|
||||||
// are empty.
|
// are empty.
|
||||||
func (ns *nurseryStore) removeOutputFromHeight(tx kvdb.RwTx, height uint32,
|
func (ns *NurseryStore) removeOutputFromHeight(tx kvdb.RwTx, height uint32,
|
||||||
chanPoint *wire.OutPoint, pfxKey []byte) error {
|
chanPoint *wire.OutPoint, pfxKey []byte) error {
|
||||||
|
|
||||||
// Retrieve the height-channel bucket and delete the prefixed output.
|
// Retrieve the height-channel bucket and delete the prefixed output.
|
||||||
@@ -1343,7 +1343,7 @@ func (ns *nurseryStore) removeOutputFromHeight(tx kvdb.RwTx, height uint32,
|
|||||||
// all active outputs at this height have been removed from their respective
|
// all active outputs at this height have been removed from their respective
|
||||||
// height-channel buckets. The returned boolean value indicated whether or not
|
// height-channel buckets. The returned boolean value indicated whether or not
|
||||||
// this invocation successfully pruned the height bucket.
|
// this invocation successfully pruned the height bucket.
|
||||||
func (ns *nurseryStore) pruneHeight(tx kvdb.RwTx, height uint32) (bool, error) {
|
func (ns *NurseryStore) pruneHeight(tx kvdb.RwTx, height uint32) (bool, error) {
|
||||||
// Fetch the existing height index and height bucket.
|
// Fetch the existing height index and height bucket.
|
||||||
_, hghtIndex, hghtBucket := ns.getHeightBucketPathWrite(tx, height)
|
_, hghtIndex, hghtBucket := ns.getHeightBucketPathWrite(tx, height)
|
||||||
if hghtBucket == nil {
|
if hghtBucket == nil {
|
||||||
@@ -1427,5 +1427,5 @@ func isBucketEmpty(parent kvdb.RBucket) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile-time constraint to ensure nurseryStore implements NurseryStore.
|
// Compile-time constraint to ensure NurseryStore implements NurseryStorer.
|
||||||
var _ NurseryStore = (*nurseryStore)(nil)
|
var _ NurseryStorer = (*NurseryStore)(nil)
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
|
//go:build !rpctest
|
||||||
// +build !rpctest
|
// +build !rpctest
|
||||||
|
|
||||||
package lnd
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ type incubateTest struct {
|
|||||||
// outputs stored in the nursery store.
|
// outputs stored in the nursery store.
|
||||||
var incubateTests []incubateTest
|
var incubateTests []incubateTest
|
||||||
|
|
||||||
|
var chainHash chainhash.Hash
|
||||||
|
|
||||||
// initIncubateTests instantiates the test vectors during package init, which
|
// initIncubateTests instantiates the test vectors during package init, which
|
||||||
// properly captures the sign descriptors and public keys.
|
// properly captures the sign descriptors and public keys.
|
||||||
func initIncubateTests() {
|
func initIncubateTests() {
|
||||||
@@ -55,7 +58,7 @@ func TestNurseryStoreInit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
ns, err := newNurseryStore(&chainreg.BitcoinTestnetGenesis, cdb)
|
ns, err := NewNurseryStore(&chainHash, cdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to open nursery store: %v", err)
|
t.Fatalf("unable to open nursery store: %v", err)
|
||||||
}
|
}
|
||||||
@@ -75,7 +78,7 @@ func TestNurseryStoreIncubate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
ns, err := newNurseryStore(&chainreg.BitcoinTestnetGenesis, cdb)
|
ns, err := NewNurseryStore(&chainHash, cdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to open nursery store: %v", err)
|
t.Fatalf("unable to open nursery store: %v", err)
|
||||||
}
|
}
|
||||||
@@ -316,7 +319,7 @@ func TestNurseryStoreGraduate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
ns, err := newNurseryStore(&chainreg.BitcoinTestnetGenesis, cdb)
|
ns, err := NewNurseryStore(&chainHash, cdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to open nursery store: %v", err)
|
t.Fatalf("unable to open nursery store: %v", err)
|
||||||
}
|
}
|
||||||
@@ -363,7 +366,7 @@ func TestNurseryStoreGraduate(t *testing.T) {
|
|||||||
|
|
||||||
// assertNumChanOutputs checks that the channel bucket has the expected number
|
// assertNumChanOutputs checks that the channel bucket has the expected number
|
||||||
// of outputs.
|
// of outputs.
|
||||||
func assertNumChanOutputs(t *testing.T, ns NurseryStore,
|
func assertNumChanOutputs(t *testing.T, ns NurseryStorer,
|
||||||
chanPoint *wire.OutPoint, expectedNum int) {
|
chanPoint *wire.OutPoint, expectedNum int) {
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
@@ -389,7 +392,7 @@ func assertNumChanOutputs(t *testing.T, ns NurseryStore,
|
|||||||
|
|
||||||
// assertNumPreschools loads all preschool outputs and verifies their count
|
// assertNumPreschools loads all preschool outputs and verifies their count
|
||||||
// matches the expected number.
|
// matches the expected number.
|
||||||
func assertNumPreschools(t *testing.T, ns NurseryStore, expected int) {
|
func assertNumPreschools(t *testing.T, ns NurseryStorer, expected int) {
|
||||||
psclOutputs, err := ns.FetchPreschools()
|
psclOutputs, err := ns.FetchPreschools()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to retrieve preschool outputs: %v", err)
|
t.Fatalf("unable to retrieve preschool outputs: %v", err)
|
||||||
@@ -403,7 +406,7 @@ func assertNumPreschools(t *testing.T, ns NurseryStore, expected int) {
|
|||||||
|
|
||||||
// assertNumChannels checks that the nursery has a given number of active
|
// assertNumChannels checks that the nursery has a given number of active
|
||||||
// channels.
|
// channels.
|
||||||
func assertNumChannels(t *testing.T, ns NurseryStore, expected int) {
|
func assertNumChannels(t *testing.T, ns NurseryStorer, expected int) {
|
||||||
channels, err := ns.ListChannels()
|
channels, err := ns.ListChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch channels from nursery store: %v",
|
t.Fatalf("unable to fetch channels from nursery store: %v",
|
||||||
@@ -418,7 +421,7 @@ func assertNumChannels(t *testing.T, ns NurseryStore, expected int) {
|
|||||||
|
|
||||||
// assertHeightIsPurged checks that the finalized transaction, kindergarten, and
|
// assertHeightIsPurged checks that the finalized transaction, kindergarten, and
|
||||||
// htlc outputs at a particular height are all nil.
|
// htlc outputs at a particular height are all nil.
|
||||||
func assertHeightIsPurged(t *testing.T, ns NurseryStore,
|
func assertHeightIsPurged(t *testing.T, ns NurseryStorer,
|
||||||
height uint32) {
|
height uint32) {
|
||||||
|
|
||||||
kndrOutputs, cribOutputs, err := ns.FetchClass(height)
|
kndrOutputs, cribOutputs, err := ns.FetchClass(height)
|
||||||
@@ -438,7 +441,7 @@ func assertHeightIsPurged(t *testing.T, ns NurseryStore,
|
|||||||
|
|
||||||
// assertCribAtExpiryHeight loads the class at the given height, and verifies
|
// assertCribAtExpiryHeight loads the class at the given height, and verifies
|
||||||
// that the given htlc output is one of the crib outputs.
|
// that the given htlc output is one of the crib outputs.
|
||||||
func assertCribAtExpiryHeight(t *testing.T, ns NurseryStore,
|
func assertCribAtExpiryHeight(t *testing.T, ns NurseryStorer,
|
||||||
htlcOutput *babyOutput) {
|
htlcOutput *babyOutput) {
|
||||||
|
|
||||||
expiryHeight := htlcOutput.expiry
|
expiryHeight := htlcOutput.expiry
|
||||||
@@ -460,7 +463,7 @@ func assertCribAtExpiryHeight(t *testing.T, ns NurseryStore,
|
|||||||
|
|
||||||
// assertCribNotAtExpiryHeight loads the class at the given height, and verifies
|
// assertCribNotAtExpiryHeight loads the class at the given height, and verifies
|
||||||
// that the given htlc output is not one of the crib outputs.
|
// that the given htlc output is not one of the crib outputs.
|
||||||
func assertCribNotAtExpiryHeight(t *testing.T, ns NurseryStore,
|
func assertCribNotAtExpiryHeight(t *testing.T, ns NurseryStorer,
|
||||||
htlcOutput *babyOutput) {
|
htlcOutput *babyOutput) {
|
||||||
|
|
||||||
expiryHeight := htlcOutput.expiry
|
expiryHeight := htlcOutput.expiry
|
||||||
@@ -481,7 +484,7 @@ func assertCribNotAtExpiryHeight(t *testing.T, ns NurseryStore,
|
|||||||
// assertKndrAtMaturityHeight loads the class at the provided height and
|
// assertKndrAtMaturityHeight loads the class at the provided height and
|
||||||
// verifies that the provided kid output is one of the kindergarten outputs
|
// verifies that the provided kid output is one of the kindergarten outputs
|
||||||
// returned.
|
// returned.
|
||||||
func assertKndrAtMaturityHeight(t *testing.T, ns NurseryStore,
|
func assertKndrAtMaturityHeight(t *testing.T, ns NurseryStorer,
|
||||||
kndrOutput *kidOutput) {
|
kndrOutput *kidOutput) {
|
||||||
|
|
||||||
maturityHeight := kndrOutput.ConfHeight() +
|
maturityHeight := kndrOutput.ConfHeight() +
|
||||||
@@ -505,7 +508,7 @@ func assertKndrAtMaturityHeight(t *testing.T, ns NurseryStore,
|
|||||||
// assertKndrNotAtMaturityHeight loads the class at the provided height and
|
// assertKndrNotAtMaturityHeight loads the class at the provided height and
|
||||||
// verifies that the provided kid output is not one of the kindergarten outputs
|
// verifies that the provided kid output is not one of the kindergarten outputs
|
||||||
// returned.
|
// returned.
|
||||||
func assertKndrNotAtMaturityHeight(t *testing.T, ns NurseryStore,
|
func assertKndrNotAtMaturityHeight(t *testing.T, ns NurseryStorer,
|
||||||
kndrOutput *kidOutput) {
|
kndrOutput *kidOutput) {
|
||||||
|
|
||||||
maturityHeight := kndrOutput.ConfHeight() +
|
maturityHeight := kndrOutput.ConfHeight() +
|
||||||
@@ -527,7 +530,7 @@ func assertKndrNotAtMaturityHeight(t *testing.T, ns NurseryStore,
|
|||||||
|
|
||||||
// assertChannelMaturity queries the nursery store for the maturity of the given
|
// assertChannelMaturity queries the nursery store for the maturity of the given
|
||||||
// channel, failing if the result does not match the expectedMaturity.
|
// channel, failing if the result does not match the expectedMaturity.
|
||||||
func assertChannelMaturity(t *testing.T, ns NurseryStore,
|
func assertChannelMaturity(t *testing.T, ns NurseryStorer,
|
||||||
chanPoint *wire.OutPoint, expectedMaturity bool) {
|
chanPoint *wire.OutPoint, expectedMaturity bool) {
|
||||||
|
|
||||||
isMature, err := ns.IsMatureChannel(chanPoint)
|
isMature, err := ns.IsMatureChannel(chanPoint)
|
||||||
@@ -543,7 +546,7 @@ func assertChannelMaturity(t *testing.T, ns NurseryStore,
|
|||||||
|
|
||||||
// assertCanRemoveChannel tries to remove a channel from the nursery store,
|
// assertCanRemoveChannel tries to remove a channel from the nursery store,
|
||||||
// failing if the result does match expected canRemove.
|
// failing if the result does match expected canRemove.
|
||||||
func assertCanRemoveChannel(t *testing.T, ns NurseryStore,
|
func assertCanRemoveChannel(t *testing.T, ns NurseryStorer,
|
||||||
chanPoint *wire.OutPoint, canRemove bool) {
|
chanPoint *wire.OutPoint, canRemove bool) {
|
||||||
|
|
||||||
err := ns.RemoveChannel(chanPoint)
|
err := ns.RemoveChannel(chanPoint)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package lnd
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -198,13 +198,13 @@ type NurseryConfig struct {
|
|||||||
|
|
||||||
// Store provides access to and modification of the persistent state
|
// Store provides access to and modification of the persistent state
|
||||||
// maintained about the utxo nursery's incubating outputs.
|
// maintained about the utxo nursery's incubating outputs.
|
||||||
Store NurseryStore
|
Store NurseryStorer
|
||||||
|
|
||||||
// Sweep sweeps an input back to the wallet.
|
// Sweep sweeps an input back to the wallet.
|
||||||
SweepInput func(input.Input, sweep.Params) (chan sweep.Result, error)
|
SweepInput func(input.Input, sweep.Params) (chan sweep.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// utxoNursery is a system dedicated to incubating time-locked outputs created
|
// UtxoNursery is a system dedicated to incubating time-locked outputs created
|
||||||
// by the broadcast of a commitment transaction either by us, or the remote
|
// by the broadcast of a commitment transaction either by us, or the remote
|
||||||
// peer. The nursery accepts outputs and "incubates" them until they've reached
|
// peer. The nursery accepts outputs and "incubates" them until they've reached
|
||||||
// maturity, then sweep the outputs into the source wallet. An output is
|
// maturity, then sweep the outputs into the source wallet. An output is
|
||||||
@@ -212,7 +212,7 @@ type NurseryConfig struct {
|
|||||||
// passed. As outputs reach their maturity age, they're swept in batches into
|
// passed. As outputs reach their maturity age, they're swept in batches into
|
||||||
// the source wallet, returning the outputs so they can be used within future
|
// the source wallet, returning the outputs so they can be used within future
|
||||||
// channels, or regular Bitcoin transactions.
|
// channels, or regular Bitcoin transactions.
|
||||||
type utxoNursery struct {
|
type UtxoNursery struct {
|
||||||
started uint32 // To be used atomically.
|
started uint32 // To be used atomically.
|
||||||
stopped uint32 // To be used atomically.
|
stopped uint32 // To be used atomically.
|
||||||
|
|
||||||
@@ -225,18 +225,18 @@ type utxoNursery struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// newUtxoNursery creates a new instance of the utxoNursery from a
|
// NewUtxoNursery creates a new instance of the UtxoNursery from a
|
||||||
// ChainNotifier and LightningWallet instance.
|
// ChainNotifier and LightningWallet instance.
|
||||||
func newUtxoNursery(cfg *NurseryConfig) *utxoNursery {
|
func NewUtxoNursery(cfg *NurseryConfig) *UtxoNursery {
|
||||||
return &utxoNursery{
|
return &UtxoNursery{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start launches all goroutines the utxoNursery needs to properly carry out
|
// Start launches all goroutines the UtxoNursery needs to properly carry out
|
||||||
// its duties.
|
// its duties.
|
||||||
func (u *utxoNursery) Start() error {
|
func (u *UtxoNursery) Start() error {
|
||||||
if !atomic.CompareAndSwapUint32(&u.started, 0, 1) {
|
if !atomic.CompareAndSwapUint32(&u.started, 0, 1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -311,8 +311,8 @@ func (u *utxoNursery) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop gracefully shuts down any lingering goroutines launched during normal
|
// Stop gracefully shuts down any lingering goroutines launched during normal
|
||||||
// operation of the utxoNursery.
|
// operation of the UtxoNursery.
|
||||||
func (u *utxoNursery) Stop() error {
|
func (u *UtxoNursery) Stop() error {
|
||||||
if !atomic.CompareAndSwapUint32(&u.stopped, 0, 1) {
|
if !atomic.CompareAndSwapUint32(&u.stopped, 0, 1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -325,11 +325,11 @@ func (u *utxoNursery) Stop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IncubateOutputs sends a request to the utxoNursery to incubate a set of
|
// IncubateOutputs sends a request to the UtxoNursery to incubate a set of
|
||||||
// outputs from an existing commitment transaction. Outputs need to incubate if
|
// outputs from an existing commitment transaction. Outputs need to incubate if
|
||||||
// they're CLTV absolute time locked, or if they're CSV relative time locked.
|
// they're CLTV absolute time locked, or if they're CSV relative time locked.
|
||||||
// Once all outputs reach maturity, they'll be swept back into the wallet.
|
// Once all outputs reach maturity, they'll be swept back into the wallet.
|
||||||
func (u *utxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
|
func (u *UtxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
|
||||||
outgoingHtlcs []lnwallet.OutgoingHtlcResolution,
|
outgoingHtlcs []lnwallet.OutgoingHtlcResolution,
|
||||||
incomingHtlcs []lnwallet.IncomingHtlcResolution,
|
incomingHtlcs []lnwallet.IncomingHtlcResolution,
|
||||||
broadcastHeight uint32) error {
|
broadcastHeight uint32) error {
|
||||||
@@ -468,8 +468,8 @@ func (u *utxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
|
|||||||
// outpoint. A nursery report details the maturity/sweeping progress for a
|
// outpoint. A nursery report details the maturity/sweeping progress for a
|
||||||
// contract that was previously force closed. If a report entry for the target
|
// contract that was previously force closed. If a report entry for the target
|
||||||
// chanPoint is unable to be constructed, then an error will be returned.
|
// chanPoint is unable to be constructed, then an error will be returned.
|
||||||
func (u *utxoNursery) NurseryReport(
|
func (u *UtxoNursery) NurseryReport(
|
||||||
chanPoint *wire.OutPoint) (*contractMaturityReport, error) {
|
chanPoint *wire.OutPoint) (*ContractMaturityReport, error) {
|
||||||
|
|
||||||
u.mu.Lock()
|
u.mu.Lock()
|
||||||
defer u.mu.Unlock()
|
defer u.mu.Unlock()
|
||||||
@@ -477,7 +477,7 @@ func (u *utxoNursery) NurseryReport(
|
|||||||
utxnLog.Debugf("NurseryReport: building nursery report for channel %v",
|
utxnLog.Debugf("NurseryReport: building nursery report for channel %v",
|
||||||
chanPoint)
|
chanPoint)
|
||||||
|
|
||||||
var report *contractMaturityReport
|
var report *ContractMaturityReport
|
||||||
|
|
||||||
if err := u.cfg.Store.ForChanOutputs(chanPoint, func(k, v []byte) error {
|
if err := u.cfg.Store.ForChanOutputs(chanPoint, func(k, v []byte) error {
|
||||||
switch {
|
switch {
|
||||||
@@ -577,7 +577,7 @@ func (u *utxoNursery) NurseryReport(
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, func() {
|
}, func() {
|
||||||
report = &contractMaturityReport{}
|
report = &ContractMaturityReport{}
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -587,7 +587,7 @@ func (u *utxoNursery) NurseryReport(
|
|||||||
|
|
||||||
// reloadPreschool re-initializes the chain notifier with all of the outputs
|
// reloadPreschool re-initializes the chain notifier with all of the outputs
|
||||||
// that had been saved to the "preschool" database bucket prior to shutdown.
|
// that had been saved to the "preschool" database bucket prior to shutdown.
|
||||||
func (u *utxoNursery) reloadPreschool() error {
|
func (u *UtxoNursery) reloadPreschool() error {
|
||||||
psclOutputs, err := u.cfg.Store.FetchPreschools()
|
psclOutputs, err := u.cfg.Store.FetchPreschools()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -634,7 +634,7 @@ func (u *utxoNursery) reloadPreschool() error {
|
|||||||
// This allows the nursery to reinitialize all state to continue sweeping
|
// This allows the nursery to reinitialize all state to continue sweeping
|
||||||
// outputs, even in the event that we missed blocks while offline. reloadClasses
|
// outputs, even in the event that we missed blocks while offline. reloadClasses
|
||||||
// is called during the startup of the UTXO Nursery.
|
// is called during the startup of the UTXO Nursery.
|
||||||
func (u *utxoNursery) reloadClasses(bestHeight uint32) error {
|
func (u *UtxoNursery) reloadClasses(bestHeight uint32) error {
|
||||||
// Loading all active heights up to and including the current block.
|
// Loading all active heights up to and including the current block.
|
||||||
activeHeights, err := u.cfg.Store.HeightsBelowOrEqual(
|
activeHeights, err := u.cfg.Store.HeightsBelowOrEqual(
|
||||||
uint32(bestHeight))
|
uint32(bestHeight))
|
||||||
@@ -674,7 +674,7 @@ func (u *utxoNursery) reloadClasses(bestHeight uint32) error {
|
|||||||
// confirmation of these spends will either 1) move a crib output into the
|
// confirmation of these spends will either 1) move a crib output into the
|
||||||
// kindergarten bucket or 2) move a kindergarten output into the graduated
|
// kindergarten bucket or 2) move a kindergarten output into the graduated
|
||||||
// bucket.
|
// bucket.
|
||||||
func (u *utxoNursery) incubator(newBlockChan *chainntnfs.BlockEpochEvent) {
|
func (u *UtxoNursery) incubator(newBlockChan *chainntnfs.BlockEpochEvent) {
|
||||||
defer u.wg.Done()
|
defer u.wg.Done()
|
||||||
defer newBlockChan.Cancel()
|
defer newBlockChan.Cancel()
|
||||||
|
|
||||||
@@ -722,7 +722,7 @@ func (u *utxoNursery) incubator(newBlockChan *chainntnfs.BlockEpochEvent) {
|
|||||||
// CLTV delay expires at the nursery's current height. This method is called
|
// CLTV delay expires at the nursery's current height. This method is called
|
||||||
// each time a new block arrives, or during startup to catch up on heights we
|
// each time a new block arrives, or during startup to catch up on heights we
|
||||||
// may have missed while the nursery was offline.
|
// may have missed while the nursery was offline.
|
||||||
func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
func (u *UtxoNursery) graduateClass(classHeight uint32) error {
|
||||||
// Record this height as the nursery's current best height.
|
// Record this height as the nursery's current best height.
|
||||||
u.mu.Lock()
|
u.mu.Lock()
|
||||||
defer u.mu.Unlock()
|
defer u.mu.Unlock()
|
||||||
@@ -769,7 +769,7 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
|||||||
// control of funds from a prior channel commitment transaction to the user's
|
// control of funds from a prior channel commitment transaction to the user's
|
||||||
// wallet. The outputs swept were previously time locked (either absolute or
|
// wallet. The outputs swept were previously time locked (either absolute or
|
||||||
// relative), but are not mature enough to sweep into the wallet.
|
// relative), but are not mature enough to sweep into the wallet.
|
||||||
func (u *utxoNursery) sweepMatureOutputs(classHeight uint32,
|
func (u *UtxoNursery) sweepMatureOutputs(classHeight uint32,
|
||||||
kgtnOutputs []kidOutput) error {
|
kgtnOutputs []kidOutput) error {
|
||||||
|
|
||||||
utxnLog.Infof("Sweeping %v CSV-delayed outputs with sweep tx for "+
|
utxnLog.Infof("Sweeping %v CSV-delayed outputs with sweep tx for "+
|
||||||
@@ -799,7 +799,7 @@ func (u *utxoNursery) sweepMatureOutputs(classHeight uint32,
|
|||||||
// received, the nursery will mark those outputs as fully graduated, and proceed
|
// received, the nursery will mark those outputs as fully graduated, and proceed
|
||||||
// to mark any mature channels as fully closed in channeldb.
|
// to mark any mature channels as fully closed in channeldb.
|
||||||
// NOTE(conner): this method MUST be called as a go routine.
|
// NOTE(conner): this method MUST be called as a go routine.
|
||||||
func (u *utxoNursery) waitForSweepConf(classHeight uint32,
|
func (u *UtxoNursery) waitForSweepConf(classHeight uint32,
|
||||||
output *kidOutput, resultChan chan sweep.Result) {
|
output *kidOutput, resultChan chan sweep.Result) {
|
||||||
|
|
||||||
defer u.wg.Done()
|
defer u.wg.Done()
|
||||||
@@ -834,7 +834,7 @@ func (u *utxoNursery) waitForSweepConf(classHeight uint32,
|
|||||||
u.mu.Lock()
|
u.mu.Lock()
|
||||||
defer u.mu.Unlock()
|
defer u.mu.Unlock()
|
||||||
|
|
||||||
// TODO(conner): add retry logic?
|
// TODO(conner): add retry utxnLogic?
|
||||||
|
|
||||||
// Mark the confirmed kindergarten output as graduated.
|
// Mark the confirmed kindergarten output as graduated.
|
||||||
if err := u.cfg.Store.GraduateKinder(classHeight, output); err != nil {
|
if err := u.cfg.Store.GraduateKinder(classHeight, output); err != nil {
|
||||||
@@ -859,7 +859,7 @@ func (u *utxoNursery) waitForSweepConf(classHeight uint32,
|
|||||||
// sweepCribOutput broadcasts the crib output's htlc timeout txn, and sets up a
|
// sweepCribOutput broadcasts the crib output's htlc timeout txn, and sets up a
|
||||||
// notification that will advance it to the kindergarten bucket upon
|
// notification that will advance it to the kindergarten bucket upon
|
||||||
// confirmation.
|
// confirmation.
|
||||||
func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) error {
|
func (u *UtxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) error {
|
||||||
utxnLog.Infof("Publishing CLTV-delayed HTLC output using timeout tx "+
|
utxnLog.Infof("Publishing CLTV-delayed HTLC output using timeout tx "+
|
||||||
"(txid=%v): %v", baby.timeoutTx.TxHash(),
|
"(txid=%v): %v", baby.timeoutTx.TxHash(),
|
||||||
newLogClosure(func() string {
|
newLogClosure(func() string {
|
||||||
@@ -884,7 +884,7 @@ func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) erro
|
|||||||
// notification for an htlc timeout transaction. If successful, a goroutine
|
// notification for an htlc timeout transaction. If successful, a goroutine
|
||||||
// will be spawned that will transition the provided baby output into the
|
// will be spawned that will transition the provided baby output into the
|
||||||
// kindergarten state within the nursery store.
|
// kindergarten state within the nursery store.
|
||||||
func (u *utxoNursery) registerTimeoutConf(baby *babyOutput, heightHint uint32) error {
|
func (u *UtxoNursery) registerTimeoutConf(baby *babyOutput, heightHint uint32) error {
|
||||||
|
|
||||||
birthTxID := baby.timeoutTx.TxHash()
|
birthTxID := baby.timeoutTx.TxHash()
|
||||||
|
|
||||||
@@ -909,7 +909,7 @@ func (u *utxoNursery) registerTimeoutConf(baby *babyOutput, heightHint uint32) e
|
|||||||
// waitForTimeoutConf watches for the confirmation of an htlc timeout
|
// waitForTimeoutConf watches for the confirmation of an htlc timeout
|
||||||
// transaction, and attempts to move the htlc output from the crib bucket to the
|
// transaction, and attempts to move the htlc output from the crib bucket to the
|
||||||
// kindergarten bucket upon success.
|
// kindergarten bucket upon success.
|
||||||
func (u *utxoNursery) waitForTimeoutConf(baby *babyOutput,
|
func (u *UtxoNursery) waitForTimeoutConf(baby *babyOutput,
|
||||||
confChan *chainntnfs.ConfirmationEvent) {
|
confChan *chainntnfs.ConfirmationEvent) {
|
||||||
|
|
||||||
defer u.wg.Done()
|
defer u.wg.Done()
|
||||||
@@ -932,7 +932,7 @@ func (u *utxoNursery) waitForTimeoutConf(baby *babyOutput,
|
|||||||
u.mu.Lock()
|
u.mu.Lock()
|
||||||
defer u.mu.Unlock()
|
defer u.mu.Unlock()
|
||||||
|
|
||||||
// TODO(conner): add retry logic?
|
// TODO(conner): add retry utxnLogic?
|
||||||
|
|
||||||
err := u.cfg.Store.CribToKinder(baby)
|
err := u.cfg.Store.CribToKinder(baby)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -950,7 +950,7 @@ func (u *utxoNursery) waitForTimeoutConf(baby *babyOutput,
|
|||||||
// HTLC on our commitment transaction.. If successful, the provided preschool
|
// HTLC on our commitment transaction.. If successful, the provided preschool
|
||||||
// output will be moved persistently into the kindergarten state within the
|
// output will be moved persistently into the kindergarten state within the
|
||||||
// nursery store.
|
// nursery store.
|
||||||
func (u *utxoNursery) registerPreschoolConf(kid *kidOutput, heightHint uint32) error {
|
func (u *UtxoNursery) registerPreschoolConf(kid *kidOutput, heightHint uint32) error {
|
||||||
txID := kid.OutPoint().Hash
|
txID := kid.OutPoint().Hash
|
||||||
|
|
||||||
// TODO(roasbeef): ensure we don't already have one waiting, need to
|
// TODO(roasbeef): ensure we don't already have one waiting, need to
|
||||||
@@ -988,7 +988,7 @@ func (u *utxoNursery) registerPreschoolConf(kid *kidOutput, heightHint uint32) e
|
|||||||
// will delete the output from the "preschool" database bucket and atomically
|
// will delete the output from the "preschool" database bucket and atomically
|
||||||
// add it to the "kindergarten" database bucket. This is the second step in
|
// add it to the "kindergarten" database bucket. This is the second step in
|
||||||
// the output incubation process.
|
// the output incubation process.
|
||||||
func (u *utxoNursery) waitForPreschoolConf(kid *kidOutput,
|
func (u *UtxoNursery) waitForPreschoolConf(kid *kidOutput,
|
||||||
confChan *chainntnfs.ConfirmationEvent) {
|
confChan *chainntnfs.ConfirmationEvent) {
|
||||||
|
|
||||||
defer u.wg.Done()
|
defer u.wg.Done()
|
||||||
@@ -1011,7 +1011,7 @@ func (u *utxoNursery) waitForPreschoolConf(kid *kidOutput,
|
|||||||
u.mu.Lock()
|
u.mu.Lock()
|
||||||
defer u.mu.Unlock()
|
defer u.mu.Unlock()
|
||||||
|
|
||||||
// TODO(conner): add retry logic?
|
// TODO(conner): add retry utxnLogic?
|
||||||
|
|
||||||
var outputType string
|
var outputType string
|
||||||
if kid.isHtlc {
|
if kid.isHtlc {
|
||||||
@@ -1030,113 +1030,120 @@ func (u *utxoNursery) waitForPreschoolConf(kid *kidOutput,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractMaturityReport is a report that details the maturity progress of a
|
// RemoveChannel channel erases all entries from the channel bucket for the
|
||||||
|
// provided channel point.
|
||||||
|
func (u *UtxoNursery) RemoveChannel(op *wire.OutPoint) error {
|
||||||
|
return u.cfg.Store.RemoveChannel(op)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractMaturityReport is a report that details the maturity progress of a
|
||||||
// particular force closed contract.
|
// particular force closed contract.
|
||||||
type contractMaturityReport struct {
|
type ContractMaturityReport struct {
|
||||||
// limboBalance is the total number of frozen coins within this
|
// limboBalance is the total number of frozen coins within this
|
||||||
// contract.
|
// contract.
|
||||||
limboBalance btcutil.Amount
|
LimboBalance btcutil.Amount
|
||||||
|
|
||||||
// recoveredBalance is the total value that has been successfully swept
|
// recoveredBalance is the total value that has been successfully swept
|
||||||
// back to the user's wallet.
|
// back to the user's wallet.
|
||||||
recoveredBalance btcutil.Amount
|
RecoveredBalance btcutil.Amount
|
||||||
|
|
||||||
// htlcs records a maturity report for each htlc output in this channel.
|
// htlcs records a maturity report for each htlc output in this channel.
|
||||||
htlcs []htlcMaturityReport
|
Htlcs []HtlcMaturityReport
|
||||||
}
|
}
|
||||||
|
|
||||||
// htlcMaturityReport provides a summary of a single htlc output, and is
|
// HtlcMaturityReport provides a summary of a single htlc output, and is
|
||||||
// embedded as party of the overarching contractMaturityReport
|
// embedded as party of the overarching ContractMaturityReport
|
||||||
type htlcMaturityReport struct {
|
type HtlcMaturityReport struct {
|
||||||
// outpoint is the final output that will be swept back to the wallet.
|
// Outpoint is the final output that will be swept back to the wallet.
|
||||||
outpoint wire.OutPoint
|
Outpoint wire.OutPoint
|
||||||
|
|
||||||
// amount is the final value that will be swept in back to the wallet.
|
// Amount is the final value that will be swept in back to the wallet.
|
||||||
amount btcutil.Amount
|
Amount btcutil.Amount
|
||||||
|
|
||||||
// maturityHeight is the absolute block height that this output will
|
// MaturityHeight is the absolute block height that this output will
|
||||||
// mature at.
|
// mature at.
|
||||||
maturityHeight uint32
|
MaturityHeight uint32
|
||||||
|
|
||||||
// stage indicates whether the htlc is in the CLTV-timeout stage (1) or
|
// Stage indicates whether the htlc is in the CLTV-timeout stage (1) or
|
||||||
// the CSV-delay stage (2). A stage 1 htlc's maturity height will be set
|
// the CSV-delay stage (2). A stage 1 htlc's maturity height will be set
|
||||||
// to its expiry height, while a stage 2 htlc's maturity height will be
|
// to its expiry height, while a stage 2 htlc's maturity height will be
|
||||||
// set to its confirmation height plus the maturity requirement.
|
// set to its confirmation height plus the maturity requirement.
|
||||||
stage uint32
|
Stage uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLimboStage1TimeoutHtlc adds an htlc crib output to the maturity report's
|
// AddLimboStage1TimeoutHtlc adds an htlc crib output to the maturity report's
|
||||||
// htlcs, and contributes its amount to the limbo balance.
|
// htlcs, and contributes its amount to the limbo balance.
|
||||||
func (c *contractMaturityReport) AddLimboStage1TimeoutHtlc(baby *babyOutput) {
|
func (c *ContractMaturityReport) AddLimboStage1TimeoutHtlc(baby *babyOutput) {
|
||||||
c.limboBalance += baby.Amount()
|
c.LimboBalance += baby.Amount()
|
||||||
|
|
||||||
// TODO(roasbeef): bool to indicate stage 1 vs stage 2?
|
// TODO(roasbeef): bool to indicate stage 1 vs stage 2?
|
||||||
c.htlcs = append(c.htlcs, htlcMaturityReport{
|
c.Htlcs = append(c.Htlcs, HtlcMaturityReport{
|
||||||
outpoint: *baby.OutPoint(),
|
Outpoint: *baby.OutPoint(),
|
||||||
amount: baby.Amount(),
|
Amount: baby.Amount(),
|
||||||
maturityHeight: baby.expiry,
|
MaturityHeight: baby.expiry,
|
||||||
stage: 1,
|
Stage: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLimboDirectHtlc adds a direct HTLC on the commitment transaction of the
|
// AddLimboDirectHtlc adds a direct HTLC on the commitment transaction of the
|
||||||
// remote party to the maturity report. This a CLTV time-locked output that
|
// remote party to the maturity report. This a CLTV time-locked output that
|
||||||
// has or hasn't expired yet.
|
// has or hasn't expired yet.
|
||||||
func (c *contractMaturityReport) AddLimboDirectHtlc(kid *kidOutput) {
|
func (c *ContractMaturityReport) AddLimboDirectHtlc(kid *kidOutput) {
|
||||||
c.limboBalance += kid.Amount()
|
c.LimboBalance += kid.Amount()
|
||||||
|
|
||||||
htlcReport := htlcMaturityReport{
|
htlcReport := HtlcMaturityReport{
|
||||||
outpoint: *kid.OutPoint(),
|
Outpoint: *kid.OutPoint(),
|
||||||
amount: kid.Amount(),
|
Amount: kid.Amount(),
|
||||||
maturityHeight: kid.absoluteMaturity,
|
MaturityHeight: kid.absoluteMaturity,
|
||||||
stage: 2,
|
Stage: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.htlcs = append(c.htlcs, htlcReport)
|
c.Htlcs = append(c.Htlcs, htlcReport)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLimboStage1SuccessHtlcHtlc adds an htlc crib output to the maturity
|
// AddLimboStage1SuccessHtlcHtlc adds an htlc crib output to the maturity
|
||||||
// report's set of HTLC's. We'll use this to report any incoming HTLC sweeps
|
// report's set of HTLC's. We'll use this to report any incoming HTLC sweeps
|
||||||
// where the second level transaction hasn't yet confirmed.
|
// where the second level transaction hasn't yet confirmed.
|
||||||
func (c *contractMaturityReport) AddLimboStage1SuccessHtlc(kid *kidOutput) {
|
func (c *ContractMaturityReport) AddLimboStage1SuccessHtlc(kid *kidOutput) {
|
||||||
c.limboBalance += kid.Amount()
|
c.LimboBalance += kid.Amount()
|
||||||
|
|
||||||
c.htlcs = append(c.htlcs, htlcMaturityReport{
|
c.Htlcs = append(c.Htlcs, HtlcMaturityReport{
|
||||||
outpoint: *kid.OutPoint(),
|
Outpoint: *kid.OutPoint(),
|
||||||
amount: kid.Amount(),
|
Amount: kid.Amount(),
|
||||||
stage: 1,
|
Stage: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLimboStage2Htlc adds an htlc kindergarten output to the maturity report's
|
// AddLimboStage2Htlc adds an htlc kindergarten output to the maturity report's
|
||||||
// htlcs, and contributes its amount to the limbo balance.
|
// htlcs, and contributes its amount to the limbo balance.
|
||||||
func (c *contractMaturityReport) AddLimboStage2Htlc(kid *kidOutput) {
|
func (c *ContractMaturityReport) AddLimboStage2Htlc(kid *kidOutput) {
|
||||||
c.limboBalance += kid.Amount()
|
c.LimboBalance += kid.Amount()
|
||||||
|
|
||||||
htlcReport := htlcMaturityReport{
|
htlcReport := HtlcMaturityReport{
|
||||||
outpoint: *kid.OutPoint(),
|
Outpoint: *kid.OutPoint(),
|
||||||
amount: kid.Amount(),
|
Amount: kid.Amount(),
|
||||||
stage: 2,
|
Stage: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the confirmation height is set, then this means the first stage
|
// If the confirmation height is set, then this means the first stage
|
||||||
// has been confirmed, and we know the final maturity height of the CSV
|
// has been confirmed, and we know the final maturity height of the CSV
|
||||||
// delay.
|
// delay.
|
||||||
if kid.ConfHeight() != 0 {
|
if kid.ConfHeight() != 0 {
|
||||||
htlcReport.maturityHeight = kid.ConfHeight() + kid.BlocksToMaturity()
|
htlcReport.MaturityHeight = kid.ConfHeight() + kid.BlocksToMaturity()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.htlcs = append(c.htlcs, htlcReport)
|
c.Htlcs = append(c.Htlcs, htlcReport)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRecoveredHtlc adds a graduate output to the maturity report's htlcs, and
|
// AddRecoveredHtlc adds a graduate output to the maturity report's htlcs, and
|
||||||
// contributes its amount to the recovered balance.
|
// contributes its amount to the recovered balance.
|
||||||
func (c *contractMaturityReport) AddRecoveredHtlc(kid *kidOutput) {
|
func (c *ContractMaturityReport) AddRecoveredHtlc(kid *kidOutput) {
|
||||||
c.recoveredBalance += kid.Amount()
|
c.RecoveredBalance += kid.Amount()
|
||||||
|
|
||||||
c.htlcs = append(c.htlcs, htlcMaturityReport{
|
c.Htlcs = append(c.Htlcs, HtlcMaturityReport{
|
||||||
outpoint: *kid.OutPoint(),
|
Outpoint: *kid.OutPoint(),
|
||||||
amount: kid.Amount(),
|
Amount: kid.Amount(),
|
||||||
maturityHeight: kid.ConfHeight() + kid.BlocksToMaturity(),
|
MaturityHeight: kid.ConfHeight() + kid.BlocksToMaturity(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1144,7 +1151,7 @@ func (c *contractMaturityReport) AddRecoveredHtlc(kid *kidOutput) {
|
|||||||
// if and only if all of its outputs have been marked graduated. If the channel
|
// if and only if all of its outputs have been marked graduated. If the channel
|
||||||
// still has ungraduated outputs, the method will succeed without altering the
|
// still has ungraduated outputs, the method will succeed without altering the
|
||||||
// database state.
|
// database state.
|
||||||
func (u *utxoNursery) closeAndRemoveIfMature(chanPoint *wire.OutPoint) error {
|
func (u *UtxoNursery) closeAndRemoveIfMature(chanPoint *wire.OutPoint) error {
|
||||||
isMature, err := u.cfg.Store.IsMatureChannel(chanPoint)
|
isMature, err := u.cfg.Store.IsMatureChannel(chanPoint)
|
||||||
if err == ErrContractNotFound {
|
if err == ErrContractNotFound {
|
||||||
return nil
|
return nil
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
//go:build !rpctest
|
||||||
// +build !rpctest
|
// +build !rpctest
|
||||||
|
|
||||||
package lnd
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -397,14 +398,14 @@ func TestBabyOutputSerialization(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type nurseryTestContext struct {
|
type nurseryTestContext struct {
|
||||||
nursery *utxoNursery
|
nursery *UtxoNursery
|
||||||
notifier *sweep.MockNotifier
|
notifier *sweep.MockNotifier
|
||||||
chainIO *mock.ChainIO
|
chainIO *mock.ChainIO
|
||||||
publishChan chan wire.MsgTx
|
publishChan chan wire.MsgTx
|
||||||
store *nurseryStoreInterceptor
|
store *nurseryStoreInterceptor
|
||||||
restart func() bool
|
restart func() bool
|
||||||
receiveTx func() wire.MsgTx
|
receiveTx func() wire.MsgTx
|
||||||
sweeper *mockSweeper
|
sweeper *mockSweeperFull
|
||||||
timeoutChan chan chan time.Time
|
timeoutChan chan chan time.Time
|
||||||
t *testing.T
|
t *testing.T
|
||||||
dbCleanup func()
|
dbCleanup func()
|
||||||
@@ -422,7 +423,7 @@ func createNurseryTestContext(t *testing.T,
|
|||||||
t.Fatalf("unable to open channeldb: %v", err)
|
t.Fatalf("unable to open channeldb: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
store, err := newNurseryStore(&chainhash.Hash{}, cdb)
|
store, err := NewNurseryStore(&chainhash.Hash{}, cdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -435,7 +436,7 @@ func createNurseryTestContext(t *testing.T,
|
|||||||
|
|
||||||
publishChan := make(chan wire.MsgTx, 1)
|
publishChan := make(chan wire.MsgTx, 1)
|
||||||
publishFunc := func(tx *wire.MsgTx, source string) error {
|
publishFunc := func(tx *wire.MsgTx, source string) error {
|
||||||
utxnLog.Tracef("Publishing tx %v by %v", tx.TxHash(), source)
|
log.Tracef("Publishing tx %v by %v", tx.TxHash(), source)
|
||||||
publishChan <- *tx
|
publishChan <- *tx
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -446,7 +447,7 @@ func createNurseryTestContext(t *testing.T,
|
|||||||
BestHeight: 0,
|
BestHeight: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
sweeper := newMockSweeper(t)
|
sweeper := newMockSweeperFull(t)
|
||||||
|
|
||||||
nurseryCfg := NurseryConfig{
|
nurseryCfg := NurseryConfig{
|
||||||
Notifier: notifier,
|
Notifier: notifier,
|
||||||
@@ -468,7 +469,7 @@ func createNurseryTestContext(t *testing.T,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
nursery := newUtxoNursery(&nurseryCfg)
|
nursery := NewUtxoNursery(&nurseryCfg)
|
||||||
nursery.Start()
|
nursery.Start()
|
||||||
|
|
||||||
ctx := &nurseryTestContext{
|
ctx := &nurseryTestContext{
|
||||||
@@ -487,7 +488,7 @@ func createNurseryTestContext(t *testing.T,
|
|||||||
var tx wire.MsgTx
|
var tx wire.MsgTx
|
||||||
select {
|
select {
|
||||||
case tx = <-ctx.publishChan:
|
case tx = <-ctx.publishChan:
|
||||||
utxnLog.Debugf("Published tx %v", tx.TxHash())
|
log.Debugf("Published tx %v", tx.TxHash())
|
||||||
return tx
|
return tx
|
||||||
case <-time.After(defaultTestTimeout):
|
case <-time.After(defaultTestTimeout):
|
||||||
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||||
@@ -499,16 +500,16 @@ func createNurseryTestContext(t *testing.T,
|
|||||||
|
|
||||||
ctx.restart = func() bool {
|
ctx.restart = func() bool {
|
||||||
return checkStartStop(func() {
|
return checkStartStop(func() {
|
||||||
utxnLog.Tracef("Restart sweeper and nursery")
|
log.Tracef("Restart sweeper and nursery")
|
||||||
// Simulate lnd restart.
|
// Simulate lnd restart.
|
||||||
ctx.nursery.Stop()
|
ctx.nursery.Stop()
|
||||||
|
|
||||||
// Restart sweeper.
|
// Restart sweeper.
|
||||||
ctx.sweeper = newMockSweeper(t)
|
ctx.sweeper = newMockSweeperFull(t)
|
||||||
|
|
||||||
/// Restart nursery.
|
/// Restart nursery.
|
||||||
nurseryCfg.SweepInput = ctx.sweeper.sweepInput
|
nurseryCfg.SweepInput = ctx.sweeper.sweepInput
|
||||||
ctx.nursery = newUtxoNursery(&nurseryCfg)
|
ctx.nursery = NewUtxoNursery(&nurseryCfg)
|
||||||
ctx.nursery.Start()
|
ctx.nursery.Start()
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -627,7 +628,7 @@ func createOutgoingRes(onLocalCommitment bool) *lnwallet.OutgoingHtlcResolution
|
|||||||
return &outgoingRes
|
return &outgoingRes
|
||||||
}
|
}
|
||||||
|
|
||||||
func incubateTestOutput(t *testing.T, nursery *utxoNursery,
|
func incubateTestOutput(t *testing.T, nursery *UtxoNursery,
|
||||||
onLocalCommitment bool) *lnwallet.OutgoingHtlcResolution {
|
onLocalCommitment bool) *lnwallet.OutgoingHtlcResolution {
|
||||||
|
|
||||||
outgoingRes := createOutgoingRes(onLocalCommitment)
|
outgoingRes := createOutgoingRes(onLocalCommitment)
|
||||||
@@ -653,7 +654,7 @@ func incubateTestOutput(t *testing.T, nursery *utxoNursery,
|
|||||||
return outgoingRes
|
return outgoingRes
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertNurseryReport(t *testing.T, nursery *utxoNursery,
|
func assertNurseryReport(t *testing.T, nursery *UtxoNursery,
|
||||||
expectedNofHtlcs int, expectedStage uint32,
|
expectedNofHtlcs int, expectedStage uint32,
|
||||||
expectedLimboBalance btcutil.Amount) {
|
expectedLimboBalance btcutil.Amount) {
|
||||||
report, err := nursery.NurseryReport(&testChanPoint)
|
report, err := nursery.NurseryReport(&testChanPoint)
|
||||||
@@ -661,27 +662,27 @@ func assertNurseryReport(t *testing.T, nursery *utxoNursery,
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(report.htlcs) != expectedNofHtlcs {
|
if len(report.Htlcs) != expectedNofHtlcs {
|
||||||
t.Fatalf("expected %v outputs to be reported, but report "+
|
t.Fatalf("expected %v outputs to be reported, but report "+
|
||||||
"only contains %v", expectedNofHtlcs, len(report.htlcs))
|
"only contains %v", expectedNofHtlcs, len(report.Htlcs))
|
||||||
}
|
}
|
||||||
|
|
||||||
if expectedNofHtlcs != 0 {
|
if expectedNofHtlcs != 0 {
|
||||||
htlcReport := report.htlcs[0]
|
htlcReport := report.Htlcs[0]
|
||||||
if htlcReport.stage != expectedStage {
|
if htlcReport.Stage != expectedStage {
|
||||||
t.Fatalf("expected htlc be advanced to stage %v, but "+
|
t.Fatalf("expected htlc be advanced to stage %v, but "+
|
||||||
"it is reported in stage %v",
|
"it is reported in stage %v",
|
||||||
expectedStage, htlcReport.stage)
|
expectedStage, htlcReport.Stage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if report.limboBalance != expectedLimboBalance {
|
if report.LimboBalance != expectedLimboBalance {
|
||||||
t.Fatalf("expected limbo balance to be %v, but it is %v instead",
|
t.Fatalf("expected limbo balance to be %v, but it is %v instead",
|
||||||
expectedLimboBalance, report.limboBalance)
|
expectedLimboBalance, report.LimboBalance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertNurseryReportUnavailable(t *testing.T, nursery *utxoNursery) {
|
func assertNurseryReportUnavailable(t *testing.T, nursery *UtxoNursery) {
|
||||||
_, err := nursery.NurseryReport(&testChanPoint)
|
_, err := nursery.NurseryReport(&testChanPoint)
|
||||||
if err != ErrContractNotFound {
|
if err != ErrContractNotFound {
|
||||||
t.Fatal("expected report to be unavailable")
|
t.Fatal("expected report to be unavailable")
|
||||||
@@ -711,7 +712,7 @@ func testRestartLoop(t *testing.T, test func(*testing.T,
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
utxnLog.Debugf("Skipping restart point %v",
|
log.Debugf("Skipping restart point %v",
|
||||||
currentStartStopIdx)
|
currentStartStopIdx)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -863,7 +864,7 @@ func testSweep(t *testing.T, ctx *nurseryTestContext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
type nurseryStoreInterceptor struct {
|
type nurseryStoreInterceptor struct {
|
||||||
ns NurseryStore
|
ns NurseryStorer
|
||||||
|
|
||||||
// TODO(joostjager): put more useful info through these channels.
|
// TODO(joostjager): put more useful info through these channels.
|
||||||
cribToKinderChan chan struct{}
|
cribToKinderChan chan struct{}
|
||||||
@@ -872,7 +873,7 @@ type nurseryStoreInterceptor struct {
|
|||||||
preschoolToKinderChan chan struct{}
|
preschoolToKinderChan chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNurseryStoreInterceptor(ns NurseryStore) *nurseryStoreInterceptor {
|
func newNurseryStoreInterceptor(ns NurseryStorer) *nurseryStoreInterceptor {
|
||||||
return &nurseryStoreInterceptor{
|
return &nurseryStoreInterceptor{
|
||||||
ns: ns,
|
ns: ns,
|
||||||
cribToKinderChan: make(chan struct{}),
|
cribToKinderChan: make(chan struct{}),
|
||||||
@@ -950,7 +951,7 @@ func (i *nurseryStoreInterceptor) RemoveChannel(chanPoint *wire.OutPoint) error
|
|||||||
return i.ns.RemoveChannel(chanPoint)
|
return i.ns.RemoveChannel(chanPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockSweeper struct {
|
type mockSweeperFull struct {
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
|
||||||
resultChans map[wire.OutPoint]chan sweep.Result
|
resultChans map[wire.OutPoint]chan sweep.Result
|
||||||
@@ -959,18 +960,18 @@ type mockSweeper struct {
|
|||||||
sweepChan chan input.Input
|
sweepChan chan input.Input
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMockSweeper(t *testing.T) *mockSweeper {
|
func newMockSweeperFull(t *testing.T) *mockSweeperFull {
|
||||||
return &mockSweeper{
|
return &mockSweeperFull{
|
||||||
resultChans: make(map[wire.OutPoint]chan sweep.Result),
|
resultChans: make(map[wire.OutPoint]chan sweep.Result),
|
||||||
sweepChan: make(chan input.Input, 1),
|
sweepChan: make(chan input.Input, 1),
|
||||||
t: t,
|
t: t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockSweeper) sweepInput(input input.Input,
|
func (s *mockSweeperFull) sweepInput(input input.Input,
|
||||||
_ sweep.Params) (chan sweep.Result, error) {
|
_ sweep.Params) (chan sweep.Result, error) {
|
||||||
|
|
||||||
utxnLog.Debugf("mockSweeper sweepInput called for %v", *input.OutPoint())
|
log.Debugf("mockSweeper sweepInput called for %v", *input.OutPoint())
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case s.sweepChan <- input:
|
case s.sweepChan <- input:
|
||||||
@@ -987,7 +988,7 @@ func (s *mockSweeper) sweepInput(input input.Input,
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockSweeper) expectSweep() {
|
func (s *mockSweeperFull) expectSweep() {
|
||||||
s.t.Helper()
|
s.t.Helper()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -997,7 +998,7 @@ func (s *mockSweeper) expectSweep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockSweeper) sweepAll() {
|
func (s *mockSweeperFull) sweepAll() {
|
||||||
s.t.Helper()
|
s.t.Helper()
|
||||||
|
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
@@ -1006,7 +1007,7 @@ func (s *mockSweeper) sweepAll() {
|
|||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
|
|
||||||
for o, c := range currentChans {
|
for o, c := range currentChans {
|
||||||
utxnLog.Debugf("mockSweeper signal swept for %v", o)
|
log.Debugf("mockSweeper signal swept for %v", o)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case c <- sweep.Result{}:
|
case c <- sweep.Result{}:
|
||||||
@@ -229,6 +229,8 @@ you.
|
|||||||
|
|
||||||
* [Refactor the interaction between the `htlcswitch` and `peer` packages for cleaner separation.](https://github.com/lightningnetwork/lnd/pull/5603)
|
* [Refactor the interaction between the `htlcswitch` and `peer` packages for cleaner separation.](https://github.com/lightningnetwork/lnd/pull/5603)
|
||||||
|
|
||||||
|
* [Moved the original breach handling and timelock UTXO handling into the contract court package](https://github.com/lightningnetwork/lnd/pull/5745)
|
||||||
|
|
||||||
* [Unused error check
|
* [Unused error check
|
||||||
removed](https://github.com/lightningnetwork/lnd/pull/5537).
|
removed](https://github.com/lightningnetwork/lnd/pull/5537).
|
||||||
|
|
||||||
|
|||||||
@@ -80,26 +80,12 @@ type plexPacket struct {
|
|||||||
err chan error
|
err chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChannelCloseType is an enum which signals the type of channel closure the
|
|
||||||
// peer should execute.
|
|
||||||
type ChannelCloseType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// CloseRegular indicates a regular cooperative channel closure
|
|
||||||
// should be attempted.
|
|
||||||
CloseRegular ChannelCloseType = iota
|
|
||||||
|
|
||||||
// CloseBreach indicates that a channel breach has been detected, and
|
|
||||||
// the link should immediately be marked as unavailable.
|
|
||||||
CloseBreach
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChanClose represents a request which close a particular channel specified by
|
// ChanClose represents a request which close a particular channel specified by
|
||||||
// its id.
|
// its id.
|
||||||
type ChanClose struct {
|
type ChanClose struct {
|
||||||
// CloseType is a variable which signals the type of channel closure the
|
// CloseType is a variable which signals the type of channel closure the
|
||||||
// peer should execute.
|
// peer should execute.
|
||||||
CloseType ChannelCloseType
|
CloseType contractcourt.ChannelCloseType
|
||||||
|
|
||||||
// ChanPoint represent the id of the channel which should be closed.
|
// ChanPoint represent the id of the channel which should be closed.
|
||||||
ChanPoint *wire.OutPoint
|
ChanPoint *wire.OutPoint
|
||||||
@@ -1454,7 +1440,8 @@ func (s *Switch) teardownCircuit(pkt *htlcPacket) error {
|
|||||||
// a starting point for close negotiation. The deliveryScript parameter is an
|
// a starting point for close negotiation. The deliveryScript parameter is an
|
||||||
// optional parameter which sets a user specified script to close out to.
|
// optional parameter which sets a user specified script to close out to.
|
||||||
func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
|
func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
|
||||||
closeType ChannelCloseType, targetFeePerKw chainfee.SatPerKWeight,
|
closeType contractcourt.ChannelCloseType,
|
||||||
|
targetFeePerKw chainfee.SatPerKWeight,
|
||||||
deliveryScript lnwire.DeliveryAddress) (chan interface{}, chan error) {
|
deliveryScript lnwire.DeliveryAddress) (chan interface{}, chan error) {
|
||||||
|
|
||||||
// TODO(roasbeef) abstract out the close updates.
|
// TODO(roasbeef) abstract out the close updates.
|
||||||
|
|||||||
4
log.go
4
log.go
@@ -81,8 +81,6 @@ var (
|
|||||||
ltndLog = addLndPkgLogger("LTND")
|
ltndLog = addLndPkgLogger("LTND")
|
||||||
rpcsLog = addLndPkgLogger("RPCS")
|
rpcsLog = addLndPkgLogger("RPCS")
|
||||||
srvrLog = addLndPkgLogger("SRVR")
|
srvrLog = addLndPkgLogger("SRVR")
|
||||||
utxnLog = addLndPkgLogger("UTXN")
|
|
||||||
brarLog = addLndPkgLogger("BRAR")
|
|
||||||
atplLog = addLndPkgLogger("ATPL")
|
atplLog = addLndPkgLogger("ATPL")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,6 +130,8 @@ func SetupLoggers(root *build.RotatingLogWriter, interceptor signal.Interceptor)
|
|||||||
AddSubLogger(root, "CMGR", interceptor, connmgr.UseLogger)
|
AddSubLogger(root, "CMGR", interceptor, connmgr.UseLogger)
|
||||||
AddSubLogger(root, "BTCN", interceptor, neutrino.UseLogger)
|
AddSubLogger(root, "BTCN", interceptor, neutrino.UseLogger)
|
||||||
AddSubLogger(root, "CNCT", interceptor, contractcourt.UseLogger)
|
AddSubLogger(root, "CNCT", interceptor, contractcourt.UseLogger)
|
||||||
|
AddSubLogger(root, "UTXN", interceptor, contractcourt.UseNurseryLogger)
|
||||||
|
AddSubLogger(root, "BRAR", interceptor, contractcourt.UseBreachLogger)
|
||||||
AddSubLogger(root, "SPHX", interceptor, sphinx.UseLogger)
|
AddSubLogger(root, "SPHX", interceptor, sphinx.UseLogger)
|
||||||
AddSubLogger(root, "SWPR", interceptor, sweep.UseLogger)
|
AddSubLogger(root, "SWPR", interceptor, sweep.UseLogger)
|
||||||
AddSubLogger(root, "SGNR", interceptor, signrpc.UseLogger)
|
AddSubLogger(root, "SGNR", interceptor, signrpc.UseLogger)
|
||||||
|
|||||||
@@ -2519,7 +2519,7 @@ func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) {
|
|||||||
// A type of CloseRegular indicates that the user has opted to close
|
// A type of CloseRegular indicates that the user has opted to close
|
||||||
// out this channel on-chain, so we execute the cooperative channel
|
// out this channel on-chain, so we execute the cooperative channel
|
||||||
// closure workflow.
|
// closure workflow.
|
||||||
case htlcswitch.CloseRegular:
|
case contractcourt.CloseRegular:
|
||||||
// First, we'll choose a delivery address that we'll use to send the
|
// First, we'll choose a delivery address that we'll use to send the
|
||||||
// funds to in the case of a successful negotiation.
|
// funds to in the case of a successful negotiation.
|
||||||
|
|
||||||
@@ -2604,7 +2604,7 @@ func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) {
|
|||||||
|
|
||||||
// A type of CloseBreach indicates that the counterparty has breached
|
// A type of CloseBreach indicates that the counterparty has breached
|
||||||
// the channel therefore we need to clean up our local state.
|
// the channel therefore we need to clean up our local state.
|
||||||
case htlcswitch.CloseBreach:
|
case contractcourt.CloseBreach:
|
||||||
// TODO(roasbeef): no longer need with newer beach logic?
|
// TODO(roasbeef): no longer need with newer beach logic?
|
||||||
peerLog.Infof("ChannelPoint(%v) has been breached, wiping "+
|
peerLog.Infof("ChannelPoint(%v) has been breached, wiping "+
|
||||||
"channel", req.ChanPoint)
|
"channel", req.ChanPoint)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/lntest/mock"
|
"github.com/lightningnetwork/lnd/lntest/mock"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
@@ -165,7 +166,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
|
|||||||
updateChan := make(chan interface{}, 1)
|
updateChan := make(chan interface{}, 1)
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
closeCommand := &htlcswitch.ChanClose{
|
closeCommand := &htlcswitch.ChanClose{
|
||||||
CloseType: htlcswitch.CloseRegular,
|
CloseType: contractcourt.CloseRegular,
|
||||||
ChanPoint: bobChan.ChannelPoint(),
|
ChanPoint: bobChan.ChannelPoint(),
|
||||||
Updates: updateChan,
|
Updates: updateChan,
|
||||||
TargetFeePerKw: 12500,
|
TargetFeePerKw: 12500,
|
||||||
@@ -491,7 +492,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
|
|||||||
updateChan := make(chan interface{}, 1)
|
updateChan := make(chan interface{}, 1)
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
closeCommand := &htlcswitch.ChanClose{
|
closeCommand := &htlcswitch.ChanClose{
|
||||||
CloseType: htlcswitch.CloseRegular,
|
CloseType: contractcourt.CloseRegular,
|
||||||
ChanPoint: bobChan.ChannelPoint(),
|
ChanPoint: bobChan.ChannelPoint(),
|
||||||
Updates: updateChan,
|
Updates: updateChan,
|
||||||
TargetFeePerKw: 12500,
|
TargetFeePerKw: 12500,
|
||||||
@@ -835,7 +836,7 @@ func TestCustomShutdownScript(t *testing.T) {
|
|||||||
updateChan := make(chan interface{}, 1)
|
updateChan := make(chan interface{}, 1)
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
closeCommand := htlcswitch.ChanClose{
|
closeCommand := htlcswitch.ChanClose{
|
||||||
CloseType: htlcswitch.CloseRegular,
|
CloseType: contractcourt.CloseRegular,
|
||||||
ChanPoint: chanPoint,
|
ChanPoint: chanPoint,
|
||||||
Updates: updateChan,
|
Updates: updateChan,
|
||||||
TargetFeePerKw: 12500,
|
TargetFeePerKw: 12500,
|
||||||
|
|||||||
23
rpcserver.go
23
rpcserver.go
@@ -2401,7 +2401,8 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateChan, errChan = r.server.htlcSwitch.CloseLink(
|
updateChan, errChan = r.server.htlcSwitch.CloseLink(
|
||||||
chanPoint, htlcswitch.CloseRegular, feeRate, deliveryScript,
|
chanPoint, contractcourt.CloseRegular, feeRate,
|
||||||
|
deliveryScript,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
@@ -2518,8 +2519,8 @@ func (r *rpcServer) abandonChan(chanPoint *wire.OutPoint,
|
|||||||
// close, then it's possible that the nursery is hanging on to some
|
// close, then it's possible that the nursery is hanging on to some
|
||||||
// state. To err on the side of caution, we'll now attempt to wipe any
|
// state. To err on the side of caution, we'll now attempt to wipe any
|
||||||
// state for this channel from the nursery.
|
// state for this channel from the nursery.
|
||||||
err = r.server.utxoNursery.cfg.Store.RemoveChannel(chanPoint)
|
err = r.server.utxoNursery.RemoveChannel(chanPoint)
|
||||||
if err != nil && err != ErrContractNotFound {
|
if err != nil && err != contractcourt.ErrContractNotFound {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3495,7 +3496,7 @@ func (r *rpcServer) nurseryPopulateForceCloseResp(chanPoint *wire.OutPoint,
|
|||||||
// didn't have any time-locked outputs, then the nursery may not know of
|
// didn't have any time-locked outputs, then the nursery may not know of
|
||||||
// the contract.
|
// the contract.
|
||||||
nurseryInfo, err := r.server.utxoNursery.NurseryReport(chanPoint)
|
nurseryInfo, err := r.server.utxoNursery.NurseryReport(chanPoint)
|
||||||
if err == ErrContractNotFound {
|
if err == contractcourt.ErrContractNotFound {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -3508,18 +3509,18 @@ func (r *rpcServer) nurseryPopulateForceCloseResp(chanPoint *wire.OutPoint,
|
|||||||
// information detailing exactly how much funds are time locked and also
|
// information detailing exactly how much funds are time locked and also
|
||||||
// the height in which we can ultimately sweep the funds into the
|
// the height in which we can ultimately sweep the funds into the
|
||||||
// wallet.
|
// wallet.
|
||||||
forceClose.LimboBalance = int64(nurseryInfo.limboBalance)
|
forceClose.LimboBalance = int64(nurseryInfo.LimboBalance)
|
||||||
forceClose.RecoveredBalance = int64(nurseryInfo.recoveredBalance)
|
forceClose.RecoveredBalance = int64(nurseryInfo.RecoveredBalance)
|
||||||
|
|
||||||
for _, htlcReport := range nurseryInfo.htlcs {
|
for _, htlcReport := range nurseryInfo.Htlcs {
|
||||||
// TODO(conner) set incoming flag appropriately after handling
|
// TODO(conner) set incoming flag appropriately after handling
|
||||||
// incoming incubation
|
// incoming incubation
|
||||||
htlc := &lnrpc.PendingHTLC{
|
htlc := &lnrpc.PendingHTLC{
|
||||||
Incoming: false,
|
Incoming: false,
|
||||||
Amount: int64(htlcReport.amount),
|
Amount: int64(htlcReport.Amount),
|
||||||
Outpoint: htlcReport.outpoint.String(),
|
Outpoint: htlcReport.Outpoint.String(),
|
||||||
MaturityHeight: htlcReport.maturityHeight,
|
MaturityHeight: htlcReport.MaturityHeight,
|
||||||
Stage: htlcReport.stage,
|
Stage: htlcReport.Stage,
|
||||||
}
|
}
|
||||||
|
|
||||||
if htlc.MaturityHeight != 0 {
|
if htlc.MaturityHeight != 0 {
|
||||||
|
|||||||
20
server.go
20
server.go
@@ -238,7 +238,7 @@ type server struct {
|
|||||||
|
|
||||||
witnessBeacon contractcourt.WitnessBeacon
|
witnessBeacon contractcourt.WitnessBeacon
|
||||||
|
|
||||||
breachArbiter *breachArbiter
|
breachArbiter *contractcourt.BreachArbiter
|
||||||
|
|
||||||
missionControl *routing.MissionControl
|
missionControl *routing.MissionControl
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ type server struct {
|
|||||||
|
|
||||||
localChanMgr *localchans.Manager
|
localChanMgr *localchans.Manager
|
||||||
|
|
||||||
utxoNursery *utxoNursery
|
utxoNursery *contractcourt.UtxoNursery
|
||||||
|
|
||||||
sweeper *sweep.UtxoSweeper
|
sweeper *sweep.UtxoSweeper
|
||||||
|
|
||||||
@@ -848,7 +848,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
FetchChannel: s.chanStateDB.FetchChannel,
|
FetchChannel: s.chanStateDB.FetchChannel,
|
||||||
}
|
}
|
||||||
|
|
||||||
utxnStore, err := newNurseryStore(
|
utxnStore, err := contractcourt.NewNurseryStore(
|
||||||
s.cfg.ActiveNetParams.GenesisHash, dbs.chanStateDB,
|
s.cfg.ActiveNetParams.GenesisHash, dbs.chanStateDB,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -884,7 +884,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
FeeRateBucketSize: sweep.DefaultFeeRateBucketSize,
|
FeeRateBucketSize: sweep.DefaultFeeRateBucketSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.utxoNursery = newUtxoNursery(&NurseryConfig{
|
s.utxoNursery = contractcourt.NewUtxoNursery(&contractcourt.NurseryConfig{
|
||||||
ChainIO: cc.ChainIO,
|
ChainIO: cc.ChainIO,
|
||||||
ConfDepth: 1,
|
ConfDepth: 1,
|
||||||
FetchClosedChannels: dbs.chanStateDB.FetchClosedChannels,
|
FetchClosedChannels: dbs.chanStateDB.FetchClosedChannels,
|
||||||
@@ -897,7 +897,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
|
|
||||||
// Construct a closure that wraps the htlcswitch's CloseLink method.
|
// Construct a closure that wraps the htlcswitch's CloseLink method.
|
||||||
closeLink := func(chanPoint *wire.OutPoint,
|
closeLink := func(chanPoint *wire.OutPoint,
|
||||||
closureType htlcswitch.ChannelCloseType) {
|
closureType contractcourt.ChannelCloseType) {
|
||||||
// TODO(conner): Properly respect the update and error channels
|
// TODO(conner): Properly respect the update and error channels
|
||||||
// returned by CloseLink.
|
// returned by CloseLink.
|
||||||
|
|
||||||
@@ -909,7 +909,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
|
|
||||||
// We will use the following channel to reliably hand off contract
|
// We will use the following channel to reliably hand off contract
|
||||||
// breach events from the ChannelArbitrator to the breachArbiter,
|
// breach events from the ChannelArbitrator to the breachArbiter,
|
||||||
contractBreaches := make(chan *ContractBreachEvent, 1)
|
contractBreaches := make(chan *contractcourt.ContractBreachEvent, 1)
|
||||||
|
|
||||||
s.chainArb = contractcourt.NewChainArbitrator(contractcourt.ChainArbitratorConfig{
|
s.chainArb = contractcourt.NewChainArbitrator(contractcourt.ChainArbitratorConfig{
|
||||||
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
|
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
|
||||||
@@ -976,7 +976,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
finalErr <- markClosed()
|
finalErr <- markClosed()
|
||||||
}
|
}
|
||||||
|
|
||||||
event := &ContractBreachEvent{
|
event := &contractcourt.ContractBreachEvent{
|
||||||
ChanPoint: chanPoint,
|
ChanPoint: chanPoint,
|
||||||
ProcessACK: processACK,
|
ProcessACK: processACK,
|
||||||
BreachRetribution: breachRet,
|
BreachRetribution: breachRet,
|
||||||
@@ -1012,7 +1012,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
Clock: clock.NewDefaultClock(),
|
Clock: clock.NewDefaultClock(),
|
||||||
}, dbs.chanStateDB)
|
}, dbs.chanStateDB)
|
||||||
|
|
||||||
s.breachArbiter = newBreachArbiter(&BreachConfig{
|
s.breachArbiter = contractcourt.NewBreachArbiter(&contractcourt.BreachConfig{
|
||||||
CloseLink: closeLink,
|
CloseLink: closeLink,
|
||||||
DB: dbs.chanStateDB,
|
DB: dbs.chanStateDB,
|
||||||
Estimator: s.cc.FeeEstimator,
|
Estimator: s.cc.FeeEstimator,
|
||||||
@@ -1021,7 +1021,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
PublishTransaction: cc.Wallet.PublishTransaction,
|
PublishTransaction: cc.Wallet.PublishTransaction,
|
||||||
ContractBreaches: contractBreaches,
|
ContractBreaches: contractBreaches,
|
||||||
Signer: cc.Wallet.Cfg.Signer,
|
Signer: cc.Wallet.Cfg.Signer,
|
||||||
Store: newRetributionStore(dbs.chanStateDB),
|
Store: contractcourt.NewRetributionStore(
|
||||||
|
dbs.chanStateDB,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select the configuration and furnding parameters for Bitcoin or
|
// Select the configuration and furnding parameters for Bitcoin or
|
||||||
|
|||||||
Reference in New Issue
Block a user