!fix(evm): Fix eth tx hashes in json-rpc responses (#1176)

* Fix eth tx hashes in json-rpc responses

Closes: #1175

- Remove Size_ field
- Validate From/Hash fields in ante handler
- Recompute tx hashes in json-rpc apis to cope with old blocks

Update CHANGELOG.md

remove Size_, validate Hash/From, add unit tests

update spec

Update CHANGELOG.md

Update app/ante/eth.go

populate From in SendRawTransaction

Apply suggestions from code review

keep Size_ field to avoid breaking tx format

* move some validation to ValidateBasic

* move validation to ValidateBasic

* make ToTransaction returns a valid msg

* restructure the protoTxProvider check

* add comment

* workaround tx hash issue in event parsing

* fix integration test

* fix unit test

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
yihuang
2022-07-19 15:12:48 +00:00
committed by GitHub
co-authored by Federico Kunze Küllmer
parent 8932a6d743
commit ffe78da36e
20 changed files with 245 additions and 101 deletions
+2
View File
@@ -257,6 +257,8 @@ func prepareEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereu
err = msgEthereumTx.Sign(s.ethSigner, tests.NewSigner(priv))
s.Require().NoError(err)
// A valid msg should have empty `From`
msgEthereumTx.From = ""
err = txBuilder.SetMsgs(msgEthereumTx)
s.Require().NoError(err)
+1 -1
View File
@@ -14,7 +14,7 @@ An EVM state transition can be achieved by using the `MsgEthereumTx`. This messa
type MsgEthereumTx struct {
// inner transaction data
Data *types.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
// encoded storage size of the transaction
// DEPRECATED: encoded storage size of the transaction
Size_ float64 `protobuf:"fixed64,2,opt,name=size,proto3" json:"-"`
// transaction hash in hex format
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty" rlp:"-"`
+24 -4
View File
@@ -130,10 +130,12 @@ func newMsgEthereumTx(
panic(err)
}
return &MsgEthereumTx{Data: dataAny}
msg := MsgEthereumTx{Data: dataAny}
msg.Hash = msg.AsTransaction().Hash().Hex()
return &msg
}
// fromEthereumTx populates the message fields from the given ethereum transaction
// FromEthereumTx populates the message fields from the given ethereum transaction
func (msg *MsgEthereumTx) FromEthereumTx(tx *ethtypes.Transaction) error {
txData, err := NewTxDataFromTx(tx)
if err != nil {
@@ -146,7 +148,6 @@ func (msg *MsgEthereumTx) FromEthereumTx(tx *ethtypes.Transaction) error {
}
msg.Data = anyTxData
msg.Size_ = float64(tx.Size())
msg.Hash = tx.Hash().Hex()
return nil
}
@@ -166,6 +167,11 @@ func (msg MsgEthereumTx) ValidateBasic() error {
}
}
// Validate Size_ field, should be kept empty
if msg.Size_ != 0 {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "tx size is deprecated")
}
txData, err := UnpackTxData(msg.Data)
if err != nil {
return sdkerrors.Wrap(err, "failed to unpack tx data")
@@ -176,7 +182,17 @@ func (msg MsgEthereumTx) ValidateBasic() error {
return sdkerrors.Wrap(ErrInvalidGasLimit, "gas limit must not be zero")
}
return txData.Validate()
if err := txData.Validate(); err != nil {
return err
}
// Validate Hash field after validated txData to avoid panic
txHash := msg.AsTransaction().Hash().Hex()
if msg.Hash != txHash {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid tx hash %s, expected: %s", msg.Hash, txHash)
}
return nil
}
// GetMsgs returns a single MsgEthereumTx as an sdk.Msg.
@@ -342,6 +358,10 @@ func (msg *MsgEthereumTx) BuildTx(b client.TxBuilder, evmDenom string) (signing.
}
builder.SetExtensionOptions(option)
// A valid msg should have empty `From`
msg.From = ""
err = builder.SetMsgs(msg)
if err != nil {
return nil, err
+75 -14
View File
@@ -372,24 +372,85 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
},
}
for i, tc := range testCases {
to := common.HexToAddress(tc.from)
for _, tc := range testCases {
suite.Run(tc.msg, func() {
to := common.HexToAddress(tc.from)
tx := types.NewTx(tc.chainID, 1, &to, tc.amount, tc.gasLimit, tc.gasPrice, tc.gasFeeCap, tc.gasTipCap, nil, tc.accessList)
tx.From = tc.from
tx := types.NewTx(tc.chainID, 1, &to, tc.amount, tc.gasLimit, tc.gasPrice, tc.gasFeeCap, tc.gasTipCap, nil, tc.accessList)
tx.From = tc.from
// apply nil assignment here to test ValidateBasic function instead of NewTx
if strings.Contains(tc.msg, "nil tx.Data") {
tx.Data = nil
}
// apply nil assignment here to test ValidateBasic function instead of NewTx
if strings.Contains(tc.msg, "nil tx.Data") {
tx.Data = nil
}
err := tx.ValidateBasic()
err := tx.ValidateBasic()
if tc.expectPass {
suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
} else {
suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
}
if tc.expectPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasicAdvanced() {
hundredInt := big.NewInt(100)
testCases := []struct {
msg string
msgBuilder func() *types.MsgEthereumTx
expectPass bool
}{
{
"fails - invalid tx hash",
func() *types.MsgEthereumTx {
msg := types.NewTxContract(
hundredInt,
1,
big.NewInt(10),
100000,
big.NewInt(150),
big.NewInt(200),
nil,
nil,
nil,
)
msg.Hash = "0x00"
return msg
},
false,
},
{
"fails - invalid size",
func() *types.MsgEthereumTx {
msg := types.NewTxContract(
hundredInt,
1,
big.NewInt(10),
100000,
big.NewInt(150),
big.NewInt(200),
nil,
nil,
nil,
)
msg.Size_ = 1
return msg
},
false,
},
}
for _, tc := range testCases {
suite.Run(tc.msg, func() {
err := tc.msgBuilder().ValidateBasic()
if tc.expectPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type MsgEthereumTx struct {
// inner transaction data
Data *types.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
// encoded storage size of the transaction
// DEPRECATED: encoded storage size of the transaction
Size_ float64 `protobuf:"fixed64,2,opt,name=size,proto3" json:"-"`
// transaction hash in hex format
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty" rlp:"-"`
+3 -1
View File
@@ -143,10 +143,12 @@ func (args *TransactionArgs) ToTransaction() *MsgEthereumTx {
from = args.From.Hex()
}
return &MsgEthereumTx{
msg := MsgEthereumTx{
Data: any,
From: from,
}
msg.Hash = msg.AsTransaction().Hash().Hex()
return &msg
}
// ToMessage converts the arguments to the Message type used by the core evm.
+3 -1
View File
@@ -62,7 +62,9 @@ func UnwrapEthereumMsg(tx *sdk.Tx, ethHash common.Hash) (*MsgEthereumTx, error)
if !ok {
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
if ethMsg.AsTransaction().Hash() == ethHash {
txHash := ethMsg.AsTransaction().Hash()
ethMsg.Hash = txHash.Hex()
if txHash == ethHash {
return ethMsg, nil
}
}