github.com/cosmos/cosmos-sdk@v0.50.10/types/mempool/mempool_test.go (about) 1 package mempool_test 2 3 import ( 4 "fmt" 5 "math/rand" 6 "testing" 7 8 cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 9 "github.com/stretchr/testify/require" 10 "github.com/stretchr/testify/suite" 11 protov2 "google.golang.org/protobuf/proto" 12 13 "cosmossdk.io/log" 14 15 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 16 sdk "github.com/cosmos/cosmos-sdk/types" 17 "github.com/cosmos/cosmos-sdk/types/mempool" 18 moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 19 simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 20 txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" 21 "github.com/cosmos/cosmos-sdk/x/auth/signing" 22 "github.com/cosmos/cosmos-sdk/x/distribution" 23 "github.com/cosmos/cosmos-sdk/x/gov" 24 ) 25 26 // testPubKey is a dummy implementation of PubKey used for testing. 27 type testPubKey struct { 28 address sdk.AccAddress 29 } 30 31 func (t testPubKey) Reset() { panic("not implemented") } 32 33 func (t testPubKey) String() string { panic("not implemented") } 34 35 func (t testPubKey) ProtoMessage() { panic("not implemented") } 36 37 func (t testPubKey) Address() cryptotypes.Address { return t.address.Bytes() } 38 39 func (t testPubKey) Bytes() []byte { panic("not implemented") } 40 41 func (t testPubKey) VerifySignature(msg, sig []byte) bool { panic("not implemented") } 42 43 func (t testPubKey) Equals(key cryptotypes.PubKey) bool { panic("not implemented") } 44 45 func (t testPubKey) Type() string { panic("not implemented") } 46 47 // testTx is a dummy implementation of Tx used for testing. 48 type testTx struct { 49 id int 50 priority int64 51 nonce uint64 52 address sdk.AccAddress 53 // useful for debugging 54 strAddress string 55 } 56 57 func (tx testTx) GetSigners() ([][]byte, error) { panic("not implemented") } 58 59 func (tx testTx) GetPubKeys() ([]cryptotypes.PubKey, error) { panic("not implemented") } 60 61 func (tx testTx) GetSignaturesV2() (res []txsigning.SignatureV2, err error) { 62 res = append(res, txsigning.SignatureV2{ 63 PubKey: testPubKey{address: tx.address}, 64 Data: nil, 65 Sequence: tx.nonce, 66 }) 67 68 return res, nil 69 } 70 71 var ( 72 _ sdk.Tx = (*testTx)(nil) 73 _ signing.SigVerifiableTx = (*testTx)(nil) 74 _ cryptotypes.PubKey = (*testPubKey)(nil) 75 ) 76 77 func (tx testTx) GetMsgs() []sdk.Msg { return nil } 78 79 func (tx testTx) GetMsgsV2() ([]protov2.Message, error) { return nil, nil } 80 81 func (tx testTx) ValidateBasic() error { return nil } 82 83 func (tx testTx) String() string { 84 return fmt.Sprintf("tx a: %s, p: %d, n: %d", tx.address, tx.priority, tx.nonce) 85 } 86 87 type sigErrTx struct { 88 getSigs func() ([]txsigning.SignatureV2, error) 89 } 90 91 func (sigErrTx) Size() int64 { return 0 } 92 93 func (sigErrTx) GetMsgs() []sdk.Msg { return nil } 94 95 func (sigErrTx) GetMsgsV2() ([]protov2.Message, error) { return nil, nil } 96 97 func (sigErrTx) ValidateBasic() error { return nil } 98 99 func (sigErrTx) GetSigners() ([][]byte, error) { return nil, nil } 100 101 func (sigErrTx) GetPubKeys() ([]cryptotypes.PubKey, error) { return nil, nil } 102 103 func (t sigErrTx) GetSignaturesV2() ([]txsigning.SignatureV2, error) { return t.getSigs() } 104 105 type txSpec struct { 106 i int 107 p int 108 n int 109 a sdk.AccAddress 110 } 111 112 func (tx txSpec) String() string { 113 return fmt.Sprintf("[tx i: %d, a: %s, p: %d, n: %d]", tx.i, tx.a, tx.p, tx.n) 114 } 115 116 func fetchTxs(iterator mempool.Iterator, maxBytes int64) []sdk.Tx { 117 const txSize = 1 118 var ( 119 txs []sdk.Tx 120 numBytes int64 121 ) 122 for iterator != nil { 123 if numBytes += txSize; numBytes > maxBytes { 124 break 125 } 126 txs = append(txs, iterator.Tx()) 127 i := iterator.Next() 128 iterator = i 129 } 130 return txs 131 } 132 133 func (s *MempoolTestSuite) TestDefaultMempool() { 134 t := s.T() 135 ctx := sdk.NewContext(nil, cmtproto.Header{}, false, log.NewNopLogger()) 136 accounts := simtypes.RandomAccounts(rand.New(rand.NewSource(0)), 10) 137 txCount := 1000 138 var txs []testTx 139 140 for i := 0; i < txCount; i++ { 141 acc := accounts[i%len(accounts)] 142 tx := testTx{ 143 nonce: 0, 144 address: acc.Address, 145 priority: rand.Int63(), 146 } 147 txs = append(txs, tx) 148 } 149 150 // empty mempool behavior 151 require.Equal(t, 0, s.mempool.CountTx()) 152 itr := s.mempool.Select(ctx, nil) 153 require.Nil(t, itr) 154 155 // same sender-nonce just overwrites a tx 156 for _, tx := range txs { 157 ctx = ctx.WithPriority(tx.priority) 158 err := s.mempool.Insert(ctx, tx) 159 require.NoError(t, err) 160 } 161 require.Equal(t, len(accounts), s.mempool.CountTx()) 162 163 // distinct sender-nonce should not overwrite a tx 164 s.resetMempool() 165 for i, tx := range txs { 166 tx.nonce = uint64(i) 167 err := s.mempool.Insert(ctx, tx) 168 require.NoError(t, err) 169 } 170 require.Equal(t, txCount, s.mempool.CountTx()) 171 172 itr = s.mempool.Select(ctx, nil) 173 sel := fetchTxs(itr, 13) 174 require.Equal(t, 13, len(sel)) 175 176 // a tx which does not implement SigVerifiableTx should not be inserted 177 tx := &sigErrTx{getSigs: func() ([]txsigning.SignatureV2, error) { 178 return nil, fmt.Errorf("error") 179 }} 180 require.Error(t, s.mempool.Insert(ctx, tx)) 181 require.Error(t, s.mempool.Remove(tx)) 182 tx.getSigs = func() ([]txsigning.SignatureV2, error) { 183 return nil, nil 184 } 185 require.Error(t, s.mempool.Insert(ctx, tx)) 186 require.Error(t, s.mempool.Remove(tx)) 187 188 // removing a tx not in the mempool should error 189 s.resetMempool() 190 require.NoError(t, s.mempool.Insert(ctx, txs[0])) 191 require.ErrorIs(t, s.mempool.Remove(txs[1]), mempool.ErrTxNotFound) 192 193 // inserting a tx with a different priority should overwrite the old tx 194 newPriorityTx := testTx{ 195 address: txs[0].address, 196 priority: txs[0].priority + 1, 197 nonce: txs[0].nonce, 198 } 199 require.NoError(t, s.mempool.Insert(ctx, newPriorityTx)) 200 require.Equal(t, 1, s.mempool.CountTx()) 201 } 202 203 type MempoolTestSuite struct { 204 suite.Suite 205 numTxs int 206 numAccounts int 207 iterations int 208 mempool mempool.Mempool 209 } 210 211 func (s *MempoolTestSuite) resetMempool() { 212 s.iterations = 0 213 s.mempool = mempool.NewSenderNonceMempool(mempool.SenderNonceMaxTxOpt(5000)) 214 } 215 216 func (s *MempoolTestSuite) SetupTest() { 217 s.numTxs = 1000 218 s.numAccounts = 100 219 s.resetMempool() 220 } 221 222 func TestMempoolTestSuite(t *testing.T) { 223 suite.Run(t, new(MempoolTestSuite)) 224 } 225 226 func (s *MempoolTestSuite) TestSampleTxs() { 227 ctxt := sdk.NewContext(nil, cmtproto.Header{}, false, log.NewNopLogger()) 228 t := s.T() 229 s.resetMempool() 230 mp := s.mempool 231 delegatorTx, err := unmarshalTx(msgWithdrawDelegatorReward) 232 233 require.NoError(t, err) 234 require.NoError(t, mp.Insert(ctxt, delegatorTx)) 235 require.Equal(t, 1, mp.CountTx()) 236 237 proposalTx, err := unmarshalTx(msgMultiSigMsgSubmitProposal) 238 require.NoError(t, err) 239 require.NoError(t, mp.Insert(ctxt, proposalTx)) 240 require.Equal(t, 2, mp.CountTx()) 241 } 242 243 func unmarshalTx(txBytes []byte) (sdk.Tx, error) { 244 cfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}, gov.AppModuleBasic{}) 245 return cfg.TxConfig.TxJSONDecoder()(txBytes) 246 } 247 248 var ( 249 msgWithdrawDelegatorReward = []byte("{\"body\":{\"messages\":[{\"@type\":\"\\/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\",\"delegator_address\":\"cosmos16w6g0whmw703t8h2m9qmq2fd9dwaw6fjszzjsw\",\"validator_address\":\"cosmosvaloper1lzhlnpahvznwfv4jmay2tgaha5kmz5qxerarrl\"},{\"@type\":\"\\/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\",\"delegator_address\":\"cosmos16w6g0whmw703t8h2m9qmq2fd9dwaw6fjszzjsw\",\"validator_address\":\"cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0\"},{\"@type\":\"\\/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\",\"delegator_address\":\"cosmos16w6g0whmw703t8h2m9qmq2fd9dwaw6fjszzjsw\",\"validator_address\":\"cosmosvaloper196ax4vc0lwpxndu9dyhvca7jhxp70rmcvrj90c\"},{\"@type\":\"\\/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\",\"delegator_address\":\"cosmos16w6g0whmw703t8h2m9qmq2fd9dwaw6fjszzjsw\",\"validator_address\":\"cosmosvaloper1k2d9ed9vgfuk2m58a2d80q9u6qljkh4vfaqjfq\"},{\"@type\":\"\\/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\",\"delegator_address\":\"cosmos16w6g0whmw703t8h2m9qmq2fd9dwaw6fjszzjsw\",\"validator_address\":\"cosmosvaloper1vygmh344ldv9qefss9ek7ggsnxparljlmj56q5\"},{\"@type\":\"\\/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward\",\"delegator_address\":\"cosmos16w6g0whmw703t8h2m9qmq2fd9dwaw6fjszzjsw\",\"validator_address\":\"cosmosvaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5wxxp9gd\"}],\"memo\":\"\",\"timeout_height\":\"0\",\"extension_options\":[],\"non_critical_extension_options\":[]},\"auth_info\":{\"signer_infos\":[{\"public_key\":{\"@type\":\"\\/cosmos.crypto.secp256k1.PubKey\",\"key\":\"AmbXAy10a0SerEefTYQzqyGQdX5kiTEWJZ1PZKX1oswX\"},\"mode_info\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"119\"}],\"fee\":{\"amount\":[{\"denom\":\"uatom\",\"amount\":\"15968\"}],\"gas_limit\":\"638717\",\"payer\":\"\",\"granter\":\"\"}},\"signatures\":[\"ji+inUo4xGlN9piRQLdLCeJWa7irwnqzrMVPcmzJyG5y6NPc+ZuNaIc3uvk5NLDJytRB8AHX0GqNETR\\/Q8fz4Q==\"]}") 250 msgMultiSigMsgSubmitProposal = []byte("{\"body\":{\"messages\":[{\"@type\":\"\\/cosmos.gov.v1beta1.MsgSubmitProposal\",\"content\":{\"@type\":\"\\/cosmos.distribution.v1beta1.CommunityPoolSpendProposal\",\"title\":\"ATOM \\ud83e\\udd1d Osmosis: Allocate Community Pool to ATOM Liquidity Incentives\",\"description\":\"ATOMs should be the base money of Cosmos, just like ETH is the base money of the entire Ethereum DeFi ecosystem. ATOM is currently well positioned to play this role among Cosmos assets because it has the highest market cap, most liquidity, largest brand, and many integrations with fiat onramps. ATOM is the gateway to Cosmos.\\n\\nIn the Cosmos Hub Port City vision, ATOMs are pitched as equity in the Cosmos Hub. However, this alone is insufficient to establish ATOM as the base currency of the Cosmos ecosystem as a whole. Instead, the ATOM community must work to actively promote the use of ATOMs throughout the Cosmos ecosystem, rather than passively relying on the Hub's reputation to create ATOM's value.\\n\\nIn order to cement the role of ATOMs in Cosmos DeFi, the Cosmos Hub should leverage its community pool to help align incentives with other protocols within the Cosmos ecosystem. We propose beginning this initiative by using the community pool ATOMs to incentivize deep ATOM base pair liquidity pools on the Osmosis Network.\\n\\nOsmosis is the first IBC-enabled DeFi application. Within its 3 weeks of existence, it has already 100x\\u2019d the number of IBC transactions ever created, demonstrating the power of IBC and the ability of the Cosmos SDK to bootstrap DeFi protocols with $100M+ TVL in a short period of time. Since its announcement Osmosis has helped bring renewed attention and interest to Cosmos from the crypto community at large and kickstarted the era of Cosmos DeFi.\\n\\nOsmosis has already helped in establishing ATOM as the Schelling Point of the Cosmos ecosystem. The genesis distribution of OSMO was primarily based on an airdrop to ATOM holders specifically, acknowledging the importance of ATOM to all future projects within the Cosmos. Furthermore, the Osmosis LP rewards currently incentivize ATOMs to be one of the main base pairs of the platform.\\n\\nOsmosis has the ability to incentivize AMM liquidity, a feature not available on any other IBC-enabled DEX. Osmosis already uses its own native OSMO liquidity rewards to incentivize ATOMs to be one of the main base pairs, leading to ~2.2 million ATOMs already providing liquidity on the platform.\\n\\nIn addition to these native OSMO LP Rewards, the platform also includes a feature called \\u201cexternal incentives\\u201d that allows anyone to permissionlessly add additional incentives in any token to the LPs of any AMM pools they wish. You can read more about this mechanism here: https:\\/\\/medium.com\\/osmosis\\/osmosis-liquidity-mining-101-2fa58d0e9d4d#f413 . Pools containing Cosmos assets such as AKT and XPRT are already planned to receive incentives from their respective community pools and\\/or foundations.\\n\\nWe propose the Cosmos Hub dedicate 100,000 ATOMs from its Community Pool to be allocated towards liquidity incentives on Osmosis over the next 3 months. This community fund proposal will transfer 100,000 ATOMs to a multisig group who will then allocate the ATOMs to bonded liquidity gauges on Osmosis on a biweekly basis, according to direction given by Cosmos Hub governance. For simplicity, we propose setting the liquidity incentives to initially point to Osmosis Pool #1, the ATOM\\/OSMO pool, which is the pool with by far the highest TVL and Volume. Cosmos Hub governance can then use Text Proposals to further direct the multisig members to reallocate incentives to new pools.\\n\\nThe multisig will consist of a 2\\/3 key holder set consisting of the following individuals whom have all agreed to participate in this process shall this proposal pass:\\n\\n- Zaki Manian\\n- Federico Kunze\\n- Marko Baricevic\\n\\nThis is one small step for the Hub, but one giant leap for ATOM-aligned.\\n\",\"recipient\":\"cosmos157n0d38vwn5dvh64rc39q3lyqez0a689g45rkc\",\"amount\":[{\"denom\":\"uatom\",\"amount\":\"100000000000\"}]},\"initial_deposit\":[{\"denom\":\"uatom\",\"amount\":\"64000000\"}],\"proposer\":\"cosmos1ey69r37gfxvxg62sh4r0ktpuc46pzjrmz29g45\"}],\"memo\":\"\",\"timeout_height\":\"0\",\"extension_options\":[],\"non_critical_extension_options\":[]},\"auth_info\":{\"signer_infos\":[{\"public_key\":{\"@type\":\"\\/cosmos.crypto.multisig.LegacyAminoPubKey\",\"threshold\":2,\"public_keys\":[{\"@type\":\"\\/cosmos.crypto.secp256k1.PubKey\",\"key\":\"AldOvgv8dU9ZZzuhGydQD5FYreLhfhoBgrDKi8ZSTbCQ\"},{\"@type\":\"\\/cosmos.crypto.secp256k1.PubKey\",\"key\":\"AxUMR\\/GKoycWplR+2otzaQZ9zhHRQWJFt3h1bPg1ltha\"},{\"@type\":\"\\/cosmos.crypto.secp256k1.PubKey\",\"key\":\"AlI9yVj2Aejow6bYl2nTRylfU+9LjQLEl3keq0sERx9+\"},{\"@type\":\"\\/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A0UvHPcvCCaIoFY9Ygh0Pxq9SZTAWtduOyinit\\/8uo+Q\"},{\"@type\":\"\\/cosmos.crypto.secp256k1.PubKey\",\"key\":\"As7R9fDUnwsUVLDr1cxspp+cY9UfXfUf7i9\\/w+N0EzKA\"}]},\"mode_info\":{\"multi\":{\"bitarray\":{\"extra_bits_stored\":5,\"elems\":\"SA==\"},\"mode_infos\":[{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}}]}},\"sequence\":\"102\"}],\"fee\":{\"amount\":[],\"gas_limit\":\"10000000\",\"payer\":\"\",\"granter\":\"\"}},\"signatures\":[\"CkB\\/KKWTFntEWbg1A0vu7DCHffJ4x4db\\/EI8dIVzRFFW7iuZBzvq+jYBtrcTlVpEVfmCY3ggIMnWfbMbb1egIlYbCkAmDf6Eaj1NbyXY8JZZtYAX3Qj81ZuKZUBeLW1ZvH1XqAg9sl\\/sqpLMnsJzKfmqEXvhoMwu1YxcSzrY6CJfuYL6\"]}") 251 )