forked from cerc-io/laconicd-deprecated
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:
+21
-21
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user