cosmos-sdk/baseapp/abci_utils_test.go

261 lines
8.1 KiB
Go

package baseapp_test
import (
"bytes"
"testing"
abci "github.com/cometbft/cometbft/abci/types"
cmtsecp256k1 "github.com/cometbft/cometbft/crypto/secp256k1"
cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
cmttypes "github.com/cometbft/cometbft/types"
protoio "github.com/cosmos/gogoproto/io"
"github.com/cosmos/gogoproto/proto"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/cosmos/cosmos-sdk/baseapp"
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
"github.com/cosmos/cosmos-sdk/baseapp/testutil/mock"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/mempool"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
)
const (
chainID = "chain-id"
)
type testValidator struct {
consAddr sdk.ConsAddress
tmPk cmtprotocrypto.PublicKey
privKey cmtsecp256k1.PrivKey
}
func newTestValidator() testValidator {
privkey := cmtsecp256k1.GenPrivKey()
pubkey := privkey.PubKey()
tmPk := cmtprotocrypto.PublicKey{
Sum: &cmtprotocrypto.PublicKey_Secp256K1{
Secp256K1: pubkey.Bytes(),
},
}
return testValidator{
consAddr: sdk.ConsAddress(pubkey.Address()),
tmPk: tmPk,
privKey: privkey,
}
}
func (t testValidator) toValidator(power int64) abci.Validator {
return abci.Validator{
Address: t.consAddr.Bytes(),
Power: power,
}
}
type ABCIUtilsTestSuite struct {
suite.Suite
vals [3]testValidator
ctx sdk.Context
}
func NewABCIUtilsTestSuite(t *testing.T) *ABCIUtilsTestSuite {
t.Helper()
// create 3 validators
s := &ABCIUtilsTestSuite{
vals: [3]testValidator{
newTestValidator(),
newTestValidator(),
newTestValidator(),
},
}
// create context
s.ctx = sdk.Context{}.WithConsensusParams(&cmtproto.ConsensusParams{})
return s
}
func TestABCIUtilsTestSuite(t *testing.T) {
suite.Run(t, NewABCIUtilsTestSuite(t))
}
func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSelection() {
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
var (
secret1 = []byte("secret1")
secret2 = []byte("secret2")
secret3 = []byte("secret3")
secret4 = []byte("secret4")
secret5 = []byte("secret5")
secret6 = []byte("secret6")
)
type testTx struct {
tx sdk.Tx
priority int64
bz []byte
size int
}
testTxs := []testTx{
// test 1
{tx: buildMsg(s.T(), txConfig, []byte(`0`), [][]byte{secret1}, []uint64{1}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`12345678910`), [][]byte{secret1}, []uint64{2}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`22`), [][]byte{secret1}, []uint64{3}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`32`), [][]byte{secret2}, []uint64{1}), priority: 8},
// test 2
{tx: buildMsg(s.T(), txConfig, []byte(`4`), [][]byte{secret1, secret2}, []uint64{3, 3}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`52345678910`), [][]byte{secret1, secret3}, []uint64{4, 3}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`62`), [][]byte{secret1, secret4}, []uint64{5, 3}), priority: 8},
{tx: buildMsg(s.T(), txConfig, []byte(`72`), [][]byte{secret3, secret5}, []uint64{4, 3}), priority: 8},
{tx: buildMsg(s.T(), txConfig, []byte(`82`), [][]byte{secret2, secret6}, []uint64{4, 3}), priority: 8},
// test 3
{tx: buildMsg(s.T(), txConfig, []byte(`9`), [][]byte{secret3, secret4}, []uint64{3, 3}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`1052345678910`), [][]byte{secret1, secret2}, []uint64{4, 4}), priority: 8},
{tx: buildMsg(s.T(), txConfig, []byte(`11`), [][]byte{secret1, secret2}, []uint64{5, 5}), priority: 8},
// test 4
{tx: buildMsg(s.T(), txConfig, []byte(`1252345678910`), [][]byte{secret1}, []uint64{3}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`13`), [][]byte{secret1}, []uint64{5}), priority: 10},
{tx: buildMsg(s.T(), txConfig, []byte(`14`), [][]byte{secret1}, []uint64{6}), priority: 8},
}
for i := range testTxs {
bz, err := txConfig.TxEncoder()(testTxs[i].tx)
s.Require().NoError(err)
testTxs[i].bz = bz
testTxs[i].size = int(cmttypes.ComputeProtoSizeForTxs([]cmttypes.Tx{bz}))
}
s.Require().Equal(testTxs[0].size, 111)
s.Require().Equal(testTxs[1].size, 121)
s.Require().Equal(testTxs[2].size, 112)
s.Require().Equal(testTxs[3].size, 112)
s.Require().Equal(testTxs[4].size, 195)
s.Require().Equal(testTxs[5].size, 205)
s.Require().Equal(testTxs[6].size, 196)
s.Require().Equal(testTxs[7].size, 196)
s.Require().Equal(testTxs[8].size, 196)
testCases := map[string]struct {
ctx sdk.Context
txInputs []testTx
req abci.RequestPrepareProposal
handler sdk.PrepareProposalHandler
expectedTxs []int
}{
"skip same-sender non-sequential sequence and then add others txs": {
ctx: s.ctx,
txInputs: []testTx{testTxs[0], testTxs[1], testTxs[2], testTxs[3]},
req: abci.RequestPrepareProposal{
MaxTxBytes: 111 + 112,
},
expectedTxs: []int{0, 3},
},
"skip multi-signers msg non-sequential sequence": {
ctx: s.ctx,
txInputs: []testTx{testTxs[4], testTxs[5], testTxs[6], testTxs[7], testTxs[8]},
req: abci.RequestPrepareProposal{
MaxTxBytes: 195 + 196,
},
expectedTxs: []int{4, 8},
},
"only the first tx is added": {
// Because tx 10 is valid, tx 11 can't be valid as they have higher sequence numbers.
ctx: s.ctx,
txInputs: []testTx{testTxs[9], testTxs[10], testTxs[11]},
req: abci.RequestPrepareProposal{
MaxTxBytes: 195 + 196,
},
expectedTxs: []int{9},
},
"no txs added": {
// Becasuse the first tx was deemed valid but too big, the next expected valid sequence is tx[0].seq (3), so
// the rest of the txs fail because they have a seq of 4.
ctx: s.ctx,
txInputs: []testTx{testTxs[12], testTxs[13], testTxs[14]},
req: abci.RequestPrepareProposal{
MaxTxBytes: 112,
},
expectedTxs: []int{},
},
}
for name, tc := range testCases {
s.Run(name, func() {
ctrl := gomock.NewController(s.T())
app := mock.NewMockProposalTxVerifier(ctrl)
mp := mempool.NewPriorityMempool()
ph := baseapp.NewDefaultProposalHandler(mp, app).PrepareProposalHandler()
for _, v := range tc.txInputs {
app.EXPECT().PrepareProposalVerifyTx(v.tx).Return(v.bz, nil).AnyTimes()
s.NoError(mp.Insert(s.ctx.WithPriority(v.priority), v.tx))
tc.req.Txs = append(tc.req.Txs, v.bz)
}
resp := ph(tc.ctx, tc.req)
respTxIndexes := []int{}
for _, tx := range resp.Txs {
for i, v := range testTxs {
if bytes.Equal(tx, v.bz) {
respTxIndexes = append(respTxIndexes, i)
}
}
}
s.Require().EqualValues(tc.expectedTxs, respTxIndexes)
})
}
}
func marshalDelimitedFn(msg proto.Message) ([]byte, error) {
var buf bytes.Buffer
if err := protoio.NewDelimitedWriter(&buf).WriteMsg(msg); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func buildMsg(t *testing.T, txConfig client.TxConfig, value []byte, secrets [][]byte, nonces []uint64) sdk.Tx {
t.Helper()
builder := txConfig.NewTxBuilder()
_ = builder.SetMsgs(
&baseapptestutil.MsgKeyValue{Value: value},
)
require.Equal(t, len(secrets), len(nonces))
signatures := make([]signingtypes.SignatureV2, 0)
for index, secret := range secrets {
nonce := nonces[index]
privKey := secp256k1.GenPrivKeyFromSecret(secret)
pubKey := privKey.PubKey()
signatures = append(signatures, signingtypes.SignatureV2{
PubKey: pubKey,
Sequence: nonce,
Data: &signingtypes.SingleSignatureData{},
})
}
setTxSignatureWithSecret(t, builder, signatures...)
return builder.GetTx()
}
func setTxSignatureWithSecret(t *testing.T, builder client.TxBuilder, signatures ...signingtypes.SignatureV2) {
t.Helper()
err := builder.SetSignatures(
signatures...,
)
require.NoError(t, err)
}