Remove old deprecated state files
This commit is contained in:
parent
c0489c86da
commit
f82443080b
@ -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{}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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() {
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
210
state/trie.go
210
state/trie.go
@ -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
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user