Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

x/evm: logger #272

Merged
merged 4 commits into from
Apr 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (`app/ante`) Moved `AnteHandler` implementation to `app/ante`
* (keys) Marked `ExportEthKeyCommand` as **UNSAFE**
* (`x/evm`) Moved `BeginBlock` and `EndBlock` to `x/evm/abci.go`
* [\#272](https://github.com/ChainSafe/ethermint/pull/272) Add `Logger` for evm module.

### Features

Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ require (
github.com/ethereum/go-ethereum v1.9.13
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
github.com/gogo/protobuf v1.3.1
github.com/golangci/golangci-lint v1.23.8 // indirect
github.com/gorilla/mux v1.7.4
github.com/huin/goupnp v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/onsi/ginkgo v1.11.0 // indirect
github.com/onsi/gomega v1.8.1 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/tsdb v0.9.1 // indirect
github.com/regen-network/cosmos-proto v0.1.1-0.20200213154359-02baa11ea7c2
Expand Down
147 changes: 2 additions & 145 deletions go.sum

Large diffs are not rendered by default.

37 changes: 26 additions & 11 deletions x/evm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*s
ethHash := common.BytesToHash(txHash)

st := types.StateTransition{
Sender: sender,
AccountNonce: msg.Data.AccountNonce,
Price: msg.Data.Price,
GasLimit: msg.Data.GasLimit,
Expand All @@ -55,7 +54,8 @@ func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*s
Payload: msg.Data.Payload,
Csdb: k.CommitStateDB.WithContext(ctx),
ChainID: intChainID,
THash: &ethHash,
TxHash: &ethHash,
Sender: sender,
Simulate: ctx.IsCheckTx(),
}

Expand All @@ -65,20 +65,23 @@ func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*s
k.TxCount++

// TODO: move to keeper
returnData, err := st.TransitionCSDB(ctx)
executionResult, err := st.TransitionDb(ctx)
if err != nil {
return nil, err
}

// update block bloom filter
k.Bloom.Or(k.Bloom, returnData.Bloom)
k.Bloom.Or(k.Bloom, executionResult.Bloom)

// update transaction logs in KVStore
err = k.SetTransactionLogs(ctx, returnData.Logs, txHash)
err = k.SetTransactionLogs(ctx, executionResult.Logs, txHash)
if err != nil {
return nil, err
}

// log successful execution
k.Logger(ctx).Info(executionResult.Result.Log)

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeEthereumTx,
Expand All @@ -101,8 +104,8 @@ func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*s
}

// set the events to the result
returnData.Result.Events = ctx.EventManager().Events().ToABCIEvents()
return returnData.Result, nil
executionResult.Result.Events = ctx.EventManager().Events().ToABCIEvents()
return executionResult.Result, nil
}

// handleMsgEthermint handles an sdk.StdTx for an Ethereum state transition
Expand All @@ -125,7 +128,7 @@ func handleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) (*sdk
Payload: msg.Payload,
Csdb: k.CommitStateDB.WithContext(ctx),
ChainID: intChainID,
THash: &ethHash,
TxHash: &ethHash,
Simulate: ctx.IsCheckTx(),
}

Expand All @@ -138,11 +141,23 @@ func handleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) (*sdk
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount)
k.TxCount++

returnData, err := st.TransitionCSDB(ctx)
executionResult, err := st.TransitionDb(ctx)
if err != nil {
return nil, err
}

// update block bloom filter
k.Bloom.Or(k.Bloom, executionResult.Bloom)

// update transaction logs in KVStore
err = k.SetTransactionLogs(ctx, executionResult.Logs, txHash)
if err != nil {
return nil, err
}

// log successful execution
k.Logger(ctx).Info(executionResult.Result.Log)

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeEthermint,
Expand All @@ -165,6 +180,6 @@ func handleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) (*sdk
}

// set the events to the result
returnData.Result.Events = ctx.EventManager().Events().ToABCIEvents()
return returnData.Result, nil
executionResult.Result.Events = ctx.EventManager().Events().ToABCIEvents()
return executionResult.Result, nil
}
7 changes: 7 additions & 0 deletions x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"fmt"

"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/codec"
ethcmn "github.com/ethereum/go-ethereum/common"
ethvm "github.com/ethereum/go-ethereum/core/vm"
Expand Down Expand Up @@ -44,6 +46,11 @@ func NewKeeper(
}
}

// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}

// ----------------------------------------------------------------------------
// Block hash mapping functions
// May be removed when using only as module (only required by rpc api)
Expand Down
165 changes: 93 additions & 72 deletions x/evm/types/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -16,30 +17,58 @@ import (

// StateTransition defines data to transitionDB in evm
type StateTransition struct {
Payload []byte
Recipient *common.Address
// TxData fields
AccountNonce uint64
GasLimit uint64
Price *big.Int
GasLimit uint64
Recipient *common.Address
Amount *big.Int
ChainID *big.Int
Csdb *CommitStateDB
THash *common.Hash
Sender common.Address
Simulate bool
Payload []byte

ChainID *big.Int
Csdb *CommitStateDB
TxHash *common.Hash
Sender common.Address
Simulate bool // i.e CheckTx execution
}

// GasInfo returns the gas limit, gas consumed and gas refunded from the EVM transition
// execution
type GasInfo struct {
GasLimit uint64
GasConsumed uint64
GasRefunded uint64
}

// ReturnData represents what's returned from a transition
type ReturnData struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logs []*ethtypes.Log
Bloom *big.Int
Result *sdk.Result
// ExecutionResult represents what's returned from a transition
type ExecutionResult struct {
Logs []*ethtypes.Log
Bloom *big.Int
Result *sdk.Result
GasInfo GasInfo
}

func (st StateTransition) newEVM(ctx sdk.Context, csdb *CommitStateDB, gasLimit uint64, gasPrice *big.Int) *vm.EVM {
// Create context for evm
context := vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Origin: st.Sender,
Coinbase: common.Address{}, // there's no benefitiary since we're not mining
BlockNumber: big.NewInt(ctx.BlockHeight()),
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
Difficulty: big.NewInt(0), // unused. Only required in PoW context
GasLimit: gasLimit,
GasPrice: gasPrice,
}

return vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{})
}

// TODO: move to keeper
// TransitionCSDB performs an evm state transition from a transaction
// TODO: update godoc, it doesn't explain what it does in depth.
func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) {
// TransitionDb will transition the state by applying the current transaction and
// returning the evm execution result.
// NOTE: State transition checks are run during AnteHandler execution.
func (st StateTransition) TransitionDb(ctx sdk.Context) (*ExecutionResult, error) {
contractCreation := st.Recipient == nil

cost, err := core.IntrinsicGas(st.Payload, contractCreation, true, false)
Expand Down Expand Up @@ -78,58 +107,54 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) {
return nil, errors.New("gas price cannot be nil")
}

// Create context for evm
context := vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Origin: st.Sender,
Coinbase: common.Address{}, // TODO: explain why this is empty
BlockNumber: big.NewInt(ctx.BlockHeight()),
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
Difficulty: big.NewInt(0), // unused. Only required in PoW context
GasLimit: gasLimit,
GasPrice: gasPrice.BigInt(),
}

evm := vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{})
evm := st.newEVM(ctx, csdb, gasLimit, gasPrice.BigInt())

var (
ret []byte
leftOverGas uint64
addr common.Address
senderRef = vm.AccountRef(st.Sender)
ret []byte
leftOverGas uint64
addr common.Address
recipientLog string
senderRef = vm.AccountRef(st.Sender)
)

// Get nonce of account outside of the EVM
currentNonce := st.Csdb.GetNonce(st.Sender)
// Set nonce of sender account before evm state transition for usage in generating Create address
st.Csdb.SetNonce(st.Sender, st.AccountNonce)

// create contract or execute call
switch contractCreation {
case true:
ret, addr, leftOverGas, err = evm.Create(senderRef, st.Payload, gasLimit, st.Amount)
recipientLog = fmt.Sprintf("contract address %s", addr)
default:
// Increment the nonce for the next transaction (just for evm state transition)
csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1)
ret, leftOverGas, err = evm.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
recipientLog = fmt.Sprintf("recipient address %s", st.Recipient)
}

gasConsumed := gasLimit - leftOverGas

if err != nil {
// Consume gas before returning
ctx.GasMeter().ConsumeGas(gasConsumed, "evm execution consumption")
return nil, err
}

gasConsumed := gasLimit - leftOverGas

// Resets nonce to value pre state transition
st.Csdb.SetNonce(st.Sender, currentNonce)

// Generate bloom filter to be saved in tx receipt data
bloomInt := big.NewInt(0)
var bloomFilter ethtypes.Bloom
var logs []*ethtypes.Log

if st.THash != nil && !st.Simulate {
logs, err = csdb.GetLogs(*st.THash)
var (
bloomFilter ethtypes.Bloom
logs []*ethtypes.Log
)

if st.TxHash != nil && !st.Simulate {
logs, err = csdb.GetLogs(*st.TxHash)
if err != nil {
return nil, err
}
Expand All @@ -138,55 +163,51 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) {
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
}

if !st.Simulate {
// Finalise state if not a simulated transaction
// TODO: change to depend on config
if err := st.Csdb.Finalise(true); err != nil {
return nil, err
}
}

// Encode all necessary data into slice of bytes to return in sdk result
res := &ResultData{
resultData := &ResultData{
Address: addr,
Bloom: bloomFilter,
Logs: logs,
Ret: ret,
TxHash: *st.THash,
TxHash: *st.TxHash,
}

resultData, err := EncodeResultData(res)
resBz, err := EncodeResultData(resultData)
if err != nil {
return nil, err
}

// handle errors
if err != nil {
if err == vm.ErrOutOfGas || err == vm.ErrCodeStoreOutOfGas {
return nil, sdkerrors.Wrap(err, "evm execution went out of gas")
}
resultLog := fmt.Sprintf(
"executed EVM state transition; sender address %s; %s", st.Sender, recipientLog,
)

// Consume gas before returning
ctx.GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
return nil, err
executionResult := &ExecutionResult{
Logs: logs,
Bloom: bloomInt,
Result: &sdk.Result{
Data: resBz,
Log: resultLog,
},
GasInfo: GasInfo{
GasConsumed: gasConsumed,
GasLimit: gasLimit,
GasRefunded: leftOverGas,
},
}

// TODO: Refund unused gas here, if intended in future

if !st.Simulate {
// Finalise state if not a simulated transaction
// TODO: change to depend on config
if err := st.Csdb.Finalise(true); err != nil {
return nil, err
}
}

// Consume gas from evm execution
// Out of gas check does not need to be done here since it is done within the EVM execution
ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")

err = st.Csdb.SetLogs(*st.THash, logs)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are already set on the handler

if err != nil {
return nil, err
}

returnData := &ReturnData{
Logs: logs,
Bloom: bloomInt,
Result: &sdk.Result{Data: resultData},
}

return returnData, nil
return executionResult, nil
}
3 changes: 2 additions & 1 deletion x/evm/types/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ func (suite *StateDBTestSuite) TestBloomFilter() {
// Get log from db
logs, err := stateDB.GetLogs(tHash)
suite.Require().NoError(err)
suite.Require().Equal(len(logs), 1)
suite.Require().Len(logs, 1)
suite.Require().Equal(log, logs[0])

// get logs bloom from the log
bloomInt := ethtypes.LogsBloom(logs)
Expand Down
Loading