fix(rpc, ante): Emit Ethereum tx hash in AnteHandler
to support query failed transactions (#1062)
* Emit eth tx hash in ante handler to support query failed transactions WIP: #1045 Solution: - emit eth tx hash in ante handler - modify rpc to use it fix ante handler support failed tx in receipt add unit tests need to patch cosmos-sdk to work update cosmos-sdk to v0.45.x release branch fix failed status fix unit tests add unit test cases cleanup dead code Apply suggestions from code review Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> fix lint fix review suggestions fix build fix gas used of failed tx add back the redundant events * fix get tx by index * add unit tests for events * Update rpc/types/events.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * update comments * refactoring * Update rpc/namespaces/ethereum/eth/api.go * fix lint * Apply suggestions from code review Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
afc09f9d59
commit
5533beed71
@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
### API Breaking
|
||||
|
||||
- (rpc) [tharsis#1081](https://github.com/tharsis/ethermint/pull/1081) Deduplicate some json-rpc logic codes, cleanup several dead functions.
|
||||
- (ante) [tharsis#1062](https://github.com/tharsis/ethermint/pull/1062) Emit event of eth tx hash in ante handler to support query failed transactions.
|
||||
|
||||
### Improvements
|
||||
|
||||
|
@ -3,6 +3,7 @@ package ante
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
@ -539,3 +540,36 @@ func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// EthEmitEventDecorator emit events in ante handler in case of tx execution failed (out of block gas limit).
|
||||
type EthEmitEventDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthEmitEventDecorator creates a new EthEmitEventDecorator
|
||||
func NewEthEmitEventDecorator(evmKeeper EVMKeeper) EthEmitEventDecorator {
|
||||
return EthEmitEventDecorator{evmKeeper}
|
||||
}
|
||||
|
||||
// AnteHandle emits some basic events for the eth messages
|
||||
func (eeed EthEmitEventDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
// After eth tx passed ante handler, the fee is deducted and nonce increased, it shouldn't be ignored by json-rpc,
|
||||
// we need to emit some basic events at the very end of ante handler to be indexed by tendermint.
|
||||
txIndex := eeed.evmKeeper.GetTxIndexTransient(ctx)
|
||||
for i, msg := range tx.GetMsgs() {
|
||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
|
||||
}
|
||||
|
||||
// emit ethereum tx hash as event, should be indexed by tm tx indexer for query purpose.
|
||||
// it's emitted in ante handler so we can query failed transaction (out of block gas limit).
|
||||
ctx.EventManager().EmitEvent(sdk.NewEvent(
|
||||
evmtypes.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(evmtypes.AttributeKeyEthereumTxHash, msgEthTx.Hash),
|
||||
sdk.NewAttribute(evmtypes.AttributeKeyTxIndex, strconv.FormatUint(txIndex+uint64(i), 10)),
|
||||
))
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ func newEthAnteHandler(options HandlerOptions) sdk.AnteHandler {
|
||||
NewEthGasConsumeDecorator(options.EvmKeeper, options.MaxTxGasWanted),
|
||||
NewCanTransferDecorator(options.EvmKeeper),
|
||||
NewEthIncrementSenderSequenceDecorator(options.AccountKeeper), // innermost AnteDecorator.
|
||||
NewEthEmitEventDecorator(options.EvmKeeper), // emit eth tx hash and index at the very last ante handler.
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ type EVMKeeper interface {
|
||||
GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int
|
||||
GetBalance(ctx sdk.Context, addr common.Address) *big.Int
|
||||
ResetTransientGasUsed(ctx sdk.Context)
|
||||
GetTxIndexTransient(ctx sdk.Context) uint64
|
||||
}
|
||||
|
||||
type protoTxProvider interface {
|
||||
|
4
go.mod
4
go.mod
@ -6,7 +6,7 @@ require (
|
||||
github.com/armon/go-metrics v0.4.0
|
||||
github.com/btcsuite/btcd v0.22.1
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
||||
github.com/cosmos/cosmos-sdk v0.45.4
|
||||
github.com/cosmos/cosmos-sdk v0.45.5-0.20220523154235-2921a1c3c918
|
||||
github.com/cosmos/go-bip39 v1.0.0
|
||||
github.com/cosmos/ibc-go/v3 v3.0.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
@ -158,5 +158,3 @@ replace (
|
||||
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
|
||||
google.golang.org/grpc => google.golang.org/grpc v1.33.2
|
||||
)
|
||||
|
||||
replace github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.44.4-0.20220518050709-bd4ca739c699
|
||||
|
16
go.sum
16
go.sum
@ -172,6 +172,7 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||
@ -256,6 +257,9 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44=
|
||||
github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU=
|
||||
github.com/cosmos/cosmos-sdk v0.45.1/go.mod h1:XXS/asyCqWNWkx2rW6pSuen+EVcpAFxq6khrhnZgHaQ=
|
||||
github.com/cosmos/cosmos-sdk v0.45.5-0.20220523154235-2921a1c3c918 h1:adHQCXXYYLO+VxH9aSifiKofXwOwRUBx0lxny5fKQCg=
|
||||
github.com/cosmos/cosmos-sdk v0.45.5-0.20220523154235-2921a1c3c918/go.mod h1:WOqtDxN3eCCmnYLVla10xG7lEXkFjpTaqm2a2WasgCc=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
|
||||
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
|
||||
@ -277,8 +281,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/crypto-org-chain/cosmos-sdk v0.44.4-0.20220518050709-bd4ca739c699 h1:ktGdNahHd9qCoUxboMajlZ9HexLfPvW2QsciQia8fL8=
|
||||
github.com/crypto-org-chain/cosmos-sdk v0.44.4-0.20220518050709-bd4ca739c699/go.mod h1:YkIkmgbvtkoaWjW7NDSVzzdKZRwCiwqt5PbJzXyJ+qM=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU=
|
||||
@ -918,6 +920,8 @@ github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChl
|
||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
@ -1062,6 +1066,7 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
@ -1069,6 +1074,7 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
@ -1083,6 +1089,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
|
||||
github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk=
|
||||
@ -1127,6 +1134,7 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM
|
||||
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
|
||||
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
|
||||
github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0=
|
||||
github.com/tendermint/tendermint v0.34.19/go.mod h1:R5+wgIwSxMdKQcmOaeudL0Cjkr3HDkhpcdum6VeU3R4=
|
||||
github.com/tendermint/tendermint v0.34.20-0.20220517115723-e6f071164839 h1:84fLknaRpFmZ33teqQSKq5tksqPDk90vhbz53Ngp4a8=
|
||||
github.com/tendermint/tendermint v0.34.20-0.20220517115723-e6f071164839/go.mod h1:Rlthqx2Hq440neL9pfBGV1TJGqqTqT++bvkL1yvpytY=
|
||||
github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw=
|
||||
@ -1384,6 +1392,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
@ -1648,6 +1657,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
@ -1749,6 +1759,7 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
@ -1796,6 +1807,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
@ -293,8 +293,8 @@ func (b *Backend) EthBlockFromTendermint(
|
||||
|
||||
for _, txsResult := range resBlockResult.TxsResults {
|
||||
// workaround for cosmos-sdk bug. https://github.com/cosmos/cosmos-sdk/issues/10832
|
||||
if txsResult.GetCode() == 11 && txsResult.GetLog() == "no block gas left to run tx: out of gas" {
|
||||
// block gas limit has exceeded, other txs must have failed for the same reason.
|
||||
if ShouldIgnoreGasUsed(txsResult) {
|
||||
// block gas limit has exceeded, other txs must have failed with same reason.
|
||||
break
|
||||
}
|
||||
gasUsed += uint64(txsResult.GetGasUsed())
|
||||
@ -454,6 +454,7 @@ func (b *Backend) GetCoinbase() (sdk.AccAddress, error) {
|
||||
func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransaction, error) {
|
||||
res, err := b.GetTxByEthHash(txHash)
|
||||
hexTx := txHash.Hex()
|
||||
|
||||
if err != nil {
|
||||
// try to find tx in mempool
|
||||
txs, err := b.PendingTransactions()
|
||||
@ -488,12 +489,17 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if res.TxResult.Code != 0 {
|
||||
if !TxSuccessOrExceedsBlockGasLimit(&res.TxResult) {
|
||||
return nil, errors.New("invalid ethereum tx")
|
||||
}
|
||||
|
||||
msgIndex, attrs := types.FindTxAttributes(res.TxResult.Events, hexTx)
|
||||
if msgIndex < 0 {
|
||||
parsedTxs, err := types.ParseTxResult(&res.TxResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse tx events: %s", hexTx)
|
||||
}
|
||||
|
||||
parsedTx := parsedTxs.GetTxByHash(txHash)
|
||||
if parsedTx == nil {
|
||||
return nil, fmt.Errorf("ethereum tx not found in msgs: %s", hexTx)
|
||||
}
|
||||
|
||||
@ -503,7 +509,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio
|
||||
}
|
||||
|
||||
// the `msgIndex` is inferred from tx events, should be within the bound.
|
||||
msg, ok := tx.GetMsgs()[msgIndex].(*evmtypes.MsgEthereumTx)
|
||||
msg, ok := tx.GetMsgs()[parsedTx.MsgIndex].(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid ethereum tx")
|
||||
}
|
||||
@ -514,12 +520,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Try to find txIndex from events
|
||||
found := false
|
||||
txIndex, err := types.GetUint64Attribute(attrs, evmtypes.AttributeKeyTxIndex)
|
||||
if err == nil {
|
||||
found = true
|
||||
} else {
|
||||
if parsedTx.EthTxIndex == -1 {
|
||||
// Fallback to find tx index by iterating all valid eth transactions
|
||||
blockRes, err := b.clientCtx.Client.BlockResults(b.ctx, &block.Block.Height)
|
||||
if err != nil {
|
||||
@ -528,13 +529,12 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio
|
||||
msgs := b.GetEthereumMsgsFromTendermintBlock(block, blockRes)
|
||||
for i := range msgs {
|
||||
if msgs[i].Hash == hexTx {
|
||||
txIndex = uint64(i)
|
||||
found = true
|
||||
parsedTx.EthTxIndex = int64(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if parsedTx.EthTxIndex == -1 {
|
||||
return nil, errors.New("can't find index of ethereum tx")
|
||||
}
|
||||
|
||||
@ -547,7 +547,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransactio
|
||||
msg,
|
||||
common.BytesToHash(block.BlockID.Hash.Bytes()),
|
||||
uint64(res.Height),
|
||||
txIndex,
|
||||
uint64(parsedTx.EthTxIndex),
|
||||
baseFee,
|
||||
)
|
||||
}
|
||||
@ -915,21 +915,23 @@ func (b *Backend) FeeHistory(
|
||||
|
||||
// GetEthereumMsgsFromTendermintBlock returns all real MsgEthereumTxs from a Tendermint block.
|
||||
// It also ensures consistency over the correct txs indexes across RPC endpoints
|
||||
func (b *Backend) GetEthereumMsgsFromTendermintBlock(block *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) []*evmtypes.MsgEthereumTx {
|
||||
func (b *Backend) GetEthereumMsgsFromTendermintBlock(resBlock *tmrpctypes.ResultBlock, blockRes *tmrpctypes.ResultBlockResults) []*evmtypes.MsgEthereumTx {
|
||||
var result []*evmtypes.MsgEthereumTx
|
||||
block := resBlock.Block
|
||||
|
||||
txResults := blockRes.TxsResults
|
||||
|
||||
for i, tx := range block.Block.Txs {
|
||||
for i, tx := range block.Txs {
|
||||
// check tx exists on EVM by cross checking with blockResults
|
||||
if txResults[i].Code != 0 {
|
||||
// include the tx that exceeds block gas limit
|
||||
if !TxSuccessOrExceedsBlockGasLimit(txResults[i]) {
|
||||
b.logger.Debug("invalid tx result code", "cosmos-hash", hexutil.Encode(tx.Hash()))
|
||||
continue
|
||||
}
|
||||
|
||||
tx, err := b.clientCtx.TxConfig.TxDecoder()(tx)
|
||||
if err != nil {
|
||||
b.logger.Debug("failed to decode transaction in block", "height", block.Block.Height, "error", err.Error())
|
||||
b.logger.Debug("failed to decode transaction in block", "height", block.Height, "error", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
@ -23,6 +24,10 @@ import (
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
// ExceedBlockGasLimitError defines the error message when tx execution exceeds the block gas limit.
|
||||
// The tx fee is deducted in ante handler, so it shouldn't be ignored in JSON-RPC API.
|
||||
const ExceedBlockGasLimitError = "out of gas in location: block gas meter; gasWanted:"
|
||||
|
||||
type txGasAndReward struct {
|
||||
gasUsed uint64
|
||||
reward *big.Int
|
||||
@ -369,3 +374,19 @@ func ParseTxLogsFromEvent(event abci.Event) ([]*ethtypes.Log, error) {
|
||||
}
|
||||
return evmtypes.LogsToEthereum(logs), nil
|
||||
}
|
||||
|
||||
// TxExceedBlockGasLimit returns true if the tx exceeds block gas limit.
|
||||
func TxExceedBlockGasLimit(res *abci.ResponseDeliverTx) bool {
|
||||
return strings.Contains(res.Log, ExceedBlockGasLimitError)
|
||||
}
|
||||
|
||||
// TxSuccessOrExceedsBlockGasLimit returns if the tx should be included in json-rpc responses
|
||||
func TxSuccessOrExceedsBlockGasLimit(res *abci.ResponseDeliverTx) bool {
|
||||
return res.Code == 0 || TxExceedBlockGasLimit(res)
|
||||
}
|
||||
|
||||
// ShouldIgnoreGasUsed returns true if the gasUsed in result should be ignored
|
||||
// workaround for issue: https://github.com/cosmos/cosmos-sdk/issues/10832
|
||||
func ShouldIgnoreGasUsed(res *abci.ResponseDeliverTx) bool {
|
||||
return res.GetCode() == 11 && strings.Contains(res.GetLog(), "no block gas left to run tx: out of gas")
|
||||
}
|
||||
|
@ -89,8 +89,12 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgIndex, _ := rpctypes.FindTxAttributes(transaction.TxResult.Events, hash.Hex())
|
||||
if msgIndex < 0 {
|
||||
parsedTxs, err := rpctypes.ParseTxResult(&transaction.TxResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse tx events: %s", hash.Hex())
|
||||
}
|
||||
parsedTx := parsedTxs.GetTxByHash(hash)
|
||||
if parsedTx == nil {
|
||||
return nil, fmt.Errorf("ethereum tx not found in msgs: %s", hash.Hex())
|
||||
}
|
||||
|
||||
@ -124,7 +128,7 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
|
||||
}
|
||||
|
||||
// add predecessor messages in current cosmos tx
|
||||
for i := 0; i < msgIndex; i++ {
|
||||
for i := 0; i < parsedTx.MsgIndex; i++ {
|
||||
ethMsg, ok := tx.GetMsgs()[i].(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
continue
|
||||
@ -132,7 +136,7 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
|
||||
predecessors = append(predecessors, ethMsg)
|
||||
}
|
||||
|
||||
ethMessage, ok := tx.GetMsgs()[msgIndex].(*evmtypes.MsgEthereumTx)
|
||||
ethMessage, ok := tx.GetMsgs()[parsedTx.MsgIndex].(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
a.logger.Debug("invalid transaction type", "type", fmt.Sprintf("%T", tx))
|
||||
return nil, fmt.Errorf("invalid transaction type %T", tx)
|
||||
|
@ -405,12 +405,23 @@ func (e *PublicAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
msgIndex, _ := rpctypes.FindTxAttributes(res.TxResult.Events, hexTx)
|
||||
if msgIndex < 0 {
|
||||
if res.TxResult.Code != 0 {
|
||||
// failed, return empty logs
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
parsedTxs, err := rpctypes.ParseTxResult(&res.TxResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse tx events: %s, %v", hexTx, err)
|
||||
}
|
||||
|
||||
parsedTx := parsedTxs.GetTxByHash(txHash)
|
||||
if parsedTx == nil {
|
||||
return nil, fmt.Errorf("ethereum tx not found in msgs: %s", hexTx)
|
||||
}
|
||||
|
||||
// parse tx logs from events
|
||||
return backend.TxLogsFromEvents(res.TxResult.Events, msgIndex)
|
||||
return parsedTx.ParseTxLogs()
|
||||
}
|
||||
|
||||
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
||||
@ -735,15 +746,20 @@ func (e *PublicAPI) getTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock,
|
||||
e.logger.Debug("invalid ethereum tx", "height", block.Block.Header, "index", idx)
|
||||
return nil, nil
|
||||
}
|
||||
// find msg index in events
|
||||
msgIndex := rpctypes.FindTxAttributesByIndex(res.TxResult.Events, uint64(idx))
|
||||
if msgIndex < 0 {
|
||||
e.logger.Debug("invalid ethereum tx", "height", block.Block.Header, "index", idx)
|
||||
return nil, nil
|
||||
|
||||
parsedTxs, err := rpctypes.ParseTxResult(&res.TxResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse tx events: %d, %v", idx, err)
|
||||
}
|
||||
|
||||
parsedTx := parsedTxs.GetTxByTxIndex(int(idx))
|
||||
if parsedTx == nil {
|
||||
return nil, fmt.Errorf("ethereum tx not found in msgs: %d", idx)
|
||||
}
|
||||
|
||||
var ok bool
|
||||
// msgIndex is inferred from tx events, should be within bound.
|
||||
msg, ok = tx.GetMsgs()[msgIndex].(*evmtypes.MsgEthereumTx)
|
||||
msg, ok = tx.GetMsgs()[parsedTx.MsgIndex].(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
e.logger.Debug("invalid ethereum tx", "height", block.Block.Header, "index", idx)
|
||||
return nil, nil
|
||||
@ -825,8 +841,18 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
msgIndex, attrs := rpctypes.FindTxAttributes(res.TxResult.Events, hexTx)
|
||||
if msgIndex < 0 {
|
||||
// don't ignore the txs which exceed block gas limit.
|
||||
if !backend.TxSuccessOrExceedsBlockGasLimit(&res.TxResult) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
parsedTxs, err := rpctypes.ParseTxResult(&res.TxResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse tx events: %s, %v", hexTx, err)
|
||||
}
|
||||
|
||||
parsedTx := parsedTxs.GetTxByHash(hash)
|
||||
if parsedTx == nil {
|
||||
return nil, fmt.Errorf("ethereum tx not found in msgs: %s", hexTx)
|
||||
}
|
||||
|
||||
@ -842,14 +868,18 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
|
||||
return nil, fmt.Errorf("failed to decode tx: %w", err)
|
||||
}
|
||||
|
||||
// the `msgIndex` is inferred from tx events, should be within the bound.
|
||||
msg := tx.GetMsgs()[msgIndex]
|
||||
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||
if !ok {
|
||||
e.logger.Debug(fmt.Sprintf("invalid tx type: %T", msg))
|
||||
return nil, fmt.Errorf("invalid tx type: %T", msg)
|
||||
if res.TxResult.Code != 0 {
|
||||
// tx failed, we should return gas limit as gas used, because that's how the fee get deducted.
|
||||
for i := 0; i <= parsedTx.MsgIndex; i++ {
|
||||
gasLimit := tx.GetMsgs()[i].(*evmtypes.MsgEthereumTx).GetGas()
|
||||
parsedTxs.Txs[i].GasUsed = gasLimit
|
||||
}
|
||||
}
|
||||
|
||||
// the `msgIndex` is inferred from tx events, should be within the bound,
|
||||
// and the tx is found by eth tx hash, so the msg type must be correct.
|
||||
ethMsg := tx.GetMsgs()[parsedTx.MsgIndex].(*evmtypes.MsgEthereumTx)
|
||||
|
||||
txData, err := evmtypes.UnpackTxData(ethMsg.Data)
|
||||
if err != nil {
|
||||
e.logger.Error("failed to unpack tx data", "error", err.Error())
|
||||
@ -862,27 +892,14 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
|
||||
e.logger.Debug("failed to retrieve block results", "height", res.Height, "error", err.Error())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for i := 0; i < int(res.Index) && i < len(blockRes.TxsResults); i++ {
|
||||
cumulativeGasUsed += uint64(blockRes.TxsResults[i].GasUsed)
|
||||
}
|
||||
cumulativeGasUsed += rpctypes.AccumulativeGasUsedOfMsg(res.TxResult.Events, msgIndex)
|
||||
|
||||
var gasUsed uint64
|
||||
if len(tx.GetMsgs()) == 1 {
|
||||
// backward compatibility
|
||||
gasUsed = uint64(res.TxResult.GasUsed)
|
||||
} else {
|
||||
gasUsed, err = rpctypes.GetUint64Attribute(attrs, evmtypes.AttributeKeyTxGasUsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cumulativeGasUsed += parsedTxs.AccumulativeGasUsed(parsedTx.MsgIndex)
|
||||
|
||||
// Get the transaction result from the log
|
||||
_, found := attrs[evmtypes.AttributeKeyEthereumTxFailed]
|
||||
var status hexutil.Uint
|
||||
if found {
|
||||
if res.TxResult.Code != 0 || parsedTx.Failed {
|
||||
status = hexutil.Uint(ethtypes.ReceiptStatusFailed)
|
||||
} else {
|
||||
status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful)
|
||||
@ -894,28 +911,23 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
|
||||
}
|
||||
|
||||
// parse tx logs from events
|
||||
logs, err := backend.TxLogsFromEvents(res.TxResult.Events, msgIndex)
|
||||
logs, err := parsedTx.ParseTxLogs()
|
||||
if err != nil {
|
||||
e.logger.Debug("logs not found", "hash", hexTx, "error", err.Error())
|
||||
e.logger.Debug("failed to parse logs", "hash", hexTx, "error", err.Error())
|
||||
}
|
||||
|
||||
// Try to find txIndex from events
|
||||
found = false
|
||||
txIndex, err := rpctypes.GetUint64Attribute(attrs, evmtypes.AttributeKeyTxIndex)
|
||||
if err == nil {
|
||||
found = true
|
||||
} else {
|
||||
if parsedTx.EthTxIndex == -1 {
|
||||
// Fallback to find tx index by iterating all valid eth transactions
|
||||
msgs := e.backend.GetEthereumMsgsFromTendermintBlock(resBlock, blockRes)
|
||||
for i := range msgs {
|
||||
if msgs[i].Hash == hexTx {
|
||||
txIndex = uint64(i)
|
||||
found = true
|
||||
parsedTx.EthTxIndex = int64(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
|
||||
if parsedTx.EthTxIndex == -1 {
|
||||
return nil, errors.New("can't find index of ethereum tx")
|
||||
}
|
||||
|
||||
@ -930,14 +942,14 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
|
||||
// They are stored in the chain database.
|
||||
"transactionHash": hash,
|
||||
"contractAddress": nil,
|
||||
"gasUsed": hexutil.Uint64(gasUsed),
|
||||
"gasUsed": hexutil.Uint64(parsedTx.GasUsed),
|
||||
"type": hexutil.Uint(txData.TxType()),
|
||||
|
||||
// Inclusion information: These fields provide information about the inclusion of the
|
||||
// transaction corresponding to this receipt.
|
||||
"blockHash": common.BytesToHash(resBlock.Block.Header.Hash()).Hex(),
|
||||
"blockNumber": hexutil.Uint64(res.Height),
|
||||
"transactionIndex": hexutil.Uint64(txIndex),
|
||||
"transactionIndex": hexutil.Uint64(parsedTx.EthTxIndex),
|
||||
|
||||
// sender and receiver (contract or EOA) addreses
|
||||
"from": from,
|
||||
|
236
rpc/types/events.go
Normal file
236
rpc/types/events.go
Normal file
@ -0,0 +1,236 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
// EventFormat is the format version of the events.
|
||||
//
|
||||
// To fix the issue of tx exceeds block gas limit, we changed the event format in a breaking way.
|
||||
// But to avoid forcing clients to re-sync from scatch, we make json-rpc logic to be compatible with both formats.
|
||||
type EventFormat int
|
||||
|
||||
const (
|
||||
eventFormatUnknown EventFormat = iota
|
||||
|
||||
// Event Format 1 (the format used before PR #1062):
|
||||
// ```
|
||||
// ethereum_tx(amount, ethereumTxHash, [txIndex, txGasUsed], txHash, [receipient], ethereumTxFailed)
|
||||
// tx_log(txLog, txLog, ...)
|
||||
// ethereum_tx(amount, ethereumTxHash, [txIndex, txGasUsed], txHash, [receipient], ethereumTxFailed)
|
||||
// tx_log(txLog, txLog, ...)
|
||||
// ...
|
||||
// ```
|
||||
eventFormat1
|
||||
|
||||
// Event Format 2 (the format used after PR #1062):
|
||||
// ```
|
||||
// ethereum_tx(ethereumTxHash, txIndex)
|
||||
// ethereum_tx(ethereumTxHash, txIndex)
|
||||
// ...
|
||||
// ethereum_tx(amount, ethereumTxHash, txIndex, txGasUsed, txHash, [receipient], ethereumTxFailed)
|
||||
// tx_log(txLog, txLog, ...)
|
||||
// ethereum_tx(amount, ethereumTxHash, txIndex, txGasUsed, txHash, [receipient], ethereumTxFailed)
|
||||
// tx_log(txLog, txLog, ...)
|
||||
// ...
|
||||
// ```
|
||||
// If the transaction exceeds block gas limit, it only emits the first part.
|
||||
eventFormat2
|
||||
)
|
||||
|
||||
// ParsedTx is the tx infos parsed from events.
|
||||
type ParsedTx struct {
|
||||
MsgIndex int
|
||||
|
||||
// the following fields are parsed from events
|
||||
|
||||
Hash common.Hash
|
||||
// -1 means uninitialized
|
||||
EthTxIndex int64
|
||||
GasUsed uint64
|
||||
Failed bool
|
||||
// unparsed tx log json strings
|
||||
RawLogs [][]byte
|
||||
}
|
||||
|
||||
// NewParsedTx initialize a ParsedTx
|
||||
func NewParsedTx(msgIndex int) ParsedTx {
|
||||
return ParsedTx{MsgIndex: msgIndex, EthTxIndex: -1}
|
||||
}
|
||||
|
||||
// ParseTxLogs decode the raw logs into ethereum format.
|
||||
func (p ParsedTx) ParseTxLogs() ([]*ethtypes.Log, error) {
|
||||
logs := make([]*evmtypes.Log, 0, len(p.RawLogs))
|
||||
for _, raw := range p.RawLogs {
|
||||
var log evmtypes.Log
|
||||
if err := json.Unmarshal(raw, &log); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logs = append(logs, &log)
|
||||
}
|
||||
return evmtypes.LogsToEthereum(logs), nil
|
||||
}
|
||||
|
||||
// ParsedTxs is the tx infos parsed from eth tx events.
|
||||
type ParsedTxs struct {
|
||||
// one item per message
|
||||
Txs []ParsedTx
|
||||
// map tx hash to msg index
|
||||
TxHashes map[common.Hash]int
|
||||
}
|
||||
|
||||
// ParseTxResult parse eth tx infos from cosmos-sdk events.
|
||||
// It supports two event formats, the formats are described in the comments of the format constants.
|
||||
func ParseTxResult(result *abci.ResponseDeliverTx) (*ParsedTxs, error) {
|
||||
format := eventFormatUnknown
|
||||
// the index of current ethereum_tx event in format 1 or the second part of format 2
|
||||
eventIndex := -1
|
||||
|
||||
p := &ParsedTxs{
|
||||
TxHashes: make(map[common.Hash]int),
|
||||
}
|
||||
for _, event := range result.Events {
|
||||
switch event.Type {
|
||||
case evmtypes.EventTypeEthereumTx:
|
||||
if format == eventFormatUnknown {
|
||||
// discover the format version by inspect the first ethereum_tx event.
|
||||
if len(event.Attributes) > 2 {
|
||||
format = eventFormat1
|
||||
} else {
|
||||
format = eventFormat2
|
||||
}
|
||||
}
|
||||
|
||||
if len(event.Attributes) == 2 {
|
||||
// the first part of format 2
|
||||
if err := p.newTx(event.Attributes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// format 1 or second part of format 2
|
||||
eventIndex++
|
||||
if format == eventFormat1 {
|
||||
// append tx
|
||||
if err := p.newTx(event.Attributes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// the second part of format 2, update tx fields
|
||||
if err := p.updateTx(eventIndex, event.Attributes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
case evmtypes.EventTypeTxLog:
|
||||
// reuse the eventIndex set by previous ethereum_tx event
|
||||
p.Txs[eventIndex].RawLogs = parseRawLogs(event.Attributes)
|
||||
}
|
||||
}
|
||||
|
||||
// some old versions miss some events, fill it with tx result
|
||||
if len(p.Txs) == 1 {
|
||||
p.Txs[0].GasUsed = uint64(result.GasUsed)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// newTx parse a new tx from events, called during parsing.
|
||||
func (p *ParsedTxs) newTx(attrs []abci.EventAttribute) error {
|
||||
msgIndex := len(p.Txs)
|
||||
tx := NewParsedTx(msgIndex)
|
||||
if err := fillTxAttributes(&tx, attrs); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Txs = append(p.Txs, tx)
|
||||
p.TxHashes[tx.Hash] = msgIndex
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateTx updates an exiting tx from events, called during parsing.
|
||||
func (p *ParsedTxs) updateTx(eventIndex int, attrs []abci.EventAttribute) error {
|
||||
return fillTxAttributes(&p.Txs[eventIndex], attrs)
|
||||
}
|
||||
|
||||
// GetTxByHash find ParsedTx by tx hash, returns nil if not exists.
|
||||
func (p *ParsedTxs) GetTxByHash(hash common.Hash) *ParsedTx {
|
||||
if idx, ok := p.TxHashes[hash]; ok {
|
||||
return &p.Txs[idx]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTxByMsgIndex returns ParsedTx by msg index
|
||||
func (p *ParsedTxs) GetTxByMsgIndex(i int) *ParsedTx {
|
||||
if i < 0 || i >= len(p.Txs) {
|
||||
return nil
|
||||
}
|
||||
return &p.Txs[i]
|
||||
}
|
||||
|
||||
// GetTxByTxIndex returns ParsedTx by tx index
|
||||
func (p *ParsedTxs) GetTxByTxIndex(txIndex int) *ParsedTx {
|
||||
if len(p.Txs) == 0 {
|
||||
return nil
|
||||
}
|
||||
// assuming the `EthTxIndex` increase continuously,
|
||||
// convert TxIndex to MsgIndex by subtract the begin TxIndex.
|
||||
msgIndex := txIndex - int(p.Txs[0].EthTxIndex)
|
||||
// GetTxByMsgIndex will check the bound
|
||||
return p.GetTxByMsgIndex(msgIndex)
|
||||
}
|
||||
|
||||
// AccumulativeGasUsed calculates the accumulated gas used within the batch of txs
|
||||
func (p *ParsedTxs) AccumulativeGasUsed(msgIndex int) (result uint64) {
|
||||
for i := 0; i <= msgIndex; i++ {
|
||||
result += p.Txs[i].GasUsed
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// fillTxAttribute parse attributes by name, less efficient than hardcode the index, but more stable against event
|
||||
// format changes.
|
||||
func fillTxAttribute(tx *ParsedTx, key []byte, value []byte) error {
|
||||
switch string(key) {
|
||||
case evmtypes.AttributeKeyEthereumTxHash:
|
||||
tx.Hash = common.HexToHash(string(value))
|
||||
case evmtypes.AttributeKeyTxIndex:
|
||||
txIndex, err := strconv.ParseInt(string(value), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.EthTxIndex = txIndex
|
||||
case evmtypes.AttributeKeyTxGasUsed:
|
||||
gasUsed, err := strconv.ParseInt(string(value), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.GasUsed = uint64(gasUsed)
|
||||
case evmtypes.AttributeKeyEthereumTxFailed:
|
||||
tx.Failed = len(value) > 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fillTxAttributes(tx *ParsedTx, attrs []abci.EventAttribute) error {
|
||||
for _, attr := range attrs {
|
||||
if err := fillTxAttribute(tx, attr.Key, attr.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRawLogs(attrs []abci.EventAttribute) (logs [][]byte) {
|
||||
for _, attr := range attrs {
|
||||
logs = append(logs, attr.Value)
|
||||
}
|
||||
return logs
|
||||
}
|
321
rpc/types/events_test.go
Normal file
321
rpc/types/events_test.go
Normal file
@ -0,0 +1,321 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
func TestParseTxResult(t *testing.T) {
|
||||
rawLogs := [][]byte{
|
||||
[]byte("{\"address\":\"0xdcC261c03cD2f33eBea404318Cdc1D9f8b78e1AD\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x000000000000000000000000569608516a81c0b1247310a3e0cd001046da0663\",\"0x0000000000000000000000002eea2c1ae0cdd2622381c2f9201b2a07c037b1f6\"],\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANB/GezJGOI=\",\"blockNumber\":1803258,\"transactionHash\":\"0xcf4354b55b9ac77436cf8b2f5c229ad3b3119b5196cd79ac5c6c382d9f7b0a71\",\"transactionIndex\":1,\"blockHash\":\"0xa69a510b0848180a094904ea9ae3f0ca2216029470c8e03e6941b402aba610d8\",\"logIndex\":5}"),
|
||||
[]byte("{\"address\":\"0x569608516A81C0B1247310A3E0CD001046dA0663\",\"topics\":[\"0xe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486\",\"0x0000000000000000000000002eea2c1ae0cdd2622381c2f9201b2a07c037b1f6\"],\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANB/GezJGOI=\",\"blockNumber\":1803258,\"transactionHash\":\"0xcf4354b55b9ac77436cf8b2f5c229ad3b3119b5196cd79ac5c6c382d9f7b0a71\",\"transactionIndex\":1,\"blockHash\":\"0xa69a510b0848180a094904ea9ae3f0ca2216029470c8e03e6941b402aba610d8\",\"logIndex\":6}"),
|
||||
[]byte("{\"address\":\"0x569608516A81C0B1247310A3E0CD001046dA0663\",\"topics\":[\"0xf279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568\",\"0x0000000000000000000000002eea2c1ae0cdd2622381c2f9201b2a07c037b1f6\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"blockNumber\":1803258,\"transactionHash\":\"0xcf4354b55b9ac77436cf8b2f5c229ad3b3119b5196cd79ac5c6c382d9f7b0a71\",\"transactionIndex\":1,\"blockHash\":\"0xa69a510b0848180a094904ea9ae3f0ca2216029470c8e03e6941b402aba610d8\",\"logIndex\":7}"),
|
||||
}
|
||||
address := "0x57f96e6B86CdeFdB3d412547816a82E3E0EbF9D2"
|
||||
txHash := common.BigToHash(big.NewInt(1))
|
||||
txHash2 := common.BigToHash(big.NewInt(2))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
response abci.ResponseDeliverTx
|
||||
expTxs []*ParsedTx // expected parse result, nil means expect error.
|
||||
}{
|
||||
{"format 1 events",
|
||||
abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: "coin_received", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("receiver"), Value: []byte("ethm12luku6uxehhak02py4rcz65zu0swh7wjun6msa")},
|
||||
{Key: []byte("amount"), Value: []byte("1252860basetcro")},
|
||||
}},
|
||||
{Type: "coin_spent", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("spender"), Value: []byte("ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl")},
|
||||
{Key: []byte("amount"), Value: []byte("1252860basetcro")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("10")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[0]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[1]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[2]},
|
||||
}},
|
||||
{Type: "message", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("action"), Value: []byte("/ethermint.evm.v1.MsgEthereumTx")},
|
||||
{Key: []byte("key"), Value: []byte("ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl")},
|
||||
{Key: []byte("module"), Value: []byte("evm")},
|
||||
{Key: []byte("sender"), Value: []byte(address)},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash2.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("11")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
{Key: []byte("ethereumTxFailed"), Value: []byte("contract reverted")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{}},
|
||||
},
|
||||
},
|
||||
[]*ParsedTx{
|
||||
{
|
||||
MsgIndex: 0,
|
||||
Hash: txHash,
|
||||
EthTxIndex: 10,
|
||||
GasUsed: 21000,
|
||||
Failed: false,
|
||||
RawLogs: rawLogs,
|
||||
},
|
||||
{
|
||||
MsgIndex: 1,
|
||||
Hash: txHash2,
|
||||
EthTxIndex: 11,
|
||||
GasUsed: 21000,
|
||||
Failed: true,
|
||||
RawLogs: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{"format 2 events",
|
||||
abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: "coin_received", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("receiver"), Value: []byte("ethm12luku6uxehhak02py4rcz65zu0swh7wjun6msa")},
|
||||
{Key: []byte("amount"), Value: []byte("1252860basetcro")},
|
||||
}},
|
||||
{Type: "coin_spent", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("spender"), Value: []byte("ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl")},
|
||||
{Key: []byte("amount"), Value: []byte("1252860basetcro")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("0")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[0]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[1]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[2]},
|
||||
}},
|
||||
{Type: "message", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("action"), Value: []byte("/ethermint.evm.v1.MsgEthereumTx")},
|
||||
{Key: []byte("key"), Value: []byte("ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl")},
|
||||
{Key: []byte("module"), Value: []byte("evm")},
|
||||
{Key: []byte("sender"), Value: []byte(address)},
|
||||
}},
|
||||
},
|
||||
},
|
||||
[]*ParsedTx{
|
||||
{
|
||||
MsgIndex: 0,
|
||||
Hash: txHash,
|
||||
EthTxIndex: 0,
|
||||
GasUsed: 21000,
|
||||
Failed: false,
|
||||
RawLogs: rawLogs,
|
||||
},
|
||||
},
|
||||
},
|
||||
{"format 1 events, failed",
|
||||
abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("10")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[0]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[1]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[2]},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash2.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("0x01")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
{Key: []byte("ethereumTxFailed"), Value: []byte("contract reverted")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{}},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{"format 1 events, failed",
|
||||
abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("10")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[0]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[1]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[2]},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash2.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("10")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("0x01")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
{Key: []byte("ethereumTxFailed"), Value: []byte("contract reverted")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{}},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{"format 2 events failed",
|
||||
abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("0x01")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{"format 2 events failed",
|
||||
abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("10")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("0x01")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
parsed, err := ParseTxResult(&tc.response)
|
||||
if tc.expTxs == nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
for msgIndex, expTx := range tc.expTxs {
|
||||
require.Equal(t, expTx, parsed.GetTxByMsgIndex(msgIndex))
|
||||
require.Equal(t, expTx, parsed.GetTxByHash(expTx.Hash))
|
||||
require.Equal(t, expTx, parsed.GetTxByTxIndex(int(expTx.EthTxIndex)))
|
||||
_, err := expTx.ParseTxLogs()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// non-exists tx hash
|
||||
require.Nil(t, parsed.GetTxByHash(common.Hash{}))
|
||||
// out of range
|
||||
require.Nil(t, parsed.GetTxByMsgIndex(len(tc.expTxs)))
|
||||
require.Nil(t, parsed.GetTxByTxIndex(99999999))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTxLogs(t *testing.T) {
|
||||
rawLogs := [][]byte{
|
||||
[]byte("{\"address\":\"0xdcC261c03cD2f33eBea404318Cdc1D9f8b78e1AD\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x000000000000000000000000569608516a81c0b1247310a3e0cd001046da0663\",\"0x0000000000000000000000002eea2c1ae0cdd2622381c2f9201b2a07c037b1f6\"],\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANB/GezJGOI=\",\"blockNumber\":1803258,\"transactionHash\":\"0xcf4354b55b9ac77436cf8b2f5c229ad3b3119b5196cd79ac5c6c382d9f7b0a71\",\"transactionIndex\":1,\"blockHash\":\"0xa69a510b0848180a094904ea9ae3f0ca2216029470c8e03e6941b402aba610d8\",\"logIndex\":5}"),
|
||||
[]byte("{\"address\":\"0x569608516A81C0B1247310A3E0CD001046dA0663\",\"topics\":[\"0xe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486\",\"0x0000000000000000000000002eea2c1ae0cdd2622381c2f9201b2a07c037b1f6\"],\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANB/GezJGOI=\",\"blockNumber\":1803258,\"transactionHash\":\"0xcf4354b55b9ac77436cf8b2f5c229ad3b3119b5196cd79ac5c6c382d9f7b0a71\",\"transactionIndex\":1,\"blockHash\":\"0xa69a510b0848180a094904ea9ae3f0ca2216029470c8e03e6941b402aba610d8\",\"logIndex\":6}"),
|
||||
[]byte("{\"address\":\"0x569608516A81C0B1247310A3E0CD001046dA0663\",\"topics\":[\"0xf279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568\",\"0x0000000000000000000000002eea2c1ae0cdd2622381c2f9201b2a07c037b1f6\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"blockNumber\":1803258,\"transactionHash\":\"0xcf4354b55b9ac77436cf8b2f5c229ad3b3119b5196cd79ac5c6c382d9f7b0a71\",\"transactionIndex\":1,\"blockHash\":\"0xa69a510b0848180a094904ea9ae3f0ca2216029470c8e03e6941b402aba610d8\",\"logIndex\":7}"),
|
||||
}
|
||||
address := "0x57f96e6B86CdeFdB3d412547816a82E3E0EbF9D2"
|
||||
txHash := common.BigToHash(big.NewInt(1))
|
||||
txHash2 := common.BigToHash(big.NewInt(2))
|
||||
response := abci.ResponseDeliverTx{
|
||||
GasUsed: 21000,
|
||||
Events: []abci.Event{
|
||||
{Type: "coin_received", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("receiver"), Value: []byte("ethm12luku6uxehhak02py4rcz65zu0swh7wjun6msa")},
|
||||
{Key: []byte("amount"), Value: []byte("1252860basetcro")},
|
||||
}},
|
||||
{Type: "coin_spent", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("spender"), Value: []byte("ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl")},
|
||||
{Key: []byte("amount"), Value: []byte("1252860basetcro")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("10")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[0]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[1]},
|
||||
{Key: []byte(evmtypes.AttributeKeyTxLog), Value: rawLogs[2]},
|
||||
}},
|
||||
{Type: "message", Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("action"), Value: []byte("/ethermint.evm.v1.MsgEthereumTx")},
|
||||
{Key: []byte("key"), Value: []byte("ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl")},
|
||||
{Key: []byte("module"), Value: []byte("evm")},
|
||||
{Key: []byte("sender"), Value: []byte(address)},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
|
||||
{Key: []byte("ethereumTxHash"), Value: []byte(txHash2.Hex())},
|
||||
{Key: []byte("txIndex"), Value: []byte("11")},
|
||||
{Key: []byte("amount"), Value: []byte("1000")},
|
||||
{Key: []byte("txGasUsed"), Value: []byte("21000")},
|
||||
{Key: []byte("txHash"), Value: []byte("14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57")},
|
||||
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
|
||||
{Key: []byte("ethereumTxFailed"), Value: []byte("contract reverted")},
|
||||
}},
|
||||
{Type: evmtypes.EventTypeTxLog, Attributes: []abci.EventAttribute{}},
|
||||
},
|
||||
}
|
||||
parsed, err := ParseTxResult(&response)
|
||||
require.NoError(t, err)
|
||||
tx1 := parsed.GetTxByMsgIndex(0)
|
||||
txLogs1, err := tx1.ParseTxLogs()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, txLogs1)
|
||||
|
||||
tx2 := parsed.GetTxByMsgIndex(1)
|
||||
txLogs2, err := tx2.ParseTxLogs()
|
||||
require.Empty(t, txLogs2)
|
||||
}
|
@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
@ -222,106 +221,3 @@ func BaseFeeFromEvents(events []abci.Event) *big.Int {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindTxAttributes returns the msg index of the eth tx in cosmos tx, and the attributes,
|
||||
// returns -1 and nil if not found.
|
||||
func FindTxAttributes(events []abci.Event, txHash string) (int, map[string]string) {
|
||||
msgIndex := -1
|
||||
for _, event := range events {
|
||||
if event.Type != evmtypes.EventTypeEthereumTx {
|
||||
continue
|
||||
}
|
||||
|
||||
msgIndex++
|
||||
|
||||
value := FindAttribute(event.Attributes, []byte(evmtypes.AttributeKeyEthereumTxHash))
|
||||
if !bytes.Equal(value, []byte(txHash)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// found, convert attributes to map for later lookup
|
||||
attrs := make(map[string]string, len(event.Attributes))
|
||||
for _, attr := range event.Attributes {
|
||||
attrs[string(attr.Key)] = string(attr.Value)
|
||||
}
|
||||
return msgIndex, attrs
|
||||
}
|
||||
// not found
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// FindTxAttributesByIndex search the msg in tx events by txIndex
|
||||
// returns the msgIndex, returns -1 if not found.
|
||||
func FindTxAttributesByIndex(events []abci.Event, txIndex uint64) int {
|
||||
strIndex := []byte(strconv.FormatUint(txIndex, 10))
|
||||
txIndexKey := []byte(evmtypes.AttributeKeyTxIndex)
|
||||
msgIndex := -1
|
||||
for _, event := range events {
|
||||
if event.Type != evmtypes.EventTypeEthereumTx {
|
||||
continue
|
||||
}
|
||||
|
||||
msgIndex++
|
||||
|
||||
value := FindAttribute(event.Attributes, txIndexKey)
|
||||
if !bytes.Equal(value, strIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
// found, convert attributes to map for later lookup
|
||||
return msgIndex
|
||||
}
|
||||
// not found
|
||||
return -1
|
||||
}
|
||||
|
||||
// FindAttribute find event attribute with specified key, if not found returns nil.
|
||||
func FindAttribute(attrs []abci.EventAttribute, key []byte) []byte {
|
||||
for _, attr := range attrs {
|
||||
if !bytes.Equal(attr.Key, key) {
|
||||
continue
|
||||
}
|
||||
return attr.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUint64Attribute parses the uint64 value from event attributes
|
||||
func GetUint64Attribute(attrs map[string]string, key string) (uint64, error) {
|
||||
value, found := attrs[key]
|
||||
if !found {
|
||||
return 0, fmt.Errorf("tx index attribute not found: %s", key)
|
||||
}
|
||||
var result int64
|
||||
result, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if result < 0 {
|
||||
return 0, fmt.Errorf("negative tx index: %d", result)
|
||||
}
|
||||
return uint64(result), nil
|
||||
}
|
||||
|
||||
// AccumulativeGasUsedOfMsg accumulate the gas used by msgs before `msgIndex`.
|
||||
func AccumulativeGasUsedOfMsg(events []abci.Event, msgIndex int) (gasUsed uint64) {
|
||||
for _, event := range events {
|
||||
if event.Type != evmtypes.EventTypeEthereumTx {
|
||||
continue
|
||||
}
|
||||
|
||||
if msgIndex < 0 {
|
||||
break
|
||||
}
|
||||
msgIndex--
|
||||
|
||||
value := FindAttribute(event.Attributes, []byte(evmtypes.AttributeKeyTxGasUsed))
|
||||
var result int64
|
||||
result, err := strconv.ParseInt(string(value), 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
gasUsed += uint64(result)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ func AddCommands(rootCmd *cobra.Command, defaultNodeHome string, appCreator type
|
||||
tendermintCmd,
|
||||
sdkserver.ExportCmd(appExport, defaultNodeHome),
|
||||
version.NewVersionCommand(),
|
||||
sdkserver.NewRollbackCmd(appCreator, defaultNodeHome),
|
||||
sdkserver.NewRollbackCmd(defaultNodeHome),
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user