app, ante, evm: Keeper StateDB refactor (#30)

* evm: keeper statedb refactor

* keeper: implement stateDB account, balance, nonce and suicide functions

* keeper: implement stateDB code and iterator functions

* keeper: implement stateDB log and preimage functions

* update code to use CommitStateDB

* tests updates

* journal changes (wip)

* cache fields

* journal and logs

* minor cleanup

* evm: remove journal related changes

* evm: delete empty account code and storage state

* app, evm: transient store

* ante, evm: refund gas transient

* evm: remove transient keeper state fields

* address comments from review

* evm: undo revision change
This commit is contained in:
Federico Kunze
2021-05-25 08:56:36 -04:00
committed by GitHub
parent bd89830d64
commit 6c1e7fec01
25 changed files with 924 additions and 991 deletions
+21 -21
View File
@@ -6,22 +6,22 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
// accessList is copied from go-ethereum
// AccessListMappings is copied from go-ethereum
// https://github.com/ethereum/go-ethereum/blob/cf856ea1ad96ac39ea477087822479b63417036a/core/state/access_list.go#L23
type accessList struct {
type AccessListMappings struct {
addresses map[common.Address]int
slots []map[common.Hash]struct{}
}
// ContainsAddress returns true if the address is in the access list.
func (al *accessList) ContainsAddress(address common.Address) bool {
func (al *AccessListMappings) ContainsAddress(address common.Address) bool {
_, ok := al.addresses[address]
return ok
}
// Contains checks if a slot within an account is present in the access list, returning
// separate flags for the presence of the account and the slot respectively.
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
func (al *AccessListMappings) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
idx, ok := al.addresses[address]
if !ok {
// no such address (and hence zero slots)
@@ -41,16 +41,16 @@ func (al *accessList) Contains(address common.Address, slot common.Hash) (addres
return true, slotPresent
}
// newAccessList creates a new accessList.
func newAccessList() *accessList {
return &accessList{
// newAccessList creates a new AccessListMappings.
func NewAccessListMappings() *AccessListMappings {
return &AccessListMappings{
addresses: make(map[common.Address]int),
}
}
// Copy creates an independent copy of an accessList.
func (al *accessList) Copy() *accessList {
cp := newAccessList()
// Copy creates an independent copy of an AccessListMappings.
func (al *AccessListMappings) Copy() *AccessListMappings {
cp := NewAccessListMappings()
for k, v := range al.addresses {
cp.addresses[k] = v
}
@@ -67,7 +67,7 @@ func (al *accessList) Copy() *accessList {
// AddAddress adds an address to the access list, and returns 'true' if the operation
// caused a change (addr was not previously in the list).
func (al *accessList) AddAddress(address common.Address) bool {
func (al *AccessListMappings) AddAddress(address common.Address) bool {
if _, present := al.addresses[address]; present {
return false
}
@@ -80,7 +80,7 @@ func (al *accessList) AddAddress(address common.Address) bool {
// - address added
// - slot added
// For any 'true' value returned, a corresponding journal entry must be made.
func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
func (al *AccessListMappings) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
idx, addrPresent := al.addresses[address]
if !addrPresent || idx == -1 {
// Address not present, or addr present but no slots there
@@ -99,7 +99,7 @@ func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrCha
slotmap := al.slots[idx]
if _, ok := slotmap[slot]; !ok {
slotmap[slot] = struct{}{}
// Journal add slot change
// journal add slot change
return false, true
}
// No changes required
@@ -110,7 +110,7 @@ func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrCha
// This operation needs to be performed in the same order as the addition happened.
// This method is meant to be used by the journal, which maintains ordering of
// operations.
func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
func (al *AccessListMappings) DeleteSlot(address common.Address, slot common.Hash) {
idx, addrOk := al.addresses[address]
// There are two ways this can fail
if !addrOk {
@@ -131,7 +131,7 @@ func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
// needs to be performed in the same order as the addition happened.
// This method is meant to be used by the journal, which maintains ordering of
// operations.
func (al *accessList) DeleteAddress(address common.Address) {
func (al *AccessListMappings) DeleteAddress(address common.Address) {
delete(al.addresses, address)
}
@@ -146,7 +146,7 @@ func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
return nil
}
var accessList AccessList
var AccessListMappings AccessList
for _, tuple := range *ethAccessList {
storageKeys := make([]string, len(tuple.StorageKeys))
@@ -154,19 +154,19 @@ func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
storageKeys[i] = tuple.StorageKeys[i].String()
}
accessList = append(accessList, AccessTuple{
AccessListMappings = append(AccessListMappings, AccessTuple{
Address: tuple.Address.String(),
StorageKeys: storageKeys,
})
}
return accessList
return AccessListMappings
}
// ToEthAccessList is an utility function to convert the protobuf compatible
// AccessList to eth core AccessList from go-ethereum
func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
var accessList ethtypes.AccessList
var AccessListMappings ethtypes.AccessList
for _, tuple := range al {
storageKeys := make([]ethcmn.Hash, len(tuple.StorageKeys))
@@ -175,11 +175,11 @@ func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
storageKeys[i] = ethcmn.HexToHash(tuple.StorageKeys[i])
}
accessList = append(accessList, ethtypes.AccessTuple{
AccessListMappings = append(AccessListMappings, ethtypes.AccessTuple{
Address: ethcmn.HexToAddress(tuple.Address),
StorageKeys: storageKeys,
})
}
return &accessList
return &AccessListMappings
}
+7 -7
View File
@@ -15,7 +15,7 @@ type AccessListTestSuite struct {
suite.Suite
address ethcmn.Address
accessList *accessList
accessList *AccessListMappings
}
func (suite *AccessListTestSuite) SetupTest() {
@@ -23,7 +23,7 @@ func (suite *AccessListTestSuite) SetupTest() {
suite.Require().NoError(err)
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
suite.accessList = newAccessList()
suite.accessList = NewAccessListMappings()
suite.accessList.addresses[suite.address] = 1
}
@@ -85,7 +85,7 @@ func (suite *AccessListTestSuite) TestContains() {
}
func (suite *AccessListTestSuite) TestCopy() {
expAccessList := newAccessList()
expAccessList := NewAccessListMappings()
testCases := []struct {
name string
@@ -96,7 +96,7 @@ func (suite *AccessListTestSuite) TestCopy() {
}},
{
"single address", func() {
expAccessList = newAccessList()
expAccessList = NewAccessListMappings()
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0)
expAccessList.addresses[suite.address] = -1
},
@@ -104,7 +104,7 @@ func (suite *AccessListTestSuite) TestCopy() {
{
"single address, single slot",
func() {
expAccessList = newAccessList()
expAccessList = NewAccessListMappings()
expAccessList.addresses[suite.address] = 0
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
expAccessList.slots[0] = make(map[ethcmn.Hash]struct{})
@@ -114,7 +114,7 @@ func (suite *AccessListTestSuite) TestCopy() {
{
"multiple addresses, single slot each",
func() {
expAccessList = newAccessList()
expAccessList = NewAccessListMappings()
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
for i := 0; i < 10; i++ {
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
@@ -126,7 +126,7 @@ func (suite *AccessListTestSuite) TestCopy() {
{
"multiple addresses, multiple slots each",
func() {
expAccessList = newAccessList()
expAccessList = NewAccessListMappings()
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
for i := 0; i < 10; i++ {
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
@@ -10,6 +10,7 @@ type AccountKeeper interface {
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
GetAllAccounts(ctx sdk.Context) (accounts []authtypes.AccountI)
IterateAccounts(ctx sdk.Context, cb func(account authtypes.AccountI) bool)
GetSequence(sdk.Context, sdk.AccAddress) (uint64, error)
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
SetAccount(ctx sdk.Context, account authtypes.AccountI)
RemoveAccount(ctx sdk.Context, account authtypes.AccountI)
@@ -18,5 +19,7 @@ type AccountKeeper interface {
// BankKeeper defines the expected interface needed to retrieve account balances.
type BankKeeper interface {
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error
}
+54 -9
View File
@@ -4,6 +4,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
ethcmn "github.com/ethereum/go-ethereum/common"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
)
const (
@@ -15,21 +16,51 @@ const (
// The EVM module should use a prefix store.
StoreKey = ModuleName
// Transient Key is the key to access the EVM transient store, that is reset
// during the Commit phase.
TransientKey = "transient_" + ModuleName
// RouterKey uses module name for routing
RouterKey = ModuleName
)
const (
prefixBlockHash = iota + 1
prefixBloom
prefixLogs
prefixCode
prefixStorage
prefixChainConfig
prefixBlockHeightHash
prefixHashTxReceipt
prefixBlockHeightTxs
)
const (
prefixTransientSuicided = iota + 1
prefixTransientBloom
prefixTransientTxIndex
prefixTransientRefund
)
// KVStore key prefixes
var (
KeyPrefixBlockHash = []byte{0x01}
KeyPrefixBloom = []byte{0x02}
KeyPrefixLogs = []byte{0x03}
KeyPrefixCode = []byte{0x04}
KeyPrefixStorage = []byte{0x05}
KeyPrefixChainConfig = []byte{0x06}
KeyPrefixBlockHeightHash = []byte{0x07}
KeyPrefixHashTxReceipt = []byte{0x08}
KeyPrefixBlockHeightTxs = []byte{0x09}
KeyPrefixBlockHash = []byte{prefixBlockHash}
KeyPrefixBloom = []byte{prefixBloom}
KeyPrefixLogs = []byte{prefixLogs}
KeyPrefixCode = []byte{prefixCode}
KeyPrefixStorage = []byte{prefixStorage}
KeyPrefixChainConfig = []byte{prefixChainConfig}
KeyPrefixBlockHeightHash = []byte{prefixBlockHeightHash}
KeyPrefixHashTxReceipt = []byte{prefixHashTxReceipt}
KeyPrefixBlockHeightTxs = []byte{prefixBlockHeightTxs}
)
var (
KeyPrefixTransientSuicided = []byte{prefixTransientSuicided}
KeyPrefixTransientBloom = []byte{prefixTransientBloom}
KeyPrefixTransientTxIndex = []byte{prefixTransientTxIndex}
KeyPrefixTransientRefund = []byte{prefixTransientRefund}
)
// BloomKey defines the store key for a block Bloom
@@ -69,3 +100,17 @@ func KeyBlockHeightTxs(height uint64) []byte {
heightBytes := sdk.Uint64ToBigEndian(height)
return append(KeyPrefixBlockHeightTxs, heightBytes...)
}
// KeyAddressStorage returns the key hash to access a given account state. The composite key
// (address + hash) is hashed using Keccak256.
func KeyAddressStorage(address ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
prefix := address.Bytes()
key := hash.Bytes()
compositeKey := make([]byte, len(prefix)+len(key))
copy(compositeKey, prefix)
copy(compositeKey[len(prefix):], key)
return ethcrypto.Keccak256Hash(compositeKey)
}
+5 -5
View File
@@ -21,7 +21,7 @@ import (
var (
_ StateObject = (*stateObject)(nil)
emptyCodeHash = ethcrypto.Keccak256(nil)
EmptyCodeHash = ethcrypto.Keccak256(nil)
)
// StateObject interface for interacting with state object
@@ -90,7 +90,7 @@ func newStateObject(db *CommitStateDB, accProto authtypes.AccountI, balance sdk.
// set empty code hash
if ethAccount.CodeHash == nil {
ethAccount.CodeHash = emptyCodeHash
ethAccount.CodeHash = EmptyCodeHash
}
return &stateObject{
@@ -307,7 +307,7 @@ func (so *stateObject) Balance() *big.Int {
// CodeHash returns the state object's code hash.
func (so *stateObject) CodeHash() []byte {
if so.account == nil || len(so.account.CodeHash) == 0 {
return emptyCodeHash
return EmptyCodeHash
}
return so.account.CodeHash
}
@@ -326,7 +326,7 @@ func (so *stateObject) Code(_ ethstate.Database) []byte {
return so.code
}
if bytes.Equal(so.CodeHash(), emptyCodeHash) {
if bytes.Equal(so.CodeHash(), EmptyCodeHash) {
return nil
}
@@ -416,7 +416,7 @@ func (so *stateObject) empty() bool {
(so.account != nil &&
so.account.Sequence == 0 &&
(so.balance.BigInt() == nil || so.balance.IsZero()) &&
bytes.Equal(so.account.CodeHash, emptyCodeHash))
bytes.Equal(so.account.CodeHash, EmptyCodeHash))
}
// EncodeRLP implements rlp.Encoder.
+2 -1
View File
@@ -98,7 +98,8 @@ func (st *StateTransition) newEVM(
}
vmConfig := vm.Config{
ExtraEips: eips,
EnablePreimageRecording: false, // no need for StateDB.AddPreimage
ExtraEips: eips,
}
if st.Debug {
+2 -2
View File
@@ -179,8 +179,8 @@ func (suite *StateDBTestSuite) TestTransitionDb() {
if tc.expPass {
suite.Require().NoError(err, tc.name)
fromBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address)
toBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, recipient)
fromBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address)
toBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(recipient)
suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name)
suite.Require().Equal(toBalance, big.NewInt(50), tc.name)
} else {
+4 -4
View File
@@ -72,14 +72,14 @@ type CommitStateDB struct {
// by StateDB.Commit.
dbErr error
// Journal of state modifications. This is the backbone of
// journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal *journal
validRevisions []revision
nextRevisionID int
// Per-transaction access list
accessList *accessList
accessList *AccessListMappings
// mutex for state deep copying
lock sync.Mutex
@@ -106,7 +106,7 @@ func NewCommitStateDB(
preimages: []preimageEntry{},
hashToPreimageIndex: make(map[ethcmn.Hash]int),
journal: newJournal(),
accessList: newAccessList(),
accessList: NewAccessListMappings(),
}
}
@@ -727,7 +727,7 @@ func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error {
csdb.logSize = 0
csdb.preimages = []preimageEntry{}
csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int)
csdb.accessList = newAccessList()
csdb.accessList = NewAccessListMappings()
csdb.clearJournalAndRefund()
return nil