breacharbiter: sweep incoming + outgoing htlcs
This commit also adds a BreachConfig to abstract the instantiation of the breach arbiter, as well as various formatting improvements.
This commit is contained in:
parent
b64d4356c1
commit
a8d667ba35
191
breacharbiter.go
191
breacharbiter.go
@ -30,6 +30,52 @@ import (
|
|||||||
// continue from the persisted state.
|
// continue from the persisted state.
|
||||||
var retributionBucket = []byte("retribution")
|
var retributionBucket = []byte("retribution")
|
||||||
|
|
||||||
|
// BreachConfig bundles the required subsystems used by the breach arbiter. An
|
||||||
|
// instance of BreachConfig is passed to newBreachArbiter during instantiation.
|
||||||
|
type BreachConfig struct {
|
||||||
|
// Signer is used by the breach arbiter to generate sweep transactions,
|
||||||
|
// which move coins from previously open channels back to the user's
|
||||||
|
// wallet.
|
||||||
|
Signer lnwallet.Signer
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// PublishTransaction facilitates the process of broadcasting a
|
||||||
|
// transaction to the network.
|
||||||
|
PublishTransaction func(*wire.MsgTx) error
|
||||||
|
|
||||||
|
// Notifier provides a publish/subscribe interface for event driven
|
||||||
|
// notifications regarding the confirmation of txids.
|
||||||
|
Notifier chainntnfs.ChainNotifier
|
||||||
|
|
||||||
|
// ChainIO is used by the breach arbiter to determine the current height
|
||||||
|
// of the blockchain, which is required to subscribe for spend
|
||||||
|
// notifications from Notifier.
|
||||||
|
ChainIO lnwallet.BlockChainIO
|
||||||
|
|
||||||
|
// Estimator is used by the breach arbiter to determine an appropriate
|
||||||
|
// fee level when generating, signing, and broadcasting sweep
|
||||||
|
// transactions.
|
||||||
|
Estimator lnwallet.FeeEstimator
|
||||||
|
|
||||||
|
// CloseLink allows the breach arbiter to shutdown any channel links for
|
||||||
|
// which it detects a breach, ensuring now further activity will
|
||||||
|
// continue across the link. The method accepts link's channel point and a
|
||||||
|
// close type to be included in the channel close summary.
|
||||||
|
CloseLink func(*wire.OutPoint, htlcswitch.ChannelCloseType)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// GenSweepScript generates the receiving scripts for swept outputs.
|
||||||
|
GenSweepScript func() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
// breachArbiter is a special subsystem which is responsible for watching and
|
// breachArbiter is a special subsystem which is responsible for watching and
|
||||||
// acting on the detection of any attempted uncooperative channel breaches by
|
// acting on the detection of any attempted uncooperative channel breaches by
|
||||||
// channel counterparties. This file essentially acts as deterrence code for
|
// channel counterparties. This file essentially acts as deterrence code for
|
||||||
@ -39,15 +85,7 @@ var retributionBucket = []byte("retribution")
|
|||||||
// counterparties.
|
// counterparties.
|
||||||
// TODO(roasbeef): closures in config for subsystem pointers to decouple?
|
// TODO(roasbeef): closures in config for subsystem pointers to decouple?
|
||||||
type breachArbiter struct {
|
type breachArbiter struct {
|
||||||
wallet *lnwallet.LightningWallet
|
cfg *BreachConfig
|
||||||
signer lnwallet.Signer
|
|
||||||
db *channeldb.DB
|
|
||||||
notifier chainntnfs.ChainNotifier
|
|
||||||
chainIO lnwallet.BlockChainIO
|
|
||||||
estimator lnwallet.FeeEstimator
|
|
||||||
htlcSwitch *htlcswitch.Switch
|
|
||||||
|
|
||||||
retributionStore RetributionStore
|
|
||||||
|
|
||||||
// breachObservers is a map which tracks all the active breach
|
// breachObservers is a map which tracks all the active breach
|
||||||
// observers we're currently managing. The key of the map is the
|
// observers we're currently managing. The key of the map is the
|
||||||
@ -82,20 +120,9 @@ type breachArbiter struct {
|
|||||||
|
|
||||||
// newBreachArbiter creates a new instance of a breachArbiter initialized with
|
// newBreachArbiter creates a new instance of a breachArbiter initialized with
|
||||||
// its dependent objects.
|
// its dependent objects.
|
||||||
func newBreachArbiter(wallet *lnwallet.LightningWallet, db *channeldb.DB,
|
func newBreachArbiter(cfg *BreachConfig) *breachArbiter {
|
||||||
notifier chainntnfs.ChainNotifier, h *htlcswitch.Switch,
|
|
||||||
chain lnwallet.BlockChainIO, fe lnwallet.FeeEstimator) *breachArbiter {
|
|
||||||
|
|
||||||
return &breachArbiter{
|
return &breachArbiter{
|
||||||
wallet: wallet,
|
cfg: cfg,
|
||||||
signer: wallet.Cfg.Signer,
|
|
||||||
db: db,
|
|
||||||
notifier: notifier,
|
|
||||||
chainIO: chain,
|
|
||||||
htlcSwitch: h,
|
|
||||||
estimator: fe,
|
|
||||||
|
|
||||||
retributionStore: newRetributionStore(db),
|
|
||||||
|
|
||||||
breachObservers: make(map[wire.OutPoint]chan struct{}),
|
breachObservers: make(map[wire.OutPoint]chan struct{}),
|
||||||
breachedContracts: make(chan *retributionInfo),
|
breachedContracts: make(chan *retributionInfo),
|
||||||
@ -121,7 +148,7 @@ func (b *breachArbiter) Start() error {
|
|||||||
// breach is reflected in channeldb.
|
// breach is reflected in channeldb.
|
||||||
breachRetInfos := make(map[wire.OutPoint]retributionInfo)
|
breachRetInfos := make(map[wire.OutPoint]retributionInfo)
|
||||||
closeSummaries := make(map[wire.OutPoint]channeldb.ChannelCloseSummary)
|
closeSummaries := make(map[wire.OutPoint]channeldb.ChannelCloseSummary)
|
||||||
err := b.retributionStore.ForAll(func(ret *retributionInfo) error {
|
err := b.cfg.Store.ForAll(func(ret *retributionInfo) error {
|
||||||
// Extract emitted retribution information.
|
// Extract emitted retribution information.
|
||||||
breachRetInfos[ret.chanPoint] = *ret
|
breachRetInfos[ret.chanPoint] = *ret
|
||||||
|
|
||||||
@ -148,7 +175,7 @@ func (b *breachArbiter) Start() error {
|
|||||||
// We need to query that database state for all currently active
|
// We need to query that database state for all currently active
|
||||||
// channels, each of these channels will need a goroutine assigned to
|
// channels, each of these channels will need a goroutine assigned to
|
||||||
// it to watch for channel breaches.
|
// it to watch for channel breaches.
|
||||||
activeChannels, err := b.db.FetchAllChannels()
|
activeChannels, err := b.cfg.DB.FetchAllChannels()
|
||||||
if err != nil && err != channeldb.ErrNoActiveChannels {
|
if err != nil && err != channeldb.ErrNoActiveChannels {
|
||||||
brarLog.Errorf("unable to fetch active channels: %v", err)
|
brarLog.Errorf("unable to fetch active channels: %v", err)
|
||||||
return err
|
return err
|
||||||
@ -185,8 +212,9 @@ func (b *breachArbiter) Start() error {
|
|||||||
channelsToWatch := make([]*lnwallet.LightningChannel, 0, nActive)
|
channelsToWatch := make([]*lnwallet.LightningChannel, 0, nActive)
|
||||||
for _, chanState := range activeChannels {
|
for _, chanState := range activeChannels {
|
||||||
// Initialize active channel from persisted channel state.
|
// Initialize active channel from persisted channel state.
|
||||||
channel, err := lnwallet.NewLightningChannel(nil, b.notifier,
|
channel, err := lnwallet.NewLightningChannel(
|
||||||
b.estimator, chanState)
|
nil, b.cfg.Notifier, b.cfg.Estimator, chanState,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to load channel from "+
|
brarLog.Errorf("unable to load channel from "+
|
||||||
"disk: %v", err)
|
"disk: %v", err)
|
||||||
@ -205,10 +233,8 @@ func (b *breachArbiter) Start() error {
|
|||||||
// notify the HTLC switch that this link should be
|
// notify the HTLC switch that this link should be
|
||||||
// closed, and that all activity on the link should
|
// closed, and that all activity on the link should
|
||||||
// cease.
|
// cease.
|
||||||
b.htlcSwitch.CloseLink(
|
b.cfg.CloseLink(&chanState.FundingOutpoint,
|
||||||
&chanState.FundingOutpoint,
|
htlcswitch.CloseBreach)
|
||||||
htlcswitch.CloseBreach,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ensure channeldb is consistent with the persisted
|
// Ensure channeldb is consistent with the persisted
|
||||||
// breach.
|
// breach.
|
||||||
@ -231,7 +257,7 @@ func (b *breachArbiter) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): instead use closure height of channel
|
// TODO(roasbeef): instead use closure height of channel
|
||||||
_, currentHeight, err := b.chainIO.GetBestBlock()
|
_, currentHeight, err := b.cfg.ChainIO.GetBestBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -249,7 +275,7 @@ func (b *breachArbiter) Start() error {
|
|||||||
// Register for a notification when the breach transaction is
|
// Register for a notification when the breach transaction is
|
||||||
// confirmed on chain.
|
// confirmed on chain.
|
||||||
breachTXID := closeSummary.ClosingTXID
|
breachTXID := closeSummary.ClosingTXID
|
||||||
confChan, err := b.notifier.RegisterConfirmationsNtfn(
|
confChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
|
||||||
&breachTXID, 1, uint32(currentHeight))
|
&breachTXID, 1, uint32(currentHeight))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to register for conf updates "+
|
brarLog.Errorf("unable to register for conf updates "+
|
||||||
@ -293,7 +319,7 @@ func (b *breachArbiter) watchForPendingCloseConfs(currentHeight int32) error {
|
|||||||
pendingClose.ChanPoint)
|
pendingClose.ChanPoint)
|
||||||
|
|
||||||
closeTXID := pendingClose.ClosingTXID
|
closeTXID := pendingClose.ClosingTXID
|
||||||
confNtfn, err := b.notifier.RegisterConfirmationsNtfn(
|
confNtfn, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
|
||||||
&closeTXID, 1, uint32(currentHeight),
|
&closeTXID, 1, uint32(currentHeight),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -321,10 +347,10 @@ func (b *breachArbiter) watchForPendingCloseConfs(currentHeight int32) error {
|
|||||||
// UnilateralCloseSummary on disk so can
|
// UnilateralCloseSummary on disk so can
|
||||||
// possibly sweep output here
|
// possibly sweep output here
|
||||||
|
|
||||||
err := b.db.MarkChanFullyClosed(&chanPoint)
|
err := b.cfg.DB.MarkChanFullyClosed(&chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to mark channel "+
|
brarLog.Errorf("unable to mark channel"+
|
||||||
"as closed: %v", err)
|
" as closed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-b.quit:
|
case <-b.quit:
|
||||||
@ -385,7 +411,7 @@ out:
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case breachInfo := <-b.breachedContracts:
|
case breachInfo := <-b.breachedContracts:
|
||||||
_, currentHeight, err := b.chainIO.GetBestBlock()
|
_, currentHeight, err := b.cfg.ChainIO.GetBestBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to get best height: %v",
|
brarLog.Errorf("unable to get best height: %v",
|
||||||
err)
|
err)
|
||||||
@ -397,7 +423,7 @@ out:
|
|||||||
// transaction) has been confirmed in the chain to
|
// transaction) has been confirmed in the chain to
|
||||||
// ensure we're not dealing with a moving target.
|
// ensure we're not dealing with a moving target.
|
||||||
breachTXID := &breachInfo.commitHash
|
breachTXID := &breachInfo.commitHash
|
||||||
confChan, err := b.notifier.RegisterConfirmationsNtfn(
|
cfChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
|
||||||
breachTXID, 1, uint32(currentHeight),
|
breachTXID, 1, uint32(currentHeight),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -417,7 +443,7 @@ out:
|
|||||||
// retribution after the breach transaction has been
|
// retribution after the breach transaction has been
|
||||||
// confirmed.
|
// confirmed.
|
||||||
b.wg.Add(1)
|
b.wg.Add(1)
|
||||||
go b.exactRetribution(confChan, breachInfo)
|
go b.exactRetribution(cfChan, breachInfo)
|
||||||
|
|
||||||
delete(b.breachObservers, breachInfo.chanPoint)
|
delete(b.breachObservers, breachInfo.chanPoint)
|
||||||
|
|
||||||
@ -523,7 +549,7 @@ func (b *breachArbiter) exactRetribution(
|
|||||||
return spew.Sdump(justiceTx)
|
return spew.Sdump(justiceTx)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
_, currentHeight, err := b.chainIO.GetBestBlock()
|
_, currentHeight, err := b.cfg.ChainIO.GetBestBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to get current height: %v", err)
|
brarLog.Errorf("unable to get current height: %v", err)
|
||||||
return
|
return
|
||||||
@ -531,7 +557,7 @@ func (b *breachArbiter) exactRetribution(
|
|||||||
|
|
||||||
// Finally, broadcast the transaction, finalizing the channels'
|
// Finally, broadcast the transaction, finalizing the channels'
|
||||||
// retribution against the cheating counterparty.
|
// retribution against the cheating counterparty.
|
||||||
if err := b.wallet.PublishTransaction(justiceTx); err != nil {
|
if err := b.cfg.PublishTransaction(justiceTx); err != nil {
|
||||||
brarLog.Errorf("unable to broadcast "+
|
brarLog.Errorf("unable to broadcast "+
|
||||||
"justice tx: %v", err)
|
"justice tx: %v", err)
|
||||||
return
|
return
|
||||||
@ -542,8 +568,8 @@ func (b *breachArbiter) exactRetribution(
|
|||||||
// notify the caller that initiated the retribution workflow that the
|
// notify the caller that initiated the retribution workflow that the
|
||||||
// deed has been done.
|
// deed has been done.
|
||||||
justiceTXID := justiceTx.TxHash()
|
justiceTXID := justiceTx.TxHash()
|
||||||
confChan, err = b.notifier.RegisterConfirmationsNtfn(&justiceTXID, 1,
|
confChan, err = b.cfg.Notifier.RegisterConfirmationsNtfn(
|
||||||
uint32(currentHeight))
|
&justiceTXID, 1, uint32(currentHeight))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to register for conf for txid: %v",
|
brarLog.Errorf("unable to register for conf for txid: %v",
|
||||||
justiceTXID)
|
justiceTXID)
|
||||||
@ -566,14 +592,14 @@ func (b *breachArbiter) exactRetribution(
|
|||||||
revokedFunds, totalFunds)
|
revokedFunds, totalFunds)
|
||||||
|
|
||||||
// With the channel closed, mark it in the database as such.
|
// With the channel closed, mark it in the database as such.
|
||||||
err := b.db.MarkChanFullyClosed(&breachInfo.chanPoint)
|
err := b.cfg.DB.MarkChanFullyClosed(&breachInfo.chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to mark chan as closed: %v", err)
|
brarLog.Errorf("unable to mark chan as closed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Justice has been carried out; we can safely delete the
|
// Justice has been carried out; we can safely delete the
|
||||||
// retribution info from the database.
|
// retribution info from the database.
|
||||||
err = b.retributionStore.Remove(&breachInfo.chanPoint)
|
err = b.cfg.Store.Remove(&breachInfo.chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to remove retribution "+
|
brarLog.Errorf("unable to remove retribution "+
|
||||||
"from the db: %v", err)
|
"from the db: %v", err)
|
||||||
@ -640,7 +666,7 @@ func (b *breachArbiter) breachObserver(contract *lnwallet.LightningChannel,
|
|||||||
// TODO(roasbeef): also notify utxoNursery, might've had
|
// TODO(roasbeef): also notify utxoNursery, might've had
|
||||||
// outbound HTLC's in flight
|
// outbound HTLC's in flight
|
||||||
go waitForChanToClose(uint32(closeInfo.SpendingHeight),
|
go waitForChanToClose(uint32(closeInfo.SpendingHeight),
|
||||||
b.notifier, nil, chanPoint, closeInfo.SpenderTxHash,
|
b.cfg.Notifier, nil, chanPoint, closeInfo.SpenderTxHash,
|
||||||
func() {
|
func() {
|
||||||
// As we just detected a channel was closed via
|
// As we just detected a channel was closed via
|
||||||
// a unilateral commitment broadcast by the
|
// a unilateral commitment broadcast by the
|
||||||
@ -661,13 +687,11 @@ func (b *breachArbiter) breachObserver(contract *lnwallet.LightningChannel,
|
|||||||
goto close
|
goto close
|
||||||
}
|
}
|
||||||
|
|
||||||
brarLog.Infof("Sweeping %v breached "+
|
brarLog.Infof("Sweeping breached "+
|
||||||
"outputs with: %v",
|
"outputs with: %v",
|
||||||
spew.Sdump(sweepTx))
|
spew.Sdump(sweepTx))
|
||||||
|
|
||||||
err = b.wallet.PublishTransaction(
|
err = b.cfg.PublishTransaction(sweepTx)
|
||||||
sweepTx,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to "+
|
brarLog.Errorf("unable to "+
|
||||||
"broadcast tx: %v", err)
|
"broadcast tx: %v", err)
|
||||||
@ -679,7 +703,7 @@ func (b *breachArbiter) breachObserver(contract *lnwallet.LightningChannel,
|
|||||||
"is fully closed, updating DB",
|
"is fully closed, updating DB",
|
||||||
chanPoint)
|
chanPoint)
|
||||||
|
|
||||||
err := b.db.MarkChanFullyClosed(chanPoint)
|
err := b.cfg.DB.MarkChanFullyClosed(chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
brarLog.Errorf("unable to mark chan "+
|
brarLog.Errorf("unable to mark chan "+
|
||||||
"as closed: %v", err)
|
"as closed: %v", err)
|
||||||
@ -699,7 +723,7 @@ func (b *breachArbiter) breachObserver(contract *lnwallet.LightningChannel,
|
|||||||
// breached in order to ensure any incoming or outgoing
|
// breached in order to ensure any incoming or outgoing
|
||||||
// multi-hop HTLCs aren't sent over this link, nor any other
|
// multi-hop HTLCs aren't sent over this link, nor any other
|
||||||
// links associated with this peer.
|
// links associated with this peer.
|
||||||
b.htlcSwitch.CloseLink(chanPoint, htlcswitch.CloseBreach)
|
b.cfg.CloseLink(chanPoint, htlcswitch.CloseBreach)
|
||||||
|
|
||||||
// TODO(roasbeef): need to handle case of remote broadcast
|
// TODO(roasbeef): need to handle case of remote broadcast
|
||||||
// mid-local initiated state-transition, possible
|
// mid-local initiated state-transition, possible
|
||||||
@ -715,7 +739,7 @@ func (b *breachArbiter) breachObserver(contract *lnwallet.LightningChannel,
|
|||||||
retInfo := newRetributionInfo(chanPoint, breachInfo, chanInfo)
|
retInfo := newRetributionInfo(chanPoint, breachInfo, chanInfo)
|
||||||
|
|
||||||
// Persist the pending retribution state to disk.
|
// Persist the pending retribution state to disk.
|
||||||
if err := b.retributionStore.Add(retInfo); err != nil {
|
if err := b.cfg.Store.Add(retInfo); err != nil {
|
||||||
brarLog.Errorf("unable to persist retribution info "+
|
brarLog.Errorf("unable to persist retribution info "+
|
||||||
"to db: %v", err)
|
"to db: %v", err)
|
||||||
}
|
}
|
||||||
@ -795,7 +819,7 @@ type breachedOutput struct {
|
|||||||
amt btcutil.Amount
|
amt btcutil.Amount
|
||||||
outpoint wire.OutPoint
|
outpoint wire.OutPoint
|
||||||
witnessType lnwallet.WitnessType
|
witnessType lnwallet.WitnessType
|
||||||
signDesc *lnwallet.SignDescriptor
|
signDesc lnwallet.SignDescriptor
|
||||||
|
|
||||||
witnessFunc lnwallet.WitnessGenerator
|
witnessFunc lnwallet.WitnessGenerator
|
||||||
}
|
}
|
||||||
@ -812,7 +836,7 @@ func newBreachedOutput(outpoint *wire.OutPoint,
|
|||||||
amt: btcutil.Amount(amount),
|
amt: btcutil.Amount(amount),
|
||||||
outpoint: *outpoint,
|
outpoint: *outpoint,
|
||||||
witnessType: witnessType,
|
witnessType: witnessType,
|
||||||
signDesc: signDescriptor,
|
signDesc: *signDescriptor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,8 +865,7 @@ func (bo *breachedOutput) BuildWitness(signer lnwallet.Signer,
|
|||||||
// been initialized for this breached output.
|
// been initialized for this breached output.
|
||||||
if bo.witnessFunc == nil {
|
if bo.witnessFunc == nil {
|
||||||
bo.witnessFunc = bo.witnessType.GenWitnessFunc(
|
bo.witnessFunc = bo.witnessType.GenWitnessFunc(
|
||||||
signer,
|
signer, &bo.signDesc,
|
||||||
bo.signDesc,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,13 +946,21 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
|
|||||||
// deterministically generate a valid witness for each output. This will
|
// deterministically generate a valid witness for each output. This will
|
||||||
// allow the breach arbiter to recover from failures, in the event that
|
// allow the breach arbiter to recover from failures, in the event that
|
||||||
// it must sign and broadcast the justice transaction.
|
// it must sign and broadcast the justice transaction.
|
||||||
var htlcOutputs = make([]*breachedOutput, nHtlcs)
|
htlcOutputs := make([]*breachedOutput, nHtlcs)
|
||||||
for i, breachedHtlc := range breachInfo.HtlcRetributions {
|
for i, breachedHtlc := range breachInfo.HtlcRetributions {
|
||||||
|
// Using the breachedHtlc's incoming flag, determine the
|
||||||
|
// appropriate witness type that needs to be generated in order
|
||||||
|
// to sweep the HTLC output.
|
||||||
|
var htlcWitnessType lnwallet.WitnessType
|
||||||
|
if breachedHtlc.IsIncoming {
|
||||||
|
htlcWitnessType = lnwallet.HtlcAcceptedRevoke
|
||||||
|
} else {
|
||||||
|
htlcWitnessType = lnwallet.HtlcOfferedRevoke
|
||||||
|
}
|
||||||
|
|
||||||
htlcOutputs[i] = newBreachedOutput(
|
htlcOutputs[i] = newBreachedOutput(
|
||||||
&breachedHtlc.OutPoint,
|
&breachInfo.HtlcRetributions[i].OutPoint, htlcWitnessType,
|
||||||
lnwallet.CommitmentRevoke,
|
&breachInfo.HtlcRetributions[i].SignDesc)
|
||||||
&breachedHtlc.SignDesc,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(conner): remove dependency on channel snapshot after decoupling
|
// TODO(conner): remove dependency on channel snapshot after decoupling
|
||||||
@ -960,24 +991,35 @@ func (b *breachArbiter) createJusticeTx(
|
|||||||
// Assemble the breached outputs into a slice of spendable outputs,
|
// Assemble the breached outputs into a slice of spendable outputs,
|
||||||
// starting with the self and revoked outputs, then adding any htlc
|
// starting with the self and revoked outputs, then adding any htlc
|
||||||
// outputs.
|
// outputs.
|
||||||
var breachedOutputs = make([]SpendableOutput, 2+nHtlcs)
|
breachedOutputs := make([]SpendableOutput, 2+nHtlcs)
|
||||||
breachedOutputs[0] = r.selfOutput
|
breachedOutputs[0] = r.selfOutput
|
||||||
breachedOutputs[1] = r.revokedOutput
|
breachedOutputs[1] = r.revokedOutput
|
||||||
for i, htlcOutput := range r.htlcOutputs {
|
for i, htlcOutput := range r.htlcOutputs {
|
||||||
breachedOutputs[2+i] = htlcOutput
|
breachedOutputs[2+i] = htlcOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compute the transaction weight of the justice transaction, which
|
||||||
|
// includes 2 + nHtlcs inputs and one output.
|
||||||
var txWeight uint64
|
var txWeight uint64
|
||||||
// Begin with a base txn weight, e.g. version, nLockTime, etc.
|
// Begin with a base txn weight, e.g. version, nLockTime, etc.
|
||||||
txWeight += 4*lnwallet.BaseSweepTxSize + lnwallet.WitnessHeaderSize
|
txWeight += 4*lnwallet.BaseSweepTxSize + lnwallet.WitnessHeaderSize
|
||||||
|
|
||||||
// Add to_local revoke script and tx input.
|
// Add to_local revoke script and tx input.
|
||||||
txWeight += 4*lnwallet.InputSize + lnwallet.ToLocalPenaltyWitnessSize
|
txWeight += 4*lnwallet.InputSize + lnwallet.ToLocalPenaltyWitnessSize
|
||||||
// Add to_remote p2wpkh witness and tx input.
|
// Add to_remote p2wpkh witness and tx input.
|
||||||
txWeight += 4*lnwallet.InputSize + lnwallet.P2WKHWitnessSize
|
txWeight += 4*lnwallet.InputSize + lnwallet.P2WKHWitnessSize
|
||||||
// Add revoked offered-htlc witnesses and tx inputs.
|
|
||||||
txWeight += uint64(len(r.htlcOutputs)) *
|
// Compute the appropriate weight contributed by each revoked accepted
|
||||||
(4*lnwallet.InputSize + lnwallet.OfferedHtlcWitnessSize)
|
// or offered HTLC witnesses and tx inputs.
|
||||||
|
for _, htlcOutput := range r.htlcOutputs {
|
||||||
|
switch htlcOutput.witnessType {
|
||||||
|
case lnwallet.HtlcOfferedRevoke:
|
||||||
|
txWeight += 4*lnwallet.InputSize +
|
||||||
|
lnwallet.OfferedHtlcPenaltyWitnessSize
|
||||||
|
case lnwallet.HtlcAcceptedRevoke:
|
||||||
|
txWeight += 4*lnwallet.InputSize +
|
||||||
|
lnwallet.AcceptedHtlcPenaltyWitnessSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return b.sweepSpendableOutputsTxn(txWeight, breachedOutputs...)
|
return b.sweepSpendableOutputsTxn(txWeight, breachedOutputs...)
|
||||||
}
|
}
|
||||||
@ -999,6 +1041,8 @@ func (b *breachArbiter) craftCommitSweepTx(
|
|||||||
closeInfo.SelfOutputSignDesc,
|
closeInfo.SelfOutputSignDesc,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Compute the transaction weight of the commit sweep transaction, which
|
||||||
|
// includes a single input and output.
|
||||||
var txWeight uint64
|
var txWeight uint64
|
||||||
// Begin with a base txn weight, e.g. version, nLockTime, etc.
|
// Begin with a base txn weight, e.g. version, nLockTime, etc.
|
||||||
txWeight += 4*lnwallet.BaseSweepTxSize + lnwallet.WitnessHeaderSize
|
txWeight += 4*lnwallet.BaseSweepTxSize + lnwallet.WitnessHeaderSize
|
||||||
@ -1017,7 +1061,7 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight uint64,
|
|||||||
// sweep the funds to.
|
// sweep the funds to.
|
||||||
// TODO(roasbeef): possibly create many outputs to minimize change in
|
// TODO(roasbeef): possibly create many outputs to minimize change in
|
||||||
// the future?
|
// the future?
|
||||||
pkScript, err := newSweepPkScript(b.wallet)
|
pkScript, err := b.cfg.GenSweepScript()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1028,14 +1072,14 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight uint64,
|
|||||||
totalAmt += input.Amount()
|
totalAmt += input.Amount()
|
||||||
}
|
}
|
||||||
|
|
||||||
feePerWeight := b.estimator.EstimateFeePerWeight(1)
|
feePerWeight := b.cfg.Estimator.EstimateFeePerWeight(1)
|
||||||
txFee := btcutil.Amount(txWeight * feePerWeight)
|
txFee := btcutil.Amount(txWeight * feePerWeight)
|
||||||
|
|
||||||
sweepAmt := int64(totalAmt - txFee)
|
sweepAmt := int64(totalAmt - txFee)
|
||||||
|
|
||||||
// With the fee calculated, we can now create the transaction using the
|
// With the fee calculated, we can now create the transaction using the
|
||||||
// information gathered above and the provided retribution information.
|
// information gathered above and the provided retribution information.
|
||||||
var txn = wire.NewMsgTx(2)
|
txn := wire.NewMsgTx(2)
|
||||||
|
|
||||||
// We begin by adding the output to which our funds will be deposited.
|
// We begin by adding the output to which our funds will be deposited.
|
||||||
txn.AddTxOut(&wire.TxOut{
|
txn.AddTxOut(&wire.TxOut{
|
||||||
@ -1072,7 +1116,7 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight uint64,
|
|||||||
// transaction using the SpendableOutput's witness generation
|
// transaction using the SpendableOutput's witness generation
|
||||||
// function.
|
// function.
|
||||||
witness, err := so.BuildWitness(
|
witness, err := so.BuildWitness(
|
||||||
b.wallet.Cfg.Signer, txn, hashCache, idx,
|
b.cfg.Signer, txn, hashCache, idx,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1339,7 +1383,7 @@ func (bo *breachedOutput) Encode(w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := lnwallet.WriteSignDescriptor(w, bo.signDesc); err != nil {
|
if err := lnwallet.WriteSignDescriptor(w, &bo.signDesc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1364,8 +1408,7 @@ func (bo *breachedOutput) Decode(r io.Reader) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bo.signDesc = &lnwallet.SignDescriptor{}
|
if err := lnwallet.ReadSignDescriptor(r, &bo.signDesc); err != nil {
|
||||||
if err := lnwallet.ReadSignDescriptor(r, bo.signDesc); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user