evm: change Hook to use tx Receipt (#849)

* Change evm_hook to use Transaction Receipt

* use ethtypes.Receipt

* wip changes

* fix receipt creation

* receipt fixes

* check for contract addr

* changelog

* test

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: Federico Kunze Küllmer <federico.kunze94@gmail.com>
This commit is contained in:
Ramiro Carlucho
2022-01-03 17:18:13 +01:00
committed by GitHub
co-authored by Federico Kunze Küllmer Federico Kunze Küllmer
parent 9f4828e5bb
commit b9804505a3
17 changed files with 287 additions and 1734 deletions
+2 -2
View File
@@ -19,9 +19,9 @@ func NewMultiEvmHooks(hooks ...types.EvmHooks) MultiEvmHooks {
}
// PostTxProcessing delegate the call to underlying hooks
func (mh MultiEvmHooks) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*ethtypes.Log) error {
func (mh MultiEvmHooks) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
for i := range mh {
if err := mh[i].PostTxProcessing(ctx, txHash, logs); err != nil {
if err := mh[i].PostTxProcessing(ctx, from, to, receipt); err != nil {
return sdkerrors.Wrapf(err, "EVM hook %T failed", mh[i])
}
}
+8 -4
View File
@@ -17,15 +17,15 @@ type LogRecordHook struct {
Logs []*ethtypes.Log
}
func (dh *LogRecordHook) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*ethtypes.Log) error {
dh.Logs = logs
func (dh *LogRecordHook) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
dh.Logs = receipt.Logs
return nil
}
// FailureHook always fail
type FailureHook struct{}
func (dh FailureHook) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*ethtypes.Log) error {
func (dh FailureHook) PostTxProcessing(ctx sdk.Context, from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
return errors.New("post tx processing failed")
}
@@ -69,7 +69,11 @@ func (suite *KeeperTestSuite) TestEvmHooks() {
Address: suite.address,
})
logs := k.GetTxLogsTransient(txHash)
result := k.PostTxProcessing(txHash, logs)
receipt := &ethtypes.Receipt{
TxHash: txHash,
Logs: logs,
}
result := k.PostTxProcessing(common.Address{}, nil, receipt)
tc.expFunc(hook, result)
}
+2 -2
View File
@@ -363,11 +363,11 @@ func (k *Keeper) SetHooks(eh types.EvmHooks) *Keeper {
}
// PostTxProcessing delegate the call to the hooks. If no hook has been registered, this function returns with a `nil` error
func (k *Keeper) PostTxProcessing(txHash common.Hash, logs []*ethtypes.Log) error {
func (k *Keeper) PostTxProcessing(from common.Address, to *common.Address, receipt *ethtypes.Receipt) error {
if k.hooks == nil {
return nil
}
return k.hooks.PostTxProcessing(k.Ctx(), txHash, logs)
return k.hooks.PostTxProcessing(k.Ctx(), from, to, receipt)
}
// Tracer return a default vm.Tracer based on current keeper state
+53 -20
View File
@@ -1,6 +1,7 @@
package keeper
import (
"math"
"math/big"
tmtypes "github.com/tendermint/tendermint/types"
@@ -208,22 +209,8 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
}
res.Hash = txHash.Hex()
logs := k.GetTxLogsTransient(txHash)
if !res.Failed() {
// Only call hooks if tx executed successfully.
if err = k.PostTxProcessing(txHash, logs); err != nil {
// If hooks return error, revert the whole tx.
res.VmError = types.ErrPostTxProcessing.Error()
k.Logger(k.Ctx()).Error("tx post processing failed", "error", err)
} else if commit != nil {
// PostTxProcessing is successful, commit the cache context
commit()
ctx.EventManager().EmitEvents(k.Ctx().EventManager().Events())
}
}
// change to original context
k.WithContext(ctx)
@@ -232,16 +219,53 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
return nil, sdkerrors.Wrapf(err, "failed to refund gas leftover gas to sender %s", msg.From())
}
var bloomReceipt ethtypes.Bloom
if len(logs) > 0 {
res.Logs = types.NewLogsFromEth(logs)
// Update transient block bloom filter
bloom := k.GetBlockBloomTransient()
bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)))
k.SetBlockBloomTransient(bloom)
bloomReceipt = ethtypes.BytesToBloom(bloom.Bytes())
}
k.IncreaseTxIndexTransient()
if !res.Failed() {
cumulativeGasUsed := res.GasUsed
if ctx.BlockGasMeter() != nil {
limit := ctx.BlockGasMeter().Limit()
consumed := ctx.BlockGasMeter().GasConsumed()
cumulativeGasUsed = uint64(math.Min(float64(cumulativeGasUsed+consumed), float64(limit)))
}
receipt := &ethtypes.Receipt{
Type: tx.Type(),
PostState: nil, // TODO: intermediate state root
Status: ethtypes.ReceiptStatusSuccessful,
CumulativeGasUsed: cumulativeGasUsed,
Bloom: bloomReceipt,
Logs: logs,
TxHash: txHash,
ContractAddress: common.HexToAddress(res.ContractAddress),
GasUsed: res.GasUsed,
BlockHash: common.BytesToHash(ctx.HeaderHash()),
BlockNumber: big.NewInt(ctx.BlockHeight()),
TransactionIndex: uint(k.GetTxIndexTransient()),
}
// Only call hooks if tx executed successfully.
if err = k.PostTxProcessing(msg.From(), tx.To(), receipt); err != nil {
// If hooks return error, revert the whole tx.
res.VmError = types.ErrPostTxProcessing.Error()
k.Logger(ctx).Error("tx post processing failed", "error", err)
} else if commit != nil {
// PostTxProcessing is successful, commit the cache context
commit()
ctx.EventManager().EmitEvents(k.Ctx().EventManager().Events())
}
}
// update the gas used after refund
k.ResetGasMeterAndConsumeGas(res.GasUsed)
return res, nil
@@ -288,8 +312,9 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
// If commit is true, the cache context stack will be committed, otherwise discarded.
func (k *Keeper) ApplyMessageWithConfig(msg core.Message, tracer vm.Tracer, commit bool, cfg *types.EVMConfig) (*types.MsgEthereumTxResponse, error) {
var (
ret []byte // return bytes from evm execution
vmErr error // vm errors do not effect consensus and are therefore not assigned to err
ret []byte // return bytes from evm execution
vmErr error // vm errors do not effect consensus and are therefore not assigned to err
contractAddr common.Address
)
if !k.ctxStack.IsEmpty() {
@@ -337,10 +362,17 @@ func (k *Keeper) ApplyMessageWithConfig(msg core.Message, tracer vm.Tracer, comm
// - reset sender's nonce to msg.Nonce() before calling evm.
// - increase sender's nonce by one no matter the result.
k.SetNonce(sender.Address(), msg.Nonce())
ret, _, leftoverGas, vmErr = evm.Create(sender, msg.Data(), leftoverGas, msg.Value())
ret, contractAddr, leftoverGas, vmErr = evm.Create(sender, msg.Data(), leftoverGas, msg.Value())
k.SetNonce(sender.Address(), msg.Nonce()+1)
} else {
ret, leftoverGas, vmErr = evm.Call(sender, *msg.To(), msg.Data(), leftoverGas, msg.Value())
to := *msg.To()
acc := k.accountKeeper.GetAccount(k.Ctx(), sdk.AccAddress(to.Bytes()))
if acc != nil {
if ethAcc, ok := acc.(*ethermint.EthAccount); ok && ethAcc.Type() == ethermint.AccountTypeContract {
contractAddr = to
}
}
}
refundQuotient := params.RefundQuotient
@@ -376,9 +408,10 @@ func (k *Keeper) ApplyMessageWithConfig(msg core.Message, tracer vm.Tracer, comm
}
return &types.MsgEthereumTxResponse{
GasUsed: gasUsed,
VmError: vmError,
Ret: ret,
GasUsed: gasUsed,
VmError: vmError,
Ret: ret,
ContractAddress: contractAddr.String(),
}, nil
}