lnwallet+sweep: add new method CheckMempoolAcceptance
This commit is contained in:
8 files changed
+310
No files matched your search
@@ -1898,3 +1898,34 @@ func (b *BtcWallet) RemoveDescendants(tx *wire.MsgTx) error {
|
||||
return b.wallet.TxStore.RemoveUnminedTx(wtxmgrNs, txRecord)
|
||||
})
|
||||
}
|
||||
|
||||
// CheckMempoolAcceptance is a wrapper around `TestMempoolAccept` which checks
|
||||
// the mempool acceptance of a transaction.
|
||||
func (b *BtcWallet) CheckMempoolAcceptance(tx *wire.MsgTx) error {
|
||||
// Use a max feerate of 0 means the default value will be used when
|
||||
// testing mempool acceptance. The default max feerate is 0.10 BTC/kvb,
|
||||
// or 10,000 sat/vb.
|
||||
results, err := b.chain.TestMempoolAccept([]*wire.MsgTx{tx}, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanity check that the expected single result is returned.
|
||||
if len(results) != 1 {
|
||||
return fmt.Errorf("expected 1 result from TestMempoolAccept, "+
|
||||
"instead got %v", len(results))
|
||||
}
|
||||
|
||||
result := results[0]
|
||||
log.Debugf("TestMempoolAccept result: %s", spew.Sdump(result))
|
||||
|
||||
// Mempool check failed, we now map the reject reason to a proper RPC
|
||||
// error and return it.
|
||||
if !result.Allowed {
|
||||
err := rpcclient.MapRPCErr(errors.New(result.RejectReason))
|
||||
|
||||
return fmt.Errorf("mempool rejection: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -3,8 +3,12 @@ package btcwallet
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcwallet/chain"
|
||||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/lightningnetwork/lnd/lnmock"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -132,3 +136,89 @@ func TestPreviousOutpoints(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckMempoolAcceptance asserts the CheckMempoolAcceptance behaves as
|
||||
// expected.
|
||||
func TestCheckMempoolAcceptance(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rt := require.New(t)
|
||||
|
||||
// Create a mock chain.Interface.
|
||||
mockChain := &lnmock.MockChain{}
|
||||
defer mockChain.AssertExpectations(t)
|
||||
|
||||
// Create a test tx and a test max feerate.
|
||||
tx := wire.NewMsgTx(2)
|
||||
maxFeeRate := float64(0)
|
||||
|
||||
// Create a test wallet.
|
||||
wallet := &BtcWallet{
|
||||
chain: mockChain,
|
||||
}
|
||||
|
||||
// Assert that when the chain backend doesn't support
|
||||
// `TestMempoolAccept`, an error is returned.
|
||||
//
|
||||
// Mock the chain backend to not support `TestMempoolAccept`.
|
||||
mockChain.On("TestMempoolAccept", []*wire.MsgTx{tx}, maxFeeRate).Return(
|
||||
nil, rpcclient.ErrBackendVersion).Once()
|
||||
|
||||
err := wallet.CheckMempoolAcceptance(tx)
|
||||
rt.ErrorIs(err, rpcclient.ErrBackendVersion)
|
||||
|
||||
// Assert that when the chain backend doesn't implement
|
||||
// `TestMempoolAccept`, an error is returned.
|
||||
//
|
||||
// Mock the chain backend to not support `TestMempoolAccept`.
|
||||
mockChain.On("TestMempoolAccept", []*wire.MsgTx{tx}, maxFeeRate).Return(
|
||||
nil, chain.ErrUnimplemented).Once()
|
||||
|
||||
// Now call the method under test.
|
||||
err = wallet.CheckMempoolAcceptance(tx)
|
||||
rt.ErrorIs(err, chain.ErrUnimplemented)
|
||||
|
||||
// Assert that when the returned results are not as expected, an error
|
||||
// is returned.
|
||||
//
|
||||
// Mock the chain backend to return more than one result.
|
||||
results := []*btcjson.TestMempoolAcceptResult{
|
||||
{Txid: "txid1", Allowed: true},
|
||||
{Txid: "txid2", Allowed: false},
|
||||
}
|
||||
mockChain.On("TestMempoolAccept", []*wire.MsgTx{tx}, maxFeeRate).Return(
|
||||
results, nil).Once()
|
||||
|
||||
// Now call the method under test.
|
||||
err = wallet.CheckMempoolAcceptance(tx)
|
||||
rt.ErrorContains(err, "expected 1 result from TestMempoolAccept")
|
||||
|
||||
// Assert that when the tx is rejected, the reason is converted to an
|
||||
// RPC error and returned.
|
||||
//
|
||||
// Mock the chain backend to return one result.
|
||||
results = []*btcjson.TestMempoolAcceptResult{{
|
||||
Txid: tx.TxHash().String(),
|
||||
Allowed: false,
|
||||
RejectReason: "insufficient fee",
|
||||
}}
|
||||
mockChain.On("TestMempoolAccept", []*wire.MsgTx{tx}, maxFeeRate).Return(
|
||||
results, nil).Once()
|
||||
|
||||
// Now call the method under test.
|
||||
err = wallet.CheckMempoolAcceptance(tx)
|
||||
rt.ErrorIs(err, rpcclient.ErrInsufficientFee)
|
||||
|
||||
// Assert that when the tx is accepted, no error is returned.
|
||||
//
|
||||
// Mock the chain backend to return one result.
|
||||
results = []*btcjson.TestMempoolAcceptResult{
|
||||
{Txid: tx.TxHash().String(), Allowed: true},
|
||||
}
|
||||
mockChain.On("TestMempoolAccept", []*wire.MsgTx{tx}, maxFeeRate).Return(
|
||||
results, nil).Once()
|
||||
|
||||
// Now call the method under test.
|
||||
err = wallet.CheckMempoolAcceptance(tx)
|
||||
rt.NoError(err)
|
||||
}
|
||||
@@ -536,6 +536,11 @@ type WalletController interface {
|
||||
// which could be e.g. btcd, bitcoind, neutrino, or another consensus
|
||||
// service.
|
||||
BackEnd() string
|
||||
|
||||
// CheckMempoolAcceptance checks whether a transaction follows mempool
|
||||
// policies and returns an error if it cannot be accepted into the
|
||||
// mempool.
|
||||
CheckMempoolAcceptance(tx *wire.MsgTx) error
|
||||
}
|
||||
|
||||
// BlockChainIO is a dedicated source which will be used to obtain queries
|
||||
|
||||
@@ -294,6 +294,10 @@ func (w *mockWalletController) RemoveDescendants(*wire.MsgTx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *mockWalletController) CheckMempoolAcceptance(tx *wire.MsgTx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mockChainNotifier is a mock implementation of the ChainNotifier interface.
|
||||
type mockChainNotifier struct {
|
||||
SpendChan chan *chainntnfs.SpendDetail
|
||||
|
||||
Reference in new issue
Block a user