From fe22102fe9079651a2b5480a08762e48daecbf6e Mon Sep 17 00:00:00 2001 From: Steven Landers Date: Wed, 17 Jul 2024 15:38:56 -0400 Subject: [PATCH 1/2] add test that reproduces issue --- go.mod | 2 - go.sum | 3 -- occ_tests/messages/test_msgs.go | 28 ++++++++---- occ_tests/occ_test.go | 80 ++++++++++++++++++++++++++++++--- occ_tests/utils/utils.go | 9 ++-- 5 files changed, 98 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index d1936690e..1aae20b4c 100644 --- a/go.mod +++ b/go.mod @@ -125,7 +125,6 @@ require ( github.com/fzipp/gocyclo v0.5.1 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect - github.com/ghodss/yaml v1.0.0 // indirect github.com/go-critic/go-critic v0.6.3 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -173,7 +172,6 @@ require ( github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect diff --git a/go.sum b/go.sum index 4ccbfb81a..844170003 100644 --- a/go.sum +++ b/go.sum @@ -457,7 +457,6 @@ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2Gihuqh github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -766,8 +765,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= diff --git a/occ_tests/messages/test_msgs.go b/occ_tests/messages/test_msgs.go index 6ea0d5c65..e505df6fb 100644 --- a/occ_tests/messages/test_msgs.go +++ b/occ_tests/messages/test_msgs.go @@ -63,7 +63,7 @@ func EVMTransferNonConflicting(tCtx *utils.TestContext, count int) []*utils.Test var msgs []*utils.TestMessage for i := 0; i < count; i++ { testAcct := utils.NewSigner() - msgs = append(msgs, evmTransfer(testAcct, testAcct.EvmAddress, "EVMTransferNonConflicting")) + msgs = append(msgs, evmTransfer(testAcct, testAcct.EvmAddress, "EVMTransferNonConflicting", big.NewInt(1), 0)) } return msgs } @@ -73,22 +73,31 @@ func EVMTransferConflicting(tCtx *utils.TestContext, count int) []*utils.TestMes var msgs []*utils.TestMessage for i := 0; i < count; i++ { testAcct := utils.NewSigner() - msgs = append(msgs, evmTransfer(testAcct, tCtx.TestAccounts[0].EvmAddress, "EVMTransferConflicting")) + msgs = append(msgs, evmTransfer(testAcct, tCtx.TestAccounts[0].EvmAddress, "EVMTransferConflicting", big.NewInt(1), 0)) + } + return msgs +} + +func EVMTransferWithAmount(tCtx *utils.TestContext, amount *big.Int, count int) []*utils.TestMessage { + var msgs []*utils.TestMessage + testAcct := utils.NewSigner() + for i := 0; i < count; i++ { + msgs = append(msgs, evmTransfer(testAcct, tCtx.TestAccounts[0].EvmAddress, "EVMTransferOutOfFunds", amount, uint64(i))) } return msgs } // EVMTransferNonConflicting generates a list of EVM transfer messages that do not conflict with each other // each message will have a brand new address -func evmTransfer(testAcct utils.TestAcct, to common.Address, scenario string) *utils.TestMessage { +func evmTransfer(testAcct utils.TestAcct, to common.Address, scenario string, amount *big.Int, nonce uint64) *utils.TestMessage { signedTx, err := ethtypes.SignTx(ethtypes.NewTx(ðtypes.DynamicFeeTx{ GasFeeCap: new(big.Int).SetUint64(1000000000000), GasTipCap: new(big.Int).SetUint64(1000000000000), Gas: 21000, ChainID: big.NewInt(config.DefaultChainID), To: &to, - Value: big.NewInt(1), - Nonce: 0, + Value: amount, + Nonce: nonce, }), testAcct.EvmSigner, testAcct.EvmPrivateKey) if err != nil { @@ -106,10 +115,11 @@ func evmTransfer(testAcct utils.TestAcct, to common.Address, scenario string) *u } return &utils.TestMessage{ - Msg: msg, - IsEVM: true, - EVMSigner: testAcct, - Type: scenario, + EVMTransaction: signedTx, + Msg: msg, + IsEVM: true, + EVMSigner: testAcct, + Type: scenario, } } diff --git a/occ_tests/occ_test.go b/occ_tests/occ_test.go index e78e6f4a4..4ab3a513f 100644 --- a/occ_tests/occ_test.go +++ b/occ_tests/occ_test.go @@ -2,18 +2,50 @@ package occ import ( "fmt" + "math/big" "reflect" "testing" "time" "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/occ_tests/messages" "github.com/sei-protocol/sei-chain/occ_tests/utils" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/types" ) +func assertReceiptStatuses(t *testing.T, tc *utils.TestContext, results []*types.ExecTxResult, txs []*utils.TestMessage) { + for i, tx := range txs { + if tx.IsEVM { + r, err := tc.TestApp.EvmKeeper.GetTransientReceipt(tc.Ctx, tx.EVMTransaction.Hash()) + require.NoError(t, err) + if results[i].Code == 0 { + require.Equal(t, uint32(1), r.Status, "EVM transaction should have a successful receipt") + } else { + require.Equal(t, uint32(0), r.Status, "EVM transaction should have a failed receipt") + } + } + } +} + +func assertNonces(t *testing.T, tc *utils.TestContext, txs []*utils.TestMessage) { + nonces := make(map[common.Address]uint64) + for _, tx := range txs { + if tx.IsEVM { + highest, _ := nonces[tx.EVMSigner.EvmAddress] + if tx.EVMTransaction.Nonce() > highest { + nonces[tx.EVMSigner.EvmAddress] = tx.EVMTransaction.Nonce() + } + } + } + for addr, latestNonce := range nonces { + nonce := tc.TestApp.EvmKeeper.GetNonce(tc.Ctx, addr) + require.Equal(t, latestNonce+1, nonce, "Nonce does not match for address %s", addr) + } +} + func assertEqualState(t *testing.T, expectedCtx sdk.Context, actualCtx sdk.Context, testName string) { expectedStoreKeys := expectedCtx.MultiStore().StoreKeys() actualStoreKeys := actualCtx.MultiStore().StoreKeys() @@ -79,6 +111,13 @@ func assertExecTxResultCode(t *testing.T, expected, actual []*types.ExecTxResult } } +func assertSameResultCodes(t *testing.T, expected, actual []*types.ExecTxResult, testName string) { + require.Equal(t, len(expected), len(actual), testName) + for i, e := range expected { + require.Equal(t, e.Code, actual[i].Code, "%s: Expected code %d, got %d", testName, e.Code, e.Code) + } +} + // assertEqualExecTxResults checks if both slices have the same transaction results, regardless of order. func assertEqualExecTxResults(t *testing.T, expected, actual []*types.ExecTxResult, testName string) { require.Equal(t, len(expected), len(actual), "%s: Number of transaction results do not match", testName) @@ -101,12 +140,16 @@ func assertEqualExecTxResults(t *testing.T, expected, actual []*types.ExecTxResu // between both parallel and sequential executions func TestParallelTransactions(t *testing.T) { runs := 3 + largeAmt := big.NewInt(0) + largeAmt.SetString("2500000000000000000000000000000", 10) + tests := []struct { - name string - runs int - shuffle bool - before func(tCtx *utils.TestContext) - txs func(tCtx *utils.TestContext) []*utils.TestMessage + name string + runs int + shuffle bool + expectFail bool + before func(tCtx *utils.TestContext) + txs func(tCtx *utils.TestContext) []*utils.TestMessage }{ { name: "Test wasm instantiations", @@ -153,6 +196,17 @@ func TestParallelTransactions(t *testing.T) { ) }, }, + { + name: "Test evm out of funds", + runs: 1, + expectFail: true, + txs: func(tCtx *utils.TestContext) []*utils.TestMessage { + return utils.JoinMsgs( + // will produce one success, one out of funds and one nonce-too-high + messages.EVMTransferWithAmount(tCtx, largeAmt, 3), + ) + }, + }, { name: "Test combinations", runs: runs, @@ -199,10 +253,24 @@ func TestParallelTransactions(t *testing.T) { require.NoError(t, pErr, tt.name) require.Len(t, pResults, len(txs)) - assertExecTxResultCode(t, sResults, pResults, 0, tt.name) + if !tt.expectFail { + // should be zero (so if it fails on both this will discover it) + assertExecTxResultCode(t, sResults, pResults, 0, tt.name) + } + + // should match synchronous & parallel results + assertSameResultCodes(t, sResults, pResults, tt.name) assertEqualEvents(t, sEvts, pEvts, tt.name) assertEqualExecTxResults(t, sResults, pResults, tt.name) assertEqualState(t, sCtx.Ctx, pCtx.Ctx, tt.name) + + // should have correct receipt status + assertReceiptStatuses(t, sCtx, sResults, txs) + assertReceiptStatuses(t, pCtx, pResults, txs) + + // should always update nonce + assertNonces(t, pCtx, txs) + assertNonces(t, sCtx, txs) } }) } diff --git a/occ_tests/utils/utils.go b/occ_tests/utils/utils.go index 877f92db1..efc797ebd 100644 --- a/occ_tests/utils/utils.go +++ b/occ_tests/utils/utils.go @@ -44,10 +44,11 @@ var ignoredStoreKeys = map[string]struct{}{ } type TestMessage struct { - Msg sdk.Msg - Type string - EVMSigner TestAcct - IsEVM bool + EVMTransaction *ethtypes.Transaction + Msg sdk.Msg + Type string + EVMSigner TestAcct + IsEVM bool } type TestContext struct { From 0e558b921b87569c8bb727f7a1a7451beb201e1c Mon Sep 17 00:00:00 2001 From: Steven Landers Date: Wed, 17 Jul 2024 16:09:58 -0400 Subject: [PATCH 2/2] cleanup --- occ_tests/occ_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/occ_tests/occ_test.go b/occ_tests/occ_test.go index 4ab3a513f..fc4eed9bb 100644 --- a/occ_tests/occ_test.go +++ b/occ_tests/occ_test.go @@ -140,8 +140,6 @@ func assertEqualExecTxResults(t *testing.T, expected, actual []*types.ExecTxResu // between both parallel and sequential executions func TestParallelTransactions(t *testing.T) { runs := 3 - largeAmt := big.NewInt(0) - largeAmt.SetString("2500000000000000000000000000000", 10) tests := []struct { name string @@ -201,6 +199,9 @@ func TestParallelTransactions(t *testing.T) { runs: 1, expectFail: true, txs: func(tCtx *utils.TestContext) []*utils.TestMessage { + largeAmt := big.NewInt(0) + largeAmt.SetString("2500000000000000000000000000000", 10) + return utils.JoinMsgs( // will produce one success, one out of funds and one nonce-too-high messages.EVMTransferWithAmount(tCtx, largeAmt, 3),