initial refactor of state and core modules

This commit is contained in:
Aleksandr Bezobchuk 2018-07-04 21:00:29 -04:00
parent fb9f6a7cdc
commit d01a73218a
3 changed files with 97 additions and 63 deletions

View File

@ -8,39 +8,39 @@ import (
// EthereumDB implements Ethereum's ethdb.Database and ethdb.Batch interfaces.
// It will be used to facilitate persistence of codeHash => code mappings.
type EthereumDB struct {
codeDB dbm.DB
CodeDB dbm.DB
}
// Put implements Ethereum's ethdb.Putter interface. It wraps the database
// write operation supported by both batches and regular databases.
func (edb *EthereumDB) Put(key []byte, value []byte) error {
edb.codeDB.Set(key, value)
edb.CodeDB.Set(key, value)
return nil
}
// Get implements Ethereum's ethdb.Database interface. It returns a value for a
// given key.
func (edb *EthereumDB) Get(key []byte) ([]byte, error) {
return edb.codeDB.Get(key), nil
return edb.CodeDB.Get(key), nil
}
// Has implements Ethereum's ethdb.Database interface. It returns a boolean
// determining if the underlying database has the given key or not.
func (edb *EthereumDB) Has(key []byte) (bool, error) {
return edb.codeDB.Has(key), nil
return edb.CodeDB.Has(key), nil
}
// Delete implements Ethereum's ethdb.Database interface. It removes a given
// key from the underlying database.
func (edb *EthereumDB) Delete(key []byte) error {
edb.codeDB.Delete(key)
edb.CodeDB.Delete(key)
return nil
}
// Close implements Ethereum's ethdb.Database interface. It closes the
// underlying database.
func (edb *EthereumDB) Close() {
edb.codeDB.Close()
edb.CodeDB.Close()
}
// NewBatch implements Ethereum's ethdb.Database interface. It returns a new

View File

@ -1,8 +1,6 @@
package state
import (
"encoding/binary"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/types"
ethcommon "github.com/ethereum/go-ethereum/common"
@ -35,7 +33,7 @@ type Database struct {
accountsCache store.CacheKVStore
storageCache store.CacheKVStore
// codeDB is a mapping of codeHash => code
// codeDB contains mappings of codeHash => code
//
// NOTE: This database will store the information in memory until is it
// committed, using the function Commit. This function is called outside of
@ -75,7 +73,7 @@ func NewDatabase(stateDB, codeDB dbm.DB) (*Database, error) {
// the ethdb.Database interface. It will be used to facilitate persistence
// of contract byte code when committing state.
db.codeDB = codeDB
db.ethTrieDB = ethtrie.NewDatabase(&core.EthereumDB{codeDB: codeDB})
db.ethTrieDB = ethtrie.NewDatabase(&core.EthereumDB{CodeDB: codeDB})
return db, nil
}
@ -89,7 +87,8 @@ func NewDatabase(stateDB, codeDB dbm.DB) (*Database, error) {
// CONTRACT: The root parameter is not interpreted as a state root hash, but as
// an encoding of an Cosmos SDK IAVL tree version.
func (db *Database) OpenTrie(root ethcommon.Hash) (ethstate.Trie, error) {
version := d.stateStore.LastCommitID().Version
var loadedState bool
version := db.stateStore.LastCommitID().Version
if !isRootEmpty(root) {
version = versionFromRootHash(root)
@ -99,27 +98,33 @@ func (db *Database) OpenTrie(root ethcommon.Hash) (ethstate.Trie, error) {
return nil, err
}
d.accountsCache = nil
loadedState = true
}
}
// reset the cache if the state was loaded for an older version ID
if db.accountsCache == nil {
d.accountsCache = store.NewCacheKVStore(d.stateStore.GetCommitKVStore(AccountsKey))
d.storageCache = store.NewCacheKVStore(d.stateStore.GetCommitKVStore(StorageKey))
// reset the cache if the state was loaded for an older version
if loadedState {
db.accountsCache = store.NewCacheKVStore(db.stateStore.GetCommitKVStore(AccountsKey))
db.storageCache = store.NewCacheKVStore(db.stateStore.GetCommitKVStore(StorageKey))
}
// binary.BigEndian.PutUint64(commitHash[:8], uint64(t.od.stateStore.LastCommitID().Version+1))
return &Trie{
od: db,
st: od.accountsCache,
prefix: nil,
store: db.accountsCache,
accountsCache: db.accountsCache,
storageCache: db.storageCache,
ethTrieDB: db.ethTrieDB,
empty: isRootEmpty(root),
root: rootHashFromVersion(db.stateStore.LastCommitID().Version),
}, nil
}
// OpenStorageTrie implements Ethereum's state.Database interface. It returns
// a Trie type which implements the Ethereum state.Trie interface. It is used
// for storage of accounts storage (state).
// for storage of contract storage (state). Also, this trie is never committed
// separately as all the data is in a single multi-store and is committed when
// the account IAVL tree is committed.
//
// NOTE: It is assumed that the account state has already been loaded via
// OpenTrie.
@ -127,11 +132,13 @@ func (db *Database) OpenTrie(root ethcommon.Hash) (ethstate.Trie, error) {
// CONTRACT: The root parameter is not interpreted as a state root hash, but as
// an encoding of an IAVL tree version.
func (db *Database) OpenStorageTrie(addrHash, root ethcommon.Hash) (ethstate.Trie, error) {
// a contract storage trie does not need an accountCache, storageCache or
// an Ethereum trie because it will not be used upon commitment.
return &Trie{
od: d,
st: d.storageCache,
store: db.storageCache,
prefix: addrHash.Bytes(),
empty: isRootEmpty(root),
root: rootHashFromVersion(db.stateStore.LastCommitID().Version),
}, nil
}
@ -148,32 +155,25 @@ func (db *Database) CopyTrie(ethstate.Trie) ethstate.Trie {
// ContractCode implements Ethereum's state.Database interface. It will return
// the contract byte code for a given code hash. It will not return an error.
func (db *Database) ContractCode(addrHash, codeHash ethcommon.Hash) ([]byte, error) {
return d.codeDB.Get(codeHash[:]), nil
return db.codeDB.Get(codeHash[:]), nil
}
// ContractCodeSize implements Ethereum's state.Database interface. It will
// return the contract byte code size for a given code hash. It will not return
// an error.
func (db *Database) ContractCodeSize(addrHash, codeHash ethcommon.Hash) (int, error) {
return len(d.codeDB.Get(codeHash[:])), nil
return len(db.codeDB.Get(codeHash[:])), nil
}
// TrieDB implements Ethereum's state.Database interface. It returns Ethereum's
// trie.Database low level trie database used for data storage. In the context
// of Ethermint, it'll be used to solely store mappings of codeHash => code.
// trie.Database low level trie database used for contract state storage. In
// the context of Ethermint, it'll be used to solely store mappings of
// codeHash => code.
func (db *Database) TrieDB() *ethtrie.Database {
return d.ethTrieDB
return db.ethTrieDB
}
// isRootEmpty returns true if a given root hash is empty or false otherwise.
func isRootEmpty(root ethcommon.Hash) bool {
return root == ethcommon.Hash{}
}
// versionFromRootHash returns an Cosmos SDK IAVL version from an Ethereum
// state root hash.
//
// CONTRACT: The encoded version is the eight MSB bytes of the root hash.
func versionFromRootHash(root ethcommon.Hash) int64 {
return int64(binary.BigEndian.Uint64(root[:8]))
}

View File

@ -9,15 +9,24 @@ import (
ethtrie "github.com/ethereum/go-ethereum/trie"
)
const (
versionLen = 8
)
// Trie implements the Ethereum state.Trie interface.
type Trie struct {
// // db is an implementation of Ethereum's state.Database. It will provide a
// // means to persist accounts and contract storage to a persistent
// // multi-store.
// db *Database
// accountsCache contains all the accounts in memory to persit when
// committing the trie. A CacheKVStore is used to provide deterministic
// ordering.
accountsCache store.CacheKVStore
// storageCache contains all the contract storage in memory to persit when
// committing the trie. A CacheKVStore is used to provide deterministic
// ordering.
storageCache store.CacheKVStore
// Store is an IAVL KV store that is part of a larger store except it used
// for a specific prefix.
// for a specific prefix. It will either be an accountsCache or a
// storageCache.
store store.KVStore
// prefix is a static prefix used for persistence operations where the
@ -30,6 +39,8 @@ type Trie struct {
// root is the encoding of an IAVL tree root (version)
root ethcommon.Hash
ethTrieDB *ethtrie.Database
}
// prefixKey returns a composite key composed of a static prefix and a given
@ -82,45 +93,52 @@ func (t *Trie) TryUpdate(key, value []byte) error {
// will be sorted giving us a deterministic ordering.
func (t *Trie) TryDelete(key []byte) error {
if t.prefix != nil {
key = t.makePrefix(key)
key = t.prefixKey(key)
}
t.store.Delete(key)
return nil
}
// Commit implements the Ethereum state.Trie interface. TODO: ...
// Commit implements the Ethereum state.Trie interface. It persists transient
// state. State is held by a merkelized multi-store IAVL tree. Commitment will
// only occur through an account trie, in other words, when the prefix of the
// trie is nil. In such a case, if either the accountCache or the storageCache
// are not nil, they are persisted. In addition, all the mappings of
// codeHash => code are also persisted. All these operations are performed in a
// deterministic order. Transient state is built up in a CacheKVStore. Finally,
// a root hash is returned or an error if any operation fails.
//
// CONTRACT: The root is an encoded IAVL tree version.
// CONTRACT: The root is an encoded IAVL tree version and each new commitment
// increments the version by one.
func (t *Trie) Commit(_ ethtrie.LeafCallback) (ethcommon.Hash, error) {
if t.empty {
return ethcommon.Hash{}, nil
}
var root ethcommon.Hash
// We assume that the next committed version will be the od.stateStore.LastCommitID().Version+1
binary.BigEndian.PutUint64(commitHash[:8], uint64(t.od.stateStore.LastCommitID().Version+1))
newRoot := rootHashFromVersion(versionFromRootHash(t.root) + 1)
if t.prefix == nil {
if t.od.accountsCache != nil {
t.od.accountsCache.Write()
t.od.accountsCache = nil
if t.accountsCache != nil {
t.accountsCache.Write()
t.accountsCache = nil
}
if t.od.storageCache != nil {
t.od.storageCache.Write()
t.od.storageCache = nil
if t.storageCache != nil {
t.storageCache.Write()
t.storageCache = nil
}
// Enumerate cached nodes from trie.Database
for _, n := range t.od.trieDbDummy.Nodes() {
if err := t.od.trieDbDummy.Commit(n, false); err != nil {
return eth_common.Hash{}, err
// persist the mappings of codeHash => code
for _, n := range t.ethTrieDB.Nodes() {
if err := t.ethTrieDB.Commit(n, false); err != nil {
return ethcommon.Hash{}, err
}
}
}
t.root = root
return root, nil
t.root = newRoot
return newRoot, nil
}
// Hash implements the Ethereum state.Trie interface. It returns the state root
@ -138,11 +156,11 @@ func (t *Trie) Hash() ethcommon.Hash {
// TODO: Determine if we need to implement such functionality for an IAVL tree.
// This will ultimately be related to if we want to support web3.
func (t *Trie) NodeIterator(startKey []byte) ethtrie.NodeIterator {
return nil, fffsadf
return nil
}
// GetKey implements the Ethereum state.Trie interface. Since the IAVL does not
// need to store preimages of keys, a simply identity can be returned.
// need to store preimages of keys, a simple identity can be returned.
func (t *Trie) GetKey(key []byte) []byte {
return key
}
@ -150,7 +168,23 @@ func (t *Trie) GetKey(key []byte) []byte {
// Prove implements the Ethereum state.Trie interface. It writes a Merkle proof
// to a ethdb.Putter, proofDB, for a given key starting at fromLevel.
//
// TODO: Determine how to use the Cosmos SDK to provide such proof.
// TODO: Determine how to integrate this with Cosmos SDK to provide such
// proofs.
func (t *Trie) Prove(key []byte, fromLevel uint, proofDB ethdb.Putter) error {
return nil
}
// versionFromRootHash returns a Cosmos SDK IAVL version from an Ethereum state
// root hash.
//
// CONTRACT: The encoded version is the eight MSB bytes of the root hash.
func versionFromRootHash(root ethcommon.Hash) int64 {
return int64(binary.BigEndian.Uint64(root[:versionLen]))
}
// rootHashFromVersion returns a state root hash from a Cosmos SDK IAVL
// version.
func rootHashFromVersion(version int64) (root ethcommon.Hash) {
binary.BigEndian.PutUint64(root[:versionLen], uint64(version))
return
}