2021-09-14 04:00:36 +02:00
|
|
|
package contractcourt
|
2016-11-29 04:43:57 +01:00
|
|
|
|
|
|
|
import (
|
2017-05-07 13:09:22 +02:00
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
"fmt"
|
2017-05-07 13:09:22 +02:00
|
|
|
"io"
|
2016-11-29 04:43:57 +01:00
|
|
|
"sync"
|
|
|
|
|
2018-07-25 06:54:45 +02:00
|
|
|
"github.com/btcsuite/btcd/blockchain"
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2018-07-25 06:54:45 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
2016-11-29 04:43:57 +01:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2024-06-04 08:02:57 +02:00
|
|
|
"github.com/lightningnetwork/lnd/fn"
|
2019-01-16 15:47:43 +01:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2021-04-26 19:08:11 +02:00
|
|
|
"github.com/lightningnetwork/lnd/kvdb"
|
2020-07-29 09:27:22 +02:00
|
|
|
"github.com/lightningnetwork/lnd/labels"
|
2024-05-24 15:56:30 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2024-07-24 13:31:21 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnutils"
|
2016-11-29 04:43:57 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2019-10-31 03:43:05 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
2024-06-24 08:33:27 +02:00
|
|
|
"github.com/lightningnetwork/lnd/sweep"
|
2024-06-04 08:02:57 +02:00
|
|
|
"github.com/lightningnetwork/lnd/tlv"
|
2016-11-29 04:43:57 +01:00
|
|
|
)
|
|
|
|
|
2021-02-15 13:31:08 +01:00
|
|
|
const (
|
|
|
|
// justiceTxConfTarget is the number of blocks we'll use as a
|
|
|
|
// confirmation target when creating the justice transaction. We'll
|
|
|
|
// choose an aggressive target, since we want to be sure it confirms
|
|
|
|
// quickly.
|
|
|
|
justiceTxConfTarget = 2
|
|
|
|
|
|
|
|
// blocksPassedSplitPublish is the number of blocks without
|
|
|
|
// confirmation of the justice tx we'll wait before starting to publish
|
|
|
|
// smaller variants of the justice tx. We do this to mitigate an attack
|
|
|
|
// the channel peer can do by pinning the HTLC outputs of the
|
|
|
|
// commitment with low-fee HTLC transactions.
|
|
|
|
blocksPassedSplitPublish = 4
|
|
|
|
)
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
var (
|
|
|
|
// retributionBucket stores retribution state on disk between detecting
|
|
|
|
// a contract breach, broadcasting a justice transaction that sweeps the
|
|
|
|
// channel, and finally witnessing the justice transaction confirm on
|
|
|
|
// the blockchain. It is critical that such state is persisted on disk,
|
|
|
|
// so that if our node restarts at any point during the retribution
|
|
|
|
// procedure, we can recover and continue from the persisted state.
|
|
|
|
retributionBucket = []byte("retribution")
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// taprootRetributionBucket stores the tarpoot specific retribution
|
|
|
|
// information. This includes things like the control blocks for both
|
|
|
|
// commitment outputs, and the taptweak needed to sweep each HTLC (one
|
|
|
|
// for the first and one for the second level).
|
|
|
|
taprootRetributionBucket = []byte("tap-retribution")
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// errBrarShuttingDown is an error returned if the BreachArbitrator has
|
2018-06-01 16:20:55 +02:00
|
|
|
// been signalled to exit.
|
2024-02-01 01:42:29 +01:00
|
|
|
errBrarShuttingDown = errors.New("BreachArbitrator shutting down")
|
2017-11-21 08:56:42 +01:00
|
|
|
)
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// ContractBreachEvent is an event the BreachArbitrator will receive in case a
|
2018-04-18 13:56:14 +02:00
|
|
|
// contract breach is observed on-chain. It contains the necessary information
|
2021-04-21 12:51:04 +02:00
|
|
|
// to handle the breach, and a ProcessACK closure we will use to ACK the event
|
2018-04-18 13:56:14 +02:00
|
|
|
// when we have safely stored all the necessary information.
|
|
|
|
type ContractBreachEvent struct {
|
|
|
|
// ChanPoint is the channel point of the breached channel.
|
|
|
|
ChanPoint wire.OutPoint
|
|
|
|
|
2021-04-21 12:51:04 +02:00
|
|
|
// ProcessACK is an closure that should be called with a nil error iff
|
|
|
|
// the breach retribution info is safely stored in the retribution
|
2018-04-18 13:56:14 +02:00
|
|
|
// store. In case storing the information to the store fails, a non-nil
|
2021-04-21 12:51:04 +02:00
|
|
|
// error should be used. When this closure returns, it means that the
|
|
|
|
// contract court has marked the channel pending close in the DB, and
|
2024-02-01 01:42:29 +01:00
|
|
|
// it is safe for the BreachArbitrator to carry on its duty.
|
2021-04-21 12:51:04 +02:00
|
|
|
ProcessACK func(error)
|
2018-04-18 13:56:14 +02:00
|
|
|
|
|
|
|
// BreachRetribution is the information needed to act on this contract
|
|
|
|
// breach.
|
|
|
|
BreachRetribution *lnwallet.BreachRetribution
|
|
|
|
}
|
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
// 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
|
2024-02-01 01:42:29 +01:00
|
|
|
// wire.OutPoint -> retributionInfo. Upon learning of a breach, a
|
|
|
|
// BreachArbitrator 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.
|
2021-09-14 04:00:36 +02:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-09-01 12:11:14 +02:00
|
|
|
// BreachConfig bundles the required subsystems used by the breach arbiter. An
|
2024-02-01 01:42:29 +01:00
|
|
|
// instance of BreachConfig is passed to NewBreachArbitrator during
|
|
|
|
// instantiation.
|
2017-09-01 12:11:14 +02:00
|
|
|
type BreachConfig struct {
|
2017-09-20 02:30:36 +02:00
|
|
|
// CloseLink allows the breach arbiter to shutdown any channel links for
|
|
|
|
// which it detects a breach, ensuring now further activity will
|
2017-11-21 08:56:42 +01:00
|
|
|
// continue across the link. The method accepts link's channel point and
|
|
|
|
// a close type to be included in the channel close summary.
|
2021-09-14 04:00:36 +02:00
|
|
|
CloseLink func(*wire.OutPoint, ChannelCloseType)
|
2017-09-01 12:11:14 +02:00
|
|
|
|
|
|
|
// DB provides access to the user's channels, allowing the breach
|
|
|
|
// arbiter to determine the current state of a user's channels, and how
|
|
|
|
// it should respond to channel closure.
|
2021-09-21 19:18:17 +02:00
|
|
|
DB *channeldb.ChannelStateDB
|
2017-09-01 12:11:14 +02:00
|
|
|
|
2017-09-20 02:30:36 +02:00
|
|
|
// Estimator is used by the breach arbiter to determine an appropriate
|
|
|
|
// fee level when generating, signing, and broadcasting sweep
|
|
|
|
// transactions.
|
2019-10-31 03:43:05 +01:00
|
|
|
Estimator chainfee.Estimator
|
2017-09-20 02:30:36 +02:00
|
|
|
|
|
|
|
// GenSweepScript generates the receiving scripts for swept outputs.
|
2024-06-24 08:30:52 +02:00
|
|
|
GenSweepScript func() fn.Result[lnwallet.AddrWithKey]
|
2017-09-01 12:11:14 +02:00
|
|
|
|
|
|
|
// Notifier provides a publish/subscribe interface for event driven
|
|
|
|
// notifications regarding the confirmation of txids.
|
|
|
|
Notifier chainntnfs.ChainNotifier
|
|
|
|
|
2017-09-20 02:30:36 +02:00
|
|
|
// PublishTransaction facilitates the process of broadcasting a
|
|
|
|
// transaction to the network.
|
2020-05-18 14:13:23 +02:00
|
|
|
PublishTransaction func(*wire.MsgTx, string) error
|
2017-09-01 12:11:14 +02:00
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// ContractBreaches is a channel where the BreachArbitrator will receive
|
2018-04-18 13:56:14 +02:00
|
|
|
// notifications in the event of a contract breach being observed. A
|
2024-02-01 01:42:29 +01:00
|
|
|
// ContractBreachEvent must be ACKed by the BreachArbitrator, such that
|
2018-04-18 13:56:14 +02:00
|
|
|
// the sending subsystem knows that the event is properly handed off.
|
|
|
|
ContractBreaches <-chan *ContractBreachEvent
|
2018-01-18 23:06:38 +01:00
|
|
|
|
2017-09-20 02:30:36 +02:00
|
|
|
// Signer is used by the breach arbiter to generate sweep transactions,
|
|
|
|
// which move coins from previously open channels back to the user's
|
|
|
|
// wallet.
|
2019-01-16 15:47:43 +01:00
|
|
|
Signer input.Signer
|
2017-09-01 12:11:14 +02:00
|
|
|
|
|
|
|
// Store is a persistent resource that maintains information regarding
|
|
|
|
// breached channels. This is used in conjunction with DB to recover
|
|
|
|
// from crashes, restarts, or other failures.
|
2021-09-14 04:00:36 +02:00
|
|
|
Store RetributionStorer
|
2024-06-24 08:33:27 +02:00
|
|
|
|
|
|
|
// AuxSweeper is an optional interface that can be used to modify the
|
|
|
|
// way sweep transaction are generated.
|
|
|
|
AuxSweeper fn.Option[sweep.AuxSweeper]
|
2017-09-01 12:11:14 +02:00
|
|
|
}
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// BreachArbitrator is a special subsystem which is responsible for watching and
|
2016-11-29 04:43:57 +01:00
|
|
|
// acting on the detection of any attempted uncooperative channel breaches by
|
2017-01-13 06:01:50 +01:00
|
|
|
// channel counterparties. This file essentially acts as deterrence code for
|
2016-11-29 04:43:57 +01:00
|
|
|
// those attempting to launch attacks against the daemon. In practice it's
|
|
|
|
// expected that the logic in this file never gets executed, but it is
|
|
|
|
// important to have it in place just in case we encounter cheating channel
|
2017-01-13 06:01:50 +01:00
|
|
|
// counterparties.
|
|
|
|
// TODO(roasbeef): closures in config for subsystem pointers to decouple?
|
2024-02-01 01:42:29 +01:00
|
|
|
type BreachArbitrator struct {
|
2019-01-23 18:15:57 +01:00
|
|
|
started sync.Once
|
|
|
|
stopped sync.Once
|
2017-11-21 08:56:42 +01:00
|
|
|
|
2017-09-01 12:11:14 +02:00
|
|
|
cfg *BreachConfig
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2022-01-13 20:45:10 +01:00
|
|
|
subscriptions map[wire.OutPoint]chan struct{}
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
quit chan struct{}
|
|
|
|
wg sync.WaitGroup
|
2018-04-18 13:56:14 +02:00
|
|
|
sync.Mutex
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// NewBreachArbitrator creates a new instance of a BreachArbitrator initialized
|
|
|
|
// with its dependent objects.
|
|
|
|
func NewBreachArbitrator(cfg *BreachConfig) *BreachArbitrator {
|
|
|
|
return &BreachArbitrator{
|
2022-01-13 20:45:10 +01:00
|
|
|
cfg: cfg,
|
|
|
|
subscriptions: make(map[wire.OutPoint]chan struct{}),
|
|
|
|
quit: make(chan struct{}),
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// Start is an idempotent method that officially starts the BreachArbitrator
|
|
|
|
// along with all other goroutines it needs to perform its functions.
|
|
|
|
func (b *BreachArbitrator) Start() error {
|
2019-01-23 18:15:57 +01:00
|
|
|
var err error
|
|
|
|
b.started.Do(func() {
|
2022-01-29 15:47:50 +01:00
|
|
|
brarLog.Info("Breach arbiter starting")
|
2019-01-23 18:15:57 +01:00
|
|
|
err = b.start()
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) start() error {
|
2017-11-21 08:56:42 +01:00
|
|
|
// Load all retributions currently persisted in the retribution store.
|
2020-10-20 16:18:40 +02:00
|
|
|
var breachRetInfos map[wire.OutPoint]retributionInfo
|
2017-11-21 08:56:42 +01:00
|
|
|
if err := b.cfg.Store.ForAll(func(ret *retributionInfo) error {
|
2017-07-26 07:57:29 +02:00
|
|
|
breachRetInfos[ret.chanPoint] = *ret
|
2017-05-07 13:09:22 +02:00
|
|
|
return nil
|
2020-10-20 16:18:40 +02:00
|
|
|
}, func() {
|
|
|
|
breachRetInfos = make(map[wire.OutPoint]retributionInfo)
|
2017-11-21 08:56:42 +01:00
|
|
|
}); err != nil {
|
2021-08-08 10:08:08 +02:00
|
|
|
brarLog.Errorf("Unable to create retribution info: %v", err)
|
2017-11-21 08:56:42 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load all currently closed channels from disk, we will use the
|
|
|
|
// channels that have been marked fully closed to filter the retribution
|
|
|
|
// information loaded from disk. This is necessary in the event that the
|
|
|
|
// channel was marked fully closed, but was not removed from the
|
|
|
|
// retribution store.
|
|
|
|
closedChans, err := b.cfg.DB.FetchClosedChannels(false)
|
2017-05-07 13:09:22 +02:00
|
|
|
if err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to fetch closing channels: %v", err)
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-08-08 10:08:08 +02:00
|
|
|
brarLog.Debugf("Found %v closing channels, %v retribution records",
|
|
|
|
len(closedChans), len(breachRetInfos))
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
// Using the set of non-pending, closed channels, reconcile any
|
|
|
|
// discrepancies between the channeldb and the retribution store by
|
|
|
|
// removing any retribution information for which we have already
|
|
|
|
// finished our responsibilities. If the removal is successful, we also
|
|
|
|
// remove the entry from our in-memory map, to avoid any further action
|
|
|
|
// for this channel.
|
2018-05-23 12:11:19 +02:00
|
|
|
// TODO(halseth): no need continue on IsPending once closed channels
|
|
|
|
// actually means close transaction is confirmed.
|
2017-11-21 08:56:42 +01:00
|
|
|
for _, chanSummary := range closedChans {
|
2021-08-08 10:08:08 +02:00
|
|
|
brarLog.Debugf("Working on close channel: %v, is_pending: %v",
|
|
|
|
chanSummary.ChanPoint, chanSummary.IsPending)
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
if chanSummary.IsPending {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
chanPoint := &chanSummary.ChanPoint
|
|
|
|
if _, ok := breachRetInfos[*chanPoint]; ok {
|
|
|
|
if err := b.cfg.Store.Remove(chanPoint); err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to remove closed "+
|
2017-11-21 08:56:42 +01:00
|
|
|
"chanid=%v from breach arbiter: %v",
|
|
|
|
chanPoint, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
delete(breachRetInfos, *chanPoint)
|
2021-08-08 10:08:08 +02:00
|
|
|
|
|
|
|
brarLog.Debugf("Skipped closed channel: %v",
|
|
|
|
chanSummary.ChanPoint)
|
2017-11-21 08:56:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-26 07:57:29 +02:00
|
|
|
// Spawn the exactRetribution tasks to monitor and resolve any breaches
|
|
|
|
// that were loaded from the retribution store.
|
2017-11-21 08:56:42 +01:00
|
|
|
for chanPoint := range breachRetInfos {
|
|
|
|
retInfo := breachRetInfos[chanPoint]
|
|
|
|
|
2021-08-08 10:08:08 +02:00
|
|
|
brarLog.Debugf("Handling breach handoff on startup "+
|
|
|
|
"for ChannelPoint(%v)", chanPoint)
|
|
|
|
|
2017-07-26 07:57:29 +02:00
|
|
|
// Register for a notification when the breach transaction is
|
|
|
|
// confirmed on chain.
|
2017-11-21 08:56:42 +01:00
|
|
|
breachTXID := retInfo.commitHash
|
2018-05-31 07:16:57 +02:00
|
|
|
breachScript := retInfo.breachedOutputs[0].signDesc.Output.PkScript
|
2017-09-01 12:11:14 +02:00
|
|
|
confChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
|
2018-05-31 07:16:57 +02:00
|
|
|
&breachTXID, breachScript, 1, retInfo.breachHeight,
|
|
|
|
)
|
2017-07-26 07:57:29 +02:00
|
|
|
if err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to register for conf updates "+
|
2017-07-26 07:57:29 +02:00
|
|
|
"for txid: %v, err: %v", breachTXID, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Launch a new goroutine which to finalize the channel
|
|
|
|
// retribution after the breach transaction confirms.
|
|
|
|
b.wg.Add(1)
|
|
|
|
go b.exactRetribution(confChan, &retInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start watching the remaining active channels!
|
2016-11-29 04:43:57 +01:00
|
|
|
b.wg.Add(1)
|
2018-04-18 13:56:14 +02:00
|
|
|
go b.contractObserver()
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// Stop is an idempotent method that signals the BreachArbitrator to execute a
|
2016-11-29 04:43:57 +01:00
|
|
|
// graceful shutdown. This function will block until all goroutines spawned by
|
2024-02-01 01:42:29 +01:00
|
|
|
// the BreachArbitrator have gracefully exited.
|
|
|
|
func (b *BreachArbitrator) Stop() error {
|
2019-01-23 18:15:57 +01:00
|
|
|
b.stopped.Do(func() {
|
2023-09-07 20:16:42 +02:00
|
|
|
brarLog.Infof("Breach arbiter shutting down...")
|
|
|
|
defer brarLog.Debug("Breach arbiter shutdown complete")
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2019-01-23 18:15:57 +01:00
|
|
|
close(b.quit)
|
|
|
|
b.wg.Wait()
|
|
|
|
})
|
2016-11-29 04:43:57 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
// IsBreached queries the breach arbiter's retribution store to see if it is
|
|
|
|
// aware of any channel breaches for a particular channel point.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
2017-11-21 08:56:42 +01:00
|
|
|
return b.cfg.Store.IsBreached(chanPoint)
|
|
|
|
}
|
|
|
|
|
2022-01-13 20:45:10 +01:00
|
|
|
// SubscribeBreachComplete is used by outside subsystems to be notified of a
|
|
|
|
// successful breach resolution.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) SubscribeBreachComplete(chanPoint *wire.OutPoint,
|
2022-01-13 20:45:10 +01:00
|
|
|
c chan struct{}) (bool, error) {
|
|
|
|
|
|
|
|
breached, err := b.cfg.Store.IsBreached(chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
// If an error occurs, no subscription will be registered.
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !breached {
|
|
|
|
// If chanPoint no longer exists in the Store, then the breach
|
|
|
|
// was cleaned up successfully. Any subscription that occurs
|
|
|
|
// happens after the breach information was persisted to the
|
|
|
|
// underlying store.
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise since the channel point is not resolved, add a
|
|
|
|
// subscription. There can only be one subscription per channel point.
|
|
|
|
b.Lock()
|
|
|
|
defer b.Unlock()
|
|
|
|
b.subscriptions[*chanPoint] = c
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// notifyBreachComplete is used by the BreachArbitrator to notify outside
|
2022-01-13 20:45:10 +01:00
|
|
|
// subsystems that the breach resolution process is complete.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) notifyBreachComplete(chanPoint *wire.OutPoint) {
|
2022-01-13 20:45:10 +01:00
|
|
|
b.Lock()
|
|
|
|
defer b.Unlock()
|
|
|
|
if c, ok := b.subscriptions[*chanPoint]; ok {
|
|
|
|
close(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the subscription.
|
|
|
|
delete(b.subscriptions, *chanPoint)
|
|
|
|
}
|
|
|
|
|
2024-02-01 01:42:29 +01:00
|
|
|
// contractObserver is the primary goroutine for the BreachArbitrator. This
|
2018-04-18 13:56:14 +02:00
|
|
|
// goroutine is responsible for handling breach events coming from the
|
|
|
|
// contractcourt on the ContractBreaches channel. If a channel breach is
|
|
|
|
// detected, then the contractObserver will execute the retribution logic
|
|
|
|
// required to sweep ALL outputs from a contested channel into the daemon's
|
|
|
|
// wallet.
|
2016-11-29 04:43:57 +01:00
|
|
|
//
|
|
|
|
// NOTE: This MUST be run as a goroutine.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) contractObserver() {
|
2016-11-29 04:43:57 +01:00
|
|
|
defer b.wg.Done()
|
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
brarLog.Infof("Starting contract observer, watching for breaches.")
|
2017-05-11 02:27:05 +02:00
|
|
|
|
2016-11-29 04:43:57 +01:00
|
|
|
for {
|
|
|
|
select {
|
2018-04-18 13:56:14 +02:00
|
|
|
case breachEvent := <-b.cfg.ContractBreaches:
|
|
|
|
// We have been notified about a contract breach!
|
|
|
|
// Handle the handoff, making sure we ACK the event
|
|
|
|
// after we have safely added it to the retribution
|
|
|
|
// store.
|
2018-01-23 02:03:34 +01:00
|
|
|
b.wg.Add(1)
|
2018-04-18 13:56:14 +02:00
|
|
|
go b.handleBreachHandoff(breachEvent)
|
2018-01-23 02:03:34 +01:00
|
|
|
|
2016-11-29 04:43:57 +01:00
|
|
|
case <-b.quit:
|
2018-04-18 13:56:14 +02:00
|
|
|
return
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
// spend is used to wrap the index of the retributionInfo output that gets
|
|
|
|
// spent together with the spend details.
|
|
|
|
type spend struct {
|
|
|
|
index int
|
|
|
|
detail *chainntnfs.SpendDetail
|
|
|
|
}
|
|
|
|
|
2018-06-01 16:20:55 +02:00
|
|
|
// waitForSpendEvent waits for any of the breached outputs to get spent, and
|
2021-02-15 13:51:07 +01:00
|
|
|
// 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
|
|
|
|
// method multiple times.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) waitForSpendEvent(breachInfo *retributionInfo,
|
2021-02-15 13:51:07 +01:00
|
|
|
spendNtfns map[wire.OutPoint]*chainntnfs.SpendEvent) ([]spend, error) {
|
2018-06-01 16:20:55 +02:00
|
|
|
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
inputs := breachInfo.breachedOutputs
|
|
|
|
|
2018-06-01 16:20:55 +02:00
|
|
|
// We create a channel the first goroutine that gets a spend event can
|
|
|
|
// signal. We make it buffered in case multiple spend events come in at
|
|
|
|
// the same time.
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
anySpend := make(chan struct{}, len(inputs))
|
2018-06-01 16:20:55 +02:00
|
|
|
|
|
|
|
// The allSpends channel will be used to pass spend events from all the
|
|
|
|
// goroutines that detects a spend before they are signalled to exit.
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
allSpends := make(chan spend, len(inputs))
|
2018-06-01 16:20:55 +02:00
|
|
|
|
|
|
|
// exit will be used to signal the goroutines that they can exit.
|
|
|
|
exit := make(chan struct{})
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
// We'll now launch a goroutine for each of the HTLC outputs, that will
|
|
|
|
// signal the moment they detect a spend event.
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
for i := range inputs {
|
|
|
|
breachedOutput := &inputs[i]
|
2018-06-01 16:20:55 +02:00
|
|
|
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
brarLog.Infof("Checking spend from %v(%v) for ChannelPoint(%v)",
|
|
|
|
breachedOutput.witnessType, breachedOutput.outpoint,
|
2018-06-01 16:20:55 +02:00
|
|
|
breachInfo.chanPoint)
|
|
|
|
|
|
|
|
// If we have already registered for a notification for this
|
|
|
|
// output, we'll reuse it.
|
|
|
|
spendNtfn, ok := spendNtfns[breachedOutput.outpoint]
|
|
|
|
if !ok {
|
|
|
|
var err error
|
|
|
|
spendNtfn, err = b.cfg.Notifier.RegisterSpendNtfn(
|
|
|
|
&breachedOutput.outpoint,
|
2018-07-18 04:27:31 +02:00
|
|
|
breachedOutput.signDesc.Output.PkScript,
|
2018-07-17 09:13:06 +02:00
|
|
|
breachInfo.breachHeight,
|
2018-06-01 16:20:55 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to check for spentness "+
|
|
|
|
"of outpoint=%v: %v",
|
2018-06-01 16:20:55 +02:00
|
|
|
breachedOutput.outpoint, err)
|
|
|
|
|
|
|
|
// Registration may have failed if we've been
|
|
|
|
// instructed to shutdown. If so, return here
|
|
|
|
// to avoid entering an infinite loop.
|
|
|
|
select {
|
|
|
|
case <-b.quit:
|
2021-02-15 13:51:07 +01:00
|
|
|
return nil, errBrarShuttingDown
|
2018-06-01 16:20:55 +02:00
|
|
|
default:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spendNtfns[breachedOutput.outpoint] = spendNtfn
|
|
|
|
}
|
|
|
|
|
|
|
|
// Launch a goroutine waiting for a spend event.
|
|
|
|
b.wg.Add(1)
|
|
|
|
wg.Add(1)
|
|
|
|
go func(index int, spendEv *chainntnfs.SpendEvent) {
|
|
|
|
defer b.wg.Done()
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
select {
|
|
|
|
// The output has been taken to the second level!
|
|
|
|
case sp, ok := <-spendEv.Spend:
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
|
|
|
brarLog.Infof("Detected spend on %s(%v) by "+
|
|
|
|
"txid(%v) for ChannelPoint(%v)",
|
|
|
|
inputs[index].witnessType,
|
|
|
|
inputs[index].outpoint,
|
|
|
|
sp.SpenderTxHash,
|
2018-06-01 16:20:55 +02:00
|
|
|
breachInfo.chanPoint)
|
|
|
|
|
|
|
|
// First we send the spend event on the
|
|
|
|
// allSpends channel, such that it can be
|
|
|
|
// handled after all go routines have exited.
|
|
|
|
allSpends <- spend{index, sp}
|
|
|
|
|
|
|
|
// Finally we'll signal the anySpend channel
|
|
|
|
// that a spend was detected, such that the
|
|
|
|
// other goroutines can be shut down.
|
|
|
|
anySpend <- struct{}{}
|
|
|
|
case <-exit:
|
|
|
|
return
|
|
|
|
case <-b.quit:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}(i, spendNtfn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll wait for any of the outputs to be spent, or that we are
|
|
|
|
// signalled to exit.
|
|
|
|
select {
|
2018-09-06 10:48:46 +02:00
|
|
|
// A goroutine have signalled that a spend occurred.
|
2018-06-01 16:20:55 +02:00
|
|
|
case <-anySpend:
|
|
|
|
// Signal for the remaining goroutines to exit.
|
|
|
|
close(exit)
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
// At this point all goroutines that can send on the allSpends
|
|
|
|
// channel have exited. We can therefore safely close the
|
|
|
|
// channel before ranging over its content.
|
|
|
|
close(allSpends)
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
// Gather all detected spends and return them.
|
|
|
|
var spends []spend
|
2018-06-01 16:20:55 +02:00
|
|
|
for s := range allSpends {
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
breachedOutput := &inputs[s.index]
|
|
|
|
delete(spendNtfns, breachedOutput.outpoint)
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
spends = append(spends, s)
|
|
|
|
}
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
return spends, nil
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
case <-b.quit:
|
|
|
|
return nil, errBrarShuttingDown
|
|
|
|
}
|
|
|
|
}
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
// 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
|
2022-02-07 13:58:28 +01:00
|
|
|
// party has already attempted to take it to the second level.
|
2021-09-14 04:00:36 +02:00
|
|
|
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.
|
2023-03-02 07:17:30 +01:00
|
|
|
isTaproot := txscript.IsPayToTaproot(bo.signDesc.Output.PkScript)
|
|
|
|
if isTaproot {
|
|
|
|
bo.witnessType = input.TaprootHtlcSecondLevelRevoke
|
|
|
|
} else {
|
|
|
|
bo.witnessType = input.HtlcSecondLevelRevoke
|
|
|
|
}
|
2021-09-14 04:00:36 +02:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// For taproot outputs, the taptweak also needs to be swapped out. We
|
2023-08-09 07:22:12 +02:00
|
|
|
// do this unconditionally as this field isn't used at all for segwit
|
|
|
|
// v0 outputs.
|
2023-03-02 07:17:30 +01:00
|
|
|
bo.signDesc.TapTweak = bo.secondLevelTapTweak[:]
|
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
// updateBreachInfo mutates the passed breachInfo by removing or converting any
|
2021-04-23 10:44:49 +02:00
|
|
|
// outputs among the spends. It also counts the total and revoked funds swept
|
|
|
|
// by our justice spends.
|
|
|
|
func updateBreachInfo(breachInfo *retributionInfo, spends []spend) (
|
|
|
|
btcutil.Amount, btcutil.Amount) {
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
inputs := breachInfo.breachedOutputs
|
|
|
|
doneOutputs := make(map[int]struct{})
|
|
|
|
|
2021-04-23 10:44:49 +02:00
|
|
|
var totalFunds, revokedFunds btcutil.Amount
|
2021-02-15 13:51:07 +01:00
|
|
|
for _, s := range spends {
|
|
|
|
breachedOutput := &inputs[s.index]
|
2021-04-20 08:46:23 +02:00
|
|
|
txIn := s.detail.SpendingTx.TxIn[s.detail.SpenderInputIndex]
|
2021-02-15 13:51:07 +01:00
|
|
|
|
|
|
|
switch breachedOutput.witnessType {
|
2023-03-02 07:17:30 +01:00
|
|
|
case input.TaprootHtlcAcceptedRevoke:
|
|
|
|
fallthrough
|
|
|
|
case input.TaprootHtlcOfferedRevoke:
|
|
|
|
fallthrough
|
2021-02-15 13:51:07 +01:00
|
|
|
case input.HtlcAcceptedRevoke:
|
|
|
|
fallthrough
|
|
|
|
case input.HtlcOfferedRevoke:
|
2021-04-20 08:46:23 +02:00
|
|
|
// If the HTLC output was spent using the revocation
|
|
|
|
// key, it is our own spend, and we can forget the
|
|
|
|
// output. Otherwise it has been taken to the second
|
|
|
|
// level.
|
|
|
|
signDesc := &breachedOutput.signDesc
|
|
|
|
ok, err := input.IsHtlcSpendRevoke(txIn, signDesc)
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Errorf("Unable to determine if "+
|
|
|
|
"revoke spend: %v", err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
brarLog.Debugf("HTLC spend was our own " +
|
|
|
|
"revocation spend")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
brarLog.Infof("Spend on second-level "+
|
|
|
|
"%s(%v) for ChannelPoint(%v) "+
|
|
|
|
"transitions to second-level output",
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
breachedOutput.witnessType,
|
2018-06-01 16:20:55 +02:00
|
|
|
breachedOutput.outpoint, breachInfo.chanPoint)
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
// In this case we'll morph our initial revoke
|
|
|
|
// spend to instead point to the second level
|
|
|
|
// output, and update the sign descriptor in the
|
|
|
|
// process.
|
|
|
|
convertToSecondLevelRevoke(
|
|
|
|
breachedOutput, breachInfo, s.detail,
|
|
|
|
)
|
|
|
|
|
|
|
|
continue
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
}
|
2018-06-01 16:20:55 +02:00
|
|
|
|
2021-04-23 10:44:49 +02:00
|
|
|
// Now that we have determined the spend is done by us, we
|
|
|
|
// count the total and revoked funds swept depending on the
|
|
|
|
// input type.
|
|
|
|
switch breachedOutput.witnessType {
|
2023-03-02 07:17:30 +01:00
|
|
|
// If the output being revoked is the remote commitment output
|
|
|
|
// or an offered HTLC output, its amount contributes to the
|
|
|
|
// value of funds being revoked from the counter party.
|
|
|
|
case input.CommitmentRevoke, input.TaprootCommitmentRevoke,
|
|
|
|
input.HtlcSecondLevelRevoke,
|
|
|
|
input.TaprootHtlcSecondLevelRevoke,
|
|
|
|
input.TaprootHtlcOfferedRevoke, input.HtlcOfferedRevoke:
|
2021-04-23 10:44:49 +02:00
|
|
|
|
|
|
|
revokedFunds += breachedOutput.Amount()
|
|
|
|
}
|
|
|
|
|
|
|
|
totalFunds += breachedOutput.Amount()
|
2021-02-15 13:51:07 +01:00
|
|
|
brarLog.Infof("Spend on %s(%v) for ChannelPoint(%v) "+
|
|
|
|
"transitions output to terminal state, "+
|
|
|
|
"removing input from justice transaction",
|
|
|
|
breachedOutput.witnessType,
|
|
|
|
breachedOutput.outpoint, breachInfo.chanPoint)
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
doneOutputs[s.index] = struct{}{}
|
|
|
|
}
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
// Filter the inputs for which we can no longer proceed.
|
|
|
|
var nextIndex int
|
|
|
|
for i := range inputs {
|
|
|
|
if _, ok := doneOutputs[i]; ok {
|
|
|
|
continue
|
|
|
|
}
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
inputs[nextIndex] = inputs[i]
|
|
|
|
nextIndex++
|
2018-06-01 16:20:55 +02:00
|
|
|
}
|
|
|
|
|
2021-02-15 13:51:07 +01:00
|
|
|
// Update our remaining set of outputs before continuing with
|
|
|
|
// another attempt at publication.
|
|
|
|
breachInfo.breachedOutputs = inputs[:nextIndex]
|
2021-04-23 10:44:49 +02:00
|
|
|
return totalFunds, revokedFunds
|
2018-06-01 16:20:55 +02:00
|
|
|
}
|
|
|
|
|
2017-07-26 05:14:03 +02:00
|
|
|
// exactRetribution is a goroutine which is executed once a contract breach has
|
|
|
|
// been detected by a breachObserver. This function is responsible for
|
|
|
|
// punishing a counterparty for violating the channel contract by sweeping ALL
|
|
|
|
// the lingering funds within the channel into the daemon's wallet.
|
|
|
|
//
|
|
|
|
// NOTE: This MUST be run as a goroutine.
|
2024-02-01 01:42:29 +01:00
|
|
|
//
|
|
|
|
//nolint:funlen
|
|
|
|
func (b *BreachArbitrator) exactRetribution(
|
|
|
|
confChan *chainntnfs.ConfirmationEvent, breachInfo *retributionInfo) {
|
2017-07-26 05:14:03 +02:00
|
|
|
|
|
|
|
defer b.wg.Done()
|
|
|
|
|
|
|
|
// TODO(roasbeef): state needs to be checkpointed here
|
|
|
|
select {
|
2021-04-20 15:42:23 +02:00
|
|
|
case _, ok := <-confChan.Confirmed:
|
2017-07-26 05:14:03 +02:00
|
|
|
// If the second value is !ok, then the channel has been closed
|
|
|
|
// signifying a daemon shutdown, so we exit.
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, if this is a real confirmation notification, then
|
|
|
|
// we fall through to complete our duty.
|
|
|
|
case <-b.quit:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
brarLog.Debugf("Breach transaction %v has been confirmed, sweeping "+
|
|
|
|
"revoked funds", breachInfo.commitHash)
|
|
|
|
|
2018-06-01 16:20:55 +02:00
|
|
|
// We may have to wait for some of the HTLC outputs to be spent to the
|
|
|
|
// second level before broadcasting the justice tx. We'll store the
|
2022-01-13 17:29:43 +01:00
|
|
|
// SpendEvents between each attempt to not re-register unnecessarily.
|
2018-06-01 16:20:55 +02:00
|
|
|
spendNtfns := make(map[wire.OutPoint]*chainntnfs.SpendEvent)
|
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
// Compute both the total value of funds being swept and the
|
|
|
|
// amount of funds that were revoked from the counter party.
|
|
|
|
var totalFunds, revokedFunds btcutil.Amount
|
|
|
|
|
2018-06-01 16:20:55 +02:00
|
|
|
justiceTxBroadcast:
|
2021-02-17 14:22:25 +01:00
|
|
|
// With the breach transaction confirmed, we now create the
|
|
|
|
// justice tx which will claim ALL the funds within the
|
|
|
|
// channel.
|
2021-02-12 12:36:45 +01:00
|
|
|
justiceTxs, err := b.createJusticeTx(breachInfo.breachedOutputs)
|
2021-02-17 14:22:25 +01:00
|
|
|
if err != nil {
|
|
|
|
brarLog.Errorf("Unable to create justice tx: %v", err)
|
|
|
|
return
|
2017-11-21 08:56:42 +01:00
|
|
|
}
|
2021-02-12 12:36:45 +01:00
|
|
|
finalTx := justiceTxs.spendAll
|
2017-11-21 08:56:42 +01:00
|
|
|
|
2024-07-25 16:18:00 +02:00
|
|
|
brarLog.Debugf("Broadcasting justice tx: %v", lnutils.SpewLogClosure(
|
|
|
|
finalTx))
|
2017-07-26 05:14:03 +02:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// As we're about to broadcast our breach transaction, we'll notify the
|
|
|
|
// aux sweeper of our broadcast attempt first.
|
|
|
|
err = fn.MapOptionZ(b.cfg.AuxSweeper, func(aux sweep.AuxSweeper) error {
|
|
|
|
bumpReq := sweep.BumpRequest{
|
|
|
|
Inputs: finalTx.inputs,
|
|
|
|
DeliveryAddress: finalTx.sweepAddr,
|
|
|
|
ExtraTxOut: finalTx.extraTxOut,
|
|
|
|
}
|
|
|
|
|
|
|
|
return aux.NotifyBroadcast(
|
2024-11-08 03:28:08 +01:00
|
|
|
&bumpReq, finalTx.justiceTx, finalTx.fee, nil,
|
2024-06-24 08:33:27 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Errorf("unable to notify broadcast: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
// We'll now attempt to broadcast the transaction which finalized the
|
|
|
|
// channel's retribution against the cheating counter party.
|
2020-07-29 09:27:22 +02:00
|
|
|
label := labels.MakeLabel(labels.LabelTypeJusticeTransaction, nil)
|
2024-06-24 08:33:27 +02:00
|
|
|
err = b.cfg.PublishTransaction(finalTx.justiceTx, label)
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
if err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to broadcast justice tx: %v", err)
|
2021-04-20 15:42:23 +02:00
|
|
|
}
|
2018-06-01 16:20:55 +02:00
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
// Regardless of publication succeeded or not, we now wait for any of
|
|
|
|
// the inputs to be spent. If any input got spent by the remote, we
|
|
|
|
// must recreate our justice transaction.
|
|
|
|
var (
|
|
|
|
spendChan = make(chan []spend, 1)
|
|
|
|
errChan = make(chan error, 1)
|
|
|
|
wg sync.WaitGroup
|
|
|
|
)
|
2018-02-08 02:42:36 +01:00
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
spends, err := b.waitForSpendEvent(breachInfo, spendNtfns)
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
spendChan <- spends
|
|
|
|
}()
|
|
|
|
|
2021-02-15 13:31:08 +01:00
|
|
|
// We'll also register for block notifications, such that in case our
|
|
|
|
// justice tx doesn't confirm within a reasonable timeframe, we can
|
|
|
|
// start to more aggressively sweep the time sensitive outputs.
|
|
|
|
newBlockChan, err := b.cfg.Notifier.RegisterBlockEpochNtfn(nil)
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Errorf("Unable to register for block notifications: %v",
|
|
|
|
err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer newBlockChan.Cancel()
|
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
Loop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case spends := <-spendChan:
|
|
|
|
// Update the breach info with the new spends.
|
2021-04-23 10:44:49 +02:00
|
|
|
t, r := updateBreachInfo(breachInfo, spends)
|
|
|
|
totalFunds += t
|
|
|
|
revokedFunds += r
|
|
|
|
|
|
|
|
brarLog.Infof("%v spends from breach tx for "+
|
|
|
|
"ChannelPoint(%v) has been detected, %v "+
|
|
|
|
"revoked funds (%v total) have been claimed",
|
|
|
|
len(spends), breachInfo.chanPoint,
|
|
|
|
revokedFunds, totalFunds)
|
2021-04-20 15:42:23 +02:00
|
|
|
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
if len(breachInfo.breachedOutputs) == 0 {
|
2021-04-23 10:44:49 +02:00
|
|
|
brarLog.Infof("Justice for ChannelPoint(%v) "+
|
|
|
|
"has been served, %v revoked funds "+
|
|
|
|
"(%v total) have been claimed. No "+
|
|
|
|
"more outputs to sweep, marking fully "+
|
|
|
|
"resolved", breachInfo.chanPoint,
|
|
|
|
revokedFunds, totalFunds)
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
|
|
|
|
err = b.cleanupBreach(&breachInfo.chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Errorf("Failed to cleanup "+
|
|
|
|
"breached ChannelPoint(%v): %v",
|
|
|
|
breachInfo.chanPoint, err)
|
|
|
|
}
|
2021-04-20 15:42:23 +02:00
|
|
|
|
|
|
|
// TODO(roasbeef): add peer to blacklist?
|
|
|
|
|
2021-04-23 10:44:49 +02:00
|
|
|
// TODO(roasbeef): close other active channels
|
|
|
|
// with offending peer
|
2021-04-20 15:42:23 +02:00
|
|
|
break Loop
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
brarLog.Infof("Attempting another justice tx "+
|
|
|
|
"with %d inputs",
|
|
|
|
len(breachInfo.breachedOutputs))
|
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
wg.Wait()
|
2018-06-01 16:20:55 +02:00
|
|
|
goto justiceTxBroadcast
|
2017-07-26 05:14:03 +02:00
|
|
|
|
2021-02-15 13:31:08 +01:00
|
|
|
// On every new block, we check whether we should republish the
|
|
|
|
// transactions.
|
|
|
|
case epoch, ok := <-newBlockChan.Epochs:
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If less than four blocks have passed since the
|
|
|
|
// breach confirmed, we'll continue waiting. It was
|
|
|
|
// published with a 2-block fee estimate, so it's not
|
|
|
|
// unexpected that four blocks without confirmation can
|
|
|
|
// pass.
|
|
|
|
splitHeight := breachInfo.breachHeight +
|
|
|
|
blocksPassedSplitPublish
|
|
|
|
if uint32(epoch.Height) < splitHeight {
|
|
|
|
continue Loop
|
|
|
|
}
|
|
|
|
|
|
|
|
brarLog.Warnf("Block height %v arrived without "+
|
|
|
|
"justice tx confirming (breached at "+
|
|
|
|
"height %v), splitting justice tx.",
|
|
|
|
epoch.Height, breachInfo.breachHeight)
|
|
|
|
|
|
|
|
// Otherwise we'll attempt to publish the two separate
|
|
|
|
// justice transactions that sweeps the commitment
|
|
|
|
// outputs and the HTLC outputs separately. This is to
|
|
|
|
// mitigate the case where our "spend all" justice TX
|
|
|
|
// doesn't propagate because the HTLC outputs have been
|
|
|
|
// pinned by low fee HTLC txs.
|
|
|
|
label := labels.MakeLabel(
|
|
|
|
labels.LabelTypeJusticeTransaction, nil,
|
|
|
|
)
|
|
|
|
if justiceTxs.spendCommitOuts != nil {
|
|
|
|
tx := justiceTxs.spendCommitOuts
|
|
|
|
|
|
|
|
brarLog.Debugf("Broadcasting justice tx "+
|
|
|
|
"spending commitment outs: %v",
|
2024-07-25 16:18:00 +02:00
|
|
|
lnutils.SpewLogClosure(tx))
|
2021-02-15 13:31:08 +01:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
err = b.cfg.PublishTransaction(
|
|
|
|
tx.justiceTx, label,
|
|
|
|
)
|
2021-02-15 13:31:08 +01:00
|
|
|
if err != nil {
|
|
|
|
brarLog.Warnf("Unable to broadcast "+
|
|
|
|
"commit out spending justice "+
|
|
|
|
"tx: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if justiceTxs.spendHTLCs != nil {
|
|
|
|
tx := justiceTxs.spendHTLCs
|
|
|
|
|
|
|
|
brarLog.Debugf("Broadcasting justice tx "+
|
|
|
|
"spending HTLC outs: %v",
|
2024-07-25 16:18:00 +02:00
|
|
|
lnutils.SpewLogClosure(tx))
|
2021-02-15 13:31:08 +01:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
err = b.cfg.PublishTransaction(
|
|
|
|
tx.justiceTx, label,
|
|
|
|
)
|
2021-02-15 13:31:08 +01:00
|
|
|
if err != nil {
|
|
|
|
brarLog.Warnf("Unable to broadcast "+
|
|
|
|
"HTLC out spending justice "+
|
|
|
|
"tx: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
for _, tx := range justiceTxs.spendSecondLevelHTLCs {
|
|
|
|
tx := tx
|
|
|
|
|
|
|
|
brarLog.Debugf("Broadcasting justice tx "+
|
|
|
|
"spending second-level HTLC output: %v",
|
2024-07-25 16:18:00 +02:00
|
|
|
lnutils.SpewLogClosure(tx))
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
err = b.cfg.PublishTransaction(
|
|
|
|
tx.justiceTx, label,
|
|
|
|
)
|
2023-03-02 07:17:30 +01:00
|
|
|
if err != nil {
|
|
|
|
brarLog.Warnf("Unable to broadcast "+
|
|
|
|
"second-level HTLC out "+
|
|
|
|
"spending justice tx: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
case err := <-errChan:
|
|
|
|
if err != errBrarShuttingDown {
|
|
|
|
brarLog.Errorf("error waiting for "+
|
|
|
|
"spend event: %v", err)
|
|
|
|
}
|
|
|
|
break Loop
|
2017-07-26 05:14:03 +02:00
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
case <-b.quit:
|
|
|
|
break Loop
|
2017-07-26 05:14:03 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-20 15:42:23 +02:00
|
|
|
|
|
|
|
// Wait for our go routine to exit.
|
|
|
|
wg.Wait()
|
2017-07-26 05:14:03 +02:00
|
|
|
}
|
|
|
|
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
// cleanupBreach marks the given channel point as fully resolved and removes the
|
|
|
|
// retribution for that the channel from the retribution store.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) cleanupBreach(chanPoint *wire.OutPoint) error {
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
// With the channel closed, mark it in the database as such.
|
|
|
|
err := b.cfg.DB.MarkChanFullyClosed(chanPoint)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return fmt.Errorf("unable to mark chan as closed: %w", err)
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Justice has been carried out; we can safely delete the retribution
|
|
|
|
// info from the database.
|
|
|
|
err = b.cfg.Store.Remove(chanPoint)
|
|
|
|
if err != nil {
|
2024-03-07 13:19:28 +01:00
|
|
|
return fmt.Errorf("unable to remove retribution from db: %w",
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
2022-01-13 20:45:10 +01:00
|
|
|
// This is after the Remove call so that the chan passed in via
|
|
|
|
// SubscribeBreachComplete is always notified, no matter when it is
|
|
|
|
// called. Otherwise, if notifyBreachComplete was before Remove, a
|
|
|
|
// very rare edge case could occur in which SubscribeBreachComplete
|
|
|
|
// is called after notifyBreachComplete and before Remove, meaning the
|
|
|
|
// caller would never be notified.
|
|
|
|
b.notifyBreachComplete(chanPoint)
|
|
|
|
|
breacharbiter: detect all spends, add terminal states
This commit modifies the breach arbiter to monitor
all breached inputs for spends and remove them from
the set of inputs to be swept if they are spent to
a terminal state. Prior, we would only watch for
spends on htlcs that may need to transition and
sweep the corresponding second-level htlc.
With these changes, we will no monitor commitment
outputs for spends, as well as spends from the
second level htlcs themselves. If either of these
is detected, we remove them from the set of inputs
to sweep via the justice transaction because there
is nothing the breach arbiter can do.
This functionality will be needed when adding
watchtower support, as the breach arbiter must
detect the case when the tower sweeps on behalf of
the user and stop pursuing the sweep itself. In
addition, this now properly handles the potential
case where somehow the remote party is able to sweep
the their commitment or second-level htlc to their
wallet, and prevent the breach arbiter from trying
to sweep the outputs as it would now.
Note that in the latter event, the internal
accounting may still be incorrect, as it is assumed
that all breached funds return to the victim.
However, these issues will deferred and fixed at a
later date, as the more crucial aspect is that the
breach arbiter doesn't blow up as a result of towers
sweeping channels.
2019-03-20 03:22:35 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// handleBreachHandoff handles a new breach event, by writing it to disk, then
|
2024-02-01 01:42:29 +01:00
|
|
|
// notifies the BreachArbitrator contract observer goroutine that a channel's
|
2018-04-18 13:56:14 +02:00
|
|
|
// contract has been breached by the prior counterparty. Once notified the
|
2024-02-01 01:42:29 +01:00
|
|
|
// BreachArbitrator will attempt to sweep ALL funds within the channel using the
|
2018-04-18 13:56:14 +02:00
|
|
|
// information provided within the BreachRetribution generated due to the
|
|
|
|
// breach of channel contract. The funds will be swept only after the breaching
|
|
|
|
// transaction receives a necessary number of confirmations.
|
|
|
|
//
|
|
|
|
// NOTE: This MUST be run as a goroutine.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) handleBreachHandoff(
|
|
|
|
breachEvent *ContractBreachEvent) {
|
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
defer b.wg.Done()
|
2018-01-20 02:25:06 +01:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
chanPoint := breachEvent.ChanPoint
|
|
|
|
brarLog.Debugf("Handling breach handoff for ChannelPoint(%v)",
|
|
|
|
chanPoint)
|
2018-01-20 02:25:06 +01:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// A read from this channel indicates that a channel breach has been
|
|
|
|
// detected! So we notify the main coordination goroutine with the
|
|
|
|
// information needed to bring the counterparty to justice.
|
|
|
|
breachInfo := breachEvent.BreachRetribution
|
|
|
|
brarLog.Warnf("REVOKED STATE #%v FOR ChannelPoint(%v) "+
|
|
|
|
"broadcast, REMOTE PEER IS DOING SOMETHING "+
|
|
|
|
"SKETCHY!!!", breachInfo.RevokedStateNum,
|
|
|
|
chanPoint)
|
2018-01-20 02:25:06 +01:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// Immediately notify the HTLC switch that this link has been
|
|
|
|
// breached in order to ensure any incoming or outgoing
|
|
|
|
// multi-hop HTLCs aren't sent over this link, nor any other
|
|
|
|
// links associated with this peer.
|
2021-09-14 04:00:36 +02:00
|
|
|
b.cfg.CloseLink(&chanPoint, CloseBreach)
|
2017-07-26 07:57:29 +02:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// TODO(roasbeef): need to handle case of remote broadcast
|
|
|
|
// mid-local initiated state-transition, possible
|
|
|
|
// false-positive?
|
2017-05-05 01:08:56 +02:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// Acquire the mutex to ensure consistency between the call to
|
|
|
|
// IsBreached and Add below.
|
|
|
|
b.Lock()
|
2017-11-23 20:41:42 +01:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// We first check if this breach info is already added to the
|
|
|
|
// retribution store.
|
|
|
|
breached, err := b.cfg.Store.IsBreached(&chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
b.Unlock()
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to check breach info in DB: %v", err)
|
2017-07-26 07:57:29 +02:00
|
|
|
|
2021-04-21 12:51:04 +02:00
|
|
|
// Notify about the failed lookup and return.
|
|
|
|
breachEvent.ProcessACK(err)
|
2018-04-18 13:56:14 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this channel is already marked as breached in the retribution
|
|
|
|
// store, we already have handled the handoff for this breach. In this
|
|
|
|
// case we can safely ACK the handoff, and return.
|
|
|
|
if breached {
|
|
|
|
b.Unlock()
|
2021-04-21 12:51:04 +02:00
|
|
|
breachEvent.ProcessACK(nil)
|
2018-04-18 13:56:14 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Using the breach information provided by the wallet and the
|
|
|
|
// channel snapshot, construct the retribution information that
|
|
|
|
// will be persisted to disk.
|
|
|
|
retInfo := newRetributionInfo(&chanPoint, breachInfo)
|
|
|
|
|
|
|
|
// Persist the pending retribution state to disk.
|
|
|
|
err = b.cfg.Store.Add(retInfo)
|
|
|
|
b.Unlock()
|
|
|
|
if err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to persist retribution "+
|
2018-04-18 13:56:14 +02:00
|
|
|
"info to db: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that the breach has been persisted, try to send an
|
|
|
|
// acknowledgment back to the close observer with the error. If
|
|
|
|
// the ack is successful, the close observer will mark the
|
|
|
|
// channel as pending-closed in the channeldb.
|
2021-04-21 12:51:04 +02:00
|
|
|
breachEvent.ProcessACK(err)
|
2017-05-05 01:07:25 +02:00
|
|
|
|
2021-04-21 12:51:04 +02:00
|
|
|
// Bail if we failed to persist retribution info.
|
|
|
|
if err != nil {
|
2016-11-29 04:43:57 +01:00
|
|
|
return
|
|
|
|
}
|
2018-04-18 13:56:14 +02:00
|
|
|
|
|
|
|
// Now that a new channel contract has been added to the retribution
|
|
|
|
// store, we first register for a notification to be dispatched once
|
|
|
|
// the breach transaction (the revoked commitment transaction) has been
|
|
|
|
// confirmed in the chain to ensure we're not dealing with a moving
|
|
|
|
// target.
|
|
|
|
breachTXID := &retInfo.commitHash
|
2018-05-31 07:16:57 +02:00
|
|
|
breachScript := retInfo.breachedOutputs[0].signDesc.Output.PkScript
|
|
|
|
cfChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
|
|
|
|
breachTXID, breachScript, 1, retInfo.breachHeight,
|
|
|
|
)
|
2018-04-18 13:56:14 +02:00
|
|
|
if err != nil {
|
2019-03-20 03:21:03 +01:00
|
|
|
brarLog.Errorf("Unable to register for conf updates for "+
|
2018-04-18 13:56:14 +02:00
|
|
|
"txid: %v, err: %v", breachTXID, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
brarLog.Warnf("A channel has been breached with txid: %v. Waiting "+
|
|
|
|
"for confirmation, then justice will be served!", breachTXID)
|
|
|
|
|
|
|
|
// With the retribution state persisted, channel close persisted, and
|
|
|
|
// notification registered, we launch a new goroutine which will
|
|
|
|
// finalize the channel retribution after the breach transaction has
|
|
|
|
// been confirmed.
|
|
|
|
b.wg.Add(1)
|
|
|
|
go b.exactRetribution(cfChan, retInfo)
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
|
|
|
|
2017-07-26 05:14:03 +02:00
|
|
|
// breachedOutput contains all the information needed to sweep a breached
|
|
|
|
// output. A breached output is an output that we are now entitled to due to a
|
|
|
|
// revoked commitment transaction being broadcast.
|
|
|
|
type breachedOutput struct {
|
2017-08-22 01:56:58 +02:00
|
|
|
amt btcutil.Amount
|
|
|
|
outpoint wire.OutPoint
|
2019-10-07 13:41:46 +02:00
|
|
|
witnessType input.StandardWitnessType
|
2019-01-16 15:47:43 +01:00
|
|
|
signDesc input.SignDescriptor
|
2018-11-07 16:35:54 +01:00
|
|
|
confHeight uint32
|
2017-05-07 13:09:22 +02:00
|
|
|
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
secondLevelWitnessScript []byte
|
2023-03-02 07:17:30 +01:00
|
|
|
secondLevelTapTweak [32]byte
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
|
2019-01-16 15:47:43 +01:00
|
|
|
witnessFunc input.WitnessGenerator
|
2024-06-04 08:02:57 +02:00
|
|
|
|
|
|
|
resolutionBlob fn.Option[tlv.Blob]
|
|
|
|
|
|
|
|
// TODO(roasbeef): function opt and hook into brar
|
2017-08-22 01:56:58 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// makeBreachedOutput assembles a new breachedOutput that can be used by the
|
2017-08-30 04:07:52 +02:00
|
|
|
// breach arbiter to construct a justice or sweep transaction.
|
2017-09-19 01:32:20 +02:00
|
|
|
func makeBreachedOutput(outpoint *wire.OutPoint,
|
2024-06-24 08:32:11 +02:00
|
|
|
witnessType input.StandardWitnessType, secondLevelScript []byte,
|
|
|
|
signDescriptor *input.SignDescriptor, confHeight uint32,
|
|
|
|
resolutionBlob fn.Option[tlv.Blob]) breachedOutput {
|
2017-08-22 01:56:58 +02:00
|
|
|
|
|
|
|
amount := signDescriptor.Output.Value
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
return breachedOutput{
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
amt: btcutil.Amount(amount),
|
|
|
|
outpoint: *outpoint,
|
|
|
|
secondLevelWitnessScript: secondLevelScript,
|
|
|
|
witnessType: witnessType,
|
|
|
|
signDesc: *signDescriptor,
|
2018-11-07 16:35:54 +01:00
|
|
|
confHeight: confHeight,
|
2024-06-24 08:32:11 +02:00
|
|
|
resolutionBlob: resolutionBlob,
|
2017-08-22 01:56:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Amount returns the number of satoshis contained in the breached output.
|
|
|
|
func (bo *breachedOutput) Amount() btcutil.Amount {
|
|
|
|
return bo.amt
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// OutPoint returns the breached output's identifier that is to be included as a
|
2017-08-22 01:56:58 +02:00
|
|
|
// transaction input.
|
2024-03-27 10:07:48 +01:00
|
|
|
func (bo *breachedOutput) OutPoint() wire.OutPoint {
|
|
|
|
return bo.outpoint
|
2017-08-22 01:56:58 +02:00
|
|
|
}
|
|
|
|
|
2020-11-06 19:59:11 +01:00
|
|
|
// RequiredTxOut returns a non-nil TxOut if input commits to a certain
|
|
|
|
// transaction output. This is used in the SINGLE|ANYONECANPAY case to make
|
|
|
|
// sure any presigned input is still valid by including the output.
|
|
|
|
func (bo *breachedOutput) RequiredTxOut() *wire.TxOut {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-06 19:35:01 +01:00
|
|
|
// RequiredLockTime returns whether this input commits to a tx locktime that
|
|
|
|
// must be used in the transaction including it.
|
|
|
|
func (bo *breachedOutput) RequiredLockTime() (uint32, bool) {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// WitnessType returns the type of witness that must be generated to spend the
|
|
|
|
// breached output.
|
2019-01-16 15:47:43 +01:00
|
|
|
func (bo *breachedOutput) WitnessType() input.WitnessType {
|
2017-09-19 01:32:20 +02:00
|
|
|
return bo.witnessType
|
|
|
|
}
|
|
|
|
|
|
|
|
// SignDesc returns the breached output's SignDescriptor, which is used during
|
|
|
|
// signing to compute the witness.
|
2019-01-16 15:47:43 +01:00
|
|
|
func (bo *breachedOutput) SignDesc() *input.SignDescriptor {
|
2017-09-19 01:32:20 +02:00
|
|
|
return &bo.signDesc
|
|
|
|
}
|
|
|
|
|
2024-10-16 04:17:06 +02:00
|
|
|
// Preimage returns the preimage that was used to create the breached output.
|
|
|
|
func (bo *breachedOutput) Preimage() fn.Option[lntypes.Preimage] {
|
|
|
|
return fn.None[lntypes.Preimage]()
|
|
|
|
}
|
|
|
|
|
2018-11-18 05:48:41 +01:00
|
|
|
// CraftInputScript computes a valid witness that allows us to spend from the
|
2017-08-22 01:56:58 +02:00
|
|
|
// breached output. It does so by first generating and memoizing the witness
|
|
|
|
// generation function, which parameterized primarily by the witness type and
|
|
|
|
// sign descriptor. The method then returns the witness computed by invoking
|
|
|
|
// this function on the first and subsequent calls.
|
2019-01-16 15:47:43 +01:00
|
|
|
func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
|
2022-03-18 18:37:44 +01:00
|
|
|
hashCache *txscript.TxSigHashes,
|
|
|
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
|
|
|
txinIdx int) (*input.Script, error) {
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
|
|
|
|
// First, we ensure that the witness generation function has been
|
|
|
|
// initialized for this breached output.
|
2022-03-18 18:37:44 +01:00
|
|
|
signDesc := bo.SignDesc()
|
|
|
|
signDesc.PrevOutputFetcher = prevOutputFetcher
|
|
|
|
bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, signDesc)
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Now that we have ensured that the witness generation function has
|
|
|
|
// been initialized, we can proceed to execute it and generate the
|
|
|
|
// witness for this particular breached output.
|
|
|
|
return bo.witnessFunc(txn, hashCache, txinIdx)
|
2017-07-26 05:14:03 +02:00
|
|
|
}
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2018-09-26 17:59:30 +02:00
|
|
|
// BlocksToMaturity returns the relative timelock, as a number of blocks, that
|
|
|
|
// must be built on top of the confirmation height before the output can be
|
|
|
|
// spent.
|
|
|
|
func (bo *breachedOutput) BlocksToMaturity() uint32 {
|
2020-03-06 16:11:47 +01:00
|
|
|
// If the output is a to_remote output we can claim, and it's of the
|
2023-03-02 07:17:30 +01:00
|
|
|
// confirmed type (or is a taproot channel that always has the CSV 1),
|
|
|
|
// we must wait one block before claiming it.
|
|
|
|
switch bo.witnessType {
|
|
|
|
case input.CommitmentToRemoteConfirmed, input.TaprootRemoteCommitSpend:
|
2020-03-06 16:11:47 +01:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// All other breached outputs have no CSV delay.
|
2018-09-26 17:59:30 +02:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2018-11-07 16:35:54 +01:00
|
|
|
// HeightHint returns the minimum height at which a confirmed spending tx can
|
|
|
|
// occur.
|
|
|
|
func (bo *breachedOutput) HeightHint() uint32 {
|
|
|
|
return bo.confHeight
|
|
|
|
}
|
|
|
|
|
2020-09-04 11:28:17 +02:00
|
|
|
// UnconfParent returns information about a possibly unconfirmed parent tx.
|
|
|
|
func (bo *breachedOutput) UnconfParent() *input.TxInfo {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-04 08:02:57 +02:00
|
|
|
// ResolutionBlob returns a special opaque blob to be used to sweep/resolve this
|
|
|
|
// input.
|
|
|
|
func (bo *breachedOutput) ResolutionBlob() fn.Option[tlv.Blob] {
|
|
|
|
return bo.resolutionBlob
|
|
|
|
}
|
|
|
|
|
2018-09-26 17:59:30 +02:00
|
|
|
// Add compile-time constraint ensuring breachedOutput implements the Input
|
|
|
|
// interface.
|
2019-01-16 15:47:43 +01:00
|
|
|
var _ input.Input = (*breachedOutput)(nil)
|
2017-08-22 01:56:58 +02:00
|
|
|
|
2017-07-26 05:14:03 +02:00
|
|
|
// retributionInfo encapsulates all the data needed to sweep all the contested
|
|
|
|
// funds within a channel whose contract has been breached by the prior
|
|
|
|
// counterparty. This struct is used to create the justice transaction which
|
|
|
|
// spends all outputs of the commitment transaction into an output controlled
|
|
|
|
// by the wallet.
|
|
|
|
type retributionInfo struct {
|
2017-11-21 08:56:42 +01:00
|
|
|
commitHash chainhash.Hash
|
|
|
|
chanPoint wire.OutPoint
|
|
|
|
chainHash chainhash.Hash
|
|
|
|
breachHeight uint32
|
2017-07-26 07:57:29 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
breachedOutputs []breachedOutput
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// newRetributionInfo constructs a retributionInfo containing all the
|
|
|
|
// information required by the breach arbiter to recover funds from breached
|
|
|
|
// channels. The information is primarily populated using the BreachRetribution
|
|
|
|
// delivered by the wallet when it detects a channel breach.
|
|
|
|
func newRetributionInfo(chanPoint *wire.OutPoint,
|
2017-11-21 08:56:42 +01:00
|
|
|
breachInfo *lnwallet.BreachRetribution) *retributionInfo {
|
2017-08-22 01:56:58 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// Determine the number of second layer HTLCs we will attempt to sweep.
|
|
|
|
nHtlcs := len(breachInfo.HtlcRetributions)
|
|
|
|
|
|
|
|
// Initialize a slice to hold the outputs we will attempt to sweep. The
|
|
|
|
// maximum capacity of the slice is set to 2+nHtlcs to handle the case
|
|
|
|
// where the local, remote, and all HTLCs are not dust outputs. All
|
|
|
|
// HTLC outputs provided by the wallet are guaranteed to be non-dust,
|
|
|
|
// though the commitment outputs are conditionally added depending on
|
|
|
|
// the nil-ness of their sign descriptors.
|
|
|
|
breachedOutputs := make([]breachedOutput, 0, nHtlcs+2)
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
isTaproot := func() bool {
|
|
|
|
if breachInfo.LocalOutputSignDesc != nil {
|
|
|
|
return txscript.IsPayToTaproot(
|
|
|
|
breachInfo.LocalOutputSignDesc.Output.PkScript,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return txscript.IsPayToTaproot(
|
|
|
|
breachInfo.RemoteOutputSignDesc.Output.PkScript,
|
|
|
|
)
|
|
|
|
}()
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// First, record the breach information for the local channel point if
|
|
|
|
// it is not considered dust, which is signaled by a non-nil sign
|
2019-08-08 05:29:15 +02:00
|
|
|
// descriptor. Here we use CommitmentNoDelay (or
|
|
|
|
// CommitmentNoDelayTweakless for newer commitments) since this output
|
2023-03-02 07:17:30 +01:00
|
|
|
// belongs to us and has no time-based constraints on spending. For
|
|
|
|
// taproot channels, this is a normal spend from our output on the
|
|
|
|
// commitment of the remote party.
|
2017-09-19 01:32:20 +02:00
|
|
|
if breachInfo.LocalOutputSignDesc != nil {
|
2023-03-02 07:17:30 +01:00
|
|
|
var witnessType input.StandardWitnessType
|
|
|
|
switch {
|
|
|
|
case isTaproot:
|
|
|
|
witnessType = input.TaprootRemoteCommitSpend
|
|
|
|
|
2023-08-09 07:22:12 +02:00
|
|
|
case !isTaproot &&
|
|
|
|
breachInfo.LocalOutputSignDesc.SingleTweak == nil:
|
|
|
|
|
2019-08-08 05:29:15 +02:00
|
|
|
witnessType = input.CommitSpendNoDelayTweakless
|
2023-03-02 07:17:30 +01:00
|
|
|
|
|
|
|
case !isTaproot:
|
|
|
|
witnessType = input.CommitmentNoDelay
|
2019-08-08 05:29:15 +02:00
|
|
|
}
|
|
|
|
|
2020-03-06 16:11:47 +01:00
|
|
|
// If the local delay is non-zero, it means this output is of
|
|
|
|
// the confirmed to_remote type.
|
2023-03-02 07:17:30 +01:00
|
|
|
if !isTaproot && breachInfo.LocalDelay != 0 {
|
2020-03-06 16:11:47 +01:00
|
|
|
witnessType = input.CommitmentToRemoteConfirmed
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
localOutput := makeBreachedOutput(
|
|
|
|
&breachInfo.LocalOutpoint,
|
2019-08-08 05:29:15 +02:00
|
|
|
witnessType,
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
// No second level script as this is a commitment
|
|
|
|
// output.
|
|
|
|
nil,
|
2018-11-07 16:35:54 +01:00
|
|
|
breachInfo.LocalOutputSignDesc,
|
2019-08-08 05:29:15 +02:00
|
|
|
breachInfo.BreachHeight,
|
2024-06-24 08:32:11 +02:00
|
|
|
breachInfo.LocalResolutionBlob,
|
2019-08-08 05:29:15 +02:00
|
|
|
)
|
2017-09-19 01:32:20 +02:00
|
|
|
|
|
|
|
breachedOutputs = append(breachedOutputs, localOutput)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Second, record the same information regarding the remote outpoint,
|
|
|
|
// again if it is not dust, which belongs to the party who tried to
|
|
|
|
// steal our money! Here we set witnessType of the breachedOutput to
|
2017-08-22 01:56:58 +02:00
|
|
|
// CommitmentRevoke, since we will be using a revoke key, withdrawing
|
|
|
|
// the funds from the commitment transaction immediately.
|
2017-09-19 01:32:20 +02:00
|
|
|
if breachInfo.RemoteOutputSignDesc != nil {
|
2023-03-02 07:17:30 +01:00
|
|
|
var witType input.StandardWitnessType
|
|
|
|
if isTaproot {
|
|
|
|
witType = input.TaprootCommitmentRevoke
|
|
|
|
} else {
|
|
|
|
witType = input.CommitmentRevoke
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
remoteOutput := makeBreachedOutput(
|
|
|
|
&breachInfo.RemoteOutpoint,
|
2023-03-02 07:17:30 +01:00
|
|
|
witType,
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
// No second level script as this is a commitment
|
|
|
|
// output.
|
|
|
|
nil,
|
2018-11-07 16:35:54 +01:00
|
|
|
breachInfo.RemoteOutputSignDesc,
|
2019-08-08 05:29:15 +02:00
|
|
|
breachInfo.BreachHeight,
|
2024-06-24 08:32:11 +02:00
|
|
|
breachInfo.RemoteResolutionBlob,
|
2019-08-08 05:29:15 +02:00
|
|
|
)
|
2017-08-22 01:56:58 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
breachedOutputs = append(breachedOutputs, remoteOutput)
|
|
|
|
}
|
2017-08-22 01:56:58 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// Lastly, for each of the breached HTLC outputs, record each as a
|
|
|
|
// breached output with the appropriate witness type based on its
|
|
|
|
// directionality. All HTLC outputs provided by the wallet are assumed
|
|
|
|
// to be non-dust.
|
2017-08-22 01:56:58 +02:00
|
|
|
for i, breachedHtlc := range breachInfo.HtlcRetributions {
|
2017-09-01 12:11:14 +02:00
|
|
|
// Using the breachedHtlc's incoming flag, determine the
|
|
|
|
// appropriate witness type that needs to be generated in order
|
|
|
|
// to sweep the HTLC output.
|
2019-10-07 13:41:46 +02:00
|
|
|
var htlcWitnessType input.StandardWitnessType
|
2023-03-02 07:17:30 +01:00
|
|
|
switch {
|
|
|
|
case isTaproot && breachedHtlc.IsIncoming:
|
|
|
|
htlcWitnessType = input.TaprootHtlcAcceptedRevoke
|
|
|
|
|
|
|
|
case isTaproot && !breachedHtlc.IsIncoming:
|
|
|
|
htlcWitnessType = input.TaprootHtlcOfferedRevoke
|
|
|
|
|
|
|
|
case !isTaproot && breachedHtlc.IsIncoming:
|
2019-01-16 15:47:43 +01:00
|
|
|
htlcWitnessType = input.HtlcAcceptedRevoke
|
2023-03-02 07:17:30 +01:00
|
|
|
|
|
|
|
case !isTaproot && !breachedHtlc.IsIncoming:
|
2019-01-16 15:47:43 +01:00
|
|
|
htlcWitnessType = input.HtlcOfferedRevoke
|
2017-09-01 12:11:14 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
htlcOutput := makeBreachedOutput(
|
|
|
|
&breachInfo.HtlcRetributions[i].OutPoint,
|
|
|
|
htlcWitnessType,
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
breachInfo.HtlcRetributions[i].SecondLevelWitnessScript,
|
2018-11-07 16:35:54 +01:00
|
|
|
&breachInfo.HtlcRetributions[i].SignDesc,
|
2023-03-02 07:17:30 +01:00
|
|
|
breachInfo.BreachHeight,
|
2024-06-24 08:32:11 +02:00
|
|
|
breachInfo.HtlcRetributions[i].ResolutionBlob,
|
2023-03-02 07:17:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// For taproot outputs, we also need to hold onto the second
|
|
|
|
// level tap tweak as well.
|
|
|
|
//nolint:lll
|
|
|
|
htlcOutput.secondLevelTapTweak = breachedHtlc.SecondLevelTapTweak
|
2017-09-19 01:32:20 +02:00
|
|
|
|
|
|
|
breachedOutputs = append(breachedOutputs, htlcOutput)
|
2017-08-22 01:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &retributionInfo{
|
2022-04-10 19:58:55 +02:00
|
|
|
commitHash: breachInfo.BreachTxHash,
|
2017-11-21 08:56:42 +01:00
|
|
|
chainHash: breachInfo.ChainHash,
|
2017-09-19 01:32:20 +02:00
|
|
|
chanPoint: *chanPoint,
|
|
|
|
breachedOutputs: breachedOutputs,
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
breachHeight: breachInfo.BreachHeight,
|
2017-08-22 01:56:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 12:36:45 +01:00
|
|
|
// justiceTxVariants is a struct that holds transactions which exacts "justice"
|
|
|
|
// by sweeping ALL the funds within the channel which we are now entitled to
|
|
|
|
// due to a breach of the channel's contract by the counterparty. There are
|
2023-03-02 07:17:30 +01:00
|
|
|
// four variants of justice transactions:
|
2021-02-12 12:36:45 +01:00
|
|
|
//
|
2023-03-02 07:17:30 +01:00
|
|
|
// 1. The "normal" justice tx that spends all breached outputs.
|
2021-02-12 12:36:45 +01:00
|
|
|
// 2. A tx that spends only the breached to_local output and to_remote output
|
2023-03-02 07:17:30 +01:00
|
|
|
// (can be nil if none of these exist).
|
|
|
|
// 3. A tx that spends all the breached commitment level HTLC outputs (can be
|
|
|
|
// nil if none of these exist or if all have been taken to the second level).
|
|
|
|
// 4. A set of txs that spend all the second-level HTLC outputs (can be empty if
|
|
|
|
// no HTLC second-level txs have been confirmed).
|
2021-02-12 12:36:45 +01:00
|
|
|
//
|
|
|
|
// The reason we create these three variants, is that in certain cases (like
|
|
|
|
// with the anchor output HTLC malleability), the channel counter party can pin
|
|
|
|
// the HTLC outputs with low fee children, hindering our normal justice tx that
|
|
|
|
// attempts to spend these outputs from propagating. In this case we want to
|
2023-03-02 07:17:30 +01:00
|
|
|
// spend the to_local output and commitment level HTLC outputs separately,
|
|
|
|
// before the CSV locks expire.
|
2021-02-12 12:36:45 +01:00
|
|
|
type justiceTxVariants struct {
|
2024-06-24 08:33:27 +02:00
|
|
|
spendAll *justiceTxCtx
|
|
|
|
spendCommitOuts *justiceTxCtx
|
|
|
|
spendHTLCs *justiceTxCtx
|
|
|
|
spendSecondLevelHTLCs []*justiceTxCtx
|
2021-02-12 12:36:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// createJusticeTx creates transactions which exacts "justice" by sweeping ALL
|
2016-11-29 04:43:57 +01:00
|
|
|
// the funds within the channel which we are now entitled to due to a breach of
|
2017-01-13 06:01:50 +01:00
|
|
|
// the channel's contract by the counterparty. This function returns a *fully*
|
2016-11-29 04:43:57 +01:00
|
|
|
// signed transaction with the witness for each input fully in place.
|
2024-02-01 01:42:29 +01:00
|
|
|
func (b *BreachArbitrator) createJusticeTx(
|
2021-02-12 12:36:45 +01:00
|
|
|
breachedOutputs []breachedOutput) (*justiceTxVariants, error) {
|
|
|
|
|
|
|
|
var (
|
2023-03-02 07:17:30 +01:00
|
|
|
allInputs []input.Input
|
|
|
|
commitInputs []input.Input
|
|
|
|
htlcInputs []input.Input
|
|
|
|
secondLevelInputs []input.Input
|
2021-02-12 12:36:45 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
for i := range breachedOutputs {
|
|
|
|
// Grab locally scoped reference to breached output.
|
|
|
|
inp := &breachedOutputs[i]
|
|
|
|
allInputs = append(allInputs, inp)
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// Check if the input is from a commitment output, a commitment
|
|
|
|
// level HTLC output or a second level HTLC output.
|
|
|
|
switch inp.WitnessType() {
|
|
|
|
case input.HtlcAcceptedRevoke, input.HtlcOfferedRevoke,
|
|
|
|
input.TaprootHtlcAcceptedRevoke,
|
|
|
|
input.TaprootHtlcOfferedRevoke:
|
2021-02-12 12:36:45 +01:00
|
|
|
|
|
|
|
htlcInputs = append(htlcInputs, inp)
|
2023-03-02 07:17:30 +01:00
|
|
|
|
|
|
|
case input.HtlcSecondLevelRevoke,
|
|
|
|
input.TaprootHtlcSecondLevelRevoke:
|
|
|
|
|
|
|
|
secondLevelInputs = append(secondLevelInputs, inp)
|
|
|
|
|
|
|
|
default:
|
2021-02-12 12:36:45 +01:00
|
|
|
commitInputs = append(commitInputs, inp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
txs = &justiceTxVariants{}
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// For each group of inputs, create a tx that spends them.
|
2023-03-02 07:17:30 +01:00
|
|
|
txs.spendAll, err = b.createSweepTx(allInputs...)
|
2021-02-12 12:36:45 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
txs.spendCommitOuts, err = b.createSweepTx(commitInputs...)
|
2021-02-12 12:36:45 +01:00
|
|
|
if err != nil {
|
2023-03-02 07:17:30 +01:00
|
|
|
brarLog.Errorf("could not create sweep tx for commitment "+
|
|
|
|
"outputs: %v", err)
|
2021-02-12 12:36:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
txs.spendHTLCs, err = b.createSweepTx(htlcInputs...)
|
2021-02-12 12:36:45 +01:00
|
|
|
if err != nil {
|
2023-03-02 07:17:30 +01:00
|
|
|
brarLog.Errorf("could not create sweep tx for HTLC outputs: %v",
|
|
|
|
err)
|
2021-02-12 12:36:45 +01:00
|
|
|
}
|
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// TODO(roasbeef): only register one of them?
|
|
|
|
|
|
|
|
secondLevelSweeps := make([]*justiceTxCtx, 0, len(secondLevelInputs))
|
2023-03-02 07:17:30 +01:00
|
|
|
for _, input := range secondLevelInputs {
|
|
|
|
sweepTx, err := b.createSweepTx(input)
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Errorf("could not create sweep tx for "+
|
|
|
|
"second-level HTLC output: %v", err)
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
secondLevelSweeps = append(secondLevelSweeps, sweepTx)
|
|
|
|
}
|
|
|
|
txs.spendSecondLevelHTLCs = secondLevelSweeps
|
|
|
|
|
2021-02-12 12:36:45 +01:00
|
|
|
return txs, nil
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// justiceTxCtx contains the justice transaction along with other related meta
|
|
|
|
// data.
|
|
|
|
type justiceTxCtx struct {
|
|
|
|
justiceTx *wire.MsgTx
|
|
|
|
|
|
|
|
sweepAddr lnwallet.AddrWithKey
|
|
|
|
|
|
|
|
extraTxOut fn.Option[sweep.SweepOutput]
|
|
|
|
|
|
|
|
fee btcutil.Amount
|
|
|
|
|
|
|
|
inputs []input.Input
|
|
|
|
}
|
|
|
|
|
2021-02-12 12:36:45 +01:00
|
|
|
// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
|
2024-06-24 08:33:27 +02:00
|
|
|
func (b *BreachArbitrator) createSweepTx(
|
|
|
|
inputs ...input.Input) (*justiceTxCtx, error) {
|
2022-02-07 13:58:28 +01:00
|
|
|
|
2021-02-12 12:36:45 +01:00
|
|
|
if len(inputs) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2017-07-26 07:57:29 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// We will assemble the breached outputs into a slice of spendable
|
|
|
|
// outputs, while simultaneously computing the estimated weight of the
|
|
|
|
// transaction.
|
|
|
|
var (
|
2019-01-16 15:47:43 +01:00
|
|
|
spendableOutputs []input.Input
|
|
|
|
weightEstimate input.TxWeightEstimator
|
2017-09-19 01:32:20 +02:00
|
|
|
)
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// Allocate enough space to potentially hold each of the breached
|
|
|
|
// outputs in the retribution info.
|
2021-02-12 12:36:45 +01:00
|
|
|
spendableOutputs = make([]input.Input, 0, len(inputs))
|
2017-09-19 01:32:20 +02:00
|
|
|
|
|
|
|
// The justice transaction we construct will be a segwit transaction
|
2022-08-11 03:25:35 +02:00
|
|
|
// that pays to a p2tr output. Components such as the version,
|
2017-09-26 05:13:53 +02:00
|
|
|
// nLockTime, and output are already included in the TxWeightEstimator.
|
2022-08-11 03:25:35 +02:00
|
|
|
weightEstimate.AddP2TROutput()
|
2017-09-01 12:11:14 +02:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// If any of our inputs has a resolution blob, then we'll add another
|
|
|
|
// P2TR _output_, since we'll want to separate the custom channel
|
|
|
|
// outputs from the regular, BTC only outputs. So we only need one such
|
|
|
|
// output, which'll carry the custom channel "valuables" from both the
|
|
|
|
// breached commitment and HTLC outputs.
|
|
|
|
hasBlobs := fn.Any(func(i input.Input) bool {
|
|
|
|
return i.ResolutionBlob().IsSome()
|
|
|
|
}, inputs)
|
|
|
|
if hasBlobs {
|
|
|
|
weightEstimate.AddP2TROutput()
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
// Next, we iterate over the breached outputs contained in the
|
|
|
|
// retribution info. For each, we switch over the witness type such
|
2021-02-12 12:36:45 +01:00
|
|
|
// that we contribute the appropriate weight for each input and
|
|
|
|
// witness, finally adding to our list of spendable outputs.
|
|
|
|
for i := range inputs {
|
2017-09-19 01:32:20 +02:00
|
|
|
// Grab locally scoped reference to breached output.
|
2021-02-12 12:36:45 +01:00
|
|
|
inp := inputs[i]
|
2017-09-19 01:32:20 +02:00
|
|
|
|
2021-02-12 12:36:45 +01:00
|
|
|
// First, determine the appropriate estimated witness weight
|
|
|
|
// for the give witness type of this breached output. If the
|
|
|
|
// witness weight cannot be estimated, we will omit it from the
|
2019-10-07 13:41:46 +02:00
|
|
|
// transaction.
|
|
|
|
witnessWeight, _, err := inp.WitnessType().SizeUpperBound()
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Warnf("could not determine witness weight "+
|
|
|
|
"for breached output in retribution info: %v",
|
|
|
|
err)
|
2017-09-19 01:32:20 +02:00
|
|
|
continue
|
2017-09-01 12:11:14 +02:00
|
|
|
}
|
2017-09-26 05:13:53 +02:00
|
|
|
weightEstimate.AddWitnessInput(witnessWeight)
|
2017-09-19 01:32:20 +02:00
|
|
|
|
|
|
|
// Finally, append this input to our list of spendable outputs.
|
2019-01-16 15:47:43 +01:00
|
|
|
spendableOutputs = append(spendableOutputs, inp)
|
2017-09-01 12:11:14 +02:00
|
|
|
}
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2024-05-24 15:56:30 +02:00
|
|
|
txWeight := weightEstimate.Weight()
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2018-07-28 03:27:51 +02:00
|
|
|
return b.sweepSpendableOutputsTxn(txWeight, spendableOutputs...)
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
2017-07-31 02:45:39 +02:00
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
|
|
|
|
// spendable outputs by sweeping the funds into a single p2wkh output.
|
2024-05-24 15:56:30 +02:00
|
|
|
func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
|
2024-06-24 08:33:27 +02:00
|
|
|
inputs ...input.Input) (*justiceTxCtx, error) {
|
2017-08-22 01:56:58 +02:00
|
|
|
|
|
|
|
// First, we obtain a new public key script from the wallet which we'll
|
|
|
|
// sweep the funds to.
|
|
|
|
// TODO(roasbeef): possibly create many outputs to minimize change in
|
|
|
|
// the future?
|
2024-06-24 08:30:52 +02:00
|
|
|
pkScript, err := b.cfg.GenSweepScript().Unpack()
|
2017-07-31 02:45:39 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Compute the total amount contained in the inputs.
|
|
|
|
var totalAmt btcutil.Amount
|
2022-03-18 18:37:44 +01:00
|
|
|
for _, inp := range inputs {
|
|
|
|
totalAmt += btcutil.Amount(inp.SignDesc().Output.Value)
|
2017-07-31 02:45:39 +02:00
|
|
|
}
|
|
|
|
|
2017-11-23 20:41:20 +01:00
|
|
|
// We'll actually attempt to target inclusion within the next two
|
|
|
|
// blocks as we'd like to sweep these funds back into our wallet ASAP.
|
2021-02-15 13:31:08 +01:00
|
|
|
feePerKw, err := b.cfg.Estimator.EstimateFeePerKW(justiceTxConfTarget)
|
2017-11-23 20:41:20 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-28 03:27:51 +02:00
|
|
|
txFee := feePerKw.FeeForWeight(txWeight)
|
2017-08-22 01:56:58 +02:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// At this point, we'll check to see if we have any extra outputs to
|
|
|
|
// add from the aux sweeper.
|
|
|
|
extraChangeOut := fn.MapOptionZ(
|
|
|
|
b.cfg.AuxSweeper,
|
|
|
|
func(aux sweep.AuxSweeper) fn.Result[sweep.SweepOutput] {
|
|
|
|
return aux.DeriveSweepAddr(inputs, pkScript)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err := extraChangeOut.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
// TODO(roasbeef): already start to siphon their funds into fees
|
2017-08-22 01:56:58 +02:00
|
|
|
sweepAmt := int64(totalAmt - txFee)
|
|
|
|
|
|
|
|
// With the fee calculated, we can now create the transaction using the
|
|
|
|
// information gathered above and the provided retribution information.
|
2017-09-01 12:11:14 +02:00
|
|
|
txn := wire.NewMsgTx(2)
|
2017-08-22 01:56:58 +02:00
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// First, we'll add the extra sweep output if it exists, subtracting the
|
|
|
|
// amount from the sweep amt.
|
|
|
|
if b.cfg.AuxSweeper.IsSome() {
|
|
|
|
extraChangeOut.WhenResult(func(o sweep.SweepOutput) {
|
|
|
|
sweepAmt -= o.Value
|
|
|
|
|
|
|
|
txn.AddTxOut(&o.TxOut)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll add the output to which our funds will be deposited.
|
2017-08-22 01:56:58 +02:00
|
|
|
txn.AddTxOut(&wire.TxOut{
|
2024-06-24 08:30:52 +02:00
|
|
|
PkScript: pkScript.DeliveryAddress,
|
2017-08-22 01:56:58 +02:00
|
|
|
Value: sweepAmt,
|
2017-07-31 02:45:39 +02:00
|
|
|
})
|
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
// TODO(roasbeef): add other output change modify sweep amt
|
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Next, we add all of the spendable outputs as inputs to the
|
|
|
|
// transaction.
|
2022-03-18 18:37:44 +01:00
|
|
|
for _, inp := range inputs {
|
2017-08-22 01:56:58 +02:00
|
|
|
txn.AddTxIn(&wire.TxIn{
|
2024-03-27 10:07:48 +01:00
|
|
|
PreviousOutPoint: inp.OutPoint(),
|
2022-03-18 18:37:44 +01:00
|
|
|
Sequence: inp.BlocksToMaturity(),
|
2017-08-22 01:56:58 +02:00
|
|
|
})
|
2017-07-31 02:45:39 +02:00
|
|
|
}
|
|
|
|
|
2017-08-30 04:07:52 +02:00
|
|
|
// Before signing the transaction, check to ensure that it meets some
|
|
|
|
// basic validity requirements.
|
|
|
|
btx := btcutil.NewTx(txn)
|
|
|
|
if err := blockchain.CheckTransactionSanity(btx); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Create a sighash cache to improve the performance of hashing and
|
|
|
|
// signing SigHashAll inputs.
|
2022-03-18 18:37:44 +01:00
|
|
|
prevOutputFetcher, err := input.MultiPrevOutFetcher(inputs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hashCache := txscript.NewTxSigHashes(txn, prevOutputFetcher)
|
2017-08-22 01:56:58 +02:00
|
|
|
|
|
|
|
// Create a closure that encapsulates the process of initializing a
|
|
|
|
// particular output's witness generation function, computing the
|
|
|
|
// witness, and attaching it to the transaction. This function accepts
|
|
|
|
// an integer index representing the intended txin index, and the
|
|
|
|
// breached output from which it will spend.
|
2019-01-16 15:47:43 +01:00
|
|
|
addWitness := func(idx int, so input.Input) error {
|
2017-08-22 01:56:58 +02:00
|
|
|
// First, we construct a valid witness for this outpoint and
|
|
|
|
// transaction using the SpendableOutput's witness generation
|
|
|
|
// function.
|
2018-11-18 05:48:41 +01:00
|
|
|
inputScript, err := so.CraftInputScript(
|
2022-03-18 18:37:44 +01:00
|
|
|
b.cfg.Signer, txn, hashCache, prevOutputFetcher, idx,
|
2018-11-18 05:48:41 +01:00
|
|
|
)
|
2017-08-22 01:56:58 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-07-31 02:45:39 +02:00
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Then, we add the witness to the transaction at the
|
|
|
|
// appropriate txin index.
|
2018-11-18 05:48:41 +01:00
|
|
|
txn.TxIn[idx].Witness = inputScript.Witness
|
2017-07-31 02:45:39 +02:00
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
return nil
|
|
|
|
}
|
2017-07-31 02:45:39 +02:00
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Finally, generate a witness for each output and attach it to the
|
|
|
|
// transaction.
|
2022-03-18 18:37:44 +01:00
|
|
|
for i, inp := range inputs {
|
|
|
|
if err := addWitness(i, inp); err != nil {
|
2017-08-22 01:56:58 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:33:27 +02:00
|
|
|
return &justiceTxCtx{
|
|
|
|
justiceTx: txn,
|
|
|
|
sweepAddr: pkScript,
|
|
|
|
extraTxOut: extraChangeOut.Option(),
|
|
|
|
fee: txFee,
|
|
|
|
inputs: inputs,
|
|
|
|
}, nil
|
2017-07-31 02:45:39 +02:00
|
|
|
}
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
// RetributionStore handles persistence of retribution states to disk and is
|
2017-05-07 13:09:22 +02:00
|
|
|
// 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
|
|
|
|
// breached contract retribution.
|
2021-09-14 04:00:36 +02:00
|
|
|
type RetributionStore struct {
|
2021-09-21 19:18:17 +02:00
|
|
|
db kvdb.Backend
|
2017-05-07 13:09:22 +02:00
|
|
|
}
|
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
// NewRetributionStore creates a new instance of a RetributionStore.
|
2021-09-21 19:18:17 +02:00
|
|
|
func NewRetributionStore(db kvdb.Backend) *RetributionStore {
|
2021-09-14 04:00:36 +02:00
|
|
|
return &RetributionStore{
|
2017-05-07 13:09:22 +02:00
|
|
|
db: db,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// taprootBriefcaseFromRetInfo creates a taprootBriefcase from a retribution
|
|
|
|
// info struct. This stores all the tap tweak informatoin we need to inrder to
|
|
|
|
// be able to hadnel breaches after a restart.
|
|
|
|
func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase {
|
|
|
|
tapCase := newTaprootBriefcase()
|
|
|
|
|
|
|
|
for _, bo := range retInfo.breachedOutputs {
|
|
|
|
switch bo.WitnessType() {
|
|
|
|
// For spending from our commitment output on the remote
|
|
|
|
// commitment, we'll need to stash the control block.
|
|
|
|
case input.TaprootRemoteCommitSpend:
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = bo.signDesc.ControlBlock
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2024-06-24 08:32:11 +02:00
|
|
|
bo.resolutionBlob.WhenSome(func(blob tlv.Blob) {
|
|
|
|
tapCase.SettledCommitBlob = tlv.SomeRecordT(
|
|
|
|
tlv.NewPrimitiveRecord[tlv.TlvType2](
|
|
|
|
blob,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// To spend the revoked output again, we'll store the same
|
|
|
|
// control block value as above, but in a different place.
|
|
|
|
case input.TaprootCommitmentRevoke:
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2024-06-24 08:32:11 +02:00
|
|
|
bo.resolutionBlob.WhenSome(func(blob tlv.Blob) {
|
|
|
|
tapCase.BreachedCommitBlob = tlv.SomeRecordT(
|
|
|
|
tlv.NewPrimitiveRecord[tlv.TlvType3](
|
|
|
|
blob,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// For spending the HTLC outputs, we'll store the first and
|
|
|
|
// second level tweak values.
|
|
|
|
case input.TaprootHtlcAcceptedRevoke:
|
|
|
|
fallthrough
|
|
|
|
case input.TaprootHtlcOfferedRevoke:
|
2024-03-27 10:07:48 +01:00
|
|
|
resID := newResolverID(bo.OutPoint())
|
2023-03-02 07:17:30 +01:00
|
|
|
|
|
|
|
var firstLevelTweak [32]byte
|
|
|
|
copy(firstLevelTweak[:], bo.signDesc.TapTweak)
|
|
|
|
secondLevelTweak := bo.secondLevelTapTweak
|
|
|
|
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
tapCase.TapTweaks.Val.BreachedHtlcTweaks[resID] = firstLevelTweak
|
2023-03-02 07:17:30 +01:00
|
|
|
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
tapCase.TapTweaks.Val.BreachedSecondLevelHltcTweaks[resID] = secondLevelTweak
|
2023-03-02 07:17:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tapCase
|
|
|
|
}
|
|
|
|
|
|
|
|
// applyTaprootRetInfo attaches the taproot specific inforamtion in the tapCase
|
|
|
|
// to the passed retInfo struct.
|
2023-08-09 07:22:12 +02:00
|
|
|
func applyTaprootRetInfo(tapCase *taprootBriefcase,
|
|
|
|
retInfo *retributionInfo) error {
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
for i := range retInfo.breachedOutputs {
|
|
|
|
bo := retInfo.breachedOutputs[i]
|
|
|
|
|
|
|
|
switch bo.WitnessType() {
|
|
|
|
// For spending from our commitment output on the remote
|
|
|
|
// commitment, we'll apply the control block.
|
|
|
|
case input.TaprootRemoteCommitSpend:
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2024-06-24 08:32:11 +02:00
|
|
|
tapCase.SettledCommitBlob.WhenSomeV(
|
|
|
|
func(blob tlv.Blob) {
|
|
|
|
bo.resolutionBlob = fn.Some(blob)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// To spend the revoked output again, we'll apply the same
|
|
|
|
// control block value as above, but to a different place.
|
|
|
|
case input.TaprootCommitmentRevoke:
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2024-06-24 08:32:11 +02:00
|
|
|
tapCase.BreachedCommitBlob.WhenSomeV(
|
|
|
|
func(blob tlv.Blob) {
|
|
|
|
bo.resolutionBlob = fn.Some(blob)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
// For spending the HTLC outputs, we'll apply the first and
|
|
|
|
// second level tweak values.
|
|
|
|
case input.TaprootHtlcAcceptedRevoke:
|
|
|
|
fallthrough
|
|
|
|
case input.TaprootHtlcOfferedRevoke:
|
2024-03-27 10:07:48 +01:00
|
|
|
resID := newResolverID(bo.OutPoint())
|
2023-03-02 07:17:30 +01:00
|
|
|
|
2024-06-04 07:55:50 +02:00
|
|
|
//nolint:lll
|
|
|
|
tap1, ok := tapCase.TapTweaks.Val.BreachedHtlcTweaks[resID]
|
2023-03-02 07:17:30 +01:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unable to find taproot "+
|
|
|
|
"tweak for: %v", bo.OutPoint())
|
|
|
|
}
|
|
|
|
bo.signDesc.TapTweak = tap1[:]
|
|
|
|
|
|
|
|
//nolint:lll
|
2024-06-04 07:55:50 +02:00
|
|
|
tap2, ok := tapCase.TapTweaks.Val.BreachedSecondLevelHltcTweaks[resID]
|
2023-03-02 07:17:30 +01:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unable to find taproot "+
|
|
|
|
"tweak for: %v", bo.OutPoint())
|
|
|
|
}
|
|
|
|
bo.secondLevelTapTweak = tap2
|
|
|
|
}
|
|
|
|
|
|
|
|
retInfo.breachedOutputs[i] = bo
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
// Add adds a retribution state to the RetributionStore, which is then persisted
|
2017-05-07 13:09:22 +02:00
|
|
|
// to disk.
|
2021-09-14 04:00:36 +02:00
|
|
|
func (rs *RetributionStore) Add(ret *retributionInfo) error {
|
2020-01-10 03:47:38 +01:00
|
|
|
return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
|
2017-07-26 07:57:29 +02:00
|
|
|
// If this is our first contract breach, the retributionBucket
|
|
|
|
// won't exist, in which case, we just create a new bucket.
|
2020-01-10 03:47:38 +01:00
|
|
|
retBucket, err := tx.CreateTopLevelBucket(retributionBucket)
|
2017-05-07 13:09:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-02 07:17:30 +01:00
|
|
|
tapRetBucket, err := tx.CreateTopLevelBucket(
|
|
|
|
taprootRetributionBucket,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-07 13:09:22 +02:00
|
|
|
|
|
|
|
var outBuf bytes.Buffer
|
2017-07-26 05:39:59 +02:00
|
|
|
if err := writeOutpoint(&outBuf, &ret.chanPoint); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var retBuf bytes.Buffer
|
|
|
|
if err := ret.Encode(&retBuf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-08-09 07:22:12 +02:00
|
|
|
err = retBucket.Put(outBuf.Bytes(), retBuf.Bytes())
|
|
|
|
if err != nil {
|
2023-03-02 07:17:30 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this isn't a taproot channel, then we can exit early here
|
|
|
|
// as there's no extra data to write.
|
|
|
|
switch {
|
|
|
|
case len(ret.breachedOutputs) == 0:
|
|
|
|
return nil
|
|
|
|
|
|
|
|
case !txscript.IsPayToTaproot(
|
|
|
|
ret.breachedOutputs[0].signDesc.Output.PkScript,
|
|
|
|
):
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll also map the ret info into the taproot storage
|
|
|
|
// structure we need for taproot channels.
|
|
|
|
var b bytes.Buffer
|
|
|
|
tapRetcase := taprootBriefcaseFromRetInfo(ret)
|
|
|
|
if err := tapRetcase.Encode(&b); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tapRetBucket.Put(outBuf.Bytes(), b.Bytes())
|
2020-10-26 14:06:32 +01:00
|
|
|
}, func() {})
|
2017-05-07 13:09:22 +02:00
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
// IsBreached queries the retribution store to discern if this channel was
|
|
|
|
// 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
|
|
|
|
// that has already been breached.
|
2021-09-14 04:00:36 +02:00
|
|
|
func (rs *RetributionStore) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
2017-11-21 08:56:42 +01:00
|
|
|
var found bool
|
2020-05-07 00:45:50 +02:00
|
|
|
err := kvdb.View(rs.db, func(tx kvdb.RTx) error {
|
2020-01-10 03:47:38 +01:00
|
|
|
retBucket := tx.ReadBucket(retributionBucket)
|
2017-11-21 08:56:42 +01:00
|
|
|
if retBucket == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var chanBuf bytes.Buffer
|
|
|
|
if err := writeOutpoint(&chanBuf, chanPoint); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
retInfo := retBucket.Get(chanBuf.Bytes())
|
|
|
|
if retInfo != nil {
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-10-20 16:18:40 +02:00
|
|
|
}, func() {
|
|
|
|
found = false
|
2017-11-21 08:56:42 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
return found, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove removes a retribution state and finalized justice transaction by
|
|
|
|
// channel point from the retribution store.
|
2021-09-14 04:00:36 +02:00
|
|
|
func (rs *RetributionStore) Remove(chanPoint *wire.OutPoint) error {
|
2020-01-10 03:47:38 +01:00
|
|
|
return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
|
|
|
|
retBucket := tx.ReadWriteBucket(retributionBucket)
|
2023-03-02 07:17:30 +01:00
|
|
|
tapRetBucket, err := tx.CreateTopLevelBucket(
|
|
|
|
taprootRetributionBucket,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-07-26 07:57:29 +02:00
|
|
|
// We return an error if the bucket is not already created,
|
2023-03-02 07:17:30 +01:00
|
|
|
// since normal operation of the breach arbiter should never
|
|
|
|
// try to remove a finalized retribution state that is not
|
|
|
|
// already stored in the db.
|
2017-05-07 13:09:22 +02:00
|
|
|
if retBucket == nil {
|
2020-04-14 19:56:05 +02:00
|
|
|
return errors.New("unable to remove retribution " +
|
|
|
|
"because the retribution bucket doesn't exist")
|
2017-05-07 13:09:22 +02:00
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
// Serialize the channel point we are intending to remove.
|
|
|
|
var chanBuf bytes.Buffer
|
|
|
|
if err := writeOutpoint(&chanBuf, chanPoint); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-11-21 08:56:42 +01:00
|
|
|
chanBytes := chanBuf.Bytes()
|
|
|
|
|
|
|
|
// Remove the persisted retribution info and finalized justice
|
|
|
|
// transaction.
|
2023-03-02 07:17:30 +01:00
|
|
|
if err := retBucket.Delete(chanBytes); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tapRetBucket.Delete(chanBytes)
|
2020-10-26 14:06:32 +01:00
|
|
|
}, func() {})
|
2017-05-07 13:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ForAll iterates through all stored retributions and executes the passed
|
|
|
|
// callback function on each retribution.
|
2021-09-14 04:00:36 +02:00
|
|
|
func (rs *RetributionStore) ForAll(cb func(*retributionInfo) error,
|
2020-10-20 16:18:40 +02:00
|
|
|
reset func()) error {
|
|
|
|
|
2020-05-07 00:45:50 +02:00
|
|
|
return kvdb.View(rs.db, func(tx kvdb.RTx) error {
|
2017-07-26 07:57:29 +02:00
|
|
|
// If the bucket does not exist, then there are no pending
|
|
|
|
// retributions.
|
2020-01-10 03:47:38 +01:00
|
|
|
retBucket := tx.ReadBucket(retributionBucket)
|
2017-05-07 13:09:22 +02:00
|
|
|
if retBucket == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-03-02 07:17:30 +01:00
|
|
|
tapRetBucket := tx.ReadBucket(
|
|
|
|
taprootRetributionBucket,
|
|
|
|
)
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-07-26 07:57:29 +02:00
|
|
|
// Otherwise, we fetch each serialized retribution info,
|
|
|
|
// deserialize it, and execute the passed in callback function
|
|
|
|
// on it.
|
2023-03-02 07:17:30 +01:00
|
|
|
return retBucket.ForEach(func(k, retBytes []byte) error {
|
2017-07-26 05:14:03 +02:00
|
|
|
ret := &retributionInfo{}
|
2017-11-21 08:56:42 +01:00
|
|
|
err := ret.Decode(bytes.NewBuffer(retBytes))
|
|
|
|
if err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-03-02 07:17:30 +01:00
|
|
|
tapInfoBytes := tapRetBucket.Get(k)
|
|
|
|
if tapInfoBytes != nil {
|
|
|
|
var tapCase taprootBriefcase
|
|
|
|
err := tapCase.Decode(
|
|
|
|
bytes.NewReader(tapInfoBytes),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = applyTaprootRetInfo(&tapCase, ret)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-07 13:09:22 +02:00
|
|
|
return cb(ret)
|
|
|
|
})
|
2020-10-20 16:18:40 +02:00
|
|
|
}, reset)
|
2017-05-07 13:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Encode serializes the retribution into the passed byte stream.
|
2017-07-26 05:14:03 +02:00
|
|
|
func (ret *retributionInfo) Encode(w io.Writer) error {
|
2017-11-21 08:56:42 +01:00
|
|
|
var scratch [4]byte
|
2017-07-26 07:57:29 +02:00
|
|
|
|
2017-05-07 13:09:22 +02:00
|
|
|
if _, err := w.Write(ret.commitHash[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-07-26 05:39:59 +02:00
|
|
|
if err := writeOutpoint(w, &ret.chanPoint); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-11 00:23:02 +01:00
|
|
|
if _, err := w.Write(ret.chainHash[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
binary.BigEndian.PutUint32(scratch[:], ret.breachHeight)
|
|
|
|
if _, err := w.Write(scratch[:]); err != nil {
|
2017-07-26 07:57:29 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
nOutputs := len(ret.breachedOutputs)
|
|
|
|
if err := wire.WriteVarInt(w, 0, uint64(nOutputs)); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
for _, output := range ret.breachedOutputs {
|
|
|
|
if err := output.Encode(w); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-03-18 18:37:44 +01:00
|
|
|
// Decode deserializes a retribution from the passed byte stream.
|
2017-07-26 05:14:03 +02:00
|
|
|
func (ret *retributionInfo) Decode(r io.Reader) error {
|
2017-11-21 08:56:42 +01:00
|
|
|
var scratch [32]byte
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-11-21 08:56:42 +01:00
|
|
|
hash, err := chainhash.NewHash(scratch[:])
|
2017-05-07 13:09:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ret.commitHash = *hash
|
|
|
|
|
2017-07-26 05:39:59 +02:00
|
|
|
if err := readOutpoint(r, &ret.chanPoint); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
2017-11-11 00:23:02 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-11-21 08:56:42 +01:00
|
|
|
chainHash, err := chainhash.NewHash(scratch[:])
|
2017-11-11 00:23:02 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ret.chainHash = *chainHash
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
if _, err := io.ReadFull(r, scratch[:4]); err != nil {
|
2017-07-26 07:57:29 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-11-21 08:56:42 +01:00
|
|
|
ret.breachHeight = binary.BigEndian.Uint32(scratch[:4])
|
2017-07-26 07:57:29 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
nOutputsU64, err := wire.ReadVarInt(r, 0)
|
2017-05-07 13:09:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-09-19 01:32:20 +02:00
|
|
|
nOutputs := int(nOutputsU64)
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-09-19 01:32:20 +02:00
|
|
|
ret.breachedOutputs = make([]breachedOutput, nOutputs)
|
|
|
|
for i := range ret.breachedOutputs {
|
|
|
|
if err := ret.breachedOutputs[i].Decode(r); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Encode serializes a breachedOutput into the passed byte stream.
|
|
|
|
func (bo *breachedOutput) Encode(w io.Writer) error {
|
|
|
|
var scratch [8]byte
|
|
|
|
|
|
|
|
binary.BigEndian.PutUint64(scratch[:8], uint64(bo.amt))
|
|
|
|
if _, err := w.Write(scratch[:8]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-07-26 05:39:59 +02:00
|
|
|
if err := writeOutpoint(w, &bo.outpoint); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-16 15:47:43 +01:00
|
|
|
err := input.WriteSignDescriptor(w, &bo.signDesc)
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = wire.WriteVarBytes(w, 0, bo.secondLevelWitnessScript)
|
|
|
|
if err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
binary.BigEndian.PutUint16(scratch[:2], uint16(bo.witnessType))
|
|
|
|
if _, err := w.Write(scratch[:2]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode deserializes a breachedOutput from the passed byte stream.
|
|
|
|
func (bo *breachedOutput) Decode(r io.Reader) error {
|
|
|
|
var scratch [8]byte
|
|
|
|
|
|
|
|
if _, err := io.ReadFull(r, scratch[:8]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bo.amt = btcutil.Amount(binary.BigEndian.Uint64(scratch[:8]))
|
|
|
|
|
2017-07-26 05:39:59 +02:00
|
|
|
if err := readOutpoint(r, &bo.outpoint); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-16 15:47:43 +01:00
|
|
|
if err := input.ReadSignDescriptor(r, &bo.signDesc); err != nil {
|
2017-05-07 13:09:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
wScript, err := wire.ReadVarBytes(r, 0, 1000, "witness script")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bo.secondLevelWitnessScript = wScript
|
|
|
|
|
2017-05-07 13:09:22 +02:00
|
|
|
if _, err := io.ReadFull(r, scratch[:2]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-10-07 13:41:46 +02:00
|
|
|
bo.witnessType = input.StandardWitnessType(
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 02:11:02 +01:00
|
|
|
binary.BigEndian.Uint16(scratch[:2]),
|
|
|
|
)
|
2017-05-07 13:09:22 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|