EVM Transaction handler and contract creation (#96)
* WIP implementing state transition function * Error handling and application setup fix * Fixed error comment * Allow creation of state objects with a BaseAccount * Fixed parameters and finalise state after transaction * updated transaction signing and cli signature * Set up consistent account encoding and decoding * Update txbuilder to get sequence before generating eth tx * Added create functionality to the CLI command * Remove need to copy over context for statedb interactions * Updated account retriever * Cleaned up handler code and updated TODO * Make recoverEthSig private again * Add error check for committing to kv store * Remove commented out code * Update evm chain config for state transition * Add time in context for dapps
This commit is contained in:
parent
72fc3ca3af
commit
1b5c33cf33
@ -25,6 +25,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
|
||||
eminttypes "github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -79,6 +80,8 @@ func MakeCodec() *codec.Codec {
|
||||
ModuleBasics.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
eminttypes.RegisterCodec(cdc)
|
||||
|
||||
return cdc
|
||||
}
|
||||
|
||||
@ -128,7 +131,7 @@ func NewEthermintApp(
|
||||
|
||||
keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
|
||||
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
|
||||
gov.StoreKey, params.StoreKey)
|
||||
gov.StoreKey, params.StoreKey, evmtypes.EvmStoreKey, evmtypes.EvmCodeKey)
|
||||
tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey)
|
||||
|
||||
app := &EthermintApp{
|
||||
@ -151,7 +154,7 @@ func NewEthermintApp(
|
||||
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
|
||||
|
||||
// add keepers
|
||||
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount)
|
||||
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, eminttypes.ProtoBaseAccount)
|
||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
|
||||
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms)
|
||||
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey], tkeys[staking.TStoreKey],
|
||||
@ -162,6 +165,7 @@ func NewEthermintApp(
|
||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper,
|
||||
slashingSubspace, slashing.DefaultCodespace)
|
||||
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
|
||||
app.evmKeeper = evm.NewKeeper(app.accountKeeper, keys[evmtypes.EvmStoreKey], keys[evmtypes.EvmCodeKey], cdc)
|
||||
|
||||
// register the proposal types
|
||||
govRouter := gov.NewRouter()
|
||||
@ -204,7 +208,7 @@ func NewEthermintApp(
|
||||
app.mm.SetOrderInitGenesis(
|
||||
genaccounts.ModuleName, distr.ModuleName, staking.ModuleName,
|
||||
auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName,
|
||||
mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName,
|
||||
mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName, evmtypes.ModuleName,
|
||||
)
|
||||
|
||||
app.mm.RegisterInvariants(&app.crisisKeeper)
|
||||
|
@ -28,8 +28,8 @@ type Account struct {
|
||||
|
||||
// merkle root of the storage trie
|
||||
//
|
||||
// TODO: good chance we may not need this
|
||||
Root ethcmn.Hash
|
||||
// TODO: add back root if needed (marshalling is broken if not initializing)
|
||||
// Root ethcmn.Hash
|
||||
|
||||
CodeHash []byte
|
||||
}
|
||||
|
77
types/account_retriever.go
Normal file
77
types/account_retriever.go
Normal file
@ -0,0 +1,77 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
// ** Modified version of github.com/cosmos/cosmos-sdk/x/auth/types/account_retriever.go
|
||||
// ** to allow passing in a codec for decoding Account types
|
||||
// AccountRetriever defines the properties of a type that can be used to
|
||||
// retrieve accounts.
|
||||
type AccountRetriever struct {
|
||||
querier auth.NodeQuerier
|
||||
codec *codec.Codec
|
||||
}
|
||||
|
||||
// * Modified to allow a codec to be passed in
|
||||
// NewAccountRetriever initialises a new AccountRetriever instance.
|
||||
func NewAccountRetriever(querier auth.NodeQuerier, codec *codec.Codec) AccountRetriever {
|
||||
if codec == nil {
|
||||
codec = auth.ModuleCdc
|
||||
}
|
||||
return AccountRetriever{querier: querier, codec: codec}
|
||||
}
|
||||
|
||||
// GetAccount queries for an account given an address and a block height. An
|
||||
// error is returned if the query or decoding fails.
|
||||
func (ar AccountRetriever) GetAccount(addr sdk.AccAddress) (exported.Account, error) {
|
||||
account, _, err := ar.GetAccountWithHeight(addr)
|
||||
return account, err
|
||||
}
|
||||
|
||||
// GetAccountWithHeight queries for an account given an address. Returns the
|
||||
// height of the query with the account. An error is returned if the query
|
||||
// or decoding fails.
|
||||
func (ar AccountRetriever) GetAccountWithHeight(addr sdk.AccAddress) (exported.Account, int64, error) {
|
||||
// ** This line was changed to use non-static codec
|
||||
bs, err := ar.codec.MarshalJSON(auth.NewQueryAccountParams(addr))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, height, err := ar.querier.QueryWithData(fmt.Sprintf("custom/%s/%s", auth.QuerierRoute, auth.QueryAccount), bs)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var account exported.Account
|
||||
// ** This line was changed to use non-static codec
|
||||
if err := ar.codec.UnmarshalJSON(res, &account); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return account, height, nil
|
||||
}
|
||||
|
||||
// EnsureExists returns an error if no account exists for the given address else nil.
|
||||
func (ar AccountRetriever) EnsureExists(addr sdk.AccAddress) error {
|
||||
if _, err := ar.GetAccount(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAccountNumberSequence returns sequence and account number for the given address.
|
||||
// It returns an error if the account couldn't be retrieved from the state.
|
||||
func (ar AccountRetriever) GetAccountNumberSequence(addr sdk.AccAddress) (uint64, uint64, error) {
|
||||
acc, err := ar.GetAccount(addr)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return acc.GetAccountNumber(), acc.GetSequence(), nil
|
||||
}
|
@ -11,6 +11,9 @@ const (
|
||||
|
||||
CodeInvalidValue sdk.CodeType = 1
|
||||
CodeInvalidChainID sdk.CodeType = 2
|
||||
CodeInvalidSender sdk.CodeType = 3
|
||||
CodeVMExecution sdk.CodeType = 4
|
||||
CodeInvalidIntrinsicGas sdk.CodeType = 5
|
||||
)
|
||||
|
||||
// CodeToDefaultMsg takes the CodeType variable and returns the error string
|
||||
@ -20,19 +23,38 @@ func CodeToDefaultMsg(code sdk.CodeType) string {
|
||||
return "invalid value"
|
||||
case CodeInvalidChainID:
|
||||
return "invalid chain ID"
|
||||
case CodeInvalidSender:
|
||||
return "could not derive sender from transaction"
|
||||
case CodeVMExecution:
|
||||
return "error while executing evm transaction"
|
||||
case CodeInvalidIntrinsicGas:
|
||||
return "invalid intrinsic gas"
|
||||
default:
|
||||
return sdk.CodeToDefaultMsg(code)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrInvalidValue returns a standardized SDK error resulting from an invalid
|
||||
// value.
|
||||
// ErrInvalidValue returns a standardized SDK error resulting from an invalid value.
|
||||
func ErrInvalidValue(msg string) sdk.Error {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidValue, msg)
|
||||
}
|
||||
|
||||
// ErrInvalidChainID returns a standardized SDK error resulting from an invalid
|
||||
// chain ID.
|
||||
// ErrInvalidChainID returns a standardized SDK error resulting from an invalid chain ID.
|
||||
func ErrInvalidChainID(msg string) sdk.Error {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidChainID, msg)
|
||||
}
|
||||
|
||||
// ErrInvalidSender returns a standardized SDK error resulting from an invalid transaction sender.
|
||||
func ErrInvalidSender(msg string) sdk.Error {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidSender, msg)
|
||||
}
|
||||
|
||||
// ErrVMExecution returns a standardized SDK error resulting from an error in EVM execution.
|
||||
func ErrVMExecution(msg string) sdk.Error {
|
||||
return sdk.NewError(DefaultCodespace, CodeVMExecution, msg)
|
||||
}
|
||||
|
||||
// ErrVMExecution returns a standardized SDK error resulting from an error in EVM execution.
|
||||
func ErrInvalidIntrinsicGas(msg string) sdk.Error {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidIntrinsicGas, msg)
|
||||
}
|
||||
|
@ -88,9 +88,9 @@ func GetCmdGenTx(cdc *codec.Codec) *cobra.Command {
|
||||
// GetCmdGenTx generates an ethereum transaction
|
||||
func GetCmdGenETHTx(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "generate-eth-tx [nonce] [ethaddress] [amount] [gaslimit] [gasprice] [payload]",
|
||||
Short: "geberate and broadcast an Ethereum tx",
|
||||
Args: cobra.ExactArgs(6),
|
||||
Use: "generate-eth-tx [amount] [gaslimit] [gasprice] [payload] [<ethereum-address>]",
|
||||
Short: "generate and broadcast an Ethereum tx. If address is not specified, contract will be created",
|
||||
Args: cobra.RangeArgs(4, 5),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := emintUtils.NewETHCLIContext().WithCodec(cdc)
|
||||
|
||||
@ -101,35 +101,41 @@ func GetCmdGenETHTx(cdc *codec.Codec) *cobra.Command {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
nonce, err := strconv.ParseUint(args[0], 0, 64)
|
||||
coins, err := sdk.ParseCoins(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
coins, err := sdk.ParseCoins(args[2])
|
||||
gasLimit, err := strconv.ParseUint(args[1], 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gasLimit, err := strconv.ParseUint(args[3], 0, 64)
|
||||
gasPrice, err := strconv.ParseUint(args[2], 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gasPrice, err := strconv.ParseUint(args[4], 0, 64)
|
||||
payload := args[3]
|
||||
|
||||
txBldr, err = emintUtils.PrepareTxBuilder(txBldr, cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payload := args[5]
|
||||
var tx *types.EthereumTxMsg
|
||||
if len(args) == 5 {
|
||||
tx = types.NewEthereumTxMsg(txBldr.Sequence(), ethcmn.HexToAddress(args[4]), big.NewInt(coins.AmountOf(emintTypes.DenomDefault).Int64()), gasLimit, new(big.Int).SetUint64(gasPrice), []byte(payload))
|
||||
} else {
|
||||
tx = types.NewEthereumTxMsgContract(txBldr.Sequence(), big.NewInt(coins.AmountOf(emintTypes.DenomDefault).Int64()), gasLimit, new(big.Int).SetUint64(gasPrice), []byte(payload))
|
||||
}
|
||||
|
||||
tx := types.NewEthereumTxMsg(nonce, ethcmn.HexToAddress(args[1]), big.NewInt(coins.AmountOf(emintTypes.DenomDefault).Int64()), gasLimit, new(big.Int).SetUint64(gasPrice), []byte(payload))
|
||||
err = tx.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return emintUtils.BroadcastETHTx(cliCtx, txBldr.WithSequence(nonce).WithKeybase(kb), tx)
|
||||
return emintUtils.BroadcastETHTx(cliCtx, txBldr.WithKeybase(kb), tx)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -42,10 +42,6 @@ func GenerateOrBroadcastETHMsgs(cliCtx context.CLIContext, txBldr authtypes.TxBu
|
||||
|
||||
// BroadcastETHTx Broadcasts an Ethereum Tx not wrapped in a Std Tx
|
||||
func BroadcastETHTx(cliCtx context.CLIContext, txBldr authtypes.TxBuilder, tx *evmtypes.EthereumTxMsg) error {
|
||||
txBldr, err := utils.PrepareTxBuilder(txBldr, cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromName := cliCtx.GetFromName()
|
||||
|
||||
@ -346,3 +342,34 @@ func getFromFields(from string, genOnly bool) (sdk.AccAddress, string, error) {
|
||||
|
||||
return info.GetAddress(), info.GetName(), nil
|
||||
}
|
||||
|
||||
// * Overriden function from cosmos-sdk/auth/client/utils/tx.go
|
||||
// PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx.
|
||||
func PrepareTxBuilder(txBldr authtypes.TxBuilder, cliCtx context.CLIContext) (authtypes.TxBuilder, error) {
|
||||
from := cliCtx.GetFromAddress()
|
||||
|
||||
// * Function is needed to override to use different account getter (to not use static codec)
|
||||
accGetter := emint.NewAccountRetriever(cliCtx, cliCtx.Codec)
|
||||
if err := accGetter.EnsureExists(from); err != nil {
|
||||
return txBldr, err
|
||||
}
|
||||
|
||||
txbldrAccNum, txbldrAccSeq := txBldr.AccountNumber(), txBldr.Sequence()
|
||||
// TODO: (ref #1903) Allow for user supplied account number without
|
||||
// automatically doing a manual lookup.
|
||||
if txbldrAccNum == 0 || txbldrAccSeq == 0 {
|
||||
num, seq, err := accGetter.GetAccountNumberSequence(from)
|
||||
if err != nil {
|
||||
return txBldr, err
|
||||
}
|
||||
|
||||
if txbldrAccNum == 0 {
|
||||
txBldr = txBldr.WithAccountNumber(num)
|
||||
}
|
||||
if txbldrAccSeq == 0 {
|
||||
txBldr = txBldr.WithSequence(seq)
|
||||
}
|
||||
}
|
||||
|
||||
return txBldr, nil
|
||||
}
|
||||
|
102
x/evm/handler.go
102
x/evm/handler.go
@ -2,8 +2,15 @@ package evm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
emint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
@ -22,20 +29,103 @@ func NewHandler(keeper Keeper) sdk.Handler {
|
||||
|
||||
// Handle an Ethereum specific tx
|
||||
func handleETHTxMsg(ctx sdk.Context, keeper Keeper, msg types.EthereumTxMsg) sdk.Result {
|
||||
// TODO: Implement transaction logic
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return sdk.ErrUnknownRequest("Basic validation failed").Result()
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// If no to address, create contract with evm.Create(...)
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||
if !ok {
|
||||
return emint.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())).Result()
|
||||
}
|
||||
|
||||
// Else Call contract with evm.Call(...)
|
||||
// Verify signature and retrieve sender address
|
||||
sender, err := msg.VerifySig(intChainID)
|
||||
if err != nil {
|
||||
return emint.ErrInvalidSender(err.Error()).Result()
|
||||
}
|
||||
contractCreation := msg.To() == nil
|
||||
|
||||
// Pay intrinsic gas
|
||||
// TODO: Check config for homestead enabled
|
||||
cost, err := core.IntrinsicGas(msg.Data.Payload, contractCreation, true)
|
||||
if err != nil {
|
||||
return emint.ErrInvalidIntrinsicGas(err.Error()).Result()
|
||||
}
|
||||
|
||||
usableGas := msg.Data.GasLimit - cost
|
||||
|
||||
// Create context for evm
|
||||
context := vm.Context{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
Origin: sender,
|
||||
Coinbase: common.Address{},
|
||||
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
||||
Time: big.NewInt(time.Now().Unix()),
|
||||
Difficulty: big.NewInt(0x30000), // unused
|
||||
GasLimit: ctx.GasMeter().Limit(),
|
||||
GasPrice: ctx.MinGasPrices().AmountOf(emint.DenomDefault).Int,
|
||||
}
|
||||
|
||||
vmenv := vm.NewEVM(context, keeper.csdb.WithContext(ctx), types.GenerateChainConfig(intChainID), vm.Config{})
|
||||
|
||||
var (
|
||||
leftOverGas uint64
|
||||
addr common.Address
|
||||
vmerr error
|
||||
senderRef = vm.AccountRef(sender)
|
||||
)
|
||||
|
||||
if contractCreation {
|
||||
_, addr, leftOverGas, vmerr = vmenv.Create(senderRef, msg.Data.Payload, usableGas, msg.Data.Amount)
|
||||
} else {
|
||||
// Increment the nonce for the next transaction
|
||||
keeper.csdb.SetNonce(sender, keeper.csdb.GetNonce(sender)+1)
|
||||
_, leftOverGas, vmerr = vmenv.Call(senderRef, *msg.To(), msg.Data.Payload, usableGas, msg.Data.Amount)
|
||||
}
|
||||
|
||||
// handle errors
|
||||
if vmerr != nil {
|
||||
return emint.ErrVMExecution(vmerr.Error()).Result()
|
||||
}
|
||||
|
||||
// Refund remaining gas from tx (Will supply keeper need to be introduced to evm Keeper to do this)
|
||||
// Refund remaining gas from tx (Check these values and ensure gas is being consumed correctly)
|
||||
refundGas(keeper.csdb, &leftOverGas, msg.Data.GasLimit, context.GasPrice, sender)
|
||||
|
||||
// add balance for the processor of the tx (determine who rewards are being processed to)
|
||||
// TODO: Double check nothing needs to be done here
|
||||
|
||||
return sdk.Result{}
|
||||
keeper.csdb.Finalise(true) // Change to depend on config
|
||||
|
||||
// TODO: Remove commit from tx handler (should be done at end of block)
|
||||
_, err = keeper.csdb.Commit(true)
|
||||
if err != nil {
|
||||
return sdk.ErrUnknownRequest("Failed to write data to kv store").Result()
|
||||
}
|
||||
|
||||
// TODO: Consume gas from sender
|
||||
|
||||
return sdk.Result{Log: addr.Hex(), GasUsed: msg.Data.GasLimit - leftOverGas}
|
||||
}
|
||||
|
||||
func refundGas(
|
||||
st vm.StateDB, gasRemaining *uint64, initialGas uint64, gasPrice *big.Int,
|
||||
from common.Address,
|
||||
) {
|
||||
// Apply refund counter, capped to half of the used gas.
|
||||
refund := (initialGas - *gasRemaining) / 2
|
||||
if refund > st.GetRefund() {
|
||||
refund = st.GetRefund()
|
||||
}
|
||||
*gasRemaining += refund
|
||||
|
||||
// Return ETH for remaining gas, exchanged at the original rate.
|
||||
remaining := new(big.Int).Mul(new(big.Int).SetUint64(*gasRemaining), gasPrice)
|
||||
st.AddBalance(from, remaining)
|
||||
|
||||
// // Also return remaining gas to the block gas counter so it is
|
||||
// // available for the next transaction.
|
||||
// TODO: Return gas to block gas meter?
|
||||
// st.gp.AddGas(st.gas)
|
||||
}
|
||||
|
@ -94,7 +94,8 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
|
||||
|
||||
func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
|
||||
|
||||
func (am AppModule) EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
// TODO: Commit database here ?
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
||||
|
26
x/evm/types/chain_config.go
Normal file
26
x/evm/types/chain_config.go
Normal file
@ -0,0 +1,26 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// GenerateChainConfig returns an Ethereum chainconfig for EVM state transitions
|
||||
func GenerateChainConfig(chainID *big.Int) *params.ChainConfig {
|
||||
// TODO: Update chainconfig to take in parameters for fork blocks
|
||||
return ¶ms.ChainConfig{
|
||||
ChainID: chainID,
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: big.NewInt(0),
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
}
|
||||
}
|
@ -80,7 +80,12 @@ type (
|
||||
func newObject(db *CommitStateDB, accProto auth.Account) *stateObject {
|
||||
acc, ok := accProto.(*types.Account)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid account type for state object: %T", acc))
|
||||
// State object can be created from a baseAccount
|
||||
baseAccount, ok := accProto.(*auth.BaseAccount)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid account type for state object: %T", accProto))
|
||||
}
|
||||
acc = &types.Account{BaseAccount: baseAccount}
|
||||
}
|
||||
|
||||
if acc.CodeHash == nil {
|
||||
|
Loading…
Reference in New Issue
Block a user