4d609b2a22
* 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>
193 lines
5.4 KiB
Go
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
|
|
}
|