Remove old deprecated state files

This commit is contained in:
Aleksandr Bezobchuk 2018-10-23 11:08:24 -04:00
parent c0489c86da
commit f82443080b
8 changed files with 0 additions and 1038 deletions

View File

@ -1,208 +0,0 @@
package state
import (
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/types"
ethcmn "github.com/ethereum/go-ethereum/common"
ethstate "github.com/ethereum/go-ethereum/core/state"
ethtrie "github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
dbm "github.com/tendermint/tendermint/libs/db"
)
// TODO: This functionality and implementation may be deprecated
var (
// CodeKey is the key used for storing Ethereum contract code in the Cosmos
// SDK multi-store.
CodeKey = sdk.NewKVStoreKey("code")
)
const (
// DefaultStoreCacheSize defines the default number of key/value pairs for
// the state stored in memory.
DefaultStoreCacheSize = 1024 * 1024
// codeSizeCacheSize is the number of codehash to size associations to
// keep in cached memory. This is to address any DoS attempts on
// EXTCODESIZE calls.
codeSizeCacheSize = 100000
)
// Database implements the Ethereum state.Database interface.
type Database struct {
// stateStore will be used for the history of accounts (balance, nonce,
// storage root hash, code hash) and for the history of contract data
// (effects of SSTORE instruction).
stateStore store.CommitMultiStore
accountsCache store.CacheKVStore
storageCache store.CacheKVStore
// 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
// the ApplyTransaction function, therefore in Ethermint we need to make
// sure this commit is invoked somewhere after each block or whatever the
// appropriate time for it.
codeDB dbm.DB
ethTrieDB *ethtrie.Database
// codeSizeCache contains an LRU cache of a specified capacity to cache
// EXTCODESIZE calls.
codeSizeCache *lru.Cache
storeCache *lru.Cache
Tracing bool
}
// NewDatabase returns a reference to an initialized Database type which
// implements Ethereum's state.Database interface. An error is returned if the
// latest state failed to load. The underlying storage structure is defined by
// the Cosmos SDK IAVL tree.
func NewDatabase(stateStore store.CommitMultiStore, codeDB dbm.DB, storeCacheSize int) (*Database, error) {
db := &Database{stateStore: stateStore}
// Set the persistent Cosmos SDK Database and initialize an Ethereum
// trie.Database using an EthereumDB as the underlying implementation of
// 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(&EthereumDB{CodeDB: codeDB})
var err error
if db.codeSizeCache, err = lru.New(codeSizeCacheSize); err != nil {
return nil, err
}
if db.storeCache, err = lru.New(storeCacheSize); err != nil {
return nil, err
}
return db, nil
}
// LatestVersion returns the latest version of the underlying mult-store.
func (db *Database) LatestVersion() int64 {
return db.stateStore.LastCommitID().Version
}
// OpenTrie implements Ethereum's state.Database interface. It returns a Trie
// type which implements the Ethereum state.Trie interface. It us used for
// storage of accounts. An error is returned if state cannot load for a
// given version. The account cache is reset if the state is successfully
// loaded and the version is not the latest.
//
// 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 ethcmn.Hash) (ethstate.Trie, error) {
if !isRootEmpty(root) {
version := versionFromRootHash(root)
if db.stateStore.LastCommitID().Version != version {
if err := db.stateStore.LoadVersion(version); err != nil {
return nil, err
}
db.accountsCache = nil
}
}
if db.accountsCache == nil {
db.accountsCache = store.NewCacheKVStore(db.stateStore.GetCommitKVStore(types.StoreKeyAccount))
db.storageCache = store.NewCacheKVStore(db.stateStore.GetCommitKVStore(types.StoreKeyStorage))
}
return &Trie{
store: db.accountsCache,
accountsCache: db.accountsCache,
storageCache: db.storageCache,
storeCache: db.storeCache,
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 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.
//
// 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 ethcmn.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{
store: db.storageCache,
storeCache: db.storeCache,
prefix: addrHash.Bytes(),
empty: isRootEmpty(root),
root: rootHashFromVersion(db.stateStore.LastCommitID().Version),
}, nil
}
// CopyTrie implements Ethereum's state.Database interface. For now, it
// performs a no-op as the underlying Cosmos SDK IAVL tree does not support
// such an operation.
//
// TODO: Does the IAVL tree need to support this operation? If so, why and
// how?
func (db *Database) CopyTrie(ethstate.Trie) ethstate.Trie {
return nil
}
// 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 ethcmn.Hash) ([]byte, error) {
code := db.codeDB.Get(codeHash[:])
if codeLen := len(code); codeLen != 0 {
db.codeSizeCache.Add(codeHash, codeLen)
}
return code, 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 ethcmn.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil
}
code, err := db.ContractCode(addrHash, codeHash)
return len(code), err
}
// Commit commits the underlying Cosmos SDK multi-store returning the commit
// ID.
func (db *Database) Commit() sdk.CommitID {
return db.stateStore.Commit()
}
// TrieDB implements Ethereum's state.Database interface. It returns Ethereum's
// 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 db.ethTrieDB
}
// isRootEmpty returns true if a given root hash is empty or false otherwise.
func isRootEmpty(root ethcmn.Hash) bool {
return root == ethcmn.Hash{}
}

View File

@ -1,110 +0,0 @@
package state
import (
"fmt"
"testing"
ethcmn "github.com/ethereum/go-ethereum/common"
ethstate "github.com/ethereum/go-ethereum/core/state"
"github.com/stretchr/testify/require"
)
func TestDatabaseInterface(t *testing.T) {
require.Implements(t, (*ethstate.Database)(nil), new(Database))
}
func TestDatabaseLatestVersion(t *testing.T) {
var version int64
testDB := newTestDatabase()
version = testDB.LatestVersion()
require.Equal(t, int64(0), version)
testDB.Commit()
version = testDB.LatestVersion()
require.Equal(t, int64(1), version)
}
func TestDatabaseCopyTrie(t *testing.T) {
// TODO: Implement once CopyTrie is implemented
t.SkipNow()
}
func TestDatabaseContractCode(t *testing.T) {
testDB := newTestDatabase()
testCases := []struct {
db *Database
data *code
codeHash ethcmn.Hash
expectedCode []byte
}{
{
db: testDB,
codeHash: ethcmn.BytesToHash([]byte("code hash")),
expectedCode: nil,
},
{
db: testDB,
data: &code{ethcmn.BytesToHash([]byte("code hash")), []byte("some awesome code")},
codeHash: ethcmn.BytesToHash([]byte("code hash")),
expectedCode: []byte("some awesome code"),
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.db.codeDB.Set(tc.data.hash[:], tc.data.blob)
}
code, err := tc.db.ContractCode(ethcmn.Hash{}, tc.codeHash)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Equal(t, tc.expectedCode, code, fmt.Sprintf("unexpected result: test case #%d", i))
}
}
func TestDatabaseContractCodeSize(t *testing.T) {
testDB := newTestDatabase()
testCases := []struct {
db *Database
data *code
codeHash ethcmn.Hash
expectedCodeLen int
}{
{
db: testDB,
codeHash: ethcmn.BytesToHash([]byte("code hash")),
expectedCodeLen: 0,
},
{
db: testDB,
data: &code{ethcmn.BytesToHash([]byte("code hash")), []byte("some awesome code")},
codeHash: ethcmn.BytesToHash([]byte("code hash")),
expectedCodeLen: 17,
},
{
db: testDB,
codeHash: ethcmn.BytesToHash([]byte("code hash")),
expectedCodeLen: 17,
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.db.codeDB.Set(tc.data.hash[:], tc.data.blob)
}
codeLen, err := tc.db.ContractCodeSize(ethcmn.Hash{}, tc.codeHash)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Equal(t, tc.expectedCodeLen, codeLen, fmt.Sprintf("unexpected result: test case #%d", i))
}
}
func TestDatabaseTrieDB(t *testing.T) {
testDB := newTestDatabase()
db := testDB.TrieDB()
require.Equal(t, testDB.ethTrieDB, db)
}

View File

@ -1,66 +0,0 @@
package state
import (
ethdb "github.com/ethereum/go-ethereum/ethdb"
dbm "github.com/tendermint/tendermint/libs/db"
)
// 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
}
// 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)
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
}
// 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
}
// 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)
return nil
}
// Close implements Ethereum's ethdb.Database interface. It closes the
// underlying database.
func (edb *EthereumDB) Close() {
edb.CodeDB.Close()
}
// NewBatch implements Ethereum's ethdb.Database interface. It returns a new
// Batch object used for batch database operations.
func (edb *EthereumDB) NewBatch() ethdb.Batch {
return edb
}
// ValueSize implements Ethereum's ethdb.Database interface. It performs a
// no-op.
func (edb *EthereumDB) ValueSize() int {
return 0
}
// Write implements Ethereum's ethdb.Database interface. It performs a no-op.
func (edb *EthereumDB) Write() error {
return nil
}
// Reset implements Ethereum's ethdb.Database interface. It performs a no-op.
func (edb *EthereumDB) Reset() {
}

View File

@ -1,141 +0,0 @@
package state
// NOTE: A bulk of these unit tests will change and evolve as the context and
// implementation of ChainConext evolves.
import (
"fmt"
"testing"
ethdb "github.com/ethereum/go-ethereum/ethdb"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tendermint/libs/db"
)
func newEthereumDB() *EthereumDB {
memDB := dbm.NewMemDB()
return &EthereumDB{CodeDB: memDB}
}
func TestEthereumDBInterface(t *testing.T) {
require.Implements(t, (*ethdb.Database)(nil), new(EthereumDB))
require.Implements(t, (*ethdb.Batch)(nil), new(EthereumDB))
}
func TestEthereumDBGet(t *testing.T) {
testEDB := newEthereumDB()
testCases := []struct {
edb *EthereumDB
data *kvPair
key []byte
expectedValue []byte
}{
{
edb: testEDB,
key: []byte("foo"),
expectedValue: nil,
},
{
edb: testEDB,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
expectedValue: []byte("bar"),
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.edb.Put(tc.data.key, tc.data.value)
}
value, err := tc.edb.Get(tc.key)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Equal(t, tc.expectedValue, value, fmt.Sprintf("unexpected result: test case #%d", i))
}
}
func TestEthereumDBHas(t *testing.T) {
testEDB := newEthereumDB()
testCases := []struct {
edb *EthereumDB
data *kvPair
key []byte
expectedValue bool
}{
{
edb: testEDB,
key: []byte("foo"),
expectedValue: false,
},
{
edb: testEDB,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
expectedValue: true,
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.edb.Put(tc.data.key, tc.data.value)
}
ok, err := tc.edb.Has(tc.key)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Equal(t, tc.expectedValue, ok, fmt.Sprintf("unexpected result: test case #%d", i))
}
}
func TestEthereumDBDelete(t *testing.T) {
testEDB := newEthereumDB()
testCases := []struct {
edb *EthereumDB
data *kvPair
key []byte
}{
{
edb: testEDB,
key: []byte("foo"),
},
{
edb: testEDB,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.edb.Put(tc.data.key, tc.data.value)
}
err := tc.edb.Delete(tc.key)
ok, _ := tc.edb.Has(tc.key)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.False(t, ok, fmt.Sprintf("unexpected existence of key: test case #%d", i))
}
}
func TestEthereumDBNewBatch(t *testing.T) {
edb := newEthereumDB()
batch := edb.NewBatch()
require.Equal(t, edb, batch)
}
func TestEthereumDBValueSize(t *testing.T) {
edb := newEthereumDB()
size := edb.ValueSize()
require.Equal(t, 0, size)
}
func TestEthereumDBWrite(t *testing.T) {
edb := newEthereumDB()
err := edb.Write()
require.Nil(t, err)
}

View File

@ -1,47 +0,0 @@
package state
import (
"fmt"
"math/rand"
"time"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/types"
ethcmn "github.com/ethereum/go-ethereum/common"
dbm "github.com/tendermint/tendermint/libs/db"
)
type (
kvPair struct {
key, value []byte
}
code struct {
hash ethcmn.Hash
blob []byte
}
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func newTestDatabase() *Database {
memDB := dbm.NewMemDB()
cms := store.NewCommitMultiStore(memDB)
cms.SetPruning(sdk.PruneNothing)
cms.MountStoreWithDB(types.StoreKeyAccount, sdk.StoreTypeIAVL, nil)
cms.MountStoreWithDB(types.StoreKeyStorage, sdk.StoreTypeIAVL, nil)
testDB, err := NewDatabase(cms, memDB, 100)
if err != nil {
panic(fmt.Sprintf("failed to create database: %v", err))
}
testDB.stateStore.LoadLatestVersion()
return testDB
}

View File

@ -1,210 +0,0 @@
package state
import (
"encoding/binary"
"github.com/cosmos/cosmos-sdk/store"
ethcmn "github.com/ethereum/go-ethereum/common"
ethdb "github.com/ethereum/go-ethereum/ethdb"
ethtrie "github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
)
// TODO: This functionality and implementation may be deprecated
const (
versionLen = 8
)
// Trie implements the Ethereum state.Trie interface.
type Trie struct {
// 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
storeCache *lru.Cache
// Store is an IAVL KV store that is part of a larger store except it used
// 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
// 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 ethcmn.Hash
ethTrieDB *ethtrie.Database
}
// 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.IsStorageTrie() {
key = t.prefixKey(key)
}
keyStr := string(key)
if cached, ok := t.storeCache.Get(keyStr); ok {
return cached.([]byte), nil
}
value := t.store.Get(key)
t.storeCache.Add(keyStr, value)
return value, 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.IsStorageTrie() {
key = t.prefixKey(key)
}
t.store.Set(key, value)
t.storeCache.Add(string(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.IsStorageTrie() {
key = t.prefixKey(key)
}
t.store.Delete(key)
t.storeCache.Remove(string(key))
return nil
}
// 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 and each new commitment
// increments the version by one.
func (t *Trie) Commit(_ ethtrie.LeafCallback) (ethcmn.Hash, error) {
if t.empty {
return ethcmn.Hash{}, nil
}
newRoot := rootHashFromVersion(versionFromRootHash(t.root) + 1)
if !t.IsStorageTrie() {
if t.accountsCache != nil {
t.accountsCache.Write()
t.accountsCache = nil
}
if t.storageCache != nil {
t.storageCache.Write()
t.storageCache = nil
}
// persist the mappings of codeHash => code
for _, n := range t.ethTrieDB.Nodes() {
if err := t.ethTrieDB.Commit(n, false); err != nil {
return ethcmn.Hash{}, err
}
}
}
t.root = newRoot
return newRoot, 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() ethcmn.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
}
// GetKey implements the Ethereum state.Trie interface. Since the IAVL does not
// need to store preimages of keys, a simple 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 integrate this with Cosmos SDK to provide such
// proofs.
func (t *Trie) Prove(key []byte, fromLevel uint, proofDB ethdb.Putter) error {
return nil
}
// IsStorageTrie returns a boolean reflecting if the Trie is created for
// contract storage.
func (t *Trie) IsStorageTrie() bool {
return t.prefix != 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 ethcmn.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 ethcmn.Hash) {
binary.BigEndian.PutUint64(root[:versionLen], uint64(version))
return
}

View File

@ -1,256 +0,0 @@
package state
import (
"fmt"
"math/rand"
"testing"
ethcmn "github.com/ethereum/go-ethereum/common"
ethstate "github.com/ethereum/go-ethereum/core/state"
"github.com/stretchr/testify/require"
)
func newTestTrie() *Trie {
testDB := newTestDatabase()
testTrie, _ := testDB.OpenTrie(rootHashFromVersion(0))
return testTrie.(*Trie)
}
func newTestPrefixTrie() *Trie {
testDB := newTestDatabase()
prefix := make([]byte, ethcmn.HashLength)
rand.Read(prefix)
testDB.OpenTrie(rootHashFromVersion(0))
testTrie, _ := testDB.OpenStorageTrie(ethcmn.BytesToHash(prefix), rootHashFromVersion(0))
return testTrie.(*Trie)
}
func TestTrieInterface(t *testing.T) {
require.Implements(t, (*ethstate.Trie)(nil), new(Trie))
}
func TestTrieTryGet(t *testing.T) {
testTrie := newTestTrie()
testPrefixTrie := newTestPrefixTrie()
testCases := []struct {
trie *Trie
data *kvPair
key []byte
expectedValue []byte
}{
{
trie: testTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
expectedValue: []byte("bar"),
},
{
trie: testTrie,
key: []byte("baz"),
expectedValue: nil,
},
{
trie: testPrefixTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
expectedValue: []byte("bar"),
},
{
trie: testPrefixTrie,
key: []byte("baz"),
expectedValue: nil,
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.trie.TryUpdate(tc.data.key, tc.data.value)
}
value, err := tc.trie.TryGet(tc.key)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Equal(t, tc.expectedValue, value, fmt.Sprintf("unexpected value: test case #%d", i))
}
}
func TestTrieTryUpdate(t *testing.T) {
testTrie := newTestTrie()
testPrefixTrie := newTestPrefixTrie()
kv := &kvPair{[]byte("foo"), []byte("bar")}
var err error
err = testTrie.TryUpdate(kv.key, kv.value)
require.Nil(t, err)
err = testPrefixTrie.TryUpdate(kv.key, kv.value)
require.Nil(t, err)
}
func TestTrieTryDelete(t *testing.T) {
testTrie := newTestTrie()
testPrefixTrie := newTestPrefixTrie()
testCases := []struct {
trie *Trie
data *kvPair
key []byte
}{
{
trie: testTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
},
{
trie: testTrie,
key: []byte("baz"),
},
{
trie: testPrefixTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
key: []byte("foo"),
},
{
trie: testPrefixTrie,
key: []byte("baz"),
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.trie.TryUpdate(tc.data.key, tc.data.value)
}
err := tc.trie.TryDelete(tc.key)
value, _ := tc.trie.TryGet(tc.key)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Nil(t, value, fmt.Sprintf("unexpected value: test case #%d", i))
}
}
func TestTrieCommit(t *testing.T) {
testTrie := newTestTrie()
testPrefixTrie := newTestPrefixTrie()
testCases := []struct {
trie *Trie
data *kvPair
code *code
expectedRoot ethcmn.Hash
}{
{
trie: &Trie{empty: true},
expectedRoot: ethcmn.Hash{},
},
{
trie: testTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
expectedRoot: rootHashFromVersion(1),
},
{
trie: testTrie,
data: &kvPair{[]byte("baz"), []byte("cat")},
code: &code{ethcmn.BytesToHash([]byte("code hash")), []byte("code hash")},
expectedRoot: rootHashFromVersion(2),
},
{
trie: testTrie,
expectedRoot: rootHashFromVersion(3),
},
{
trie: testPrefixTrie,
expectedRoot: rootHashFromVersion(0),
},
{
trie: testPrefixTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
expectedRoot: rootHashFromVersion(1),
},
{
trie: testPrefixTrie,
expectedRoot: rootHashFromVersion(2),
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.trie.TryUpdate(tc.data.key, tc.data.value)
}
if tc.code != nil {
tc.trie.ethTrieDB.InsertBlob(tc.code.hash, tc.code.blob)
}
root, err := tc.trie.Commit(nil)
require.Nil(t, err, fmt.Sprintf("unexpected error: test case #%d", i))
require.Equal(t, tc.expectedRoot, root, fmt.Sprintf("unexpected root: test case #%d", i))
}
}
func TestTrieHash(t *testing.T) {
testTrie := newTestTrie()
testPrefixTrie := newTestPrefixTrie()
testCases := []struct {
trie *Trie
data *kvPair
expectedRoot ethcmn.Hash
}{
{
trie: testTrie,
expectedRoot: rootHashFromVersion(0),
},
{
trie: testTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
expectedRoot: rootHashFromVersion(1),
},
{
trie: testPrefixTrie,
expectedRoot: rootHashFromVersion(0),
},
{
trie: testPrefixTrie,
data: &kvPair{[]byte("foo"), []byte("bar")},
expectedRoot: rootHashFromVersion(1),
},
}
for i, tc := range testCases {
if tc.data != nil {
tc.trie.TryUpdate(tc.data.key, tc.data.value)
tc.trie.Commit(nil)
}
root := tc.trie.Hash()
require.Equal(t, tc.expectedRoot, root, fmt.Sprintf("unexpected root: test case #%d", i))
}
}
func TestTrieNodeIterator(t *testing.T) {
// TODO: Implement once NodeIterator is implemented
t.SkipNow()
}
func TestTrieGetKey(t *testing.T) {
testTrie := newTestTrie()
testPrefixTrie := newTestPrefixTrie()
var key []byte
expectedKey := []byte("foo")
key = testTrie.GetKey(expectedKey)
require.Equal(t, expectedKey, key)
key = testPrefixTrie.GetKey(expectedKey)
require.Equal(t, expectedKey, key)
}
func TestTrieProve(t *testing.T) {
// TODO: Implement once Prove is implemented
t.SkipNow()
}

View File