laconicd/x/evm/types/state_transition.go
Federico Kunze 4d609b2a22
bump Cosmos SDK version to v0.38.2 (#183)
* evm: move Keeper and Querier to /keeper package

* keeper: update keeper_test.go

* fix format

* evm: use aliased types

* bump SDK version to v0.38.1

* app: updates from new version

* errors: switch sdk.Error -> error

* errors: switch sdk.Error -> error. Continuation

* more fixes

* update app/

* update keys and client pkgs

* build

* fix tests

* lint

* minor changes

* changelog

* address @austinbell comments

* Fix keyring usage in rpc API and CLI

* fix keyring

* break line

* Misc cleanup (#188)

* evm: move Begin and EndBlock to abci.go

* evm: use expected keeper interfaces

* app: use EthermintApp for integration and unit test setup

* evm: remove count type; update codec

* go mod verify

* evm: rename msgs for consistency

* evm: events

* minor cleanup

* lint

* ante: update tests

* changelog

* nolint

* evm: update statedb to create ethermint Account instead of BaseAccount

* fix importer test

* address @austinabell comments

* update README

* changelog

* evm: update codec

* fix event sender

* store logs in keeper after transition (#210)

* add some comments

* begin log handler test

* update TransitionCSDB to return ReturnData

* use rlp for result data encode/decode

* update tests

* implement SetBlockLogs

* implement GetBlockLogs

* test log set/get

* update keeper get/set logs to use hash as key

* fix test

* move logsKey to csdb

* attempt to fix test

* attempt to fix test

* attempt to fix test

* lint

* lint

* lint

* save logs after handling msg

* update k.Logs

* cleanup

* remove unused

* fix issues

* comment out handler test

* address comments

* lint

* fix handler test

* address comments

* use amino

* lint

* address comments

* merge

* fix encoding bug

* minor fix

* rpc: error handling

* rpc: simulate only returns gasConsumed

* rpc: error ineffassign

* go: bump version to 1.14 and SDK version to latest master

* rpc: fix simulation return value

* breaking changes from SDK

* sdk: breaking changes; build

* tests: fixes

* minor fix

* proto: ethermint types attempt

* proto: define EthAccount proto type and extend sdk std.Codec

* evm: fix panic on handler test

* evm: minor state object changes

* cleanup

* tests: update test-importer

* fix pubkey registration

* lint

* cleanup

* more test checks for importer

* minor change

* codec fixes

* rm init func

* fix importer test build

* fix marshaling for TxDecoder

* use amino codec for evm

* fix marshaling for SimulationResponse

* use jsonpb for unmarshaling

* fix method handler crashed

* return err on VerifySig

* switch stateObject balance to sdk.Int

* fixes to codec and encoding

* cleanup

* set tmhash -> ethhash in state transition

* add tmhash->ethereumhash to csdb.GetLogs

* attempt to  fix tests

* update GetLogs to switch with Has

* ante panic

* diff changes

* update SetLogs

* evm/cli: use ethermint codec

* use LengthPrefixed for encoding

* add check for nil *big.Int

* add balance to UpdateAccounts

* fix previous balance

* fix balance bug

* prevent panic on make test-import

Co-authored-by: austinabell <austinabell8@gmail.com>
Co-authored-by: noot <36753753+noot@users.noreply.github.com>
Co-authored-by: noot <elizabethjbinks@gmail.com>
2020-04-22 15:26:01 -04:00

193 lines
5.4 KiB
Go

package types
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
emint "github.com/cosmos/ethermint/types"
)
// StateTransition defines data to transitionDB in evm
type StateTransition struct {
Payload []byte
Recipient *common.Address
AccountNonce uint64
GasLimit uint64
Price *big.Int
Amount *big.Int
ChainID *big.Int
Csdb *CommitStateDB
THash *common.Hash
Sender common.Address
Simulate bool
}
// ReturnData represents what's returned from a transition
type ReturnData struct {
Logs []*ethtypes.Log
Bloom *big.Int
Result *sdk.Result
}
// 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) {
contractCreation := st.Recipient == nil
cost, err := core.IntrinsicGas(st.Payload, contractCreation, true)
if err != nil {
return nil, sdkerrors.Wrap(err, "invalid intrinsic gas for transaction")
}
// This gas limit the the transaction gas limit with intrinsic gas subtracted
gasLimit := st.GasLimit - ctx.GasMeter().GasConsumed()
csdb := st.Csdb.WithContext(ctx)
if st.Simulate {
// gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call
// the cost needs to be the same as an Ethereum transaction sent through the web3 API
consumedGas := ctx.GasMeter().GasConsumed()
gasLimit = st.GasLimit - cost
if consumedGas < cost {
// If Cosmos standard tx ante handler cost is less than EVM intrinsic cost
// gas must be consumed to match to accurately simulate an Ethereum transaction
ctx.GasMeter().ConsumeGas(cost-consumedGas, "Intrinsic gas match")
}
csdb = st.Csdb.Copy()
}
// This gas meter is set up to consume gas from gaskv during evm execution and be ignored
currentGasMeter := ctx.GasMeter()
evmGasMeter := sdk.NewInfiniteGasMeter()
csdb.WithContext(ctx.WithGasMeter(evmGasMeter))
// Clear cache of accounts to handle changes outside of the EVM
csdb.UpdateAccounts()
gasPrice := ctx.MinGasPrices().AmountOf(emint.DenomDefault)
if gasPrice.IsNil() {
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{})
var (
ret []byte
leftOverGas uint64
addr common.Address
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)
switch contractCreation {
case true:
ret, addr, leftOverGas, err = evm.Create(senderRef, st.Payload, gasLimit, st.Amount)
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)
}
if err != nil {
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)
if err != nil {
return nil, err
}
bloomInt = ethtypes.LogsBloom(logs)
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
}
// Encode all necessary data into slice of bytes to return in sdk result
res := &ResultData{
Address: addr,
Bloom: bloomFilter,
Logs: logs,
Ret: ret,
TxHash: *st.THash,
}
resultData, err := EncodeResultData(res)
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")
}
// Consume gas before returning
ctx.GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
return nil, err
}
// 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)
if err != nil {
return nil, err
}
returnData := &ReturnData{
Logs: logs,
Bloom: bloomInt,
Result: &sdk.Result{Data: resultData},
}
return returnData, nil
}