initial refactor of state and core modules
This commit is contained in:
parent
fb9f6a7cdc
commit
d01a73218a
@ -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
|
||||
|
@ -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]))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user