forked from cerc-io/laconicd-deprecated
impr: support batch eth txs (#901)
* support batch eth tx Closes: 896 Allow multiple MsgEthereumTx in single tx * fix transaction receipt api * fix tx receipt api and accumulate tx gas used * fix lint * fix test * fix rpc test * cleanup * fix cumulativeGasUsed and gasUsed * fix lint * Update app/ante/eth.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update app/ante/eth.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update rpc/ethereum/backend/utils.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * pr suggestions * typo * fix lint Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
co-authored by
Federico Kunze Küllmer
parent
aeb6aeb715
commit
7d8664043e
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -94,17 +95,22 @@ func getEthTransactionByHash(clientCtx client.Context, hashHex string) ([]byte,
|
||||
|
||||
blockHash := common.BytesToHash(block.Block.Header.Hash())
|
||||
|
||||
ethTx, err := rpctypes.RawTxToEthTx(clientCtx, tx.Tx)
|
||||
ethTxs, err := rpctypes.RawTxToEthTx(clientCtx, tx.Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
height := uint64(tx.Height)
|
||||
|
||||
rpcTx, err := rpctypes.NewRPCTransaction(ethTx.AsTransaction(), blockHash, height, uint64(tx.Index), baseFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, ethTx := range ethTxs {
|
||||
if common.HexToHash(ethTx.Hash) == common.BytesToHash(hash) {
|
||||
rpcTx, err := rpctypes.NewRPCTransaction(ethTx.AsTransaction(), blockHash, height, uint64(tx.Index), baseFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(rpcTx)
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(rpcTx)
|
||||
return nil, errors.New("eth tx not found")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
@@ -301,3 +302,36 @@ func (k Keeper) BaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
||||
}
|
||||
return baseFee
|
||||
}
|
||||
|
||||
// ResetTransientGasUsed reset gas used to prepare for execution of current cosmos tx, called in ante handler.
|
||||
func (k Keeper) ResetTransientGasUsed(ctx sdk.Context) {
|
||||
store := ctx.TransientStore(k.transientKey)
|
||||
store.Delete(types.KeyPrefixTransientGasUsed)
|
||||
}
|
||||
|
||||
// GetTransientGasUsed returns the gas used by current cosmos tx.
|
||||
func (k Keeper) GetTransientGasUsed(ctx sdk.Context) uint64 {
|
||||
store := ctx.TransientStore(k.transientKey)
|
||||
bz := store.Get(types.KeyPrefixTransientGasUsed)
|
||||
if len(bz) == 0 {
|
||||
return 0
|
||||
}
|
||||
return sdk.BigEndianToUint64(bz)
|
||||
}
|
||||
|
||||
// SetTransientGasUsed sets the gas used by current cosmos tx.
|
||||
func (k Keeper) SetTransientGasUsed(ctx sdk.Context, gasUsed uint64) {
|
||||
store := ctx.TransientStore(k.transientKey)
|
||||
bz := sdk.Uint64ToBigEndian(gasUsed)
|
||||
store.Set(types.KeyPrefixTransientGasUsed, bz)
|
||||
}
|
||||
|
||||
// AddTransientGasUsed accumulate gas used by each eth msgs included in current cosmos tx.
|
||||
func (k Keeper) AddTransientGasUsed(ctx sdk.Context, gasUsed uint64) (uint64, error) {
|
||||
result := k.GetTransientGasUsed(ctx) + gasUsed
|
||||
if result < gasUsed {
|
||||
return 0, sdkerrors.Wrap(types.ErrGasOverflow, "transient gas used")
|
||||
}
|
||||
k.SetTransientGasUsed(ctx, result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
// add event for ethereum transaction hash format
|
||||
sdk.NewAttribute(types.AttributeKeyEthereumTxHash, response.Hash),
|
||||
// add event for index of valid ethereum tx
|
||||
sdk.NewAttribute(types.AttributeKeyTxIndex, strconv.FormatInt(int64(txIndex), 10)),
|
||||
sdk.NewAttribute(types.AttributeKeyTxIndex, strconv.FormatUint(txIndex, 10)),
|
||||
// add event for eth tx gas used, we can't get it from cosmos tx result when it contains multiple eth tx msgs.
|
||||
sdk.NewAttribute(types.AttributeKeyTxGasUsed, strconv.FormatUint(response.GasUsed, 10)),
|
||||
}
|
||||
|
||||
if len(ctx.TxBytes()) > 0 {
|
||||
|
||||
@@ -287,8 +287,13 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t
|
||||
|
||||
k.SetTxIndexTransient(ctx, uint64(txConfig.TxIndex)+1)
|
||||
|
||||
// update the gas used after refund
|
||||
k.ResetGasMeterAndConsumeGas(ctx, res.GasUsed)
|
||||
totalGasUsed, err := k.AddTransientGasUsed(ctx, res.GasUsed)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "failed to add transient gas used")
|
||||
}
|
||||
|
||||
// reset the gas meter for current cosmos transaction
|
||||
k.ResetGasMeterAndConsumeGas(ctx, totalGasUsed)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ The `x/evm` module keeps the following objects in state:
|
||||
| Block Bloom | Block bloom filter, used to accumulate the bloom filter of current block, emitted to events at end blocker. | `[]byte{1} + []byte(tx.Hash)` | `protobuf([]Log)` | Transient |
|
||||
| Tx Index | Index of current transaction in current block. | `[]byte{2}` | `BigEndian(uint64)` | Transient |
|
||||
| Log Size | Number of the logs emitted so far in current block. Used to decide the log index of following logs. | `[]byte{3}` | `BigEndian(uint64)` | Transient |
|
||||
| Gas Used | Amount of gas used by ethereum messages of current cosmos-sdk tx, it's necessary when cosmos-sdk tx contains multiple ethereum messages. | `[]byte{4}` | `BigEndian(uint64)` | Transient |
|
||||
|
||||
## StateDB
|
||||
|
||||
@@ -161,7 +162,7 @@ With `AddLog()` you can append the given ethereum `Log` to the list of Logs asso
|
||||
|
||||
## Keeper
|
||||
|
||||
The EVM module `Keeper` grants access to the EVM module state and implements `statedb.Keeper` interface to support the `StateDB` implementation. The Keeper contains a store key that allows the DB to write to a concrete subtree of the multistore that is only accessible to the EVM module. Instead of using a trie and database for querying and persistence (the `StateDB` implementation on Ethermint), use the Cosmos `KVStore` (key-value store) and Cosmos SDK `Keeper` to facilitate state transitions.
|
||||
The EVM module `Keeper` grants access to the EVM module state and implements `statedb.Keeper` interface to support the `StateDB` implementation. The Keeper contains a store key that allows the DB to write to a concrete subtree of the multistore that is only accessible to the EVM module. Instead of using a trie and database for querying and persistence (the `StateDB` implementation on Ethermint), use the Cosmos `KVStore` (key-value store) and Cosmos SDK `Keeper` to facilitate state transitions.
|
||||
|
||||
To support the interface functionality, it imports 4 module Keepers:
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ The `x/evm` module emits the Cosmos SDK events after a state execution. The EVM
|
||||
| ethereum_tx | `"txHash"` | `{tendermint_hex_hash}` |
|
||||
| ethereum_tx | `"ethereumTxHash"` | `{hex_hash}` |
|
||||
| ethereum_tx | `"txIndex"` | `{tx_index}` |
|
||||
| ethereum_tx | `"txGasUsed"` | `{gas_used}` |
|
||||
| tx_log | `"txLog"` | `{tx_log}` |
|
||||
| message | `"sender"` | `{eth_address}` |
|
||||
| message | `"action"` | `"ethereum"` |
|
||||
|
||||
@@ -11,6 +11,7 @@ const (
|
||||
AttributeKeyTxHash = "txHash"
|
||||
AttributeKeyEthereumTxHash = "ethereumTxHash"
|
||||
AttributeKeyTxIndex = "txIndex"
|
||||
AttributeKeyTxGasUsed = "txGasUsed"
|
||||
AttributeKeyTxType = "txType"
|
||||
AttributeKeyTxLog = "txLog"
|
||||
// tx failed in eth vm execution
|
||||
|
||||
@@ -32,6 +32,7 @@ const (
|
||||
prefixTransientBloom = iota + 1
|
||||
prefixTransientTxIndex
|
||||
prefixTransientLogSize
|
||||
prefixTransientGasUsed
|
||||
)
|
||||
|
||||
// KVStore key prefixes
|
||||
@@ -45,6 +46,7 @@ var (
|
||||
KeyPrefixTransientBloom = []byte{prefixTransientBloom}
|
||||
KeyPrefixTransientTxIndex = []byte{prefixTransientTxIndex}
|
||||
KeyPrefixTransientLogSize = []byte{prefixTransientLogSize}
|
||||
KeyPrefixTransientGasUsed = []byte{prefixTransientGasUsed}
|
||||
)
|
||||
|
||||
// AddressStoragePrefix returns a prefix to iterate over a given account storage.
|
||||
|
||||
+11
-8
@@ -9,6 +9,7 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
@@ -54,20 +55,22 @@ func DecodeTransactionLogs(data []byte) (TransactionLogs, error) {
|
||||
}
|
||||
|
||||
// UnwrapEthereumMsg extract MsgEthereumTx from wrapping sdk.Tx
|
||||
func UnwrapEthereumMsg(tx *sdk.Tx) (*MsgEthereumTx, error) {
|
||||
func UnwrapEthereumMsg(tx *sdk.Tx, ethHash common.Hash) (*MsgEthereumTx, error) {
|
||||
if tx == nil {
|
||||
return nil, fmt.Errorf("invalid tx: nil")
|
||||
}
|
||||
|
||||
if len((*tx).GetMsgs()) != 1 {
|
||||
return nil, fmt.Errorf("invalid tx type: %T", tx)
|
||||
}
|
||||
msg, ok := (*tx).GetMsgs()[0].(*MsgEthereumTx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid tx type: %T", tx)
|
||||
for _, msg := range (*tx).GetMsgs() {
|
||||
ethMsg, ok := msg.(*MsgEthereumTx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid tx type: %T", tx)
|
||||
}
|
||||
if ethMsg.AsTransaction().Hash() == ethHash {
|
||||
return ethMsg, nil
|
||||
}
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
return nil, fmt.Errorf("eth tx not found: %s", ethHash)
|
||||
}
|
||||
|
||||
// BinSearch execute the binary search and hone in on an executable gas limit
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestEvmDataEncoding(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnwrapEthererumMsg(t *testing.T) {
|
||||
_, err := evmtypes.UnwrapEthereumMsg(nil)
|
||||
_, err := evmtypes.UnwrapEthereumMsg(nil, common.Hash{})
|
||||
require.NotNil(t, err)
|
||||
|
||||
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
|
||||
@@ -56,14 +56,14 @@ func TestUnwrapEthererumMsg(t *testing.T) {
|
||||
builder, _ := clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
||||
|
||||
tx := builder.GetTx().(sdk.Tx)
|
||||
_, err = evmtypes.UnwrapEthereumMsg(&tx)
|
||||
_, err = evmtypes.UnwrapEthereumMsg(&tx, common.Hash{})
|
||||
require.NotNil(t, err)
|
||||
|
||||
msg := evmtypes.NewTx(big.NewInt(1), 0, &common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil, nil, []byte{}, nil)
|
||||
err = builder.SetMsgs(msg)
|
||||
|
||||
tx = builder.GetTx().(sdk.Tx)
|
||||
msg_, err := evmtypes.UnwrapEthereumMsg(&tx)
|
||||
msg_, err := evmtypes.UnwrapEthereumMsg(&tx, msg.AsTransaction().Hash())
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, msg_, msg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user