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.
|
// EthereumDB implements Ethereum's ethdb.Database and ethdb.Batch interfaces.
|
||||||
// It will be used to facilitate persistence of codeHash => code mappings.
|
// It will be used to facilitate persistence of codeHash => code mappings.
|
||||||
type EthereumDB struct {
|
type EthereumDB struct {
|
||||||
codeDB dbm.DB
|
CodeDB dbm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put implements Ethereum's ethdb.Putter interface. It wraps the database
|
// Put implements Ethereum's ethdb.Putter interface. It wraps the database
|
||||||
// write operation supported by both batches and regular databases.
|
// write operation supported by both batches and regular databases.
|
||||||
func (edb *EthereumDB) Put(key []byte, value []byte) error {
|
func (edb *EthereumDB) Put(key []byte, value []byte) error {
|
||||||
edb.codeDB.Set(key, value)
|
edb.CodeDB.Set(key, value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get implements Ethereum's ethdb.Database interface. It returns a value for a
|
// Get implements Ethereum's ethdb.Database interface. It returns a value for a
|
||||||
// given key.
|
// given key.
|
||||||
func (edb *EthereumDB) Get(key []byte) ([]byte, error) {
|
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
|
// Has implements Ethereum's ethdb.Database interface. It returns a boolean
|
||||||
// determining if the underlying database has the given key or not.
|
// determining if the underlying database has the given key or not.
|
||||||
func (edb *EthereumDB) Has(key []byte) (bool, error) {
|
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
|
// Delete implements Ethereum's ethdb.Database interface. It removes a given
|
||||||
// key from the underlying database.
|
// key from the underlying database.
|
||||||
func (edb *EthereumDB) Delete(key []byte) error {
|
func (edb *EthereumDB) Delete(key []byte) error {
|
||||||
edb.codeDB.Delete(key)
|
edb.CodeDB.Delete(key)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements Ethereum's ethdb.Database interface. It closes the
|
// Close implements Ethereum's ethdb.Database interface. It closes the
|
||||||
// underlying database.
|
// underlying database.
|
||||||
func (edb *EthereumDB) Close() {
|
func (edb *EthereumDB) Close() {
|
||||||
edb.codeDB.Close()
|
edb.CodeDB.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBatch implements Ethereum's ethdb.Database interface. It returns a new
|
// NewBatch implements Ethereum's ethdb.Database interface. It returns a new
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
"github.com/cosmos/cosmos-sdk/types"
|
"github.com/cosmos/cosmos-sdk/types"
|
||||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
@ -35,7 +33,7 @@ type Database struct {
|
|||||||
accountsCache store.CacheKVStore
|
accountsCache store.CacheKVStore
|
||||||
storageCache 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
|
// NOTE: This database will store the information in memory until is it
|
||||||
// committed, using the function Commit. This function is called outside of
|
// 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
|
// the ethdb.Database interface. It will be used to facilitate persistence
|
||||||
// of contract byte code when committing state.
|
// of contract byte code when committing state.
|
||||||
db.codeDB = codeDB
|
db.codeDB = codeDB
|
||||||
db.ethTrieDB = ethtrie.NewDatabase(&core.EthereumDB{codeDB: codeDB})
|
db.ethTrieDB = ethtrie.NewDatabase(&core.EthereumDB{CodeDB: codeDB})
|
||||||
|
|
||||||
return db, nil
|
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
|
// CONTRACT: The root parameter is not interpreted as a state root hash, but as
|
||||||
// an encoding of an Cosmos SDK IAVL tree version.
|
// an encoding of an Cosmos SDK IAVL tree version.
|
||||||
func (db *Database) OpenTrie(root ethcommon.Hash) (ethstate.Trie, error) {
|
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) {
|
if !isRootEmpty(root) {
|
||||||
version = versionFromRootHash(root)
|
version = versionFromRootHash(root)
|
||||||
@ -99,27 +98,33 @@ func (db *Database) OpenTrie(root ethcommon.Hash) (ethstate.Trie, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.accountsCache = nil
|
loadedState = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the cache if the state was loaded for an older version ID
|
// reset the cache if the state was loaded for an older version
|
||||||
if db.accountsCache == nil {
|
if loadedState {
|
||||||
d.accountsCache = store.NewCacheKVStore(d.stateStore.GetCommitKVStore(AccountsKey))
|
db.accountsCache = store.NewCacheKVStore(db.stateStore.GetCommitKVStore(AccountsKey))
|
||||||
d.storageCache = store.NewCacheKVStore(d.stateStore.GetCommitKVStore(StorageKey))
|
db.storageCache = store.NewCacheKVStore(db.stateStore.GetCommitKVStore(StorageKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// binary.BigEndian.PutUint64(commitHash[:8], uint64(t.od.stateStore.LastCommitID().Version+1))
|
||||||
|
|
||||||
return &Trie{
|
return &Trie{
|
||||||
od: db,
|
store: db.accountsCache,
|
||||||
st: od.accountsCache,
|
accountsCache: db.accountsCache,
|
||||||
prefix: nil,
|
storageCache: db.storageCache,
|
||||||
empty: isRootEmpty(root),
|
ethTrieDB: db.ethTrieDB,
|
||||||
|
empty: isRootEmpty(root),
|
||||||
|
root: rootHashFromVersion(db.stateStore.LastCommitID().Version),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenStorageTrie implements Ethereum's state.Database interface. It returns
|
// OpenStorageTrie implements Ethereum's state.Database interface. It returns
|
||||||
// a Trie type which implements the Ethereum state.Trie interface. It is used
|
// 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
|
// NOTE: It is assumed that the account state has already been loaded via
|
||||||
// OpenTrie.
|
// 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
|
// CONTRACT: The root parameter is not interpreted as a state root hash, but as
|
||||||
// an encoding of an IAVL tree version.
|
// an encoding of an IAVL tree version.
|
||||||
func (db *Database) OpenStorageTrie(addrHash, root ethcommon.Hash) (ethstate.Trie, error) {
|
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{
|
return &Trie{
|
||||||
od: d,
|
store: db.storageCache,
|
||||||
st: d.storageCache,
|
|
||||||
prefix: addrHash.Bytes(),
|
prefix: addrHash.Bytes(),
|
||||||
empty: isRootEmpty(root),
|
empty: isRootEmpty(root),
|
||||||
|
root: rootHashFromVersion(db.stateStore.LastCommitID().Version),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,32 +155,25 @@ func (db *Database) CopyTrie(ethstate.Trie) ethstate.Trie {
|
|||||||
// ContractCode implements Ethereum's state.Database interface. It will return
|
// 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.
|
// 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) {
|
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
|
// ContractCodeSize implements Ethereum's state.Database interface. It will
|
||||||
// return the contract byte code size for a given code hash. It will not return
|
// return the contract byte code size for a given code hash. It will not return
|
||||||
// an error.
|
// an error.
|
||||||
func (db *Database) ContractCodeSize(addrHash, codeHash ethcommon.Hash) (int, 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
|
// TrieDB implements Ethereum's state.Database interface. It returns Ethereum's
|
||||||
// trie.Database low level trie database used for data storage. In the context
|
// trie.Database low level trie database used for contract state storage. In
|
||||||
// of Ethermint, it'll be used to solely store mappings of codeHash => code.
|
// the context of Ethermint, it'll be used to solely store mappings of
|
||||||
|
// codeHash => code.
|
||||||
func (db *Database) TrieDB() *ethtrie.Database {
|
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.
|
// isRootEmpty returns true if a given root hash is empty or false otherwise.
|
||||||
func isRootEmpty(root ethcommon.Hash) bool {
|
func isRootEmpty(root ethcommon.Hash) bool {
|
||||||
return root == ethcommon.Hash{}
|
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"
|
ethtrie "github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
versionLen = 8
|
||||||
|
)
|
||||||
|
|
||||||
// Trie implements the Ethereum state.Trie interface.
|
// Trie implements the Ethereum state.Trie interface.
|
||||||
type Trie struct {
|
type Trie struct {
|
||||||
// // db is an implementation of Ethereum's state.Database. It will provide a
|
// accountsCache contains all the accounts in memory to persit when
|
||||||
// // means to persist accounts and contract storage to a persistent
|
// committing the trie. A CacheKVStore is used to provide deterministic
|
||||||
// // multi-store.
|
// ordering.
|
||||||
// db *Database
|
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
|
// 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
|
store store.KVStore
|
||||||
|
|
||||||
// prefix is a static prefix used for persistence operations where the
|
// 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 is the encoding of an IAVL tree root (version)
|
||||||
root ethcommon.Hash
|
root ethcommon.Hash
|
||||||
|
|
||||||
|
ethTrieDB *ethtrie.Database
|
||||||
}
|
}
|
||||||
|
|
||||||
// prefixKey returns a composite key composed of a static prefix and a given
|
// 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.
|
// will be sorted giving us a deterministic ordering.
|
||||||
func (t *Trie) TryDelete(key []byte) error {
|
func (t *Trie) TryDelete(key []byte) error {
|
||||||
if t.prefix != nil {
|
if t.prefix != nil {
|
||||||
key = t.makePrefix(key)
|
key = t.prefixKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.store.Delete(key)
|
t.store.Delete(key)
|
||||||
return nil
|
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) {
|
func (t *Trie) Commit(_ ethtrie.LeafCallback) (ethcommon.Hash, error) {
|
||||||
if t.empty {
|
if t.empty {
|
||||||
return ethcommon.Hash{}, nil
|
return ethcommon.Hash{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var root ethcommon.Hash
|
newRoot := rootHashFromVersion(versionFromRootHash(t.root) + 1)
|
||||||
|
|
||||||
// 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))
|
|
||||||
|
|
||||||
if t.prefix == nil {
|
if t.prefix == nil {
|
||||||
if t.od.accountsCache != nil {
|
if t.accountsCache != nil {
|
||||||
t.od.accountsCache.Write()
|
t.accountsCache.Write()
|
||||||
t.od.accountsCache = nil
|
t.accountsCache = nil
|
||||||
}
|
}
|
||||||
if t.od.storageCache != nil {
|
|
||||||
t.od.storageCache.Write()
|
if t.storageCache != nil {
|
||||||
t.od.storageCache = nil
|
t.storageCache.Write()
|
||||||
|
t.storageCache = nil
|
||||||
}
|
}
|
||||||
// Enumerate cached nodes from trie.Database
|
|
||||||
for _, n := range t.od.trieDbDummy.Nodes() {
|
// persist the mappings of codeHash => code
|
||||||
if err := t.od.trieDbDummy.Commit(n, false); err != nil {
|
for _, n := range t.ethTrieDB.Nodes() {
|
||||||
return eth_common.Hash{}, err
|
if err := t.ethTrieDB.Commit(n, false); err != nil {
|
||||||
|
return ethcommon.Hash{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.root = root
|
t.root = newRoot
|
||||||
return root, nil
|
return newRoot, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash implements the Ethereum state.Trie interface. It returns the state root
|
// 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.
|
// 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.
|
// This will ultimately be related to if we want to support web3.
|
||||||
func (t *Trie) NodeIterator(startKey []byte) ethtrie.NodeIterator {
|
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
|
// 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 {
|
func (t *Trie) GetKey(key []byte) []byte {
|
||||||
return key
|
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
|
// Prove implements the Ethereum state.Trie interface. It writes a Merkle proof
|
||||||
// to a ethdb.Putter, proofDB, for a given key starting at fromLevel.
|
// 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 {
|
func (t *Trie) Prove(key []byte, fromLevel uint, proofDB ethdb.Putter) error {
|
||||||
return nil
|
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