From 4ab9d68e74a07a29bef2ef7bee3f62bda0dc8fe2 Mon Sep 17 00:00:00 2001 From: Steven Landers Date: Fri, 19 Jan 2024 11:05:40 -0500 Subject: [PATCH] [EVM] Allow multiple txs from same account in a block (#397) ## Describe your changes and provide context - needs tendermint pr and go.mod update - adds evm properties to the `ResponseCheckTxV2` - adds evm properties to context ## Testing performed to validate your change - hardhat tests on sei-chain repo - unit tests on tendermint repo --- baseapp/abci.go | 21 ++++++++------ baseapp/baseapp.go | 13 +++++---- baseapp/test_helpers.go | 6 ++-- go.mod | 2 +- go.sum | 4 +-- types/context.go | 63 +++++++++++++++++++++++++++++++---------- 6 files changed, 74 insertions(+), 35 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 4f7d36913..148de4fc6 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -227,22 +227,27 @@ func (app *BaseApp) CheckTx(ctx context.Context, req *abci.RequestCheckTx) (*abc res := sdkerrors.ResponseCheckTx(err, 0, 0, app.trace) return &abci.ResponseCheckTxV2{ResponseCheckTx: &res}, err } - gInfo, result, _, priority, pendingTxChecker, expireTxHandler, err := app.runTx(sdkCtx, mode, tx, sha256.Sum256(req.Tx)) + gInfo, result, _, priority, pendingTxChecker, expireTxHandler, txCtx, err := app.runTx(sdkCtx, mode, tx, sha256.Sum256(req.Tx)) if err != nil { res := sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) return &abci.ResponseCheckTxV2{ResponseCheckTx: &res}, err } - res := &abci.ResponseCheckTxV2{ResponseCheckTx: &abci.ResponseCheckTx{ - GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints? - Data: result.Data, - Priority: priority, - }} + res := &abci.ResponseCheckTxV2{ + ResponseCheckTx: &abci.ResponseCheckTx{ + GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints? + Data: result.Data, + Priority: priority, + }, + ExpireTxHandler: expireTxHandler, + EVMNonce: txCtx.EVMNonce(), + EVMSenderAddress: txCtx.EVMSenderAddress(), + IsEVM: txCtx.IsEVM(), + } if pendingTxChecker != nil { res.IsPendingTransaction = true res.Checker = pendingTxChecker } - res.ExpireTxHandler = expireTxHandler return res, nil } @@ -272,7 +277,7 @@ func (app *BaseApp) DeliverTx(ctx sdk.Context, req abci.RequestDeliverTx, tx sdk telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted") }() - gInfo, result, anteEvents, _, _, _, err := app.runTx(ctx.WithTxBytes(req.Tx).WithVoteInfos(app.voteInfos), runTxModeDeliver, tx, checksum) + gInfo, result, anteEvents, _, _, _, _, err := app.runTx(ctx.WithTxBytes(req.Tx).WithVoteInfos(app.voteInfos), runTxModeDeliver, tx, checksum) if err != nil { resultStr = "failed" // if we have a result, use those events instead of just the anteEvents diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 66c8f35f1..62ac9fe1b 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -829,6 +829,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ priority int64, pendingTxChecker abci.PendingTxChecker, expireHandler abci.ExpireTxHandler, + txCtx sdk.Context, err error, ) { defer telemetry.MeasureThroughputSinceWithLabels( @@ -864,7 +865,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ // only run the tx if there is block gas remaining if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() { - return gInfo, nil, nil, -1, nil, nil, sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "no block gas left to run tx") + return gInfo, nil, nil, -1, nil, nil, ctx, sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "no block gas left to run tx") } defer func() { @@ -901,13 +902,13 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ } if tx == nil { - return sdk.GasInfo{}, nil, nil, 0, nil, nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx decode error") + return sdk.GasInfo{}, nil, nil, 0, nil, nil, ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx decode error") } msgs := tx.GetMsgs() if err := validateBasicTxMsgs(msgs); err != nil { - return sdk.GasInfo{}, nil, nil, 0, nil, nil, err + return sdk.GasInfo{}, nil, nil, 0, nil, nil, ctx, err } if app.anteHandler != nil { @@ -949,7 +950,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ // GasMeter expected to be set in AnteHandler gasWanted = ctx.GasMeter().Limit() if err != nil { - return gInfo, nil, nil, 0, nil, nil, err + return gInfo, nil, nil, 0, nil, nil, ctx, err } // Dont need to validate in checkTx mode @@ -964,7 +965,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ op.EmitValidationFailMetrics() } errMessage := fmt.Sprintf("Invalid Concurrent Execution antehandler missing %d access operations", len(missingAccessOps)) - return gInfo, nil, nil, 0, nil, nil, sdkerrors.Wrap(sdkerrors.ErrInvalidConcurrencyExecution, errMessage) + return gInfo, nil, nil, 0, nil, nil, ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidConcurrencyExecution, errMessage) } } @@ -1000,7 +1001,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ if ctx.CheckTxCallback() != nil { ctx.CheckTxCallback()(err) } - return gInfo, result, anteEvents, priority, pendingTxChecker, expireHandler, err + return gInfo, result, anteEvents, priority, pendingTxChecker, expireHandler, ctx, err } // runMsgs iterates through a list of messages and executes them with the provided diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 1cc05327a..dc8b5150f 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -17,7 +17,7 @@ func (app *BaseApp) Check(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk return sdk.GasInfo{}, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err) } ctx := app.checkState.ctx.WithTxBytes(bz).WithVoteInfos(app.voteInfos).WithConsensusParams(app.GetConsensusParams(app.checkState.ctx)) - gasInfo, result, _, _, _, _, err := app.runTx(ctx, runTxModeCheck, tx, sha256.Sum256(bz)) + gasInfo, result, _, _, _, _, _, err := app.runTx(ctx, runTxModeCheck, tx, sha256.Sum256(bz)) if len(ctx.MultiStore().GetEvents()) > 0 { panic("Expected checkTx events to be empty") } @@ -31,7 +31,7 @@ func (app *BaseApp) Simulate(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) { if err != nil { return sdk.GasInfo{}, nil, err } - gasInfo, result, _, _, _, _, err := app.runTx(ctx, runTxModeSimulate, tx, sha256.Sum256(txBytes)) + gasInfo, result, _, _, _, _, _, err := app.runTx(ctx, runTxModeSimulate, tx, sha256.Sum256(txBytes)) if len(ctx.MultiStore().GetEvents()) > 0 { panic("Expected simulate events to be empty") } @@ -49,7 +49,7 @@ func (app *BaseApp) Deliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *s if err != nil { return sdk.GasInfo{}, &sdk.Result{}, err } - gasInfo, result, _, _, _, _, err := app.runTx(ctx, runTxModeDeliver, decoded, sha256.Sum256(bz)) + gasInfo, result, _, _, _, _, _, err := app.runTx(ctx, runTxModeDeliver, decoded, sha256.Sum256(bz)) return gasInfo, result, err } diff --git a/go.mod b/go.mod index 9a1daef0b..b762151f5 100644 --- a/go.mod +++ b/go.mod @@ -183,7 +183,7 @@ replace ( github.com/sei-protocol/sei-db => github.com/sei-protocol/sei-db v0.0.25 // Latest goleveldb is broken, we have to stick to this version github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.2.36-evm-rebase + github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.2.36-evm-multi-tx-per-account // latest grpc doesn't work with with our modified proto compiler, so we need to enforce // the following version across all dependencies. google.golang.org/grpc => google.golang.org/grpc v1.33.2 diff --git a/go.sum b/go.sum index 4c26b243b..b1d34b0da 100644 --- a/go.sum +++ b/go.sum @@ -785,8 +785,8 @@ github.com/sei-protocol/sei-db v0.0.25 h1:jC1ivcaNxSR7EmxqvxexqPpnN/G0vUTZNHZI+C github.com/sei-protocol/sei-db v0.0.25/go.mod h1:F/ZKZA8HJPcUzSZPA8yt6pfwlGriJ4RDR4eHKSGLStI= github.com/sei-protocol/sei-iavl v0.1.9 h1:y4mVYftxLNRs6533zl7N0/Ch+CzRQc04JDfHolIxgBE= github.com/sei-protocol/sei-iavl v0.1.9/go.mod h1:7PfkEVT5dcoQE+s/9KWdoXJ8VVVP1QpYYPLdxlkSXFk= -github.com/sei-protocol/sei-tendermint v0.2.36-evm-rebase h1:qUD1G5IqlssdobYMN9GPJnQgKvF2tcUFAMKybkRcFAI= -github.com/sei-protocol/sei-tendermint v0.2.36-evm-rebase/go.mod h1:4LSlJdhl3nf3OmohliwRNUFLOB1XWlrmSodrIP7fLh4= +github.com/sei-protocol/sei-tendermint v0.2.36-evm-multi-tx-per-account h1:EhgenzZk9MI6Dhnb9byPnxMKRl0i72H8mvG7ZHF/2cY= +github.com/sei-protocol/sei-tendermint v0.2.36-evm-multi-tx-per-account/go.mod h1:4LSlJdhl3nf3OmohliwRNUFLOB1XWlrmSodrIP7fLh4= github.com/sei-protocol/sei-tm-db v0.0.5 h1:3WONKdSXEqdZZeLuWYfK5hP37TJpfaUa13vAyAlvaQY= github.com/sei-protocol/sei-tm-db v0.0.5/go.mod h1:Cpa6rGyczgthq7/0pI31jys2Fw0Nfrc+/jKdP1prVqY= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= diff --git a/types/context.go b/types/context.go index 6382df0d7..1cf3ac063 100644 --- a/types/context.go +++ b/types/context.go @@ -24,21 +24,22 @@ but please do not over-use it. We try to keep all data structured and standard additions here would be better just to add to the Context struct */ type Context struct { - ctx context.Context - ms MultiStore - header tmproto.Header - headerHash tmbytes.HexBytes - chainID string - txBytes []byte - logger log.Logger - voteInfo []abci.VoteInfo - gasMeter GasMeter - blockGasMeter GasMeter - checkTx bool - recheckTx bool // if recheckTx == true, then checkTx must also be true - minGasPrice DecCoins - consParams *tmproto.ConsensusParams - eventManager *EventManager + ctx context.Context + ms MultiStore + header tmproto.Header + headerHash tmbytes.HexBytes + chainID string + txBytes []byte + logger log.Logger + voteInfo []abci.VoteInfo + gasMeter GasMeter + blockGasMeter GasMeter + checkTx bool + recheckTx bool // if recheckTx == true, then checkTx must also be true + minGasPrice DecCoins + consParams *tmproto.ConsensusParams + eventManager *EventManager + priority int64 // The tx priority, only relevant in CheckTx pendingTxChecker abci.PendingTxChecker // Checker for pending transaction, only relevant in CheckTx checkTxCallback func(error) // callback to make at the end of CheckTx. Input param is the error (nil-able) of `runMsgs` @@ -48,6 +49,11 @@ type Context struct { txCompletionChannels acltypes.MessageAccessOpsChannelMapping txMsgAccessOps map[int][]acltypes.AccessOperation + // EVM properties + evm bool // EVM transaction flag + evmNonce uint64 // EVM Transaction nonce + evmSenderAddress string // EVM Sender address + msgValidator *acltypes.MsgValidator messageIndex int // Used to track current message being processed txIndex int @@ -123,6 +129,18 @@ func (c Context) ExpireTxHandler() abci.ExpireTxHandler { return c.expireTxHandler } +func (c Context) EVMSenderAddress() string { + return c.evmSenderAddress +} + +func (c Context) EVMNonce() uint64 { + return c.evmNonce +} + +func (c Context) IsEVM() bool { + return c.evm +} + func (c Context) PendingTxChecker() abci.PendingTxChecker { return c.pendingTxChecker } @@ -364,6 +382,21 @@ func (c Context) WithTraceSpanContext(ctx context.Context) Context { return c } +func (c Context) WithEVMSenderAddress(address string) Context { + c.evmSenderAddress = address + return c +} + +func (c Context) WithEVMNonce(nonce uint64) Context { + c.evmNonce = nonce + return c +} + +func (c Context) WithIsEVM(isEVM bool) Context { + c.evm = isEVM + return c +} + func (c Context) WithPendingTxChecker(checker abci.PendingTxChecker) Context { c.pendingTxChecker = checker return c