Skip to content

Commit

Permalink
Add logic to export/import all persistent EVM state (#1636)
Browse files Browse the repository at this point in the history
* add EVM state to genesis

* export/import EVM state
  • Loading branch information
codchen authored May 7, 2024
1 parent b7c1875 commit 56732e6
Show file tree
Hide file tree
Showing 12 changed files with 1,534 additions and 114 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ require (
github.com/fzipp/gocyclo v0.5.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-critic/go-critic v0.6.3 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
Expand Down Expand Up @@ -172,6 +173,7 @@ require (
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2Gihuqh
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
Expand Down Expand Up @@ -765,6 +766,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag=
Expand Down
26 changes: 26 additions & 0 deletions proto/evm/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,34 @@ message AddressAssociation {
string eth_address = 2; // Ethereum address
}

message Code {
string address = 1;
bytes code = 2;
}

message ContractState {
string address = 1;
bytes key = 2;
bytes value = 3;
}

message Nonce {
string address = 1;
uint64 nonce = 2;
}

message Serialized {
bytes prefix = 1;
bytes key = 2;
bytes value = 3;
}

// GenesisState defines the evm module's genesis state.
message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];
repeated AddressAssociation address_associations = 2; // List of address associations
repeated Code codes = 3; // List of stored code
repeated ContractState states = 4; // List of contract state
repeated Nonce nonces = 5;
repeated Serialized serialized = 6;
}
61 changes: 61 additions & 0 deletions x/evm/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,79 @@ import (

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/sei-protocol/sei-chain/x/evm/keeper"
"github.com/sei-protocol/sei-chain/x/evm/types"
)

func InitGenesis(ctx sdk.Context, k *keeper.Keeper, genState types.GenesisState) {
k.InitGenesis(ctx, genState)
k.SetParams(ctx, genState.Params)
for _, aa := range genState.AddressAssociations {
k.SetAddressMapping(ctx, sdk.MustAccAddressFromBech32(aa.SeiAddress), common.HexToAddress(aa.EthAddress))
}
for _, code := range genState.Codes {
k.SetCode(ctx, common.HexToAddress(code.Address), code.Code)
}
for _, state := range genState.States {
k.SetState(ctx, common.HexToAddress(state.Address), common.BytesToHash(state.Key), common.BytesToHash(state.Value))
}
for _, nonce := range genState.Nonces {
k.SetNonce(ctx, common.HexToAddress(nonce.Address), nonce.Nonce)
}
for _, serialized := range genState.Serialized {
k.PrefixStore(ctx, serialized.Prefix).Set(serialized.Key, serialized.Value)
}
}

func ExportGenesis(ctx sdk.Context, k *keeper.Keeper) *types.GenesisState {
genesis := types.DefaultGenesis()
genesis.Params = k.GetParams(ctx)
k.IterateSeiAddressMapping(ctx, func(evmAddr common.Address, seiAddr sdk.AccAddress) bool {
genesis.AddressAssociations = append(genesis.AddressAssociations, &types.AddressAssociation{
SeiAddress: seiAddr.String(),
EthAddress: evmAddr.Hex(),
})
return false
})
k.IterateAllCode(ctx, func(addr common.Address, code []byte) bool {
genesis.Codes = append(genesis.Codes, &types.Code{
Address: addr.Hex(),
Code: code,
})
return false
})
k.IterateState(ctx, func(addr common.Address, key, val common.Hash) bool {
genesis.States = append(genesis.States, &types.ContractState{
Address: addr.Hex(),
Key: key[:],
Value: val[:],
})
return false
})
k.IterateAllNonces(ctx, func(addr common.Address, nonce uint64) bool {
genesis.Nonces = append(genesis.Nonces, &types.Nonce{
Address: addr.Hex(),
Nonce: nonce,
})
return false
})
for _, prefix := range [][]byte{
types.ReceiptKeyPrefix,
types.BlockBloomPrefix,
types.TxHashesPrefix,
types.PointerRegistryPrefix,
types.PointerCWCodePrefix,
} {
k.IterateAll(ctx, prefix, func(key, val []byte) bool {
genesis.Serialized = append(genesis.Serialized, &types.Serialized{
Prefix: prefix,
Key: key,
Value: val,
})
return false
})
}

return genesis
}
Expand Down
31 changes: 29 additions & 2 deletions x/evm/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,46 @@ package evm_test
import (
"testing"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper"
"github.com/sei-protocol/sei-chain/x/evm"
"github.com/sei-protocol/sei-chain/x/evm/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestExportGenesis(t *testing.T) {
keeper, ctx := testkeeper.MockEVMKeeper()
func TestExportImportGenesis(t *testing.T) {
keeper, origctx := testkeeper.MockEVMKeeper()
ctx := origctx.WithMultiStore(origctx.MultiStore().CacheMultiStore())
seiAddr, evmAddr := testkeeper.MockAddressPair()
keeper.SetAddressMapping(ctx, seiAddr, evmAddr)
_, codeAddr := testkeeper.MockAddressPair()
keeper.SetCode(ctx, codeAddr, []byte("abcde"))
keeper.SetState(ctx, codeAddr, common.BytesToHash([]byte("123")), common.BytesToHash([]byte("456")))
keeper.SetNonce(ctx, evmAddr, 2)
keeper.SetReceipt(ctx, common.BytesToHash([]byte("789")), &types.Receipt{TxType: 2})
keeper.SetBlockBloom(ctx, 5, []ethtypes.Bloom{{1}})
keeper.SetTxHashesOnHeight(ctx, 5, []common.Hash{common.BytesToHash([]byte("123"))})
keeper.SetERC20CW20Pointer(ctx, "cw20addr", codeAddr)
genesis := evm.ExportGenesis(ctx, keeper)
assert.NoError(t, genesis.Validate())
param := genesis.GetParams()
assert.Equal(t, types.DefaultParams().PriorityNormalizer, param.PriorityNormalizer)
assert.Equal(t, types.DefaultParams().BaseFeePerGas, param.BaseFeePerGas)
assert.Equal(t, types.DefaultParams().MinimumFeePerGas, param.MinimumFeePerGas)
assert.Equal(t, types.DefaultParams().WhitelistedCwCodeHashesForDelegateCall, param.WhitelistedCwCodeHashesForDelegateCall)
evm.InitGenesis(origctx, keeper, *genesis)
require.Equal(t, evmAddr, keeper.GetEVMAddressOrDefault(origctx, seiAddr))
require.Equal(t, keeper.GetCode(ctx, codeAddr), keeper.GetCode(origctx, codeAddr))
require.Equal(t, keeper.GetCodeHash(ctx, codeAddr), keeper.GetCodeHash(origctx, codeAddr))
require.Equal(t, keeper.GetCodeSize(ctx, codeAddr), keeper.GetCodeSize(origctx, codeAddr))
require.Equal(t, keeper.GetState(ctx, codeAddr, common.BytesToHash([]byte("123"))), keeper.GetState(origctx, codeAddr, common.BytesToHash([]byte("123"))))
require.Equal(t, keeper.GetNonce(ctx, evmAddr), keeper.GetNonce(origctx, evmAddr))
_, err := keeper.GetReceipt(origctx, common.BytesToHash([]byte("789")))
require.Nil(t, err)
require.Equal(t, keeper.GetBlockBloom(ctx, 5), keeper.GetBlockBloom(origctx, 5))
require.Equal(t, keeper.GetTxHashesOnHeight(ctx, 5), keeper.GetTxHashesOnHeight(origctx, 5))
_, _, exists := keeper.GetERC20CW20Pointer(origctx, "cw20addr")
require.True(t, exists)
}
13 changes: 13 additions & 0 deletions x/evm/keeper/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/sei-protocol/sei-chain/x/evm/types"
Expand Down Expand Up @@ -68,3 +69,15 @@ func (k *Keeper) GetSeiAddressOrDefault(ctx sdk.Context, evmAddress common.Addre
}
return sdk.AccAddress(evmAddress[:])
}

func (k *Keeper) IterateSeiAddressMapping(ctx sdk.Context, cb func(evmAddr common.Address, seiAddr sdk.AccAddress) bool) {
iter := prefix.NewStore(ctx.KVStore(k.storeKey), types.EVMAddressToSeiAddressKeyPrefix).Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
evmAddr := common.BytesToAddress(iter.Key())
seiAddr := sdk.AccAddress(iter.Value())
if cb(evmAddr, seiAddr) {
break
}
}
}
12 changes: 12 additions & 0 deletions x/evm/keeper/code.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"encoding/binary"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -48,3 +49,14 @@ func (k *Keeper) GetCodeSize(ctx sdk.Context, addr common.Address) int {
}
return int(binary.BigEndian.Uint64(bz))
}

func (k *Keeper) IterateAllCode(ctx sdk.Context, cb func(addr common.Address, code []byte) bool) {
iter := prefix.NewStore(ctx.KVStore(k.storeKey), types.CodeKeyPrefix).Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
evmAddr := common.BytesToAddress(iter.Key())
if cb(evmAddr, iter.Value()) {
break
}
}
}
10 changes: 10 additions & 0 deletions x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ func (k *Keeper) GetStoreKey() sdk.StoreKey {
return k.storeKey
}

func (k *Keeper) IterateAll(ctx sdk.Context, pref []byte, cb func(key, val []byte) bool) {
iter := k.PrefixStore(ctx, pref).Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
if cb(iter.Key(), iter.Value()) {
break
}
}
}

func (k *Keeper) PrefixStore(ctx sdk.Context, pref []byte) sdk.KVStore {
store := ctx.KVStore(k.GetStoreKey())
return prefix.NewStore(store, pref)
Expand Down
12 changes: 12 additions & 0 deletions x/evm/keeper/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"encoding/binary"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/sei-protocol/sei-chain/x/evm/types"
Expand All @@ -21,3 +22,14 @@ func (k *Keeper) SetNonce(ctx sdk.Context, addr common.Address, nonce uint64) {
binary.BigEndian.PutUint64(length, nonce)
k.PrefixStore(ctx, types.NonceKeyPrefix).Set(addr[:], length)
}

func (k *Keeper) IterateAllNonces(ctx sdk.Context, cb func(addr common.Address, nonce uint64) bool) {
iter := prefix.NewStore(ctx.KVStore(k.storeKey), types.NonceKeyPrefix).Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
evmAddr := common.BytesToAddress(iter.Key())
if cb(evmAddr, binary.BigEndian.Uint64(iter.Value())) {
break
}
}
}
13 changes: 13 additions & 0 deletions x/evm/keeper/state.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/sei-protocol/sei-chain/x/evm/types"
Expand Down Expand Up @@ -29,3 +30,15 @@ func (k *Keeper) GetState(ctx sdk.Context, addr common.Address, hash common.Hash
func (k *Keeper) SetState(ctx sdk.Context, addr common.Address, key common.Hash, val common.Hash) {
k.PrefixStore(ctx, types.StateKey(addr)).Set(key[:], val[:])
}

func (k *Keeper) IterateState(ctx sdk.Context, cb func(addr common.Address, key common.Hash, val common.Hash) bool) {
iter := prefix.NewStore(ctx.KVStore(k.storeKey), types.StateKeyPrefix).Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
k := iter.Key()
evmAddr := common.BytesToAddress(k[:common.AddressLength])
if cb(evmAddr, common.BytesToHash(k[common.AddressLength:]), common.BytesToHash(iter.Value())) {
break
}
}
}
2 changes: 1 addition & 1 deletion x/evm/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestModuleExportGenesis(t *testing.T) {
module := evm.NewAppModule(nil, k)
jsonMsg := module.ExportGenesis(ctx, types.ModuleCdc)
jsonStr := string(jsonMsg)
assert.Equal(t, "{\"params\":{\"priority_normalizer\":\"1.000000000000000000\",\"base_fee_per_gas\":\"0.000000000000000000\",\"minimum_fee_per_gas\":\"1000000000.000000000000000000\",\"whitelisted_cw_code_hashes_for_delegate_call\":[\"ol1416zS7kfMOcIk4WL+ebU+a75u0qVujAqGWT6+YQI=\",\"lM3Zw+hcJvfOxDwjv7SzsrLXGgqNhcWN8S/+wHQf68g=\"]},\"address_associations\":[]}", jsonStr)
assert.Equal(t, "{\"params\":{\"priority_normalizer\":\"1.000000000000000000\",\"base_fee_per_gas\":\"0.000000000000000000\",\"minimum_fee_per_gas\":\"1000000000.000000000000000000\",\"whitelisted_cw_code_hashes_for_delegate_call\":[\"ol1416zS7kfMOcIk4WL+ebU+a75u0qVujAqGWT6+YQI=\",\"lM3Zw+hcJvfOxDwjv7SzsrLXGgqNhcWN8S/+wHQf68g=\"]},\"address_associations\":[{\"sei_address\":\"sei17xpfvakm2amg962yls6f84z3kell8c5la4jkdu\",\"eth_address\":\"0x27F7B8B8B5A4e71E8E9aA671f4e4031E3773303F\"}],\"codes\":[],\"states\":[],\"nonces\":[],\"serialized\":[{\"prefix\":\"Fg==\",\"key\":\"AwAB\",\"value\":\"AAAAAAAAAAM=\"},{\"prefix\":\"Fg==\",\"key\":\"BAAB\",\"value\":\"AAAAAAAAAAQ=\"}]}", jsonStr)
}

func TestConsensusVersion(t *testing.T) {
Expand Down
Loading

0 comments on commit 56732e6

Please sign in to comment.