fix: return ethereum-formatted tx hash to client (#202)

* return eth tx hash to client

Closes #67

Update ethereum/rpc/namespaces/eth/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

Update ethereum/rpc/namespaces/eth/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

change GetTxByEthHash to method

add entry to changelog

* use eth tx hash internally
This commit is contained in:
yihuang 2021-06-30 17:35:11 +08:00 committed by GitHub
parent 5ba8ffe669
commit 0113b4d2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 69 additions and 75 deletions

View File

@ -51,6 +51,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* The `ContractAddress`, `Bloom` have been removed from the `MsgEthereumTxResponse` and the * The `ContractAddress`, `Bloom` have been removed from the `MsgEthereumTxResponse` and the
response now contains the ethereum-formatted `Hash` in hex format. response now contains the ethereum-formatted `Hash` in hex format.
* (eth) [\#845](https://github.com/cosmos/ethermint/pull/845) The `eth` namespace must be included in the list of API's as default to run the rpc server without error. * (eth) [\#845](https://github.com/cosmos/ethermint/pull/845) The `eth` namespace must be included in the list of API's as default to run the rpc server without error.
* (evm) [#202](https://github.com/tharsis/ethermint/pull/202) Web3 api `SendTransaction`/`SendRawTransaction` returns ethereum compatible transaction hash, and query api `GetTransaction*` also accept that.
### Improvements ### Improvements

View File

@ -247,14 +247,13 @@ func NewEthermintApp(
appCodec := encodingConfig.Marshaler appCodec := encodingConfig.Marshaler
cdc := encodingConfig.Amino cdc := encodingConfig.Amino
interfaceRegistry := encodingConfig.InterfaceRegistry interfaceRegistry := encodingConfig.InterfaceRegistry
txDecoder := encodingConfig.TxConfig.TxDecoder()
// NOTE we use custom transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx // NOTE we use custom transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx
bApp := baseapp.NewBaseApp( bApp := baseapp.NewBaseApp(
appName, appName,
logger, logger,
db, db,
txDecoder, encodingConfig.TxConfig.TxDecoder(),
baseAppOptions..., baseAppOptions...,
) )
bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetCommitMultiStoreTracer(traceStore)
@ -337,7 +336,7 @@ func NewEthermintApp(
// Create Ethermint keepers // Create Ethermint keepers
app.EvmKeeper = evmkeeper.NewKeeper( app.EvmKeeper = evmkeeper.NewKeeper(
appCodec, txDecoder, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName), appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName),
app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper,
bApp.Trace(), // debug EVM based on Baseapp options bApp.Trace(), // debug EVM based on Baseapp options
) )

View File

@ -138,23 +138,20 @@ func (e *EVMBackend) EthBlockFromTendermint(
ethRPCTxs := []interface{}{} ethRPCTxs := []interface{}{}
for i, txBz := range block.Txs { for i, txBz := range block.Txs {
// TODO: use msg.AsTransaction.Hash() for txHash once hashing is fixed on Tendermint
// https://github.com/tendermint/tendermint/issues/6539
hash := common.BytesToHash(txBz.Hash())
tx, gas := types.DecodeTx(e.clientCtx, txBz) tx, gas := types.DecodeTx(e.clientCtx, txBz)
gasUsed += gas gasUsed += gas
msg, isEthTx := tx.(*evmtypes.MsgEthereumTx) msg, isEthTx := tx.(*evmtypes.MsgEthereumTx)
if fullTx {
if !isEthTx { if !isEthTx {
// TODO: eventually support Cosmos txs in the block // TODO: eventually support Cosmos txs in the block
continue continue
} }
tx, err := types.NewTransactionFromData( hash := msg.AsTransaction().Hash()
if fullTx {
ethTx, err := types.NewTransactionFromData(
msg.Data, msg.Data,
common.HexToAddress(msg.From), common.HexToAddress(msg.From),
hash, hash,
@ -163,7 +160,7 @@ func (e *EVMBackend) EthBlockFromTendermint(
uint64(i), uint64(i),
) )
ethRPCTxs = append(ethRPCTxs, tx) ethRPCTxs = append(ethRPCTxs, ethTx)
if err != nil { if err != nil {
e.logger.WithError(err).Debugln("NewTransactionFromData for receipt failed", "hash", hash.Hex) e.logger.WithError(err).Debugln("NewTransactionFromData for receipt failed", "hash", hash.Hex)

View File

@ -21,7 +21,7 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types" tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -415,10 +415,7 @@ func (e *PublicAPI) SendTransaction(args rpctypes.SendTxArgs) (common.Hash, erro
return common.Hash{}, err return common.Hash{}, err
} }
// TODO: use msg.AsTransaction.Hash() for txHash once hashing is fixed on Tendermint txHash := msg.AsTransaction().Hash()
// https://github.com/tendermint/tendermint/issues/6539
tmTx := tmtypes.Tx(txBytes)
txHash := common.BytesToHash(tmTx.Hash())
// Broadcast transaction in sync mode (default) // Broadcast transaction in sync mode (default)
// NOTE: If error is encountered on the node, the broadcast will not return an error // NOTE: If error is encountered on the node, the broadcast will not return an error
@ -488,10 +485,7 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
return common.Hash{}, err return common.Hash{}, err
} }
// TODO: use msg.AsTransaction.Hash() for txHash once hashing is fixed on Tendermint txHash := ethereumTx.AsTransaction().Hash()
// https://github.com/tendermint/tendermint/issues/6539
tmTx := tmtypes.Tx(txBytes)
txHash := common.BytesToHash(tmTx.Hash())
syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync) syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync)
rsp, err := syncCtx.BroadcastTx(txBytes) rsp, err := syncCtx.BroadcastTx(txBytes)
@ -682,11 +676,26 @@ func (e *PublicAPI) GetBlockByNumber(ethBlockNum rpctypes.BlockNumber, fullTx bo
return e.backend.GetBlockByNumber(ethBlockNum, fullTx) return e.backend.GetBlockByNumber(ethBlockNum, fullTx)
} }
// GetTxByEthHash uses `/tx_query` to find transaction by ethereum tx hash
// TODO: Don't need to convert once hashing is fixed on Tendermint
// https://github.com/tendermint/tendermint/issues/6539
func (e *PublicAPI) GetTxByEthHash(hash common.Hash) (*tmrpctypes.ResultTx, error) {
query := fmt.Sprintf("%s.%s='%s'", evmtypes.TypeMsgEthereumTx, evmtypes.AttributeKeyEthereumTxHash, hash.Hex())
resTxs, err := e.clientCtx.Client.TxSearch(e.ctx, query, false, nil, nil, "")
if err != nil {
return nil, err
}
if len(resTxs.Txs) == 0 {
return nil, errors.Errorf("ethereum tx not found for hash %s", hash.Hex())
}
return resTxs.Txs[0], nil
}
// GetTransactionByHash returns the transaction identified by hash. // GetTransactionByHash returns the transaction identified by hash.
func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransaction, error) { func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransaction, error) {
e.logger.Debugln("eth_getTransactionByHash", "hash", hash.Hex()) e.logger.Debugln("eth_getTransactionByHash", "hash", hash.Hex())
res, err := e.clientCtx.Client.Tx(e.ctx, hash.Bytes(), false) res, err := e.GetTxByEthHash(hash)
if err != nil { if err != nil {
e.logger.WithError(err).Debugln("tx not found", "hash", hash.Hex()) e.logger.WithError(err).Debugln("tx not found", "hash", hash.Hex())
return nil, nil return nil, nil
@ -714,9 +723,6 @@ func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransac
return nil, fmt.Errorf("invalid tx type: %T", tx) return nil, fmt.Errorf("invalid tx type: %T", tx)
} }
// TODO: use msg.AsTransaction.Hash() for txHash once hashing is fixed on Tendermint
// https://github.com/tendermint/tendermint/issues/6539
return rpctypes.NewTransactionFromData( return rpctypes.NewTransactionFromData(
msg.Data, msg.Data,
common.HexToAddress(msg.From), common.HexToAddress(msg.From),
@ -750,15 +756,17 @@ func (e *PublicAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexu
return nil, fmt.Errorf("failed to decode tx: %w", err) return nil, fmt.Errorf("failed to decode tx: %w", err)
} }
msg, ok := tx.(*evmtypes.MsgEthereumTx) if len(tx.GetMsgs()) != 1 {
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
msg, ok := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
e.logger.Debugln("invalid tx") e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx) return nil, fmt.Errorf("invalid tx type: %T", tx)
} }
// TODO: use msg.AsTransaction.Hash() for txHash once hashing is fixed on Tendermint txHash := msg.AsTransaction().Hash()
// https://github.com/tendermint/tendermint/issues/6539
txHash := common.BytesToHash(txBz.Hash())
return rpctypes.NewTransactionFromData( return rpctypes.NewTransactionFromData(
msg.Data, msg.Data,
@ -803,9 +811,7 @@ func (e *PublicAPI) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockN
return nil, fmt.Errorf("invalid tx type: %T", tx) return nil, fmt.Errorf("invalid tx type: %T", tx)
} }
// TODO: use msg.AsTransaction.Hash() for txHash once hashing is fixed on Tendermint txHash := msg.AsTransaction().Hash()
// https://github.com/tendermint/tendermint/issues/6539
txHash := common.BytesToHash(txBz.Hash())
return rpctypes.NewTransactionFromData( return rpctypes.NewTransactionFromData(
msg.Data, msg.Data,
@ -821,7 +827,7 @@ func (e *PublicAPI) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockN
func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) {
e.logger.Debugln("eth_getTransactionReceipt", "hash", hash.Hex()) e.logger.Debugln("eth_getTransactionReceipt", "hash", hash.Hex())
res, err := e.clientCtx.Client.Tx(e.ctx, hash.Bytes(), false) res, err := e.GetTxByEthHash(hash)
if err != nil { if err != nil {
e.logger.WithError(err).Debugln("tx not found", "hash", hash.Hex()) e.logger.WithError(err).Debugln("tx not found", "hash", hash.Hex())
return nil, nil return nil, nil

View File

@ -22,7 +22,6 @@ import (
type Keeper struct { type Keeper struct {
// Protobuf codec // Protobuf codec
cdc codec.BinaryCodec cdc codec.BinaryCodec
txDecoder sdk.TxDecoder
// Store key required for the EVM Prefix KVStore. It is required by: // Store key required for the EVM Prefix KVStore. It is required by:
// - storing Account's Storage State // - storing Account's Storage State
// - storing Account's Code // - storing Account's Code
@ -47,7 +46,7 @@ type Keeper struct {
// NewKeeper generates new evm module keeper // NewKeeper generates new evm module keeper
func NewKeeper( func NewKeeper(
cdc codec.BinaryCodec, txDecoder sdk.TxDecoder, cdc codec.BinaryCodec,
storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace, storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace,
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
debug bool, debug bool,
@ -66,7 +65,6 @@ func NewKeeper(
// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations // NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
return &Keeper{ return &Keeper{
cdc: cdc, cdc: cdc,
txDecoder: txDecoder,
paramSpace: paramSpace, paramSpace: paramSpace,
accountKeeper: ak, accountKeeper: ak,
bankKeeper: bankKeeper, bankKeeper: bankKeeper,
@ -152,10 +150,27 @@ func (k Keeper) SetBlockBloomTransient(bloom *big.Int) {
// Tx // Tx
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// GetTxHashTransient returns the hash of current processing transaction
func (k Keeper) GetTxHashTransient() common.Hash {
store := k.ctx.TransientStore(k.transientKey)
bz := store.Get(types.KeyPrefixTransientTxHash)
if len(bz) == 0 {
return common.Hash{}
}
return common.BytesToHash(bz)
}
// SetTxHashTransient set the hash of processing transaction
func (k Keeper) SetTxHashTransient(hash common.Hash) {
store := k.ctx.TransientStore(k.transientKey)
store.Set(types.KeyPrefixTransientTxHash, hash.Bytes())
}
// GetTxIndexTransient returns EVM transaction index on the current block. // GetTxIndexTransient returns EVM transaction index on the current block.
func (k Keeper) GetTxIndexTransient() uint64 { func (k Keeper) GetTxIndexTransient() uint64 {
store := k.ctx.TransientStore(k.transientKey) store := k.ctx.TransientStore(k.transientKey)
bz := store.Get(types.KeyPrefixTransientBloom) bz := store.Get(types.KeyPrefixTransientTxIndex)
if len(bz) == 0 { if len(bz) == 0 {
return 0 return 0
} }
@ -168,7 +183,7 @@ func (k Keeper) GetTxIndexTransient() uint64 {
func (k Keeper) IncreaseTxIndexTransient() { func (k Keeper) IncreaseTxIndexTransient() {
txIndex := k.GetTxIndexTransient() txIndex := k.GetTxIndexTransient()
store := k.ctx.TransientStore(k.transientKey) store := k.ctx.TransientStore(k.transientKey)
store.Set(types.KeyPrefixTransientBloom, sdk.Uint64ToBigEndian(txIndex+1)) store.Set(types.KeyPrefixTransientTxIndex, sdk.Uint64ToBigEndian(txIndex+1))
} }
// ResetRefundTransient resets the available refund amount to 0 // ResetRefundTransient resets the available refund amount to 0

View File

@ -128,6 +128,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
evm := k.NewEVM(msg, ethCfg) evm := k.NewEVM(msg, ethCfg)
k.SetTxHashTransient(tx.Hash())
k.IncreaseTxIndexTransient() k.IncreaseTxIndexTransient()
// create an ethereum StateTransition instance and run TransitionDb // create an ethereum StateTransition instance and run TransitionDb
@ -136,9 +137,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
return nil, stacktrace.Propagate(err, "failed to apply ethereum core message") return nil, stacktrace.Propagate(err, "failed to apply ethereum core message")
} }
// NOTE: we set up the transaction hash from tendermint as it is the format expected by the application: txHash := tx.Hash()
// Remove once hashing is fixed on Tendermint. See https://github.com/tendermint/tendermint/issues/6539
txHash := common.BytesToHash(tmtypes.Tx(k.ctx.TxBytes()).Hash())
res.Hash = txHash.Hex() res.Hash = txHash.Hex()
logs := k.GetTxLogs(txHash) logs := k.GetTxLogs(txHash)

View File

@ -10,8 +10,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -588,35 +586,19 @@ func (k *Keeper) RevertToSnapshot(_ int) {}
// context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log // context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log
// to store. // to store.
func (k *Keeper) AddLog(log *ethtypes.Log) { func (k *Keeper) AddLog(log *ethtypes.Log) {
tx, err := k.txDecoder(k.ctx.TxBytes())
if err != nil {
// safety check, should be checked when processing the tx
panic(err)
}
// NOTE: tx length checked on AnteHandler
ethTx, ok := tx.GetMsgs()[0].(*types.MsgEthereumTx)
if !ok {
panic("invalid ethereum tx")
}
// NOTE: we set up the transaction hash from tendermint as it is the format expected by the application:
// Remove once hashing is fixed on Tendermint. See https://github.com/tendermint/tendermint/issues/6539
key := common.BytesToHash(tmtypes.Tx(k.ctx.TxBytes()).Hash())
log.TxHash = common.HexToHash(ethTx.Hash)
log.BlockHash = common.BytesToHash(k.ctx.HeaderHash()) log.BlockHash = common.BytesToHash(k.ctx.HeaderHash())
log.TxIndex = uint(k.GetTxIndexTransient()) log.TxIndex = uint(k.GetTxIndexTransient())
log.TxHash = k.GetTxHashTransient()
logs := k.GetTxLogs(key) logs := k.GetTxLogs(log.TxHash)
log.Index = uint(len(logs)) log.Index = uint(len(logs))
logs = append(logs, log) logs = append(logs, log)
k.SetLogs(key, logs) k.SetLogs(log.TxHash, logs)
k.Logger(k.ctx).Debug( k.Logger(k.ctx).Debug(
"log added", "log added",
"tx-hash-tendermint", key.Hex(),
"tx-hash-ethereum", log.TxHash.Hex(), "tx-hash-ethereum", log.TxHash.Hex(),
"log-index", int(log.Index), "log-index", int(log.Index),
) )

View File

@ -10,7 +10,6 @@ import (
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
@ -483,15 +482,8 @@ func (suite *KeeperTestSuite) TestAddLog() {
msg.From = addr.Hex() msg.From = addr.Hex()
tx := suite.CreateTestTx(msg, privKey) tx := suite.CreateTestTx(msg, privKey)
txBz, err := suite.clientCtx.TxConfig.TxEncoder()(tx)
suite.Require().NoError(err)
tmHash := common.BytesToHash(tmtypes.Tx(txBz).Hash())
msg, _ = tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx) msg, _ = tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
ethTx := msg.AsTransaction() txHash := msg.AsTransaction().Hash()
txHash := ethTx.Hash()
suite.app.EvmKeeper.WithContext(suite.ctx.WithTxBytes(txBz))
testCases := []struct { testCases := []struct {
name string name string
@ -501,7 +493,7 @@ func (suite *KeeperTestSuite) TestAddLog() {
}{ }{
{ {
"tx hash from message", "tx hash from message",
tmHash, txHash,
&ethtypes.Log{ &ethtypes.Log{
Address: addr, Address: addr,
}, },
@ -518,6 +510,7 @@ func (suite *KeeperTestSuite) TestAddLog() {
tc.malleate() tc.malleate()
prev := suite.app.EvmKeeper.GetTxLogs(tc.hash) prev := suite.app.EvmKeeper.GetTxLogs(tc.hash)
suite.app.EvmKeeper.SetTxHashTransient(tc.hash)
suite.app.EvmKeeper.AddLog(tc.log) suite.app.EvmKeeper.AddLog(tc.log)
post := suite.app.EvmKeeper.GetTxLogs(tc.hash) post := suite.app.EvmKeeper.GetTxLogs(tc.hash)

View File

@ -42,6 +42,7 @@ const (
prefixTransientRefund prefixTransientRefund
prefixTransientAccessListAddress prefixTransientAccessListAddress
prefixTransientAccessListSlot prefixTransientAccessListSlot
prefixTransientTxHash
) )
// KVStore key prefixes // KVStore key prefixes
@ -63,6 +64,7 @@ var (
KeyPrefixTransientRefund = []byte{prefixTransientRefund} KeyPrefixTransientRefund = []byte{prefixTransientRefund}
KeyPrefixTransientAccessListAddress = []byte{prefixTransientAccessListAddress} KeyPrefixTransientAccessListAddress = []byte{prefixTransientAccessListAddress}
KeyPrefixTransientAccessListSlot = []byte{prefixTransientAccessListSlot} KeyPrefixTransientAccessListSlot = []byte{prefixTransientAccessListSlot}
KeyPrefixTransientTxHash = []byte{prefixTransientTxHash}
) )
// BloomKey defines the store key for a block Bloom // BloomKey defines the store key for a block Bloom