-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rpctest: add itest for
testmempoolaccept
- Loading branch information
1 parent
50f880f
commit a5087fb
Showing
1 changed file
with
203 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
//go:build rpctest | ||
// +build rpctest | ||
|
||
package integration | ||
|
||
import ( | ||
"encoding/hex" | ||
"testing" | ||
|
||
"github.com/btcsuite/btcd/btcjson" | ||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcd/integration/rpctest" | ||
"github.com/btcsuite/btcd/rpcclient" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// TestTestMempoolAccept checks that `TestTestMempoolAccept` behaves as | ||
// expected. It checks that, | ||
// - an error is returned when invalid params are used. | ||
// - orphan tx is rejected. | ||
// - fee rate above the max is rejected. | ||
// - a mixed of both allowed and rejected can be returned in the same response. | ||
func TestTestMempoolAccept(t *testing.T) { | ||
t.Parallel() | ||
|
||
// Boilerplate codetestDir to make a pruned node. | ||
btcdCfg := []string{"--rejectnonstd", "--debuglevel=debug"} | ||
r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "") | ||
require.NoError(t, err) | ||
|
||
// Setup the node. | ||
require.NoError(t, r.SetUp(true, 100)) | ||
t.Cleanup(func() { | ||
require.NoError(t, r.TearDown()) | ||
}) | ||
|
||
// Create testing txns. | ||
invalidTx := decodeHex(t, missingParentsHex) | ||
validTx := createTestTx(t, r) | ||
|
||
// Create testing constants. | ||
const feeRate = 10 | ||
|
||
testCases := []struct { | ||
name string | ||
txns []*wire.MsgTx | ||
maxFeeRate float64 | ||
expectedErr error | ||
expectedResult []*btcjson.TestMempoolAcceptResult | ||
}{ | ||
{ | ||
// When too many txns are provided, the method should | ||
// return an error. | ||
name: "too many txns", | ||
txns: make([]*wire.MsgTx, 26), | ||
maxFeeRate: 0, | ||
expectedErr: rpcclient.ErrInvalidParam, | ||
expectedResult: nil, | ||
}, | ||
{ | ||
// When no txns are provided, the method should return | ||
// an error. | ||
name: "empty txns", | ||
txns: nil, | ||
maxFeeRate: 0, | ||
expectedErr: rpcclient.ErrInvalidParam, | ||
expectedResult: nil, | ||
}, | ||
{ | ||
// When a corrupted txn is provided, the method should | ||
// an error. | ||
name: "corrupted tx", | ||
txns: []*wire.MsgTx{{}}, | ||
maxFeeRate: 0, | ||
expectedErr: rpcclient.ErrInvalidParam, | ||
expectedResult: nil, | ||
}, | ||
{ | ||
// When an orphan tx is provided, the method should a | ||
// test mempool accept result which says this tx is not | ||
// allowed. | ||
name: "orphan tx", | ||
txns: []*wire.MsgTx{invalidTx}, | ||
maxFeeRate: 0, | ||
expectedResult: []*btcjson.TestMempoolAcceptResult{{ | ||
Txid: invalidTx.TxHash().String(), | ||
Wtxid: invalidTx.TxHash().String(), | ||
Allowed: false, | ||
RejectReason: "missing-inputs", | ||
}}, | ||
}, | ||
{ | ||
// When a valid tx is provided but it exceeds the max | ||
// fee rate, the method should a test mempool accept | ||
// result which says it's allowed. | ||
name: "valid tx but exceeds max fee rate", | ||
txns: []*wire.MsgTx{validTx}, | ||
maxFeeRate: 1e-5, | ||
expectedResult: []*btcjson.TestMempoolAcceptResult{{ | ||
Txid: validTx.TxHash().String(), | ||
Wtxid: validTx.TxHash().String(), | ||
Allowed: false, | ||
RejectReason: "max-fee-exceeded", | ||
}}, | ||
}, | ||
{ | ||
// When a valid tx is provided and it doesn't exceeds | ||
// the max fee rate, the method should a test mempool | ||
// accept result which says it's allowed. | ||
name: "valid tx and sane fee rate", | ||
txns: []*wire.MsgTx{validTx}, | ||
expectedResult: []*btcjson.TestMempoolAcceptResult{{ | ||
Txid: validTx.TxHash().String(), | ||
Wtxid: validTx.TxHash().String(), | ||
Allowed: true, | ||
// TODO(yy): need to calculate the fees, atm | ||
// there's no easy way. | ||
// Fees: &btcjson.TestMempoolAcceptFees{}, | ||
}}, | ||
}, | ||
{ | ||
// When multiple txns are provided, the method should | ||
// the correct resultsfor each of the txns. | ||
name: "multiple txns", | ||
txns: []*wire.MsgTx{invalidTx, validTx}, | ||
expectedResult: []*btcjson.TestMempoolAcceptResult{{ | ||
Txid: invalidTx.TxHash().String(), | ||
Wtxid: invalidTx.TxHash().String(), | ||
Allowed: false, | ||
RejectReason: "missing-inputs", | ||
}, { | ||
Txid: validTx.TxHash().String(), | ||
Wtxid: validTx.TxHash().String(), | ||
Allowed: true, | ||
}}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
t.Run(tc.name, func(t *testing.T) { | ||
require := require.New(t) | ||
|
||
results, err := r.Client.TestMempoolAccept( | ||
tc.txns, tc.maxFeeRate, | ||
) | ||
|
||
require.ErrorIs(err, tc.expectedErr) | ||
require.Len(results, len(tc.expectedResult)) | ||
|
||
// Check each item is returned as expected. | ||
for i, r := range results { | ||
expected := tc.expectedResult[i] | ||
|
||
// TODO(yy): check all the fields? | ||
require.Equal(expected.Txid, r.Txid) | ||
require.Equal(expected.Wtxid, r.Wtxid) | ||
require.Equal(expected.Allowed, r.Allowed) | ||
require.Equal(expected.RejectReason, | ||
r.RejectReason) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
var ( | ||
//nolint:lll | ||
missingParentsHex = "0100000003bcb2054607a921b3c6df992a9486776863b28485e731a805931b6feb14221acff2000000001c75619cdff9d694a434b13abfbbd618e2ece4460f24b4821cf47d5afc481a386c59565c4900000000cff75994dceb5f5568f8ada45d428630f512fb8efacd46682b4367b4edaf1985c5e4af4b07010000003c029216047236f3000000000017a9141d5a2c690c3e2dacb3cead240f0ce4a273b9d0e48758020000000000001600149d38710eb90e420b159c7a9263994c88e6810bc758020000000000001976a91490770ceff2b1c32e9dbf952fbe65b04a54d1949388ac580200000000000017a914f017945d4d088c7d42ab3bcbc1adce51d74fbd9f8784d7ee4b" | ||
) | ||
|
||
// createTestTx creates a `wire.MsgTx` and asserts its creation. | ||
func createTestTx(t *testing.T, h *rpctest.Harness) *wire.MsgTx { | ||
addr, err := h.NewAddress() | ||
require.NoError(t, err) | ||
|
||
script, err := txscript.PayToAddrScript(addr) | ||
require.NoError(t, err) | ||
|
||
output := &wire.TxOut{ | ||
PkScript: script, | ||
Value: 1e6, | ||
} | ||
|
||
tx, err := h.CreateTransaction([]*wire.TxOut{output}, 10, true) | ||
require.NoError(t, err) | ||
|
||
return tx | ||
} | ||
|
||
// decodeHex takes a tx hexstring and asserts it can be decoded into a | ||
// `wire.MsgTx`. | ||
func decodeHex(t *testing.T, txHex string) *wire.MsgTx { | ||
serializedTx, err := hex.DecodeString(txHex) | ||
require.NoError(t, err) | ||
|
||
tx, err := btcutil.NewTxFromBytes(serializedTx) | ||
require.NoError(t, err) | ||
|
||
return tx.MsgTx() | ||
} |