Skip to content

Commit

Permalink
Problem: parallel tx execution not integrated
Browse files Browse the repository at this point in the history
Solution:
- update ethermint and dependencies
- fix sdk 50 integration
  • Loading branch information
yihuang committed Apr 8, 2024
1 parent 6a16d57 commit c3a6daa
Show file tree
Hide file tree
Showing 99 changed files with 4,295 additions and 4,267 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## UNRELEASED

### State Machine Breaking

* [#]() Upgrade sdk to 0.50, and integrate block-stm parallel tx execution.

*April 2, 2024*

## v1.2.0
Expand All @@ -12,7 +18,6 @@

* [#1366](https://github.com/crypto-org-chain/ethermint/pull/1366) Keep behavior of random opcode as before.


*March 26, 2024*

## v1.1.1
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ endif
###############################################################################

HTTPS_GIT := https://github.com/crypto-org-chain/cronos.git
protoVer=0.11.6
protoVer=0.14.0
protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer)
protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName)

Expand Down
619 changes: 362 additions & 257 deletions app/app.go

Large diffs are not rendered by default.

224 changes: 148 additions & 76 deletions app/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import (
"encoding/binary"
"encoding/json"
"math/big"
"os"
"path/filepath"
"testing"

"cosmossdk.io/log"
sdkmath "cosmossdk.io/math"
dbm "github.com/cometbft/cometbft-db"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/libs/log"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
tmtypes "github.com/cometbft/cometbft/types"
dbm "github.com/cosmos/cosmos-db"
baseapp "github.com/cosmos/cosmos-sdk/baseapp"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/client/flags"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil/mock"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
Expand All @@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
srvflags "github.com/evmos/ethermint/server/flags"
"github.com/evmos/ethermint/tests"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/stretchr/testify/require"
Expand All @@ -34,137 +35,208 @@ import (
func BenchmarkERC20Transfer(b *testing.B) {
b.Run("memdb", func(b *testing.B) {
db := dbm.NewMemDB()
benchmarkERC20Transfer(b, db)
benchmarkERC20Transfer(b, db, true, 16)
})
b.Run("leveldb", func(b *testing.B) {
db, err := dbm.NewGoLevelDB("application", b.TempDir())
db, err := dbm.NewDB("application", dbm.GoLevelDBBackend, b.TempDir())
require.NoError(b, err)
benchmarkERC20Transfer(b, db)
benchmarkERC20Transfer(b, db, true, 16)
})
b.Run("memiavl", func(b *testing.B) {
benchmarkERC20Transfer(b, nil)
b.Run("memiavl-stm-8", func(b *testing.B) {
benchmarkERC20Transfer(b, nil, true, 8)
})
b.Run("memiavl-stm-16", func(b *testing.B) {
benchmarkERC20Transfer(b, nil, true, 16)
})
b.Run("memiavl-stm-32", func(b *testing.B) {
benchmarkERC20Transfer(b, nil, true, 32)
})
b.Run("memiavl-stm-1", func(b *testing.B) {
benchmarkERC20Transfer(b, nil, true, 1)
})
b.Run("memiavl-seq", func(b *testing.B) {
benchmarkERC20Transfer(b, nil, false, 0)
})
}

type TestAccount struct {
Address common.Address
Priv cryptotypes.PrivKey
Nonce uint64
}

// pass `nil` to db to use memiavl
func benchmarkERC20Transfer(b *testing.B, db dbm.DB) {
txsPerBlock := 1000
func benchmarkERC20Transfer(b *testing.B, db dbm.DB, stm bool, stmWorkers int) {
txsPerBlock := 5000
accounts := 100
gasPrice := big.NewInt(100000000000)
var appOpts servertypes.AppOptions = EmptyAppOptions{}
homePath := b.TempDir()
appOpts := make(AppOptionsMap)
appOpts[flags.FlagHome] = homePath

if db == nil {
appOpts = AppOptionsMap(map[string]interface{}{
memiavlstore.FlagMemIAVL: true,
})
require.NoError(b, os.RemoveAll(filepath.Join(DefaultNodeHome, "data/memiavl.db")))
// memiavl
appOpts[memiavlstore.FlagMemIAVL] = true
appOpts[memiavlstore.FlagCacheSize] = 0
}
encodingConfig := MakeEncodingConfig()
app := New(log.NewNopLogger(), db, nil, true, true, map[int64]bool{}, DefaultNodeHome, 0, encodingConfig, appOpts, baseapp.SetChainID(TestAppChainID))

if stm {
// block-stm
appOpts[srvflags.EVMBlockExecutor] = "block-stm"
appOpts[srvflags.EVMBlockSTMWorkers] = stmWorkers
}

app := New(log.NewNopLogger(), db, nil, true, appOpts, baseapp.SetChainID(TestAppChainID))
defer app.Close()

priv, err := ethsecp256k1.GenerateKey()
address := common.BytesToAddress(priv.PubKey().Address().Bytes())
signer := tests.NewSigner(priv)
chainID := big.NewInt(777)
ethSigner := ethtypes.LatestSignerForChainID(chainID)

signTx := func(msg *evmtypes.MsgEthereumTx) ([]byte, error) {
msg.From = address.Bytes()
if err := msg.Sign(ethSigner, signer); err != nil {
var testAccounts []TestAccount
for i := 0; i < accounts; i++ {
priv, err := ethsecp256k1.GenerateKey()
require.NoError(b, err)
address := common.BytesToAddress(priv.PubKey().Address().Bytes())
testAccounts = append(testAccounts, TestAccount{Address: address, Priv: priv})
}

signTx := func(acc *TestAccount, msg *evmtypes.MsgEthereumTx) ([]byte, error) {
msg.From = acc.Address.Bytes()
if err := msg.Sign(ethSigner, tests.NewSigner(acc.Priv)); err != nil {
return nil, err
}
require.NoError(b, err)
tx, err := msg.BuildTx(encodingConfig.TxConfig.NewTxBuilder(), evmtypes.DefaultEVMDenom)
tx, err := msg.BuildTx(app.TxConfig().NewTxBuilder(), evmtypes.DefaultEVMDenom)
if err != nil {
return nil, err
}
return encodingConfig.TxConfig.TxEncoder()(tx)
return app.EncodingConfig().TxConfig.TxEncoder()(tx)
}

privVal := mock.NewPV()
pubKey, err := privVal.GetPubKey()
consAddress := sdk.ConsAddress(pubKey.Address())
require.NoError(b, err)

consAddress := sdk.ConsAddress(pubKey.Address())
validator := tmtypes.NewValidator(pubKey, 1)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
acc := authtypes.NewBaseAccount(priv.PubKey().Address().Bytes(), priv.PubKey(), 0, 0)
balance := banktypes.Balance{
Address: acc.GetAddress().String(),
Coins: sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewIntWithDecimal(10000000, 18))),

var (
balances []banktypes.Balance
accs []authtypes.GenesisAccount
)
for _, acc := range testAccounts {
baseAcct := authtypes.NewBaseAccount(acc.Priv.PubKey().Address().Bytes(), acc.Priv.PubKey(), 0, 0)
accs = append(accs, baseAcct)
balances = append(balances, banktypes.Balance{
Address: baseAcct.GetAddress().String(),
Coins: sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewIntWithDecimal(10000000, 18))),
})
}
genesisState := NewDefaultGenesisState(encodingConfig.Codec)
genesisState = genesisStateWithValSet(b, app, genesisState, valSet, []authtypes.GenesisAccount{acc}, balance)
genesisState, err := simtestutil.GenesisStateWithValSet(
app.AppCodec(),
app.DefaultGenesis(),
valSet,
accs,
balances...,
)
require.NoError(b, err)

appState, err := json.MarshalIndent(genesisState, "", " ")
require.NoError(b, err)
app.InitChain(abci.RequestInitChain{
_, err = app.InitChain(&abci.RequestInitChain{
ChainId: TestAppChainID,
AppStateBytes: appState,
ConsensusParams: DefaultConsensusParams,
})
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: 1,
ChainID: TestAppChainID,
ProposerAddress: consAddress,
},
})
require.NoError(b, err)

// deploy contract
ctx := app.GetContextForDeliverTx(nil)
contractAddr, err := app.CronosKeeper.DeployModuleCRC21(ctx, "test")
require.NoError(b, err)
ctx := app.GetContextForFinalizeBlock(nil).WithBlockHeader(cmtproto.Header{
ChainID: TestAppChainID,
Height: 1,
ProposerAddress: consAddress,
})

// mint to sender
var contractAddr common.Address
amount := int64(100000000)
_, err = app.CronosKeeper.CallModuleCRC21(ctx, contractAddr, "mint_by_cronos_module", address, big.NewInt(amount))

{
ctx, write := ctx.CacheContext()
contractAddr, err = app.CronosKeeper.DeployModuleCRC21(ctx, "test")
require.NoError(b, err)
for _, acc := range testAccounts {
_, err = app.CronosKeeper.CallModuleCRC21(ctx, contractAddr, "mint_by_cronos_module", acc.Address, big.NewInt(amount))
require.NoError(b, err)
}
write()

// check balance
ret, err := app.CronosKeeper.CallModuleCRC21(ctx, contractAddr, "balanceOf", testAccounts[0].Address)
require.NoError(b, err)
require.Equal(b, uint64(amount), binary.BigEndian.Uint64(ret[32-8:]))
}

// do a dummy FinalizeBlock just to flush finalize state
_, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1})
require.NoError(b, err)

// check balance
ret, err := app.CronosKeeper.CallModuleCRC21(ctx, contractAddr, "balanceOf", address)
_, err = app.Commit()
require.NoError(b, err)
require.Equal(b, uint64(amount), binary.BigEndian.Uint64(ret[32-8:]))

app.EndBlock(abci.RequestEndBlock{})
app.Commit()
// check remaining balance
ctx = app.GetContextForCheckTx(nil)

codeRsp, err := app.EvmKeeper.Code(ctx, &evmtypes.QueryCodeRequest{
Address: contractAddr.Hex(),
})
require.NoError(b, err)
require.NotEmpty(b, codeRsp.Code)

// prepare transactions
var transferTxs [][]byte
for i := 0; i < b.N; i++ {
for j := 0; j < txsPerBlock; j++ {
idx := int64(i*txsPerBlock + j)
recipient := common.BigToAddress(big.NewInt(idx))
data, err := types.ModuleCRC21Contract.ABI.Pack("transfer", recipient, big.NewInt(1))
idx := i*txsPerBlock + j
acct := &testAccounts[idx%len(testAccounts)]
data, err := types.ModuleCRC21Contract.ABI.Pack("transfer", acct.Address, big.NewInt(1))
require.NoError(b, err)
bz, err := signTx(evmtypes.NewTx(chainID, uint64(idx), &contractAddr, big.NewInt(0), 210000, gasPrice, nil, nil, data, nil))
tx := evmtypes.NewTx(
TestEthChainID,
acct.Nonce, //nonce
&contractAddr, //to
big.NewInt(0), //value
210000, //gas limit
gasPrice, // gas price
nil, //gasFeeCap
nil, // gasTipCap
data, // data
nil, // access list
)
acct.Nonce++
bz, err := signTx(acct, tx)
require.NoError(b, err)
transferTxs = append(transferTxs, bz)
}
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: int64(i) + 2,
ChainID: TestAppChainID,
ProposerAddress: consAddress,
},
rsp, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{
Txs: transferTxs[i*txsPerBlock : (i+1)*txsPerBlock],
Height: int64(i) + 2,
ProposerAddress: consAddress,
})
for j := 0; j < txsPerBlock; j++ {
idx := i*txsPerBlock + j
res := app.DeliverTx(abci.RequestDeliverTx{
Tx: transferTxs[idx],
})
require.Equal(b, 0, int(res.Code))
require.NoError(b, err)
for _, txResult := range rsp.TxResults {
require.Equal(b, abci.CodeTypeOK, txResult.Code, txResult.Log)
}
_, err = app.Commit()
require.NoError(b, err)

// check remaining balance
ctx := app.GetContextForDeliverTx(nil)
ret, err = app.CronosKeeper.CallModuleCRC21(ctx, contractAddr, "balanceOf", address)
ctx := app.GetContextForCheckTx(nil)
ret, err := app.CronosKeeper.CallModuleCRC21(ctx, contractAddr, "balanceOf", testAccounts[0].Address)
require.NoError(b, err)
require.Equal(b, uint64(amount)-uint64((i+1)*txsPerBlock), binary.BigEndian.Uint64(ret[32-8:]))

app.EndBlock(abci.RequestEndBlock{})
app.Commit()
require.Equal(b, uint64(amount), binary.BigEndian.Uint64(ret[32-8:]))
}
}
11 changes: 8 additions & 3 deletions app/block_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
)

// BlockAddressesDecorator block addresses from sending transactions
Expand All @@ -19,10 +20,14 @@ func NewBlockAddressesDecorator(blacklist map[string]struct{}) BlockAddressesDec

func (bad BlockAddressesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
if ctx.IsCheckTx() {
for _, msg := range tx.GetMsgs() {
for _, signer := range msg.GetSigners() {
if sigTx, ok := tx.(signing.SigVerifiableTx); ok {
signers, err := sigTx.GetSigners()
if err != nil {
return ctx, err
}
for _, signer := range signers {
if _, ok := bad.blockedMap[string(signer)]; ok {
return ctx, fmt.Errorf("signer is blocked: %s", signer.String())
return ctx, fmt.Errorf("signer is blocked: %s", sdk.AccAddress(signer).String())
}
}
}
Expand Down
11 changes: 0 additions & 11 deletions app/encoding.go

This file was deleted.

Loading

0 comments on commit c3a6daa

Please sign in to comment.