evm: add missing genesis fields and export genesis state logic (#255)

* evm: export genesis state

* x/evm: split keeper.go

* x/evm: retrieve storage from address

* changelog

* fixes

* add check for nil logs

* update validation func

* fixes

* fix non-determinism

* stop storage iteration

* remove error return value

* update changelog

* fix test

* lint
This commit is contained in:
Federico Kunze
2020-05-18 15:21:12 -04:00
committed by GitHub
parent 846f48a572
commit 16df7725c5
15 changed files with 411 additions and 317 deletions
+1
View File
@@ -8,6 +8,7 @@ import (
// AccountKeeper defines the expected account keeper interface
type AccountKeeper interface {
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
GetAllAccounts(ctx sdk.Context) (accounts []authexported.Account)
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
SetAccount(ctx sdk.Context, account authexported.Account)
RemoveAccount(ctx sdk.Context, account authexported.Account)
+36 -16
View File
@@ -1,13 +1,15 @@
package types
import (
"bytes"
"errors"
"math/big"
"github.com/cosmos/ethermint/types"
ethcmn "github.com/ethereum/go-ethereum/common"
)
var zeroAddrBytes = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
type (
// GenesisState defines the application's genesis state. It contains all the
// information required and accounts to initialize the blockchain.
@@ -15,31 +17,49 @@ type (
Accounts []GenesisAccount `json:"accounts"`
}
// GenesisStorage represents the GenesisAccount Storage map as single key value
// pairs. This is to prevent non determinism at genesis initialization or export.
GenesisStorage struct {
Key ethcmn.Hash `json:"key"`
Value ethcmn.Hash `json:"value"`
}
// GenesisAccount defines an account to be initialized in the genesis state.
// Its main difference between with Geth's GenesisAccount is that it uses a custom
// storage type and that it doesn't contain the private key field.
GenesisAccount struct {
Address ethcmn.Address `json:"address"`
Balance *big.Int `json:"balance"`
Code []byte `json:"code,omitempty"`
Storage types.Storage `json:"storage,omitempty"`
Address ethcmn.Address `json:"address"`
Balance *big.Int `json:"balance"`
Code []byte `json:"code,omitempty"`
Storage []GenesisStorage `json:"storage,omitempty"`
}
)
// ValidateGenesis validates evm genesis config
func ValidateGenesis(data GenesisState) error {
for _, acct := range data.Accounts {
if len(acct.Address.Bytes()) == 0 {
return errors.New("invalid GenesisAccount: address cannot be empty")
}
if acct.Balance == nil {
return errors.New("invalid GenesisAccount: balance cannot be empty")
}
// NewGenesisStorage creates a new GenesisStorage instance
func NewGenesisStorage(key, value ethcmn.Hash) GenesisStorage {
return GenesisStorage{
Key: key,
Value: value,
}
return nil
}
// DefaultGenesisState sets default evm genesis config
// DefaultGenesisState sets default evm genesis state with empty accounts.
func DefaultGenesisState() GenesisState {
return GenesisState{
Accounts: []GenesisAccount{},
}
}
// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
for _, acc := range gs.Accounts {
if bytes.Equal(acc.Address.Bytes(), zeroAddrBytes) {
return errors.New("invalid GenesisAccount: address cannot be empty")
}
if acc.Balance == nil {
return errors.New("invalid GenesisAccount: balance cannot be empty")
}
}
return nil
}
+29 -15
View File
@@ -1,45 +1,59 @@
package types
import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
ethcmn "github.com/ethereum/go-ethereum/common"
)
func TestValidateGenesis(t *testing.T) {
testCases := []struct {
msg string
genstate GenesisState
name string
genState GenesisState
expPass bool
}{
{
msg: "pass with defaultState ",
genstate: DefaultGenesisState(),
name: "default",
genState: DefaultGenesisState(),
expPass: true,
},
{
msg: "empty address",
genstate: GenesisState{
Accounts: []GenesisAccount{{}},
name: "empty account address bytes",
genState: GenesisState{
Accounts: []GenesisAccount{
{
Address: ethcmn.Address{},
Balance: big.NewInt(1),
},
},
},
expPass: false,
},
{
msg: "empty balance",
genstate: GenesisState{
Accounts: []GenesisAccount{{Balance: nil}},
name: "nil account balance",
genState: GenesisState{
Accounts: []GenesisAccount{
{
Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}),
Balance: nil,
},
},
},
expPass: false,
},
}
for i, tc := range testCases {
err := ValidateGenesis(tc.genstate)
for _, tc := range testCases {
tc := tc
err := tc.genState.Validate()
if tc.expPass {
require.NoError(t, err, "test (%d) %s", i, tc.msg)
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, "test (%d): %s", i, tc.msg)
require.Error(t, err, tc.name)
}
}
}
+9 -2
View File
@@ -722,11 +722,18 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
value := iter.Value()
if value, dirty := so.dirtyStorage[key]; dirty {
cb(key, value)
// check if iteration stops
if cb(key, value) {
break
}
continue
}
cb(key, ethcmn.BytesToHash(value))
// check if iteration stops
if cb(key, ethcmn.BytesToHash(value)) {
break
}
}
return nil