2019-01-24 14:28:25 +01:00
|
|
|
package lnd
|
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"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcutil"
|
2016-11-29 04:43:57 +01:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
2018-09-20 12:23:01 +02:00
|
|
|
|
2016-11-29 04:43:57 +01:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2017-05-02 22:04:58 +02:00
|
|
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
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"
|
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"
|
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")
|
|
|
|
|
|
|
|
// justiceTxnBucket holds the finalized justice transactions for all
|
|
|
|
// breached contracts. Entries are added to the justice txn bucket just
|
|
|
|
// before broadcasting the sweep txn.
|
|
|
|
justiceTxnBucket = []byte("justice-txn")
|
2018-06-01 16:20:55 +02:00
|
|
|
|
|
|
|
// errBrarShuttingDown is an error returned if the breacharbiter has
|
|
|
|
// been signalled to exit.
|
|
|
|
errBrarShuttingDown = errors.New("breacharbiter shutting down")
|
2017-11-21 08:56:42 +01:00
|
|
|
)
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// ContractBreachEvent is an event the breachArbiter will receive in case a
|
|
|
|
// 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
|
|
|
|
// it is safe for the BreachArbiter to carry on its duty.
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-09-01 12:11:14 +02:00
|
|
|
// BreachConfig bundles the required subsystems used by the breach arbiter. An
|
|
|
|
// instance of BreachConfig is passed to newBreachArbiter during instantiation.
|
|
|
|
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.
|
2017-09-20 02:30:36 +02:00
|
|
|
CloseLink func(*wire.OutPoint, htlcswitch.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.
|
|
|
|
DB *channeldb.DB
|
|
|
|
|
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.
|
|
|
|
GenSweepScript func() ([]byte, error)
|
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
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// ContractBreaches is a channel where the breachArbiter will receive
|
|
|
|
// notifications in the event of a contract breach being observed. A
|
|
|
|
// ContractBreachEvent must be ACKed by the breachArbiter, such that
|
|
|
|
// 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.
|
|
|
|
Store RetributionStore
|
|
|
|
}
|
|
|
|
|
2017-01-13 06:01:50 +01:00
|
|
|
// breachArbiter 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?
|
2016-11-29 04:43:57 +01:00
|
|
|
type breachArbiter 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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-01-13 06:01:50 +01:00
|
|
|
// newBreachArbiter creates a new instance of a breachArbiter initialized with
|
|
|
|
// its dependent objects.
|
2017-09-01 12:11:14 +02:00
|
|
|
func newBreachArbiter(cfg *BreachConfig) *breachArbiter {
|
2016-11-29 04:43:57 +01:00
|
|
|
return &breachArbiter{
|
2018-04-18 13:56:14 +02:00
|
|
|
cfg: cfg,
|
|
|
|
quit: make(chan struct{}),
|
2016-11-29 04:43:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start is an idempotent method that officially starts the breachArbiter along
|
|
|
|
// with all other goroutines it needs to perform its functions.
|
|
|
|
func (b *breachArbiter) Start() error {
|
2019-01-23 18:15:57 +01:00
|
|
|
var err error
|
|
|
|
b.started.Do(func() {
|
|
|
|
err = b.start()
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
2016-11-29 04:43:57 +01:00
|
|
|
|
2019-01-23 18:15:57 +01:00
|
|
|
func (b *breachArbiter) start() error {
|
2017-01-31 06:45:02 +01:00
|
|
|
brarLog.Tracef("Starting breach arbiter")
|
2016-11-29 04:43:57 +01:00
|
|
|
|
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 {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
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 {
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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]
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-11-29 04:43:57 +01:00
|
|
|
// Stop is an idempotent method that signals the breachArbiter to execute a
|
|
|
|
// graceful shutdown. This function will block until all goroutines spawned by
|
|
|
|
// the breachArbiter have gracefully exited.
|
|
|
|
func (b *breachArbiter) Stop() error {
|
2019-01-23 18:15:57 +01:00
|
|
|
b.stopped.Do(func() {
|
|
|
|
brarLog.Infof("Breach arbiter shutting down")
|
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.
|
|
|
|
func (b *breachArbiter) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
|
|
|
return b.cfg.Store.IsBreached(chanPoint)
|
|
|
|
}
|
|
|
|
|
2016-11-29 04:43:57 +01:00
|
|
|
// contractObserver is the primary goroutine for the breachArbiter. 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.
|
2018-04-18 13:56:14 +02:00
|
|
|
func (b *breachArbiter) 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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// convertToSecondLevelRevoke takes a breached output, and a transaction that
|
|
|
|
// spends it to the second level, and mutates the breach output into one that
|
|
|
|
// is able to properly sweep that second level output. We'll use this function
|
|
|
|
// when we go to sweep a breached commitment transaction, but the cheating
|
|
|
|
// party has already attempted to take it to the second level
|
|
|
|
func convertToSecondLevelRevoke(bo *breachedOutput, breachInfo *retributionInfo,
|
|
|
|
spendDetails *chainntnfs.SpendDetail) {
|
|
|
|
|
|
|
|
// In this case, we'll modify the witness type of this output to
|
|
|
|
// actually prepare for a second level revoke.
|
2019-01-16 15:47:43 +01:00
|
|
|
bo.witnessType = input.HtlcSecondLevelRevoke
|
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 also redirect the outpoint to this second level output, so the
|
|
|
|
// spending transaction updates it inputs accordingly.
|
|
|
|
spendingTx := spendDetails.SpendingTx
|
|
|
|
oldOp := bo.outpoint
|
|
|
|
bo.outpoint = wire.OutPoint{
|
|
|
|
Hash: spendingTx.TxHash(),
|
|
|
|
Index: 0,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we need to update the amount so we can do fee estimation
|
|
|
|
// properly, and also so we can generate a valid signature as we need
|
|
|
|
// to know the new input value (the second level transactions shaves
|
|
|
|
// off some funds to fees).
|
|
|
|
newAmt := spendingTx.TxOut[0].Value
|
|
|
|
bo.amt = btcutil.Amount(newAmt)
|
|
|
|
bo.signDesc.Output.Value = newAmt
|
2018-07-18 04:26:53 +02:00
|
|
|
bo.signDesc.Output.PkScript = spendingTx.TxOut[0].PkScript
|
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
|
|
|
|
|
|
|
// 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
|
|
|
// 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.
|
2018-06-01 16:20:55 +02:00
|
|
|
func (b *breachArbiter) 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-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 {
|
|
|
|
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 {
|
|
|
|
|
|
|
|
// If the output being revoked is the remote commitment
|
|
|
|
// output or an offered HTLC output, it's amount
|
|
|
|
// contributes to the value of funds being revoked from
|
|
|
|
// the counter party.
|
|
|
|
case input.CommitmentRevoke, input.HtlcSecondLevelRevoke,
|
|
|
|
input.HtlcOfferedRevoke:
|
|
|
|
|
|
|
|
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.
|
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
|
|
|
func (b *breachArbiter) exactRetribution(confChan *chainntnfs.ConfirmationEvent,
|
2017-07-26 05:14:03 +02:00
|
|
|
breachInfo *retributionInfo) {
|
|
|
|
|
|
|
|
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
|
|
|
|
// SpendEvents between each attempt to not re-register uneccessarily.
|
|
|
|
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
|
|
|
|
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
|
|
|
brarLog.Debugf("Broadcasting justice tx: %v", newLogClosure(func() string {
|
|
|
|
return spew.Sdump(finalTx)
|
|
|
|
}))
|
2017-07-26 05:14:03 +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
|
|
|
// 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)
|
|
|
|
err = b.cfg.PublishTransaction(finalTx, 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 {
|
2021-02-15 13:31:08 +01:00
|
|
|
|
2021-04-20 15:42:23 +02:00
|
|
|
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",
|
|
|
|
newLogClosure(func() string {
|
|
|
|
return spew.Sdump(tx)
|
|
|
|
}))
|
|
|
|
|
|
|
|
err = b.cfg.PublishTransaction(tx, label)
|
|
|
|
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",
|
|
|
|
newLogClosure(func() string {
|
|
|
|
return spew.Sdump(tx)
|
|
|
|
}))
|
|
|
|
|
|
|
|
err = b.cfg.PublishTransaction(tx, label)
|
|
|
|
if err != nil {
|
|
|
|
brarLog.Warnf("Unable to broadcast "+
|
|
|
|
"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.
|
|
|
|
func (b *breachArbiter) cleanupBreach(chanPoint *wire.OutPoint) error {
|
|
|
|
// With the channel closed, mark it in the database as such.
|
|
|
|
err := b.cfg.DB.MarkChanFullyClosed(chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to mark chan as closed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Justice has been carried out; we can safely delete the retribution
|
|
|
|
// info from the database.
|
|
|
|
err = b.cfg.Store.Remove(chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to remove retribution from db: %v",
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-18 13:56:14 +02:00
|
|
|
// handleBreachHandoff handles a new breach event, by writing it to disk, then
|
|
|
|
// notifies the breachArbiter contract observer goroutine that a channel's
|
|
|
|
// contract has been breached by the prior counterparty. Once notified the
|
|
|
|
// breachArbiter will attempt to sweep ALL funds within the channel using the
|
|
|
|
// 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.
|
|
|
|
func (b *breachArbiter) handleBreachHandoff(breachEvent *ContractBreachEvent) {
|
|
|
|
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.
|
|
|
|
b.cfg.CloseLink(&chanPoint, htlcswitch.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
|
|
|
|
|
2019-01-16 15:47:43 +01:00
|
|
|
witnessFunc input.WitnessGenerator
|
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,
|
2019-10-07 13:41:46 +02:00
|
|
|
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
|
|
|
secondLevelScript []byte,
|
2019-01-16 15:47:43 +01:00
|
|
|
signDescriptor *input.SignDescriptor,
|
2018-11-07 16:35:54 +01:00
|
|
|
confHeight uint32) 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,
|
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.
|
|
|
|
func (bo *breachedOutput) OutPoint() *wire.OutPoint {
|
|
|
|
return &bo.outpoint
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
hashCache *txscript.TxSigHashes, 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.
|
2019-10-07 13:41:46 +02:00
|
|
|
bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, bo.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
|
|
|
|
// confirmed type, we must wait one block before claiming it.
|
|
|
|
if bo.witnessType == input.CommitmentToRemoteConfirmed {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// belongs to us and has no time-based constraints on spending.
|
2017-09-19 01:32:20 +02:00
|
|
|
if breachInfo.LocalOutputSignDesc != nil {
|
2019-08-08 05:29:15 +02:00
|
|
|
witnessType := input.CommitmentNoDelay
|
|
|
|
if breachInfo.LocalOutputSignDesc.SingleTweak == nil {
|
|
|
|
witnessType = input.CommitSpendNoDelayTweakless
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
if breachInfo.LocalDelay != 0 {
|
|
|
|
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,
|
|
|
|
)
|
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 {
|
|
|
|
remoteOutput := makeBreachedOutput(
|
|
|
|
&breachInfo.RemoteOutpoint,
|
2019-01-16 15:47:43 +01:00
|
|
|
input.CommitmentRevoke,
|
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,
|
|
|
|
)
|
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
|
2017-09-01 12:11:14 +02:00
|
|
|
if breachedHtlc.IsIncoming {
|
2019-01-16 15:47:43 +01:00
|
|
|
htlcWitnessType = input.HtlcAcceptedRevoke
|
2017-09-01 12:11:14 +02:00
|
|
|
} else {
|
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,
|
|
|
|
breachInfo.BreachHeight)
|
2017-09-19 01:32:20 +02:00
|
|
|
|
|
|
|
breachedOutputs = append(breachedOutputs, htlcOutput)
|
2017-08-22 01:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &retributionInfo{
|
2017-09-19 01:32:20 +02:00
|
|
|
commitHash: breachInfo.BreachTransaction.TxHash(),
|
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
|
|
|
|
// three variants of the justice transaction:
|
|
|
|
//
|
|
|
|
// 1. The "normal" justice tx that spends all breached outputs
|
|
|
|
// 2. A tx that spends only the breached to_local output and to_remote output
|
|
|
|
// (can be nil if none of these exist)
|
|
|
|
// 3. A tx that spends all the breached HTLC outputs, and second-level HTLC
|
|
|
|
// outputs (can be nil if no HTLC outputs exist).
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
// spend the to_local output separately, before the CSV lock expires.
|
|
|
|
type justiceTxVariants struct {
|
|
|
|
spendAll *wire.MsgTx
|
|
|
|
spendCommitOuts *wire.MsgTx
|
|
|
|
spendHTLCs *wire.MsgTx
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2017-07-26 07:57:29 +02:00
|
|
|
func (b *breachArbiter) createJusticeTx(
|
2021-02-12 12:36:45 +01:00
|
|
|
breachedOutputs []breachedOutput) (*justiceTxVariants, error) {
|
|
|
|
|
|
|
|
var (
|
|
|
|
allInputs []input.Input
|
|
|
|
commitInputs []input.Input
|
|
|
|
htlcInputs []input.Input
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := range breachedOutputs {
|
|
|
|
// Grab locally scoped reference to breached output.
|
|
|
|
inp := &breachedOutputs[i]
|
|
|
|
allInputs = append(allInputs, inp)
|
|
|
|
|
|
|
|
// Check if the input is from an HTLC or a commitment output.
|
|
|
|
if inp.WitnessType() == input.HtlcAcceptedRevoke ||
|
|
|
|
inp.WitnessType() == input.HtlcOfferedRevoke ||
|
|
|
|
inp.WitnessType() == input.HtlcSecondLevelRevoke {
|
|
|
|
|
|
|
|
htlcInputs = append(htlcInputs, inp)
|
|
|
|
} else {
|
|
|
|
commitInputs = append(commitInputs, inp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
txs = &justiceTxVariants{}
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// For each group of inputs, create a tx that spends them.
|
|
|
|
txs.spendAll, err = b.createSweepTx(allInputs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
txs.spendCommitOuts, err = b.createSweepTx(commitInputs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
txs.spendHTLCs, err = b.createSweepTx(htlcInputs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return txs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
|
|
|
|
func (b *breachArbiter) createSweepTx(inputs []input.Input) (*wire.MsgTx,
|
|
|
|
error) {
|
|
|
|
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
|
|
|
|
// that pays to a p2wkh output. Components such as the version,
|
2017-09-26 05:13:53 +02:00
|
|
|
// nLockTime, and output are already included in the TxWeightEstimator.
|
|
|
|
weightEstimate.AddP2WKHOutput()
|
2017-09-01 12:11:14 +02:00
|
|
|
|
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
|
|
|
|
2018-07-28 03:27:51 +02:00
|
|
|
txWeight := int64(weightEstimate.Weight())
|
|
|
|
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.
|
2018-07-28 03:27:51 +02:00
|
|
|
func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
2019-01-16 15:47:43 +01:00
|
|
|
inputs ...input.Input) (*wire.MsgTx, 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?
|
2017-09-01 12:11:14 +02:00
|
|
|
pkScript, err := b.cfg.GenSweepScript()
|
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
|
|
|
|
for _, input := range inputs {
|
2018-09-26 17:59:30 +02:00
|
|
|
totalAmt += btcutil.Amount(input.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
|
|
|
|
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
|
|
|
|
|
|
|
// We begin by adding the output to which our funds will be deposited.
|
|
|
|
txn.AddTxOut(&wire.TxOut{
|
|
|
|
PkScript: pkScript,
|
|
|
|
Value: sweepAmt,
|
2017-07-31 02:45:39 +02:00
|
|
|
})
|
|
|
|
|
2017-08-22 01:56:58 +02:00
|
|
|
// Next, we add all of the spendable outputs as inputs to the
|
|
|
|
// transaction.
|
|
|
|
for _, input := range inputs {
|
|
|
|
txn.AddTxIn(&wire.TxIn{
|
|
|
|
PreviousOutPoint: *input.OutPoint(),
|
2020-03-06 16:11:47 +01:00
|
|
|
Sequence: input.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.
|
|
|
|
hashCache := txscript.NewTxSigHashes(txn)
|
|
|
|
|
|
|
|
// 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(
|
|
|
|
b.cfg.Signer, txn, hashCache, idx,
|
|
|
|
)
|
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.
|
|
|
|
for i, input := range inputs {
|
|
|
|
if err := addWitness(i, input); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return txn, nil
|
2017-07-31 02:45:39 +02:00
|
|
|
}
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-07-26 07:57:29 +02:00
|
|
|
// RetributionStore provides an interface for managing a persistent map from
|
|
|
|
// wire.OutPoint -> retributionInfo. Upon learning of a breach, a BreachArbiter
|
|
|
|
// should record the retributionInfo for the breached channel, which serves a
|
|
|
|
// checkpoint in the event that retribution needs to be resumed after failure.
|
|
|
|
// A RetributionStore provides an interface for managing the persisted set, as
|
|
|
|
// well as mapping user defined functions over the entire on-disk contents.
|
|
|
|
//
|
|
|
|
// Calls to RetributionStore may occur concurrently. A concrete instance of
|
|
|
|
// RetributionStore should use appropriate synchronization primitives, or
|
|
|
|
// be otherwise safe for concurrent access.
|
|
|
|
type RetributionStore interface {
|
|
|
|
// Add persists the retributionInfo to disk, using the information's
|
|
|
|
// chanPoint as the key. This method should overwrite any existing
|
2017-12-18 03:40:05 +01:00
|
|
|
// entries found under the same key, and an error should be raised if
|
2017-07-26 07:57:29 +02:00
|
|
|
// the addition fails.
|
|
|
|
Add(retInfo *retributionInfo) error
|
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
// 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)
|
|
|
|
|
2017-07-26 07:57:29 +02:00
|
|
|
// 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.
|
2020-10-20 16:18:40 +02:00
|
|
|
ForAll(cb func(*retributionInfo) error, reset func()) error
|
2017-05-07 13:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// retributionStore handles persistence of retribution states to disk and is
|
|
|
|
// 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.
|
|
|
|
type retributionStore struct {
|
|
|
|
db *channeldb.DB
|
|
|
|
}
|
|
|
|
|
|
|
|
// newRetributionStore creates a new instance of a retributionStore.
|
|
|
|
func newRetributionStore(db *channeldb.DB) *retributionStore {
|
|
|
|
return &retributionStore{
|
|
|
|
db: db,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add adds a retribution state to the retributionStore, which is then persisted
|
|
|
|
// to disk.
|
2017-07-26 05:14:03 +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
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-08-15 00:14:43 +02:00
|
|
|
return retBucket.Put(outBuf.Bytes(), retBuf.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.
|
|
|
|
func (rs *retributionStore) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
|
|
|
|
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.
|
|
|
|
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)
|
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,
|
|
|
|
// 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.
|
|
|
|
if err := retBucket.Delete(chanBytes); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have not finalized this channel breach, we can exit
|
|
|
|
// early.
|
2020-01-10 03:47:38 +01:00
|
|
|
justiceBkt := tx.ReadWriteBucket(justiceTxnBucket)
|
2017-11-21 08:56:42 +01:00
|
|
|
if justiceBkt == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2017-05-07 13:09:22 +02:00
|
|
|
|
2017-11-21 08:56:42 +01:00
|
|
|
return justiceBkt.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.
|
2020-10-20 16:18:40 +02:00
|
|
|
func (rs *retributionStore) ForAll(cb func(*retributionInfo) error,
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
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.
|
2017-11-21 08:56:42 +01:00
|
|
|
return retBucket.ForEach(func(_, 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
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dencode 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
|
|
|
|
}
|