-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8512 from lightningnetwork/rbf-coop-fsm
[3/4] - lnwallet/chancloser: add new protofsm based RBF chan closer
- Loading branch information
Showing
17 changed files
with
4,160 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package chancloser | ||
|
||
import ( | ||
"sync/atomic" | ||
|
||
"github.com/btcsuite/btcd/btcec/v2" | ||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/chaincfg/chainhash" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/lightningnetwork/lnd/chainntnfs" | ||
"github.com/lightningnetwork/lnd/channeldb" | ||
"github.com/lightningnetwork/lnd/fn/v2" | ||
"github.com/lightningnetwork/lnd/input" | ||
"github.com/lightningnetwork/lnd/lnwallet" | ||
"github.com/lightningnetwork/lnd/lnwallet/chainfee" | ||
"github.com/lightningnetwork/lnd/lnwire" | ||
"github.com/stretchr/testify/mock" | ||
) | ||
|
||
type dummyAdapters struct { | ||
mock.Mock | ||
|
||
msgSent atomic.Bool | ||
|
||
confChan chan *chainntnfs.TxConfirmation | ||
spendChan chan *chainntnfs.SpendDetail | ||
} | ||
|
||
func newDaemonAdapters() *dummyAdapters { | ||
return &dummyAdapters{ | ||
confChan: make(chan *chainntnfs.TxConfirmation, 1), | ||
spendChan: make(chan *chainntnfs.SpendDetail, 1), | ||
} | ||
} | ||
|
||
func (d *dummyAdapters) SendMessages(pub btcec.PublicKey, | ||
msgs []lnwire.Message) error { | ||
|
||
defer d.msgSent.Store(true) | ||
|
||
args := d.Called(pub, msgs) | ||
|
||
return args.Error(0) | ||
} | ||
|
||
func (d *dummyAdapters) BroadcastTransaction(tx *wire.MsgTx, | ||
label string) error { | ||
|
||
args := d.Called(tx, label) | ||
|
||
return args.Error(0) | ||
} | ||
|
||
func (d *dummyAdapters) DisableChannel(op wire.OutPoint) error { | ||
args := d.Called(op) | ||
|
||
return args.Error(0) | ||
} | ||
|
||
func (d *dummyAdapters) RegisterConfirmationsNtfn(txid *chainhash.Hash, | ||
pkScript []byte, numConfs, heightHint uint32, | ||
opts ...chainntnfs.NotifierOption, | ||
) (*chainntnfs.ConfirmationEvent, error) { | ||
|
||
args := d.Called(txid, pkScript, numConfs) | ||
|
||
err := args.Error(0) | ||
|
||
return &chainntnfs.ConfirmationEvent{ | ||
Confirmed: d.confChan, | ||
}, err | ||
} | ||
|
||
func (d *dummyAdapters) RegisterSpendNtfn(outpoint *wire.OutPoint, | ||
pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { | ||
|
||
args := d.Called(outpoint, pkScript, heightHint) | ||
|
||
err := args.Error(0) | ||
|
||
return &chainntnfs.SpendEvent{ | ||
Spend: d.spendChan, | ||
}, err | ||
} | ||
|
||
type mockFeeEstimator struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *mockFeeEstimator) EstimateFee(chanType channeldb.ChannelType, | ||
localTxOut, remoteTxOut *wire.TxOut, | ||
idealFeeRate chainfee.SatPerKWeight) btcutil.Amount { | ||
|
||
args := m.Called(chanType, localTxOut, remoteTxOut, idealFeeRate) | ||
return args.Get(0).(btcutil.Amount) | ||
} | ||
|
||
type mockChanObserver struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *mockChanObserver) NoDanglingUpdates() bool { | ||
args := m.Called() | ||
return args.Bool(0) | ||
} | ||
|
||
func (m *mockChanObserver) DisableIncomingAdds() error { | ||
args := m.Called() | ||
return args.Error(0) | ||
} | ||
|
||
func (m *mockChanObserver) DisableOutgoingAdds() error { | ||
args := m.Called() | ||
return args.Error(0) | ||
} | ||
|
||
func (m *mockChanObserver) MarkCoopBroadcasted(txn *wire.MsgTx, | ||
local bool) error { | ||
|
||
args := m.Called(txn, local) | ||
return args.Error(0) | ||
} | ||
|
||
func (m *mockChanObserver) MarkShutdownSent(deliveryAddr []byte, | ||
isInitiator bool) error { | ||
|
||
args := m.Called(deliveryAddr, isInitiator) | ||
return args.Error(0) | ||
} | ||
|
||
func (m *mockChanObserver) FinalBalances() fn.Option[ShutdownBalances] { | ||
args := m.Called() | ||
return args.Get(0).(fn.Option[ShutdownBalances]) | ||
} | ||
|
||
func (m *mockChanObserver) DisableChannel() error { | ||
args := m.Called() | ||
return args.Error(0) | ||
} | ||
|
||
type mockErrorReporter struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *mockErrorReporter) ReportError(err error) { | ||
m.Called(err) | ||
} | ||
|
||
type mockCloseSigner struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *mockCloseSigner) CreateCloseProposal(fee btcutil.Amount, | ||
localScript []byte, remoteScript []byte, | ||
closeOpt ...lnwallet.ChanCloseOpt) ( | ||
input.Signature, *wire.MsgTx, btcutil.Amount, error) { | ||
|
||
args := m.Called(fee, localScript, remoteScript, closeOpt) | ||
|
||
return args.Get(0).(input.Signature), args.Get(1).(*wire.MsgTx), | ||
args.Get(2).(btcutil.Amount), args.Error(3) | ||
} | ||
|
||
func (m *mockCloseSigner) CompleteCooperativeClose(localSig, | ||
remoteSig input.Signature, | ||
localScript, remoteScript []byte, | ||
fee btcutil.Amount, closeOpt ...lnwallet.ChanCloseOpt, | ||
) (*wire.MsgTx, btcutil.Amount, error) { | ||
|
||
args := m.Called( | ||
localSig, remoteSig, localScript, remoteScript, fee, closeOpt, | ||
) | ||
|
||
return args.Get(0).(*wire.MsgTx), args.Get(1).(btcutil.Amount), | ||
args.Error(2) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package chancloser | ||
|
||
import ( | ||
"github.com/lightningnetwork/lnd/fn/v2" | ||
"github.com/lightningnetwork/lnd/lnwire" | ||
) | ||
|
||
// RbfMsgMapper is a struct that implements the MsgMapper interface for the | ||
// rbf-coop close state machine. This enables the state machine to be used with | ||
// protofsm. | ||
type RbfMsgMapper struct { | ||
// blockHeight is the height of the block when the co-op close request | ||
// was initiated. This is used to validate conditions related to the | ||
// thaw height. | ||
blockHeight uint32 | ||
|
||
// chanID is the channel ID of the channel being closed. | ||
chanID lnwire.ChannelID | ||
} | ||
|
||
// NewRbfMsgMapper creates a new RbfMsgMapper instance given the current block | ||
// height when the co-op close request was initiated. | ||
func NewRbfMsgMapper(blockHeight uint32, | ||
chanID lnwire.ChannelID) *RbfMsgMapper { | ||
|
||
return &RbfMsgMapper{ | ||
blockHeight: blockHeight, | ||
chanID: chanID, | ||
} | ||
} | ||
|
||
// someEvent returns the target type as a protocol event option. | ||
func someEvent[T ProtocolEvent](m T) fn.Option[ProtocolEvent] { | ||
return fn.Some(ProtocolEvent(m)) | ||
} | ||
|
||
// isExpectedChanID returns true if the channel ID of the message matches the | ||
// bound instance. | ||
func (r *RbfMsgMapper) isExpectedChanID(chanID lnwire.ChannelID) bool { | ||
return r.chanID == chanID | ||
} | ||
|
||
// MapMsg maps a wire message into a FSM event. If the message is not mappable, | ||
// then an error is returned. | ||
func (r *RbfMsgMapper) MapMsg(wireMsg lnwire.Message) fn.Option[ProtocolEvent] { | ||
switch msg := wireMsg.(type) { | ||
case *lnwire.Shutdown: | ||
if !r.isExpectedChanID(msg.ChannelID) { | ||
return fn.None[ProtocolEvent]() | ||
} | ||
|
||
return someEvent(&ShutdownReceived{ | ||
BlockHeight: r.blockHeight, | ||
ShutdownScript: msg.Address, | ||
}) | ||
|
||
case *lnwire.ClosingComplete: | ||
if !r.isExpectedChanID(msg.ChannelID) { | ||
return fn.None[ProtocolEvent]() | ||
} | ||
|
||
return someEvent(&OfferReceivedEvent{ | ||
SigMsg: *msg, | ||
}) | ||
|
||
case *lnwire.ClosingSig: | ||
if !r.isExpectedChanID(msg.ChannelID) { | ||
return fn.None[ProtocolEvent]() | ||
} | ||
|
||
return someEvent(&LocalSigReceived{ | ||
SigMsg: *msg, | ||
}) | ||
} | ||
|
||
return fn.None[ProtocolEvent]() | ||
} |
Oops, something went wrong.