157 lines
4.8 KiB
Go
157 lines
4.8 KiB
Go
|
package state
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
|
||
|
"github.com/cosmos/cosmos-sdk/store"
|
||
|
ethcommon "github.com/ethereum/go-ethereum/common"
|
||
|
ethdb "github.com/ethereum/go-ethereum/ethdb"
|
||
|
ethtrie "github.com/ethereum/go-ethereum/trie"
|
||
|
)
|
||
|
|
||
|
// 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
|
||
|
|
||
|
// Store is an IAVL KV store that is part of a larger store except it used
|
||
|
// for a specific prefix.
|
||
|
store store.KVStore
|
||
|
|
||
|
// prefix is a static prefix used for persistence operations where the
|
||
|
// storage data is a contract state. This is to prevent key collisions
|
||
|
// since the IAVL tree is used for all contract state.
|
||
|
prefix []byte
|
||
|
|
||
|
// empty reflects if there exists any data in the tree
|
||
|
empty bool
|
||
|
|
||
|
// root is the encoding of an IAVL tree root (version)
|
||
|
root ethcommon.Hash
|
||
|
}
|
||
|
|
||
|
// prefixKey returns a composite key composed of a static prefix and a given
|
||
|
// key. This is used in situations where the storage data is contract state and
|
||
|
// the underlying structure to store said state is a single IAVL tree. To
|
||
|
// prevent collision, a static prefix is used.
|
||
|
func (t *Trie) prefixKey(key []byte) []byte {
|
||
|
compositeKey := make([]byte, len(t.prefix)+len(key))
|
||
|
|
||
|
copy(compositeKey, t.prefix)
|
||
|
copy(compositeKey[len(t.prefix):], key)
|
||
|
|
||
|
return compositeKey
|
||
|
}
|
||
|
|
||
|
// TryGet implements the Ethereum state.Trie interface. It returns the value
|
||
|
// for key stored in the trie. The value bytes must not be modified by the
|
||
|
// caller.
|
||
|
func (t *Trie) TryGet(key []byte) ([]byte, error) {
|
||
|
if t.prefix != nil {
|
||
|
key = t.prefixKey(key)
|
||
|
}
|
||
|
|
||
|
return t.store.Get(key), nil
|
||
|
}
|
||
|
|
||
|
// TryUpdate implements the Ethereum state.Trie interface. It associates a
|
||
|
// given key with a value in the trie. Subsequent calls to Get will return a
|
||
|
// value. It also marks the tree as not empty.
|
||
|
//
|
||
|
// CONTRACT: The order of insertions must be deterministic due to the nature of
|
||
|
// the IAVL tree. Since a CacheKVStore is used as the storage type, the keys
|
||
|
// will be sorted giving us a deterministic ordering.
|
||
|
func (t *Trie) TryUpdate(key, value []byte) error {
|
||
|
t.empty = false
|
||
|
|
||
|
if t.prefix != nil {
|
||
|
key = t.prefixKey(key)
|
||
|
}
|
||
|
|
||
|
t.store.Set(key, value)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// TryDelete implements the Ethereum state.Trie interface. It removes any
|
||
|
// existing value for a given key from the trie.
|
||
|
//
|
||
|
// CONTRACT: The order of deletions must be deterministic due to the nature of
|
||
|
// the IAVL tree. Since a CacheKVStore is used as the storage type, the keys
|
||
|
// will be sorted giving us a deterministic ordering.
|
||
|
func (t *Trie) TryDelete(key []byte) error {
|
||
|
if t.prefix != nil {
|
||
|
key = t.makePrefix(key)
|
||
|
}
|
||
|
|
||
|
t.store.Delete(key)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Commit implements the Ethereum state.Trie interface. TODO: ...
|
||
|
//
|
||
|
// CONTRACT: The root is an encoded IAVL tree version.
|
||
|
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))
|
||
|
|
||
|
if t.prefix == nil {
|
||
|
if t.od.accountsCache != nil {
|
||
|
t.od.accountsCache.Write()
|
||
|
t.od.accountsCache = nil
|
||
|
}
|
||
|
if t.od.storageCache != nil {
|
||
|
t.od.storageCache.Write()
|
||
|
t.od.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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
t.root = root
|
||
|
return root, nil
|
||
|
}
|
||
|
|
||
|
// Hash implements the Ethereum state.Trie interface. It returns the state root
|
||
|
// of the Trie which is an encoding of the underlying IAVL tree.
|
||
|
//
|
||
|
// CONTRACT: The root is an encoded IAVL tree version.
|
||
|
func (t *Trie) Hash() ethcommon.Hash {
|
||
|
return t.root
|
||
|
}
|
||
|
|
||
|
// NodeIterator implements the Ethereum state.Trie interface. Such a node
|
||
|
// iterator is used primarily for the implementation of RPC API functions. It
|
||
|
// performs a no-op.
|
||
|
//
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
// GetKey implements the Ethereum state.Trie interface. Since the IAVL does not
|
||
|
// need to store preimages of keys, a simply identity can be returned.
|
||
|
func (t *Trie) GetKey(key []byte) []byte {
|
||
|
return key
|
||
|
}
|
||
|
|
||
|
// 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.
|
||
|
func (t *Trie) Prove(key []byte, fromLevel uint, proofDB ethdb.Putter) error {
|
||
|
return nil
|
||
|
}
|