[WIP] structure/dep refactor

This commit is contained in:
Aleksandr Bezobchuk 2018-07-04 19:38:20 -04:00
parent 631cac4e3b
commit fb9f6a7cdc
8 changed files with 707 additions and 799 deletions

327
Gopkg.lock generated
View File

@ -1,327 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/aristanetworks/goarista"
packages = ["monotime"]
revision = "59944ff78bc1de686b0aba1444dfd380f48f03d4"
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64"
[[projects]]
name = "github.com/cosmos/cosmos-sdk"
packages = [
"store",
"types",
"wire"
]
revision = "cf46be225b7f3853e36af0feac04e29188cd4c95"
version = "v0.17.5"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/edsrzf/mmap-go"
packages = ["."]
revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e"
[[projects]]
name = "github.com/ethereum/go-ethereum"
packages = [
"common",
"common/bitutil",
"common/hexutil",
"common/math",
"common/mclock",
"consensus",
"consensus/ethash",
"consensus/misc",
"core",
"core/rawdb",
"core/state",
"core/types",
"core/vm",
"crypto",
"crypto/bn256",
"crypto/bn256/cloudflare",
"crypto/bn256/google",
"crypto/secp256k1",
"crypto/sha3",
"ethdb",
"event",
"log",
"metrics",
"p2p/netutil",
"params",
"rlp",
"rpc",
"trie"
]
revision = "dea1ce052a10cd7d401a5c04f83f371a06fe293c"
version = "v1.8.11"
[[projects]]
name = "github.com/go-kit/kit"
packages = [
"log",
"log/level",
"log/term"
]
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
name = "github.com/go-logfmt/logfmt"
packages = ["."]
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
name = "github.com/go-stack/stack"
packages = ["."]
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
"jsonpb",
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types"
]
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
version = "v1.0.0"
[[projects]]
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
]
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
branch = "master"
name = "github.com/hashicorp/golang-lru"
packages = [
".",
"simplelru"
]
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
[[projects]]
branch = "master"
name = "github.com/jmhodges/levigo"
packages = ["."]
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]]
branch = "master"
name = "github.com/kr/logfmt"
packages = ["."]
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/rs/cors"
packages = ["."]
revision = "ca016a06a5753f8ba03029c0aa5e54afb1bf713f"
version = "v1.4.0"
[[projects]]
branch = "master"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
"leveldb/cache",
"leveldb/comparer",
"leveldb/errors",
"leveldb/filter",
"leveldb/iterator",
"leveldb/journal",
"leveldb/memdb",
"leveldb/opt",
"leveldb/storage",
"leveldb/table",
"leveldb/util"
]
revision = "e2150783cd35f5b607daca48afd8c57ec54cc995"
[[projects]]
name = "github.com/tendermint/abci"
packages = ["types"]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
[[projects]]
branch = "master"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519"
]
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
version = "0.9.9"
[[projects]]
name = "github.com/tendermint/go-crypto"
packages = ["."]
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/iavl"
packages = [
".",
"sha256truncated"
]
revision = "c9206995e8f948e99927f5084a88a7e94ca256da"
version = "v0.8.0-rc0"
[[projects]]
name = "github.com/tendermint/tmlibs"
packages = [
"common",
"db",
"log",
"merkle"
]
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38"
version = "v0.8.4"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"poly1305",
"ripemd160",
"salsa20/salsa"
]
revision = "8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"trace",
"websocket"
]
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[[projects]]
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"codes",
"connectivity",
"credentials",
"grpclb/grpc_lb_v1/messages",
"grpclog",
"internal",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"stats",
"status",
"tap",
"transport"
]
revision = "5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e"
version = "v1.7.5"
[[projects]]
name = "gopkg.in/fatih/set.v0"
packages = ["."]
revision = "57907de300222151a123d29255ed17f5ed43fad3"
version = "v0.1.0"
[[projects]]
branch = "v2"
name = "gopkg.in/karalabe/cookiejar.v2"
packages = ["collections/prque"]
revision = "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57"
[[projects]]
branch = "v2"
name = "gopkg.in/natefinch/npipe.v2"
packages = ["."]
revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c70e98edb879cb6bb6dbea237d8b58d872aea54a8217f895afdcf8efdd962601"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,41 +1,23 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/cosmos/cosmos-sdk"
version = "0.19.0"
[[override]]
name = "github.com/tendermint/iavl"
version = "0.8.0-rc0"
[[constraint]]
name = "github.com/ethereum/go-ethereum"
version = "1.8.11"
[[constraint]]
name = "github.com/tendermint/tendermint"
version = "0.22.0-rc2"
[[constraint]]
name = "github.com/golang/protobuf"
version = "~1.0.0"
[[override]]
name = "google.golang.org/genproto"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
#[[constraint]]
# name = "github.com/cosmos/cosmos-sdk"
# version = "0.18.0"
[prune]
go-tests = true
unused-packages = true

View File

@ -6,10 +6,10 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/params"
)
@ -58,7 +58,7 @@ type StructLogRes struct {
Storage *map[string]string `json:"storage,omitempty"`
}
// formatLogs formats EVM returned structured logs for json output
// formatLogs formats EVM returned structured logs for json output
func FormatLogs(logs []vm.StructLog) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {

124
core/chain.go Normal file
View File

@ -0,0 +1,124 @@
package core
import (
"math/big"
ethcommon "github.com/ethereum/go-ethereum/common"
ethconsensus "github.com/ethereum/go-ethereum/consensus"
ethstate "github.com/ethereum/go-ethereum/core/state"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethrpc "github.com/ethereum/go-ethereum/rpc"
)
// ChainContext implements Ethereum's core.ChainContext and consensus.Engine
// interfaces. It is needed in order to apply and process Ethereum
// transactions. There should only be a single implementation in Ethermint. For
// the purposes of Ethermint, it should be support retrieving headers and
// consensus parameters from the current blockchain to be used during
// transaction processing.
//
// NOTE: Ethermint will distribute the fees out to validators, so the structure
// and functionality of this is a WIP and subject to change.
type ChainContext struct {
coinbase ethcommon.Address
}
// Engine implements Ethereum's core.ChainContext interface. As a ChainContext
// implements the consensus.Engine interface, it is simply returned.
func (cc *ChainContext) Engine() ethconsensus.Engine {
return cc
}
// GetHeader implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: The Cosmos SDK supports retreiving such information in contexts and
// multi-store, so this will be need to be integrated.
func (cc *ChainContext) GetHeader(ethcommon.Hash, uint64) *ethtypes.Header {
return nil
}
// Author implements Ethereum's consensus.Engine interface. It is responsible
// for returned the address of the validtor to receive any fees. This function
// is only invoked if the given author in the ApplyTransaction call is nil.
//
// NOTE: Ethermint will distribute the fees out to validators, so the structure
// and functionality of this is a WIP and subject to change.
func (cc *ChainContext) Author(_ *ethtypes.Header) (ethcommon.Address, error) {
return cc.coinbase, nil
}
// APIs implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: Do we need to support such RPC APIs? This will tie into a bigger
// discussion on if we want to support web3.
func (cc *ChainContext) APIs(_ ethconsensus.ChainReader) []ethrpc.API {
return nil
}
// CalcDifficulty implements Ethereum's core.ChainContext interface. It
// currently performs a no-op.
func (cc *ChainContext) CalcDifficulty(_ ethconsensus.ChainReader, _ uint64, _ *ethtypes.Header) *big.Int {
return nil
}
// Finalize implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: Figure out if this needs to be hooked up to any part of the ABCI?
func (cc *ChainContext) Finalize(
_ ethconsensus.ChainReader, _ *ethtypes.Header, _ *ethstate.StateDB,
_ []*ethtypes.Transaction, _ []*ethtypes.Header, _ []*ethtypes.Receipt,
) (*ethtypes.Block, error) {
return nil, nil
}
// Prepare implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: Figure out if this needs to be hooked up to any part of the ABCI?
func (cc *ChainContext) Prepare(_ ethconsensus.ChainReader, _ *ethtypes.Header) error {
return nil
}
// Seal implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: Figure out if this needs to be hooked up to any part of the ABCI?
func (cc *ChainContext) Seal(_ ethconsensus.ChainReader, _ *ethtypes.Block, _ <-chan struct{}) (*ethtypes.Block, error) {
return nil, nil
}
// VerifyHeader implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK
// handlers?
func (cc *ChainContext) VerifyHeader(_ ethconsensus.ChainReader, _ *ethtypes.Header, _ bool) error {
return nil
}
// VerifyHeaders implements Ethereum's core.ChainContext interface. It
// currently performs a no-op.
//
// TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK
// handlers?
func (cc *ChainContext) VerifyHeaders(_ ethconsensus.ChainReader, _ []*ethtypes.Header, _ []bool) (chan<- struct{}, <-chan error) {
return nil, nil
}
// VerifySeal implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
//
// TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK
// handlers?
func (cc *ChainContext) VerifySeal(_ ethconsensus.ChainReader, _ *ethtypes.Header) error {
return nil
}
// VerifyUncles implements Ethereum's core.ChainContext interface. It currently
// performs a no-op.
func (cc *ChainContext) VerifyUncles(_ ethconsensus.ChainReader, _ *ethtypes.Block) error {
return nil
}

65
core/ethdb.go Normal file
View File

@ -0,0 +1,65 @@
package core
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() {
}

603
main.go
View File

@ -1,443 +1,172 @@
// The implementation below is to be considered highly unstable and a continual
// WIP. It is a means to replicate and test replaying Ethereum transactions
// using the Cosmos SDK and the EVM. The ultimate result will be what is known
// as Ethermint.
package main
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math/big"
"os"
// eth_common "github.com/ethereum/go-ethereum/common"
// eth_misc "github.com/ethereum/go-ethereum/consensus/misc"
// eth_core "github.com/ethereum/go-ethereum/core"
// eth_state "github.com/ethereum/go-ethereum/core/state"
// eth_types "github.com/ethereum/go-ethereum/core/types"
// eth_vm "github.com/ethereum/go-ethereum/core/vm"
// eth_params "github.com/ethereum/go-ethereum/params"
// eth_rlp "github.com/ethereum/go-ethereum/rlp"
// dbm "github.com/tendermint/tendermint/libs/db"
eth_common "github.com/ethereum/go-ethereum/common"
eth_core "github.com/ethereum/go-ethereum/core"
eth_state "github.com/ethereum/go-ethereum/core/state"
eth_types "github.com/ethereum/go-ethereum/core/types"
eth_vm "github.com/ethereum/go-ethereum/core/vm"
eth_rlp "github.com/ethereum/go-ethereum/rlp"
eth_ethdb "github.com/ethereum/go-ethereum/ethdb"
eth_params "github.com/ethereum/go-ethereum/params"
eth_rpc "github.com/ethereum/go-ethereum/rpc"
eth_trie "github.com/ethereum/go-ethereum/trie"
eth_consensus "github.com/ethereum/go-ethereum/consensus"
eth_misc "github.com/ethereum/go-ethereum/consensus/misc"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/types"
)
var (
// Key for the sub-store with Ethereum accounts
AccountsKey = types.NewKVStoreKey("account")
// Key for the sub-store with storage data of Ethereum contracts
StorageKey = types.NewKVStoreKey("storage")
// Key for the sub-store with the code for contracts
CodeKey = types.NewKVStoreKey("code")
)
type CommitHashPreimage struct {
VersionId int64
Prefix []byte
}
var miner501 = eth_common.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D")
// Implementation of eth_state.Database
type OurDatabase struct {
stateStore store.CommitMultiStore // For the history of accounts <balance, nonce, storage root hash, code hash>
// Also, for the history of contract data (effects of SSTORE instruction)
accountsCache store.CacheKVStore
storageCache store.CacheKVStore
codeDb dbm.DB // Mapping [codeHash] -> <code>
tracing bool
trieDbDummy *eth_trie.Database
}
func OurNewDatabase(stateDb, codeDb dbm.DB) (*OurDatabase, error) {
od := &OurDatabase{}
od.stateStore = store.NewCommitMultiStore(stateDb)
od.stateStore.MountStoreWithDB(AccountsKey, types.StoreTypeIAVL, nil)
od.stateStore.MountStoreWithDB(StorageKey, types.StoreTypeIAVL, nil)
if err := od.stateStore.LoadLatestVersion(); err != nil {
return nil, err
}
od.codeDb = codeDb
od.trieDbDummy = eth_trie.NewDatabase(&OurEthDb{codeDb: codeDb})
return od, nil
}
// root is not interpreted as a hash, but as an encoding of version
func (od *OurDatabase) OpenTrie(root eth_common.Hash) (eth_state.Trie, error) {
// Look up version id to use
hasData := root != (eth_common.Hash{})
versionId := od.stateStore.LastCommitID().Version
if hasData {
// First 8 bytes encode version
versionId = int64(binary.BigEndian.Uint64(root[:8]))
if od.stateStore.LastCommitID().Version != versionId {
if err := od.stateStore.LoadVersion(versionId); err != nil {
return nil, err
}
od.accountsCache = nil
}
}
if od.accountsCache == nil {
od.accountsCache = store.NewCacheKVStore(od.stateStore.GetCommitKVStore(AccountsKey))
od.storageCache = store.NewCacheKVStore(od.stateStore.GetCommitKVStore(StorageKey))
}
return &OurTrie{od: od, st: od.accountsCache, prefix: nil, hasData: hasData}, nil
}
func (od *OurDatabase) OpenStorageTrie(addrHash, root eth_common.Hash) (eth_state.Trie, error) {
hasData := root != (eth_common.Hash{})
return &OurTrie{od:od, st: od.storageCache, prefix: addrHash[:], hasData: hasData}, nil
}
func (od *OurDatabase) CopyTrie(eth_state.Trie) eth_state.Trie {
return nil
}
func (od *OurDatabase) ContractCode(addrHash, codeHash eth_common.Hash) ([]byte, error) {
code := od.codeDb.Get(codeHash[:])
return code, nil
}
func (od *OurDatabase) ContractCodeSize(addrHash, codeHash eth_common.Hash) (int, error) {
code := od.codeDb.Get(codeHash[:])
return len(code), nil
}
func (od *OurDatabase) TrieDB() *eth_trie.Database {
return od.trieDbDummy
}
// Implementation of state.Trie from go-ethereum
type OurTrie struct {
od *OurDatabase
// This is essentially part of the KVStore for a specific prefix
st store.KVStore
prefix []byte
hasData bool
}
func (ot *OurTrie) makePrefix(key []byte) []byte {
kk := make([]byte, len(ot.prefix)+len(key))
copy(kk, ot.prefix)
copy(kk[len(ot.prefix):], key)
return kk
}
func (ot *OurTrie) TryGet(key []byte) ([]byte, error) {
if ot.prefix == nil {
return ot.st.Get(key), nil
}
return ot.st.Get(ot.makePrefix(key)), nil
}
func (ot *OurTrie) TryUpdate(key, value []byte) error {
ot.hasData = true
if ot.prefix == nil {
ot.st.Set(key, value)
return nil
}
ot.st.Set(ot.makePrefix(key), value)
return nil
}
func (ot *OurTrie) TryDelete(key []byte) error {
if ot.prefix == nil {
ot.st.Delete(key)
return nil
}
ot.st.Delete(ot.makePrefix(key))
return nil
}
func (ot *OurTrie) Commit(onleaf eth_trie.LeafCallback) (eth_common.Hash, error) {
if !ot.hasData {
return eth_common.Hash{}, nil
}
var commitHash eth_common.Hash
// We assume here that the next committed version will be od.stateStore.LastCommitID().Version+1
binary.BigEndian.PutUint64(commitHash[:8], uint64(ot.od.stateStore.LastCommitID().Version+1))
if ot.prefix == nil {
if ot.od.accountsCache != nil {
ot.od.accountsCache.Write()
ot.od.accountsCache = nil
}
if ot.od.storageCache != nil {
ot.od.storageCache.Write()
ot.od.storageCache = nil
}
// Enumerate cached nodes from trie.Database
for _, n := range ot.od.trieDbDummy.Nodes() {
if err := ot.od.trieDbDummy.Commit(n, false); err != nil {
return eth_common.Hash{}, err
}
}
}
return commitHash, nil
}
func (ot *OurTrie) Hash() eth_common.Hash {
return eth_common.Hash{}
}
func (ot *OurTrie) NodeIterator(startKey []byte) eth_trie.NodeIterator {
return nil
}
func (ot *OurTrie) GetKey([]byte) []byte {
return nil
}
func (ot *OurTrie) Prove(key []byte, fromLevel uint, proofDb eth_ethdb.Putter) error {
return nil
}
// Dummy implementation of core.ChainContext and consensus Engine from go-ethereum
type OurChainContext struct {
coinbase eth_common.Address // This is where the transaction fees will go
}
func (occ *OurChainContext) Engine() eth_consensus.Engine {
return occ
}
func (occ *OurChainContext) GetHeader(eth_common.Hash, uint64) *eth_types.Header {
return nil
}
func (occ *OurChainContext) Author(header *eth_types.Header) (eth_common.Address, error) {
return occ.coinbase, nil
}
func (occ *OurChainContext) APIs(chain eth_consensus.ChainReader) []eth_rpc.API {
return nil
}
func (occ *OurChainContext) CalcDifficulty(chain eth_consensus.ChainReader, time uint64, parent *eth_types.Header) *big.Int {
return nil
}
func (occ *OurChainContext) Finalize(chain eth_consensus.ChainReader, header *eth_types.Header, state *eth_state.StateDB, txs []*eth_types.Transaction,
uncles []*eth_types.Header, receipts []*eth_types.Receipt) (*eth_types.Block, error) {
return nil, nil
}
func (occ *OurChainContext) Prepare(chain eth_consensus.ChainReader, header *eth_types.Header) error {
return nil
}
func (occ *OurChainContext) Seal(chain eth_consensus.ChainReader, block *eth_types.Block, stop <-chan struct{}) (*eth_types.Block, error) {
return nil, nil
}
func (occ *OurChainContext) VerifyHeader(chain eth_consensus.ChainReader, header *eth_types.Header, seal bool) error {
return nil
}
func (occ *OurChainContext) VerifyHeaders(chain eth_consensus.ChainReader, headers []*eth_types.Header, seals []bool) (chan<- struct{}, <-chan error) {
return nil, nil
}
func (occ *OurChainContext) VerifySeal(chain eth_consensus.ChainReader, header *eth_types.Header) error {
return nil
}
func (occ *OurChainContext) VerifyUncles(chain eth_consensus.ChainReader, block *eth_types.Block) error {
return nil
}
// Implementation of ethdb.Database and ethdb.Batch from go-ethereum
type OurEthDb struct {
codeDb dbm.DB
}
func (oedb *OurEthDb) Put(key []byte, value []byte) error {
oedb.codeDb.Set(key, value)
return nil
}
func (oedb *OurEthDb) Get(key []byte) ([]byte, error) {
return nil, nil
}
func (oedb *OurEthDb) Has(key []byte) (bool, error) {
return false, nil
}
func (oedb *OurEthDb) Delete(key []byte) error {
return nil
}
func (oedb *OurEthDb) Close() {
}
func (oedb *OurEthDb) NewBatch() eth_ethdb.Batch {
return oedb
}
func (oedb *OurEthDb) ValueSize() int {
return 0
}
func (oedb *OurEthDb) Write() error {
return nil
}
func (oedb *OurEthDb) Reset() {
}
// var (
// // TODO: Document...
// miner501 = eth_common.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D")
// )
func main() {
stateDb := dbm.NewDB("state" /* name */, dbm.MemDBBackend, "" /* dir */)
codeDb := dbm.NewDB("code" /* name */, dbm.MemDBBackend, "" /* dir */)
d, err := OurNewDatabase(stateDb, codeDb)
if err != nil {
panic(err)
}
fmt.Printf("Instantiating state.StateDB\n")
// With empty root hash, i.e. empty state
statedb, err := eth_state.New(eth_common.Hash{}, d)
if err != nil {
panic(err)
}
g := eth_core.DefaultGenesisBlock()
for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance)
statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce)
for key, value := range account.Storage {
statedb.SetState(addr, key, value)
}
}
// stateDb := dbm.NewDB("state" /* name */, dbm.MemDBBackend, "" /* dir */)
// codeDB := dbm.NewDB("code" /* name */, dbm.MemDBBackend, "" /* dir */)
// d, err := OurNewDatabase(stateDb, codeDB)
// if err != nil {
// panic(err)
// }
// fmt.Printf("Instantiating state.StateDB\n")
// // With empty root hash, i.e. empty state
// statedb, err := eth_state.New(eth_common.Hash{}, d)
// if err != nil {
// panic(err)
// }
// g := eth_core.DefaultGenesisBlock()
// for addr, account := range g.Alloc {
// statedb.AddBalance(addr, account.Balance)
// statedb.SetCode(addr, account.Code)
// statedb.SetNonce(addr, account.Nonce)
// for key, value := range account.Storage {
// statedb.SetState(addr, key, value)
// }
// }
// One of the genesis account having 200 ETH
b := statedb.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0"))
fmt.Printf("Balance: %s\n", b)
genesis_root, err := statedb.Commit(false /* deleteEmptyObjects */)
if err != nil {
panic(err)
}
commitID := d.stateStore.Commit()
fmt.Printf("CommitID after genesis: %v\n", commitID)
fmt.Printf("Genesis state root hash: %x\n", genesis_root[:])
// File with blockchain data exported from geth by using "geth expordb" command
input, err := os.Open("/Users/alexeyakhunov/mygit/blockchain")
if err != nil {
panic(err)
}
defer input.Close()
// Ethereum mainnet config
chainConfig := eth_params.MainnetChainConfig
stream := eth_rlp.NewStream(input, 0)
var block eth_types.Block
n := 0
var root500 eth_common.Hash // Root hash after block 500
var root501 eth_common.Hash // Root hash after block 501
prev_root := genesis_root
d.tracing = true
chainContext := &OurChainContext{}
vmConfig := eth_vm.Config{}
for {
if err = stream.Decode(&block); err == io.EOF {
err = nil // Clear it
break
} else if err != nil {
panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err))
}
// don't import first block
if block.NumberU64() == 0 {
continue
}
header := block.Header()
chainContext.coinbase = header.Coinbase
statedb, err := eth_state.New(prev_root, d)
if err != nil {
panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err))
}
var (
receipts eth_types.Receipts
usedGas = new(uint64)
allLogs []*eth_types.Log
gp = new(eth_core.GasPool).AddGas(block.GasLimit())
)
if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
eth_misc.ApplyDAOHardFork(statedb)
}
for i, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), block.Hash(), i)
var h eth_common.Hash = tx.Hash()
if bytes.Equal(h[:], eth_common.FromHex("0xc438cfcc3b74a28741bda361032f1c6362c34aa0e1cedff693f31ec7d6a12717")) {
vmConfig.Tracer = eth_vm.NewStructLogger(&eth_vm.LogConfig{})
vmConfig.Debug = true
}
receipt, _, err := eth_core.ApplyTransaction(chainConfig, chainContext, nil, gp, statedb, header, tx, usedGas, vmConfig)
if vmConfig.Tracer != nil {
w, err := os.Create("structlogs.txt")
if err != nil {
panic(err)
}
encoder := json.NewEncoder(w)
logs := FormatLogs(vmConfig.Tracer.(*eth_vm.StructLogger).StructLogs())
if err := encoder.Encode(logs); err != nil {
panic(err)
}
if err := w.Close(); err != nil {
panic(err)
}
vmConfig.Debug = false
vmConfig.Tracer = nil
}
if err != nil {
panic(fmt.Errorf("at block %d, tx %x: %v", block.NumberU64(), tx.Hash(), err))
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
}
// Apply mining rewards to the statedb
accumulateRewards(chainConfig, statedb, header, block.Uncles())
// Commit block
prev_root, err = statedb.Commit(chainConfig.IsEIP158(block.Number()) /* deleteEmptyObjects */)
if err != nil {
panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err))
}
//fmt.Printf("State root after block %d: %x\n", block.NumberU64(), prev_root)
d.stateStore.Commit()
//fmt.Printf("CommitID after block %d: %v\n", block.NumberU64(), commitID)
switch block.NumberU64() {
case 500:
root500 = prev_root
case 501:
root501 = prev_root
}
n++
if n % 10000 == 0 {
fmt.Printf("Processed %d blocks\n", n)
}
if n >= 100000 {
break
}
}
fmt.Printf("Processed %d blocks\n", n)
d.tracing = true
genesis_state, err := eth_state.New(genesis_root, d)
fmt.Printf("Balance of one of the genesis investors: %s\n", genesis_state.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")))
//miner501 := eth_common.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") // Miner of the block 501
// Try to create a new statedb from root of the block 500
fmt.Printf("root500: %x\n", root500[:])
state500, err := eth_state.New(root500, d)
if err != nil {
panic(err)
}
miner501_balance_at_500 := state500.GetBalance(miner501)
state501, err := eth_state.New(root501, d)
if err != nil {
panic(err)
}
miner501_balance_at_501 := state501.GetBalance(miner501)
fmt.Printf("Investor's balance after block 500: %d\n", state500.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")))
fmt.Printf("Miner of block 501's balance after block 500: %d\n", miner501_balance_at_500)
fmt.Printf("Miner of block 501's balance after block 501: %d\n", miner501_balance_at_501)
}
// // One of the genesis account having 200 ETH
// b := statedb.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0"))
// fmt.Printf("Balance: %s\n", b)
// genesis_root, err := statedb.Commit(false /* deleteEmptyObjects */)
// if err != nil {
// panic(err)
// }
// commitID := d.stateStore.Commit()
// fmt.Printf("CommitID after genesis: %v\n", commitID)
// fmt.Printf("Genesis state root hash: %x\n", genesis_root[:])
// // File with blockchain data exported from geth by using "geth expordb" command
// input, err := os.Open("/Users/alexeyakhunov/mygit/blockchain")
// if err != nil {
// panic(err)
// }
// defer input.Close()
// // Ethereum mainnet config
// chainConfig := eth_params.MainnetChainConfig
// stream := eth_rlp.NewStream(input, 0)
// var block eth_types.Block
// n := 0
// var root500 eth_common.Hash // Root hash after block 500
// var root501 eth_common.Hash // Root hash after block 501
// prev_root := genesis_root
// d.tracing = true
// chainContext := &OurChainContext{}
// vmConfig := eth_vm.Config{}
// for {
// if err = stream.Decode(&block); err == io.EOF {
// err = nil // Clear it
// break
// } else if err != nil {
// panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err))
// }
// // don't import first block
// if block.NumberU64() == 0 {
// continue
// }
// header := block.Header()
// chainContext.coinbase = header.Coinbase
// statedb, err := eth_state.New(prev_root, d)
// if err != nil {
// panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err))
// }
// var (
// receipts eth_types.Receipts
// usedGas = new(uint64)
// allLogs []*eth_types.Log
// gp = new(eth_core.GasPool).AddGas(block.GasLimit())
// )
// if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
// eth_misc.ApplyDAOHardFork(statedb)
// }
// for i, tx := range block.Transactions() {
// statedb.Prepare(tx.Hash(), block.Hash(), i)
// var h eth_common.Hash = tx.Hash()
// if bytes.Equal(h[:], eth_common.FromHex("0xc438cfcc3b74a28741bda361032f1c6362c34aa0e1cedff693f31ec7d6a12717")) {
// vmConfig.Tracer = eth_vm.NewStructLogger(&eth_vm.LogConfig{})
// vmConfig.Debug = true
// }
// receipt, _, err := eth_core.ApplyTransaction(chainConfig, chainContext, nil, gp, statedb, header, tx, usedGas, vmConfig)
// if vmConfig.Tracer != nil {
// w, err := os.Create("structlogs.txt")
// if err != nil {
// panic(err)
// }
// encoder := json.NewEncoder(w)
// logs := FormatLogs(vmConfig.Tracer.(*eth_vm.StructLogger).StructLogs())
// if err := encoder.Encode(logs); err != nil {
// panic(err)
// }
// if err := w.Close(); err != nil {
// panic(err)
// }
// vmConfig.Debug = false
// vmConfig.Tracer = nil
// }
// if err != nil {
// panic(fmt.Errorf("at block %d, tx %x: %v", block.NumberU64(), tx.Hash(), err))
// }
// receipts = append(receipts, receipt)
// allLogs = append(allLogs, receipt.Logs...)
// }
// // Apply mining rewards to the statedb
// accumulateRewards(chainConfig, statedb, header, block.Uncles())
// // Commit block
// prev_root, err = statedb.Commit(chainConfig.IsEIP158(block.Number()) /* deleteEmptyObjects */)
// if err != nil {
// panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err))
// }
// //fmt.Printf("State root after block %d: %x\n", block.NumberU64(), prev_root)
// d.stateStore.Commit()
// //fmt.Printf("CommitID after block %d: %v\n", block.NumberU64(), commitID)
// switch block.NumberU64() {
// case 500:
// root500 = prev_root
// case 501:
// root501 = prev_root
// }
// n++
// if n%10000 == 0 {
// fmt.Printf("Processed %d blocks\n", n)
// }
// if n >= 100000 {
// break
// }
// }
// fmt.Printf("Processed %d blocks\n", n)
// d.tracing = true
// genesis_state, err := eth_state.New(genesis_root, d)
// fmt.Printf("Balance of one of the genesis investors: %s\n", genesis_state.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")))
// //miner501 := eth_common.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") // Miner of the block 501
// // Try to create a new statedb from root of the block 500
// fmt.Printf("root500: %x\n", root500[:])
// state500, err := eth_state.New(root500, d)
// if err != nil {
// panic(err)
// }
// miner501_balance_at_500 := state500.GetBalance(miner501)
// state501, err := eth_state.New(root501, d)
// if err != nil {
// panic(err)
// }
// miner501_balance_at_501 := state501.GetBalance(miner501)
// fmt.Printf("Investor's balance after block 500: %d\n", state500.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")))
// fmt.Printf("Miner of block 501's balance after block 500: %d\n", miner501_balance_at_500)
// fmt.Printf("Miner of block 501's balance after block 501: %d\n", miner501_balance_at_501)
}

179
state/database.go Normal file
View File

@ -0,0 +1,179 @@
package state
import (
"encoding/binary"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/types"
ethcommon "github.com/ethereum/go-ethereum/common"
ethstate "github.com/ethereum/go-ethereum/core/state"
ethtrie "github.com/ethereum/go-ethereum/trie"
"github.com/ledgerwatch/ethermint/core"
dbm "github.com/tendermint/tendermint/libs/db"
)
var (
// AccountsKey is the key used for storing Ethereum accounts in the Cosmos
// SDK multi-store.
AccountsKey = types.NewKVStoreKey("account")
// StorageKey is the key used for storing Ethereum contract storage in the
// Cosmos SDK multi-store.
StorageKey = types.NewKVStoreKey("storage")
// CodeKey is the key used for storing Ethereum contract code in the Cosmos
// SDK multi-store.
CodeKey = types.NewKVStoreKey("code")
)
// 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 is a mapping 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
// TODO: Do we need this?
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(stateDB, codeDB dbm.DB) (*Database, error) {
// Initialize an implementation of Ethereum state.Database and create a
// Cosmos SDK multi-store.
db := &Database{
stateStore: store.NewCommitMultiStore(stateDB),
}
// Create the underlying multi-store stores that will persist account and
// account storage data.
db.stateStore.MountStoreWithDB(AccountsKey, types.StoreTypeIAVL, nil)
db.stateStore.MountStoreWithDB(StorageKey, types.StoreTypeIAVL, nil)
// Load the latest account state from the Cosmos SDK multi-store.
if err := db.stateStore.LoadLatestVersion(); err != nil {
return nil, err
}
// 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(&core.EthereumDB{codeDB: codeDB})
return db, nil
}
// 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 ethcommon.Hash) (ethstate.Trie, error) {
version := d.stateStore.LastCommitID().Version
if !isRootEmpty(root) {
version = versionFromRootHash(root)
if db.stateStore.LastCommitID().Version != version {
if err := db.stateStore.LoadVersion(version); err != nil {
return nil, err
}
d.accountsCache = nil
}
}
// 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))
}
return &Trie{
od: db,
st: od.accountsCache,
prefix: nil,
empty: isRootEmpty(root),
}, 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).
//
// 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 ethcommon.Hash) (ethstate.Trie, error) {
return &Trie{
od: d,
st: d.storageCache,
prefix: addrHash.Bytes(),
empty: isRootEmpty(root),
}, 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 ethcommon.Hash) ([]byte, error) {
return d.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
}
// 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.
func (db *Database) TrieDB() *ethtrie.Database {
return d.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]))
}

156
state/trie.go Normal file
View File

@ -0,0 +1,156 @@
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
}