From 17fa941fa1e3fe90de430ea35c84f198f042ae4e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 28 Sep 2018 18:02:00 -0400 Subject: [PATCH 01/49] Update vendor config to use forked geth --- Gopkg.lock | 3 ++- Gopkg.toml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 8ce87699..470426e5 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -74,6 +74,7 @@ revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e" [[projects]] + branch = "ethermint-statedb" digest = "1:c24d17ef5d37ae7215811cf1cade45822faa232d6bcfbadff30bbeba52225a98" name = "github.com/ethereum/go-ethereum" packages = [ @@ -123,7 +124,7 @@ ] pruneopts = "T" revision = "477eb0933b9529f7deeccc233cc815fe34a8ea56" - version = "v1.8.16" + source = "github.com/alexanderbez/go-ethereum" [[projects]] digest = "1:0b9c3ad6c948d57a379da9c4e1cdd989b1c73ddc5ec8673f52a9539ce60a109b" diff --git a/Gopkg.toml b/Gopkg.toml index 7883b9c5..c177500f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,6 +1,8 @@ [[constraint]] name = "github.com/ethereum/go-ethereum" - version = "=1.8.16" + # TODO: Remove this forked source and branch + source = "github.com/alexanderbez/go-ethereum" + branch = "ethermint-statedb" [[constraint]] name = "github.com/cosmos/cosmos-sdk" From be81045e26aac7d8d87e104b57ebf3dd93d71708 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 2 Oct 2018 20:22:15 -0400 Subject: [PATCH 02/49] Initial stateDB and related interface implementations --- core/chain.go | 2 +- state/database.go | 3 +- {core => state}/ethdb.go | 2 +- {core => state}/ethdb_test.go | 2 +- state/journal.go | 236 ++++++++++++++++++++++++++++++++ state/state_object.go | 247 ++++++++++++++++++++++++++++++++++ state/statedb.go | 110 +++++++++++++++ test/importer/utils.go | 2 +- types/account.go | 107 ++++++++++----- x/bank/keeper.go | 7 + 10 files changed, 679 insertions(+), 39 deletions(-) rename {core => state}/ethdb.go (99%) rename {core => state}/ethdb_test.go (99%) create mode 100644 state/journal.go create mode 100644 state/state_object.go create mode 100644 state/statedb.go create mode 100644 x/bank/keeper.go diff --git a/core/chain.go b/core/chain.go index 5c3c41b0..6a9aee66 100644 --- a/core/chain.go +++ b/core/chain.go @@ -84,7 +84,7 @@ func (cc *ChainContext) CalcDifficulty(_ ethcons.ChainReader, _ uint64, _ *ethty // // TODO: Figure out if this needs to be hooked up to any part of the ABCI? func (cc *ChainContext) Finalize( - _ ethcons.ChainReader, _ *ethtypes.Header, _ *ethstate.StateDB, + _ ethcons.ChainReader, _ *ethtypes.Header, _ ethstate.StateDB, _ []*ethtypes.Transaction, _ []*ethtypes.Header, _ []*ethtypes.Receipt, ) (*ethtypes.Block, error) { return nil, nil diff --git a/state/database.go b/state/database.go index 0be36a53..46e0a2eb 100644 --- a/state/database.go +++ b/state/database.go @@ -4,7 +4,6 @@ import ( "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ethermint/core" "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" @@ -73,7 +72,7 @@ func NewDatabase(stateStore store.CommitMultiStore, codeDB dbm.DB, storeCacheSiz // 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}) + db.ethTrieDB = ethtrie.NewDatabase(&EthereumDB{CodeDB: codeDB}) var err error diff --git a/core/ethdb.go b/state/ethdb.go similarity index 99% rename from core/ethdb.go rename to state/ethdb.go index 89211509..f05e3fa9 100644 --- a/core/ethdb.go +++ b/state/ethdb.go @@ -1,4 +1,4 @@ -package core +package state import ( ethdb "github.com/ethereum/go-ethereum/ethdb" diff --git a/core/ethdb_test.go b/state/ethdb_test.go similarity index 99% rename from core/ethdb_test.go rename to state/ethdb_test.go index 881169b2..626be049 100644 --- a/core/ethdb_test.go +++ b/state/ethdb_test.go @@ -1,4 +1,4 @@ -package core +package state // NOTE: A bulk of these unit tests will change and evolve as the context and // implementation of ChainConext evolves. diff --git a/state/journal.go b/state/journal.go new file mode 100644 index 00000000..77a09d2e --- /dev/null +++ b/state/journal.go @@ -0,0 +1,236 @@ +package state + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/common" +) + +// journalEntry is a modification entry in the state change journal that can be +// reverted on demand. +type journalEntry interface { + // revert undoes the changes introduced by this journal entry. + revert(*CommitStateDB) + + // dirtied returns the Ethereum address modified by this journal entry. + dirtied() *common.Address +} + +// journal contains the list of state modifications applied since the last state +// commit. These are tracked to be able to be reverted in case of an execution +// exception or revertal request. +type journal struct { + entries []journalEntry // Current changes tracked by the journal + dirties map[common.Address]int // Dirty accounts and the number of changes +} + +// newJournal create a new initialized journal. +func newJournal() *journal { + return &journal{ + dirties: make(map[common.Address]int), + } +} + +// append inserts a new modification entry to the end of the change journal. +func (j *journal) append(entry journalEntry) { + j.entries = append(j.entries, entry) + if addr := entry.dirtied(); addr != nil { + j.dirties[*addr]++ + } +} + +// revert undoes a batch of journalled modifications along with any reverted +// dirty handling too. +func (j *journal) revert(statedb *CommitStateDB, snapshot int) { + for i := len(j.entries) - 1; i >= snapshot; i-- { + // Undo the changes made by the operation + j.entries[i].revert(statedb) + + // Drop any dirty tracking induced by the change + if addr := j.entries[i].dirtied(); addr != nil { + if j.dirties[*addr]--; j.dirties[*addr] == 0 { + delete(j.dirties, *addr) + } + } + } + j.entries = j.entries[:snapshot] +} + +// dirty explicitly sets an address to dirty, even if the change entries would +// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD +// precompile consensus exception. +func (j *journal) dirty(addr common.Address) { + j.dirties[addr]++ +} + +// length returns the current number of entries in the journal. +func (j *journal) length() int { + return len(j.entries) +} + +type ( + // Changes to the account trie. + createObjectChange struct { + account *common.Address + } + + resetObjectChange struct { + prev *stateObject + } + + suicideChange struct { + account *common.Address + prev bool // whether account had already suicided + prevbalance sdk.Int + } + + // Changes to individual accounts. + balanceChange struct { + account *common.Address + prev sdk.Int + } + + nonceChange struct { + account *common.Address + prev uint64 + } + + storageChange struct { + account *common.Address + key, prevalue common.Hash + } + + codeChange struct { + account *common.Address + prevcode, prevhash []byte + } + + // Changes to other state values. + refundChange struct { + prev uint64 + } + + addLogChange struct { + txhash common.Hash + } + + addPreimageChange struct { + hash common.Hash + } + + touchChange struct { + account *common.Address + prev bool + prevDirty bool + } +) + +func (ch createObjectChange) revert(s *CommitStateDB) { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) +} + +func (ch createObjectChange) dirtied() *common.Address { + return ch.account +} + +func (ch resetObjectChange) revert(s *CommitStateDB) { + // TODO: ... + // s.setStateObject(ch.prev) +} + +func (ch resetObjectChange) dirtied() *common.Address { + return nil +} + +func (ch suicideChange) revert(s *CommitStateDB) { + // TODO: ... + // obj := s.getStateObject(*ch.account) + // if obj != nil { + // obj.suicided = ch.prev + // obj.setBalance(ch.prevbalance) + // } +} + +func (ch suicideChange) dirtied() *common.Address { + return ch.account +} + +var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") + +func (ch touchChange) revert(s *CommitStateDB) { +} + +func (ch touchChange) dirtied() *common.Address { + return ch.account +} + +func (ch balanceChange) revert(s *CommitStateDB) { + // TODO: ... + // s.getStateObject(*ch.account).setBalance(ch.prev) +} + +func (ch balanceChange) dirtied() *common.Address { + return ch.account +} + +func (ch nonceChange) revert(s *CommitStateDB) { + // TODO: ... + // s.getStateObject(*ch.account).setNonce(ch.prev) +} + +func (ch nonceChange) dirtied() *common.Address { + return ch.account +} + +func (ch codeChange) revert(s *CommitStateDB) { + // TODO: ... + // s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +} + +func (ch codeChange) dirtied() *common.Address { + return ch.account +} + +func (ch storageChange) revert(s *CommitStateDB) { + // TODO: ... + // s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) +} + +func (ch storageChange) dirtied() *common.Address { + return ch.account +} + +func (ch refundChange) revert(s *CommitStateDB) { + // TODO: ... + // s.refund = ch.prev +} + +func (ch refundChange) dirtied() *common.Address { + return nil +} + +func (ch addLogChange) revert(s *CommitStateDB) { + // TODO: ... + // logs := s.logs[ch.txhash] + // if len(logs) == 1 { + // delete(s.logs, ch.txhash) + // } else { + // s.logs[ch.txhash] = logs[:len(logs)-1] + // } + + // s.logSize-- +} + +func (ch addLogChange) dirtied() *common.Address { + return nil +} + +func (ch addPreimageChange) revert(s *CommitStateDB) { + // TODO: ... + // delete(s.preimages, ch.hash) +} + +func (ch addPreimageChange) dirtied() *common.Address { + return nil +} diff --git a/state/state_object.go b/state/state_object.go new file mode 100644 index 00000000..12444304 --- /dev/null +++ b/state/state_object.go @@ -0,0 +1,247 @@ +package state + +import ( + "bytes" + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/ethermint/types" + ethcmn "github.com/ethereum/go-ethereum/common" + ethstate "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + _ ethstate.StateObject = (*stateObject)(nil) + + emptyCodeHash = crypto.Keccak256(nil) +) + +type ( + // stateObject represents an Ethereum account which is being modified. + // + // The usage pattern is as follows: + // First you need to obtain a state object. + // Account values can be accessed and modified through the object. + // Finally, call CommitTrie to write the modified storage trie into a database. + stateObject struct { + address ethcmn.Address + stateDB *CommitStateDB + account *types.Account + + // DB error. + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + dbErr error + + code types.Code // contract bytecode, which gets set when code is loaded + + originStorage types.Storage // Storage cache of original entries to dedup rewrites + dirtyStorage types.Storage // Storage entries that need to be flushed to disk + + // cache flags + // + // When an object is marked suicided it will be delete from the trie during + // the "update" phase of the state transition. + dirtyCode bool // true if the code was updated + suicided bool + deleted bool + } + + // // Account is the Ethereum consensus representation of accounts. + // // These objects are stored in the main account trie. + // Account struct { + // Nonce uint64 + // Balance *big.Int + // Root ethcmn.Hash // merkle root of the storage trie + // CodeHash []byte + // } +) + +func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { + // if acc.Balance == nil { + // data.Balance = new(big.Int) + // } + acc, ok := accProto.(*types.Account) + if !ok { + panic(fmt.Sprintf("invalid account type for state object: %T", acc)) + } + + if acc.CodeHash == nil { + acc.CodeHash = emptyCodeHash + } + + return &stateObject{ + stateDB: db, + account: acc, + address: ethcmn.BytesToAddress(acc.Address.Bytes()), + originStorage: make(types.Storage), + dirtyStorage: make(types.Storage), + } +} + +// Address returns the address of the state object. +func (so stateObject) Address() ethcmn.Address { + return so.address +} + +// GetState retrieves a value from the account storage trie. +func (so *stateObject) GetState(_ Database, key ethcmn.Hash) ethcmn.Hash { + // if we have a dirty value for this state entry, return it + value, dirty := so.dirtyStorage[key] + if dirty { + return value + } + + // otherwise return the entry's original value + return so.getCommittedState(key) +} + +// SetState updates a value in account storage. +func (so *stateObject) SetState(db Database, key, value ethcmn.Hash) { + // if the new value is the same as old, don't set + prev := so.GetState(db, key) + if prev == value { + return + } + + // since the new value is different, update and journal the change + so.stateDB.journal.append(storageChange{ + account: &so.address, + key: key, + prevalue: prev, + }) + + so.setState(key, value) +} + +// AddBalance adds an amount to a state object's balance. It is used to add +// funds to the destination account of a transfer. +func (so *stateObject) AddBalance(amount *big.Int) { + amt := sdk.NewIntFromBigInt(amount) + + // EIP158: We must check emptiness for the objects such that the account + // clearing (0,0,0 objects) can take effect. + if amt.Sign() == 0 { + if so.empty() { + so.touch() + } + + return + } + + newBalance := so.account.Balance().Add(amt) + so.SetBalance(newBalance.BigInt()) +} + +func (so *stateObject) SetBalance(amount *big.Int) { + amt := sdk.NewIntFromBigInt(amount) + + so.stateDB.journal.append(balanceChange{ + account: &so.address, + prev: so.account.Balance(), + }) + + so.setBalance(amt) +} + +// SubBalance removes amount from c's balance. +// It is used to remove funds from the origin account of a transfer. +func (so *stateObject) SubBalance(amount *big.Int) { + if amount.Sign() == 0 { + return + } + + c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) +} + +// func (so *stateObject) Balance() *big.Int { + +// } + +// func (so *stateObject) ReturnGas(gas *big.Int) { + +// } + +// func (so *stateObject) Address() ethcmn.Address { + +// } + +// func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { + +// } + +// func (so *stateObject) SetNonce(nonce uint64) { + +// } + +// func (so *stateObject) Nonce() uint64 { + +// } + +// func (so *stateObject) Code(db Database) []byte { + +// } + +// func (so *stateObject) CodeHash() []byte { + +// } + +func (so *stateObject) setBalance(amount sdk.Int) { + so.account.SetBalance(amount) +} + +// GetCommittedState retrieves a value from the committed account storage trie. +func (so *stateObject) getCommittedState(key ethcmn.Hash) ethcmn.Hash { + // if we have the original value cached, return that + value, cached := so.originStorage[key] + if cached { + return value + } + + // otherwise load the value from the KVStore + store := so.stateDB.ctx.KVStore(so.stateDB.storageKey) + rawValue := store.Get(key.Bytes()) + + if len(rawValue) > 0 { + value.SetBytes(rawValue) + } + + so.originStorage[key] = value + return value +} + +func (so *stateObject) setState(key, value ethcmn.Hash) { + so.dirtyStorage[key] = value +} + +// setError remembers the first non-nil error it is called with. +func (so *stateObject) setError(err error) { + if so.dbErr == nil { + so.dbErr = err + } +} + +// empty returns whether the account is considered empty. +func (so *stateObject) empty() bool { + return so.account.Sequence == 0 && + so.account.Balance().Sign() == 0 && + bytes.Equal(so.account.CodeHash, emptyCodeHash) +} + +func (so *stateObject) touch() { + so.stateDB.journal.append(touchChange{ + account: &so.address, + }) + + if so.address == ripemd { + // Explicitly put it in the dirty-cache, which is otherwise generated from + // flattened journals. + so.stateDB.journal.dirty(so.address) + } +} diff --git a/state/statedb.go b/state/statedb.go new file mode 100644 index 00000000..50b305af --- /dev/null +++ b/state/statedb.go @@ -0,0 +1,110 @@ +package state + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethstate "github.com/ethereum/go-ethereum/core/state" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +var _ ethstate.StateDB = (*CommitStateDB)(nil) + +type CommitStateDB struct { + // TODO: Figure out a way to not need to store a context as part of the + // structure + ctx sdk.Context + + am auth.AccountMapper + storageKey sdk.StoreKey + + // maps that hold 'live' objects, which will get modified while processing a + // state transition + stateObjects map[ethcmn.Address]*stateObject + stateObjectsDirty map[ethcmn.Address]struct{} + + thash, bhash ethcmn.Hash + txIndex int + logs map[ethcmn.Hash][]*ethtypes.Log + logSize uint + + // DB error. + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + dbErr error + + // Journal of state modifications. This is the backbone of + // Snapshot and RevertToSnapshot. + journal *journal + validRevisions []ethstate.Revision + nextRevisionID int +} + +func NewCommitStateDB(ctx sdk.Context) (*CommitStateDB, error) { + // tr, err := db.OpenTrie(root) + // if err != nil { + // return nil, err + // } + + return &CommitStateDB{ + // stateObjects: make(map[ethcmn.Address]*stateObject), + // stateObjectsDirty: make(map[ethcmn.Address]struct{}), + // logs: make(map[ethcmn.Hash][]*types.Log), + // preimages: make(map[ethcmn.Hash][]byte), + journal: newJournal(), + }, nil +} + +// setError remembers the first non-nil error it is called with. +func (csdb *CommitStateDB) setError(err error) { + if csdb.dbErr == nil { + csdb.dbErr = err + } +} + +// Error returns the first non-nil error the StateDB encountered. +func (csdb *CommitStateDB) Error() error { + return csdb.dbErr +} + +// Retrieve the balance from the given address or 0 if object not found +func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int { + stateObject := csdb.getStateObject(addr) + if stateObject != nil { + return stateObject.Balance() + } + + return common.Big0 +} + +// Retrieve a state object given by the address. Returns nil if not found. +func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { + // prefer 'live' (cached) objects + if obj := csdb.stateObjects[addr]; obj != nil { + if obj.deleted { + return nil + } + + return obj + } + + acc := csdb.am.GetAccount(csdb.ctx, addr.Bytes()) + if acc == nil { + csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes())) + } + + // insert the state object into the live set + obj := newObject(csdb, acc) + csdb.setStateObject(obj) + return obj +} + +func (csdb *CommitStateDB) setStateObject(object *stateObject) { + csdb.stateObjects[object.Address()] = object +} diff --git a/test/importer/utils.go b/test/importer/utils.go index 4e29b2a8..a70e783d 100644 --- a/test/importer/utils.go +++ b/test/importer/utils.go @@ -18,7 +18,7 @@ var ( // accumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *ethparams.ChainConfig, state *ethstate.StateDB, header *ethtypes.Header, uncles []*ethtypes.Header) { +func accumulateRewards(config *ethparams.ChainConfig, state ethstate.StateDB, header *ethtypes.Header, uncles []*ethtypes.Header) { // select the correct block reward based on chain progression blockReward := ethash.FrontierBlockReward if config.IsByzantium(header.Number) { diff --git a/types/account.go b/types/account.go index b82808c4..6a2a8989 100644 --- a/types/account.go +++ b/types/account.go @@ -1,53 +1,94 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/ethermint/x/bank" ethcmn "github.com/ethereum/go-ethereum/common" ) var _ auth.Account = (*Account)(nil) -type ( - // Storage defines account storage - Storage map[ethcmn.Hash]ethcmn.Hash +// BaseAccount implements the auth.Account interface and embeds an +// auth.BaseAccount type. It is compatible with the auth.AccountMapper. +type Account struct { + *auth.BaseAccount - // Account defines an auth.BaseAccount extension for Ethermint. It is - // compatible with the auth.AccountMapper. - Account struct { - auth.BaseAccount - - Code []byte - Storage Storage - } -) - -// NewAccount returns a reference to a new initialized account. -func NewAccount(base auth.BaseAccount, code []byte, storage Storage) *Account { - return &Account{ - BaseAccount: base, - Code: code, - Storage: storage, - } + Root ethcmn.Hash // merkle root of the storage trie + CodeHash []byte } +// ProtoBaseAccount defines the prototype function for BaseAccount used for an +// account mapper. +func ProtoBaseAccount() auth.Account { + return &Account{BaseAccount: &auth.BaseAccount{}} +} + +// Balance returns the balance of an account. +func (acc Account) Balance() sdk.Int { + return acc.GetCoins().AmountOf(bank.DenomEthereum) +} + +// SetBalance sets an account's balance. +func (acc Account) SetBalance(amt sdk.Int) { + acc.SetCoins(sdk.Coins{sdk.NewCoin(bank.DenomEthereum, amt)}) +} + +// // NewAccount returns a reference to a new initialized account. +// func NewAccount(base auth.BaseAccount, code []byte) *Account { +// return &Account{ +// BaseAccount: base, +// Code: code, +// Storage: storage, +// } +// } + // GetAccountDecoder returns the auth.AccountDecoder function for the custom // Account type. -func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder { - return func(accBytes []byte) (auth.Account, error) { - if len(accBytes) == 0 { - return nil, sdk.ErrTxDecode("account bytes are empty") - } +// func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder { +// return func(accBytes []byte) (auth.Account, error) { +// if len(accBytes) == 0 { +// return nil, sdk.ErrTxDecode("account bytes are empty") +// } - acc := new(Account) +// acc := new(Account) - err := cdc.UnmarshalBinaryBare(accBytes, &acc) - if err != nil { - return nil, sdk.ErrTxDecode("failed to decode account bytes") - } +// err := cdc.UnmarshalBinaryBare(accBytes, &acc) +// if err != nil { +// return nil, sdk.ErrTxDecode("failed to decode account bytes") +// } - return acc, err - } +// return acc, err +// } +// } + +// Account code and storage type aliases. +type ( + Code []byte + Storage map[ethcmn.Hash]ethcmn.Hash +) + +func (c Code) String() string { + return string(c) +} + +func (c Storage) String() (str string) { + for key, value := range c { + str += fmt.Sprintf("%X : %X\n", key, value) + } + + return +} + +// Copy returns a copy of storage. +func (c Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range c { + cpy[key] = value + } + + return cpy } diff --git a/x/bank/keeper.go b/x/bank/keeper.go new file mode 100644 index 00000000..d63b5346 --- /dev/null +++ b/x/bank/keeper.go @@ -0,0 +1,7 @@ +package bank + +const ( + // DenomEthereum defines the single coin type/denomination supported in + // Ethermint. + DenomEthereum = "ETH" +) From b77d74f4057a81106e7f514465e789bf86328e6e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 09:42:07 -0400 Subject: [PATCH 03/49] Finish state object implementation --- state/state_object.go | 215 +++++++++++++++++++++++++----------------- 1 file changed, 131 insertions(+), 84 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index 12444304..1ce9b1f4 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -52,21 +52,9 @@ type ( suicided bool deleted bool } - - // // Account is the Ethereum consensus representation of accounts. - // // These objects are stored in the main account trie. - // Account struct { - // Nonce uint64 - // Balance *big.Int - // Root ethcmn.Hash // merkle root of the storage trie - // CodeHash []byte - // } ) func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { - // if acc.Balance == nil { - // data.Balance = new(big.Int) - // } acc, ok := accProto.(*types.Account) if !ok { panic(fmt.Sprintf("invalid account type for state object: %T", acc)) @@ -85,13 +73,9 @@ func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { } } -// Address returns the address of the state object. -func (so stateObject) Address() ethcmn.Address { - return so.address -} - -// GetState retrieves a value from the account storage trie. -func (so *stateObject) GetState(_ Database, key ethcmn.Hash) ethcmn.Hash { +// GetState retrieves a value from the account storage trie. Note, the key must +// be prefixed with the address. +func (so *stateObject) GetState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { // if we have a dirty value for this state entry, return it value, dirty := so.dirtyStorage[key] if dirty { @@ -99,11 +83,33 @@ func (so *stateObject) GetState(_ Database, key ethcmn.Hash) ethcmn.Hash { } // otherwise return the entry's original value - return so.getCommittedState(key) + return so.getCommittedState(so.stateDB.ctx, key) } -// SetState updates a value in account storage. -func (so *stateObject) SetState(db Database, key, value ethcmn.Hash) { +// GetCommittedState retrieves a value from the committed account storage trie. +// Note, the must be prefixed with the address. +func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcmn.Hash { + // if we have the original value cached, return that + value, cached := so.originStorage[key] + if cached { + return value + } + + // otherwise load the value from the KVStore + store := ctx.KVStore(so.stateDB.storageKey) + rawValue := store.Get(key.Bytes()) + + if len(rawValue) > 0 { + value.SetBytes(rawValue) + } + + so.originStorage[key] = value + return value +} + +// SetState updates a value in account storage. Note, the key must be prefixed +// with the address. +func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { // if the new value is the same as old, don't set prev := so.GetState(db, key) if prev == value { @@ -120,6 +126,50 @@ func (so *stateObject) SetState(db Database, key, value ethcmn.Hash) { so.setState(key, value) } +func (so *stateObject) setState(key, value ethcmn.Hash) { + so.dirtyStorage[key] = value +} + +// Code returns the contract code associated with this object, if any. +func (so *stateObject) Code(_ ethstate.Database) []byte { + if so.code != nil { + return so.code + } + + if bytes.Equal(so.CodeHash(), emptyCodeHash) { + return nil + } + + store := so.stateDB.ctx.KVStore(so.stateDB.codeKey) + code := store.Get(so.CodeHash()) + + if len(code) == 0 { + so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address())) + } + + so.code = code + return code +} + +// SetCode sets the state object's code. +func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { + prevcode := so.Code(nil) + + so.stateDB.journal.append(codeChange{ + account: &so.address, + prevhash: so.CodeHash(), + prevcode: prevcode, + }) + + so.setCode(codeHash, code) +} + +func (so *stateObject) setCode(codeHash ethcmn.Hash, code []byte) { + so.code = code + so.account.CodeHash = codeHash.Bytes() + so.dirtyCode = true +} + // AddBalance adds an amount to a state object's balance. It is used to add // funds to the destination account of a transfer. func (so *stateObject) AddBalance(amount *big.Int) { @@ -139,6 +189,20 @@ func (so *stateObject) AddBalance(amount *big.Int) { so.SetBalance(newBalance.BigInt()) } +// SubBalance removes an amount from the stateObject's balance. It is used to +// remove funds from the origin account of a transfer. +func (so *stateObject) SubBalance(amount *big.Int) { + amt := sdk.NewIntFromBigInt(amount) + + if amt.Sign() == 0 { + return + } + + newBalance := so.account.Balance().Sub(amt) + so.SetBalance(newBalance.BigInt()) +} + +// SetBalance sets the state object's balance. func (so *stateObject) SetBalance(amount *big.Int) { amt := sdk.NewIntFromBigInt(amount) @@ -150,74 +214,46 @@ func (so *stateObject) SetBalance(amount *big.Int) { so.setBalance(amt) } -// SubBalance removes amount from c's balance. -// It is used to remove funds from the origin account of a transfer. -func (so *stateObject) SubBalance(amount *big.Int) { - if amount.Sign() == 0 { - return - } - - c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) -} - -// func (so *stateObject) Balance() *big.Int { - -// } - -// func (so *stateObject) ReturnGas(gas *big.Int) { - -// } - -// func (so *stateObject) Address() ethcmn.Address { - -// } - -// func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { - -// } - -// func (so *stateObject) SetNonce(nonce uint64) { - -// } - -// func (so *stateObject) Nonce() uint64 { - -// } - -// func (so *stateObject) Code(db Database) []byte { - -// } - -// func (so *stateObject) CodeHash() []byte { - -// } - func (so *stateObject) setBalance(amount sdk.Int) { so.account.SetBalance(amount) } -// GetCommittedState retrieves a value from the committed account storage trie. -func (so *stateObject) getCommittedState(key ethcmn.Hash) ethcmn.Hash { - // if we have the original value cached, return that - value, cached := so.originStorage[key] - if cached { - return value - } - - // otherwise load the value from the KVStore - store := so.stateDB.ctx.KVStore(so.stateDB.storageKey) - rawValue := store.Get(key.Bytes()) - - if len(rawValue) > 0 { - value.SetBytes(rawValue) - } - - so.originStorage[key] = value - return value +// Balance returns the state object's current balance. +func (so *stateObject) Balance() *big.Int { + return so.account.Balance().BigInt() } -func (so *stateObject) setState(key, value ethcmn.Hash) { - so.dirtyStorage[key] = value +// ReturnGas returns the gas back to the origin. Used by the Virtual machine or +// Closures. It performs a no-op. +func (so *stateObject) ReturnGas(gas *big.Int) {} + +// Address returns the address of the state object. +func (so stateObject) Address() ethcmn.Address { + return so.address +} + +// CodeHash returns the state object's code hash. +func (so *stateObject) CodeHash() []byte { + return so.account.CodeHash +} + +// Nonce returns the state object's current nonce (sequence number). +func (so *stateObject) Nonce() uint64 { + return uint64(so.account.Sequence) +} + +// SetNonce sets the state object's nonce (sequence number). +func (so *stateObject) SetNonce(nonce uint64) { + so.stateDB.journal.append(nonceChange{ + account: &so.address, + prev: so.account.Sequence, + }) + + so.setNonce(int64(nonce)) +} + +func (so *stateObject) setNonce(nonce int64) { + so.account.Sequence = nonce } // setError remembers the first non-nil error it is called with. @@ -245,3 +281,14 @@ func (so *stateObject) touch() { so.stateDB.journal.dirty(so.address) } } + +// prefixStorageKey prefixes a storage key with the state object's address. +func (so stateObject) prefixStorageKey(key []byte) []byte { + prefix := so.Address().Bytes() + compositeKey := make([]byte, len(prefix)+len(key)) + + copy(compositeKey, prefix) + copy(compositeKey[len(prefix):], key) + + return compositeKey +} From 2a079d1cbe793f46b4468447b759d9dab635a99c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 09:42:19 -0400 Subject: [PATCH 04/49] Update journal types --- state/journal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/journal.go b/state/journal.go index 77a09d2e..655cea1a 100644 --- a/state/journal.go +++ b/state/journal.go @@ -92,7 +92,7 @@ type ( nonceChange struct { account *common.Address - prev uint64 + prev int64 } storageChange struct { From f2b455b0555294c47583a5bc1e1e30ecc908d02e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 10:57:02 -0400 Subject: [PATCH 05/49] Structure and commenting cleanup --- state/state_object.go | 168 ++++++++++++++++++++++-------------------- types/account.go | 34 ++------- 2 files changed, 97 insertions(+), 105 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index 1ce9b1f4..5fdc40a5 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -73,39 +73,9 @@ func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { } } -// GetState retrieves a value from the account storage trie. Note, the key must -// be prefixed with the address. -func (so *stateObject) GetState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { - // if we have a dirty value for this state entry, return it - value, dirty := so.dirtyStorage[key] - if dirty { - return value - } - - // otherwise return the entry's original value - return so.getCommittedState(so.stateDB.ctx, key) -} - -// GetCommittedState retrieves a value from the committed account storage trie. -// Note, the must be prefixed with the address. -func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcmn.Hash { - // if we have the original value cached, return that - value, cached := so.originStorage[key] - if cached { - return value - } - - // otherwise load the value from the KVStore - store := ctx.KVStore(so.stateDB.storageKey) - rawValue := store.Get(key.Bytes()) - - if len(rawValue) > 0 { - value.SetBytes(rawValue) - } - - so.originStorage[key] = value - return value -} +// ---------------------------------------------------------------------------- +// Setters +// ---------------------------------------------------------------------------- // SetState updates a value in account storage. Note, the key must be prefixed // with the address. @@ -130,27 +100,6 @@ func (so *stateObject) setState(key, value ethcmn.Hash) { so.dirtyStorage[key] = value } -// Code returns the contract code associated with this object, if any. -func (so *stateObject) Code(_ ethstate.Database) []byte { - if so.code != nil { - return so.code - } - - if bytes.Equal(so.CodeHash(), emptyCodeHash) { - return nil - } - - store := so.stateDB.ctx.KVStore(so.stateDB.codeKey) - code := store.Get(so.CodeHash()) - - if len(code) == 0 { - so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address())) - } - - so.code = code - return code -} - // SetCode sets the state object's code. func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { prevcode := so.Code(nil) @@ -218,30 +167,6 @@ func (so *stateObject) setBalance(amount sdk.Int) { so.account.SetBalance(amount) } -// Balance returns the state object's current balance. -func (so *stateObject) Balance() *big.Int { - return so.account.Balance().BigInt() -} - -// ReturnGas returns the gas back to the origin. Used by the Virtual machine or -// Closures. It performs a no-op. -func (so *stateObject) ReturnGas(gas *big.Int) {} - -// Address returns the address of the state object. -func (so stateObject) Address() ethcmn.Address { - return so.address -} - -// CodeHash returns the state object's code hash. -func (so *stateObject) CodeHash() []byte { - return so.account.CodeHash -} - -// Nonce returns the state object's current nonce (sequence number). -func (so *stateObject) Nonce() uint64 { - return uint64(so.account.Sequence) -} - // SetNonce sets the state object's nonce (sequence number). func (so *stateObject) SetNonce(nonce uint64) { so.stateDB.journal.append(nonceChange{ @@ -263,6 +188,93 @@ func (so *stateObject) setError(err error) { } } +// ---------------------------------------------------------------------------- +// Getters +// ---------------------------------------------------------------------------- + +// Address returns the address of the state object. +func (so stateObject) Address() ethcmn.Address { + return so.address +} + +// Balance returns the state object's current balance. +func (so *stateObject) Balance() *big.Int { + return so.account.Balance().BigInt() +} + +// CodeHash returns the state object's code hash. +func (so *stateObject) CodeHash() []byte { + return so.account.CodeHash +} + +// Nonce returns the state object's current nonce (sequence number). +func (so *stateObject) Nonce() uint64 { + return uint64(so.account.Sequence) +} + +// Code returns the contract code associated with this object, if any. +func (so *stateObject) Code(_ ethstate.Database) []byte { + if so.code != nil { + return so.code + } + + if bytes.Equal(so.CodeHash(), emptyCodeHash) { + return nil + } + + store := so.stateDB.ctx.KVStore(so.stateDB.codeKey) + code := store.Get(so.CodeHash()) + + if len(code) == 0 { + so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address())) + } + + so.code = code + return code +} + +// GetState retrieves a value from the account storage trie. Note, the key must +// be prefixed with the address. +func (so *stateObject) GetState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { + // if we have a dirty value for this state entry, return it + value, dirty := so.dirtyStorage[key] + if dirty { + return value + } + + // otherwise return the entry's original value + return so.getCommittedState(so.stateDB.ctx, key) +} + +// GetCommittedState retrieves a value from the committed account storage trie. +// Note, the must be prefixed with the address. +func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcmn.Hash { + // if we have the original value cached, return that + value, cached := so.originStorage[key] + if cached { + return value + } + + // otherwise load the value from the KVStore + store := ctx.KVStore(so.stateDB.storageKey) + rawValue := store.Get(key.Bytes()) + + if len(rawValue) > 0 { + value.SetBytes(rawValue) + } + + so.originStorage[key] = value + return value +} + +// ---------------------------------------------------------------------------- +// Auxiliary +// ---------------------------------------------------------------------------- + +// ReturnGas returns the gas back to the origin. Used by the Virtual machine or +// Closures. It performs a no-op. +func (so *stateObject) ReturnGas(gas *big.Int) {} + // empty returns whether the account is considered empty. func (so *stateObject) empty() bool { return so.account.Sequence == 0 && diff --git a/types/account.go b/types/account.go index 6a2a8989..67fae188 100644 --- a/types/account.go +++ b/types/account.go @@ -12,6 +12,10 @@ import ( var _ auth.Account = (*Account)(nil) +// ---------------------------------------------------------------------------- +// Main Ethermint account +// ---------------------------------------------------------------------------- + // BaseAccount implements the auth.Account interface and embeds an // auth.BaseAccount type. It is compatible with the auth.AccountMapper. type Account struct { @@ -37,33 +41,9 @@ func (acc Account) SetBalance(amt sdk.Int) { acc.SetCoins(sdk.Coins{sdk.NewCoin(bank.DenomEthereum, amt)}) } -// // NewAccount returns a reference to a new initialized account. -// func NewAccount(base auth.BaseAccount, code []byte) *Account { -// return &Account{ -// BaseAccount: base, -// Code: code, -// Storage: storage, -// } -// } - -// GetAccountDecoder returns the auth.AccountDecoder function for the custom -// Account type. -// func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder { -// return func(accBytes []byte) (auth.Account, error) { -// if len(accBytes) == 0 { -// return nil, sdk.ErrTxDecode("account bytes are empty") -// } - -// acc := new(Account) - -// err := cdc.UnmarshalBinaryBare(accBytes, &acc) -// if err != nil { -// return nil, sdk.ErrTxDecode("failed to decode account bytes") -// } - -// return acc, err -// } -// } +// ---------------------------------------------------------------------------- +// Code & Storage +// ---------------------------------------------------------------------------- // Account code and storage type aliases. type ( From 355244b5bc3bb2fe8f686a2572cafd2113084d18 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 11:07:55 -0400 Subject: [PATCH 06/49] Implement deepCopy on state object --- state/state_object.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/state/state_object.go b/state/state_object.go index 5fdc40a5..bc01f8ee 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -275,6 +275,19 @@ func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcm // Closures. It performs a no-op. func (so *stateObject) ReturnGas(gas *big.Int) {} +func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject { + newStateObj := newObject(db, so.account) + + newStateObj.code = so.code + newStateObj.dirtyStorage = so.dirtyStorage.Copy() + newStateObj.originStorage = so.originStorage.Copy() + newStateObj.suicided = so.suicided + newStateObj.dirtyCode = so.dirtyCode + newStateObj.deleted = so.deleted + + return newStateObj +} + // empty returns whether the account is considered empty. func (so *stateObject) empty() bool { return so.account.Sequence == 0 && From e4c1e28f30e67a47f50c517b2297356465ef1585 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 12:42:38 -0400 Subject: [PATCH 07/49] Update stateObject interface implementation --- state/state_object.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index bc01f8ee..26d208cc 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -222,7 +222,8 @@ func (so *stateObject) Code(_ ethstate.Database) []byte { return nil } - store := so.stateDB.ctx.KVStore(so.stateDB.codeKey) + ctx := so.stateDB.ctx + store := ctx.KVStore(so.stateDB.codeKey) code := store.Get(so.CodeHash()) if len(code) == 0 { @@ -235,7 +236,7 @@ func (so *stateObject) Code(_ ethstate.Database) []byte { // GetState retrieves a value from the account storage trie. Note, the key must // be prefixed with the address. -func (so *stateObject) GetState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { +func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash { // if we have a dirty value for this state entry, return it value, dirty := so.dirtyStorage[key] if dirty { @@ -243,12 +244,12 @@ func (so *stateObject) GetState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Has } // otherwise return the entry's original value - return so.getCommittedState(so.stateDB.ctx, key) + return so.GetCommittedState(db, key) } // GetCommittedState retrieves a value from the committed account storage trie. // Note, the must be prefixed with the address. -func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcmn.Hash { +func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { // if we have the original value cached, return that value, cached := so.originStorage[key] if cached { @@ -256,6 +257,7 @@ func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcm } // otherwise load the value from the KVStore + ctx := so.stateDB.ctx store := ctx.KVStore(so.stateDB.storageKey) rawValue := store.Get(key.Bytes()) @@ -307,8 +309,9 @@ func (so *stateObject) touch() { } } -// prefixStorageKey prefixes a storage key with the state object's address. -func (so stateObject) prefixStorageKey(key []byte) []byte { +// GetStorageByAddressKey returns a composite key for a state object's storage +// prefixed with it's address. +func (so stateObject) GetStorageByAddressKey(key []byte) []byte { prefix := so.Address().Bytes() compositeKey := make([]byte, len(prefix)+len(key)) From ae64fb6e333ba1a7f5326daa3355f29b927dd58b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 12:43:00 -0400 Subject: [PATCH 08/49] Update ante handler eth tx handler docs --- handlers/ante.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/handlers/ante.go b/handlers/ante.go index df9e067c..c63f33d5 100644 --- a/handlers/ante.go +++ b/handlers/ante.go @@ -73,9 +73,6 @@ func AnteHandler(accMapper auth.AccountMapper, _ auth.FeeCollectionKeeper) sdk.A // handleEthTx implements an ante handler for an Ethereum transaction. It // validates the signature and if valid returns an OK result. -// -// TODO: Do we need to do any further validation or account manipulation -// (e.g. increment nonce)? func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (sdk.Context, sdk.Result, bool) { ethTx, ok := tx.(types.Transaction) if !ok { @@ -85,7 +82,6 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (s // the SDK chainID is a string representation of integer chainID, ok := new(big.Int).SetString(sdkCtx.ChainID(), 10) if !ok { - // TODO: ErrInternal may not be correct error to throw here? return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid chainID: %s", sdkCtx.ChainID())).Result(), true } @@ -104,10 +100,16 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (s return sdkCtx, sdk.ErrInvalidSequence(fmt.Sprintf("invalid account nonce; expected: %d", seq)).Result(), true } - err = acc.SetSequence(seq + 1) - if err != nil { - return sdkCtx, sdk.ErrInternal(err.Error()).Result(), true - } + // TODO: The EVM will handle incrementing the nonce (sequence number) + // + // TODO: Investigate gas consumption models as the EVM instruction set has its + // own and we should probably not charge for additional gas where we don't have + // to. + // + // err = acc.SetSequence(seq + 1) + // if err != nil { + // return sdkCtx, sdk.ErrInternal(err.Error()).Result(), true + // } accMapper.SetAccount(sdkCtx, acc) return sdkCtx, sdk.Result{GasWanted: int64(ethTx.Data().GasLimit)}, false From f06071decdd4db2bd661ad196600fb2739d00673 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 15:44:22 -0400 Subject: [PATCH 09/49] Update get/set state functionality in sate object to hash composite key --- state/state_object.go | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index 26d208cc..729c4641 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -77,8 +77,8 @@ func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { // Setters // ---------------------------------------------------------------------------- -// SetState updates a value in account storage. Note, the key must be prefixed -// with the address. +// SetState updates a value in account storage. Note, the key will be prefixed +// with the address of the state object. func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { // if the new value is the same as old, don't set prev := so.GetState(db, key) @@ -86,14 +86,16 @@ func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { return } + prefixKey := so.GetStorageByAddressKey(key.Bytes()) + // since the new value is different, update and journal the change so.stateDB.journal.append(storageChange{ account: &so.address, - key: key, + key: prefixKey, prevalue: prev, }) - so.setState(key, value) + so.setState(prefixKey, value) } func (so *stateObject) setState(key, value ethcmn.Hash) { @@ -234,11 +236,13 @@ func (so *stateObject) Code(_ ethstate.Database) []byte { return code } -// GetState retrieves a value from the account storage trie. Note, the key must -// be prefixed with the address. +// GetState retrieves a value from the account storage trie. Note, the key will +// be prefixed with the address of the state object. func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash { + prefixKey := so.GetStorageByAddressKey(key.Bytes()) + // if we have a dirty value for this state entry, return it - value, dirty := so.dirtyStorage[key] + value, dirty := so.dirtyStorage[prefixKey] if dirty { return value } @@ -248,10 +252,12 @@ func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Ha } // GetCommittedState retrieves a value from the committed account storage trie. -// Note, the must be prefixed with the address. +// Note, the key will be prefixed with the address of the state object. func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { + prefixKey := so.GetStorageByAddressKey(key.Bytes()) + // if we have the original value cached, return that - value, cached := so.originStorage[key] + value, cached := so.originStorage[prefixKey] if cached { return value } @@ -259,13 +265,13 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e // otherwise load the value from the KVStore ctx := so.stateDB.ctx store := ctx.KVStore(so.stateDB.storageKey) - rawValue := store.Get(key.Bytes()) + rawValue := store.Get(prefixKey.Bytes()) if len(rawValue) > 0 { value.SetBytes(rawValue) } - so.originStorage[key] = value + so.originStorage[prefixKey] = value return value } @@ -309,14 +315,14 @@ func (so *stateObject) touch() { } } -// GetStorageByAddressKey returns a composite key for a state object's storage -// prefixed with it's address. -func (so stateObject) GetStorageByAddressKey(key []byte) []byte { +// GetStorageByAddressKey returns a hash of the composite key for a state +// object's storage prefixed with it's address. +func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { prefix := so.Address().Bytes() compositeKey := make([]byte, len(prefix)+len(key)) copy(compositeKey, prefix) copy(compositeKey[len(prefix):], key) - return compositeKey + return crypto.Keccak256Hash(compositeKey) } From 6a741fc5015a093c818b9ddaab8e6ab84970849a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 21:24:32 -0400 Subject: [PATCH 10/49] Implement journal --- state/dump.go | 12 ++++++ state/journal.go | 107 ++++++++++++++++++++++------------------------- 2 files changed, 61 insertions(+), 58 deletions(-) create mode 100644 state/dump.go diff --git a/state/dump.go b/state/dump.go new file mode 100644 index 00000000..b4e3ed8b --- /dev/null +++ b/state/dump.go @@ -0,0 +1,12 @@ +package state + +import ( + ethstate "github.com/ethereum/go-ethereum/core/state" +) + +// RawDump returns a raw state drump. +// +// TODO: Implement if we need it, especially for the RPC API. +func (csdb *CommitStateDB) RawDump() ethstate.Dump { + return ethstate.Dump{} +} diff --git a/state/journal.go b/state/journal.go index 655cea1a..9095c7f4 100644 --- a/state/journal.go +++ b/state/journal.go @@ -3,9 +3,11 @@ package state import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" + ethcmn "github.com/ethereum/go-ethereum/common" ) +var ripemd = ethcmn.HexToAddress("0000000000000000000000000000000000000003") + // journalEntry is a modification entry in the state change journal that can be // reverted on demand. type journalEntry interface { @@ -13,7 +15,7 @@ type journalEntry interface { revert(*CommitStateDB) // dirtied returns the Ethereum address modified by this journal entry. - dirtied() *common.Address + dirtied() *ethcmn.Address } // journal contains the list of state modifications applied since the last state @@ -21,13 +23,13 @@ type journalEntry interface { // exception or revertal request. type journal struct { entries []journalEntry // Current changes tracked by the journal - dirties map[common.Address]int // Dirty accounts and the number of changes + dirties map[ethcmn.Address]int // Dirty accounts and the number of changes } // newJournal create a new initialized journal. func newJournal() *journal { return &journal{ - dirties: make(map[common.Address]int), + dirties: make(map[ethcmn.Address]int), } } @@ -59,7 +61,7 @@ func (j *journal) revert(statedb *CommitStateDB, snapshot int) { // dirty explicitly sets an address to dirty, even if the change entries would // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD // precompile consensus exception. -func (j *journal) dirty(addr common.Address) { +func (j *journal) dirty(addr ethcmn.Address) { j.dirties[addr]++ } @@ -71,7 +73,7 @@ func (j *journal) length() int { type ( // Changes to the account trie. createObjectChange struct { - account *common.Address + account *ethcmn.Address } resetObjectChange struct { @@ -79,30 +81,30 @@ type ( } suicideChange struct { - account *common.Address + account *ethcmn.Address prev bool // whether account had already suicided - prevbalance sdk.Int + prevBalance sdk.Int } // Changes to individual accounts. balanceChange struct { - account *common.Address + account *ethcmn.Address prev sdk.Int } nonceChange struct { - account *common.Address + account *ethcmn.Address prev int64 } storageChange struct { - account *common.Address - key, prevalue common.Hash + account *ethcmn.Address + key, prevValue ethcmn.Hash } codeChange struct { - account *common.Address - prevcode, prevhash []byte + account *ethcmn.Address + prevCode, prevHash []byte } // Changes to other state values. @@ -111,15 +113,15 @@ type ( } addLogChange struct { - txhash common.Hash + txhash ethcmn.Hash } addPreimageChange struct { - hash common.Hash + hash ethcmn.Hash } touchChange struct { - account *common.Address + account *ethcmn.Address prev bool prevDirty bool } @@ -130,107 +132,96 @@ func (ch createObjectChange) revert(s *CommitStateDB) { delete(s.stateObjectsDirty, *ch.account) } -func (ch createObjectChange) dirtied() *common.Address { +func (ch createObjectChange) dirtied() *ethcmn.Address { return ch.account } func (ch resetObjectChange) revert(s *CommitStateDB) { - // TODO: ... - // s.setStateObject(ch.prev) + s.setStateObject(ch.prev) } -func (ch resetObjectChange) dirtied() *common.Address { +func (ch resetObjectChange) dirtied() *ethcmn.Address { return nil } func (ch suicideChange) revert(s *CommitStateDB) { - // TODO: ... - // obj := s.getStateObject(*ch.account) - // if obj != nil { - // obj.suicided = ch.prev - // obj.setBalance(ch.prevbalance) - // } + so := s.getStateObject(*ch.account) + if so != nil { + so.suicided = ch.prev + so.setBalance(ch.prevBalance) + } } -func (ch suicideChange) dirtied() *common.Address { +func (ch suicideChange) dirtied() *ethcmn.Address { return ch.account } -var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") - func (ch touchChange) revert(s *CommitStateDB) { } -func (ch touchChange) dirtied() *common.Address { +func (ch touchChange) dirtied() *ethcmn.Address { return ch.account } func (ch balanceChange) revert(s *CommitStateDB) { - // TODO: ... - // s.getStateObject(*ch.account).setBalance(ch.prev) + s.getStateObject(*ch.account).setBalance(ch.prev) } -func (ch balanceChange) dirtied() *common.Address { +func (ch balanceChange) dirtied() *ethcmn.Address { return ch.account } func (ch nonceChange) revert(s *CommitStateDB) { - // TODO: ... - // s.getStateObject(*ch.account).setNonce(ch.prev) + s.getStateObject(*ch.account).setNonce(ch.prev) } -func (ch nonceChange) dirtied() *common.Address { +func (ch nonceChange) dirtied() *ethcmn.Address { return ch.account } func (ch codeChange) revert(s *CommitStateDB) { - // TODO: ... - // s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) + s.getStateObject(*ch.account).setCode(ethcmn.BytesToHash(ch.prevHash), ch.prevCode) } -func (ch codeChange) dirtied() *common.Address { +func (ch codeChange) dirtied() *ethcmn.Address { return ch.account } func (ch storageChange) revert(s *CommitStateDB) { - // TODO: ... - // s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) + s.getStateObject(*ch.account).setState(ch.key, ch.prevValue) } -func (ch storageChange) dirtied() *common.Address { +func (ch storageChange) dirtied() *ethcmn.Address { return ch.account } func (ch refundChange) revert(s *CommitStateDB) { - // TODO: ... - // s.refund = ch.prev + s.refund = ch.prev } -func (ch refundChange) dirtied() *common.Address { +func (ch refundChange) dirtied() *ethcmn.Address { return nil } func (ch addLogChange) revert(s *CommitStateDB) { - // TODO: ... - // logs := s.logs[ch.txhash] - // if len(logs) == 1 { - // delete(s.logs, ch.txhash) - // } else { - // s.logs[ch.txhash] = logs[:len(logs)-1] - // } + logs := s.logs[ch.txhash] + if len(logs) == 1 { + delete(s.logs, ch.txhash) + } else { + s.logs[ch.txhash] = logs[:len(logs)-1] + } - // s.logSize-- + s.logSize-- } -func (ch addLogChange) dirtied() *common.Address { +func (ch addLogChange) dirtied() *ethcmn.Address { return nil } func (ch addPreimageChange) revert(s *CommitStateDB) { - // TODO: ... - // delete(s.preimages, ch.hash) + delete(s.preimages, ch.hash) } -func (ch addPreimageChange) dirtied() *common.Address { +func (ch addPreimageChange) dirtied() *ethcmn.Address { return nil } From 7f4dfa5d5954ac63855ad5c3f004f35d8f89320d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 21:24:53 -0400 Subject: [PATCH 11/49] Update state object to reflect journal changes --- state/state_object.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index 729c4641..95304de5 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -90,9 +90,9 @@ func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { // since the new value is different, update and journal the change so.stateDB.journal.append(storageChange{ - account: &so.address, - key: prefixKey, - prevalue: prev, + account: &so.address, + key: prefixKey, + prevValue: prev, }) so.setState(prefixKey, value) @@ -104,12 +104,12 @@ func (so *stateObject) setState(key, value ethcmn.Hash) { // SetCode sets the state object's code. func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { - prevcode := so.Code(nil) + prevCode := so.Code(nil) so.stateDB.journal.append(codeChange{ account: &so.address, - prevhash: so.CodeHash(), - prevcode: prevcode, + prevHash: so.CodeHash(), + prevCode: prevCode, }) so.setCode(codeHash, code) @@ -190,6 +190,10 @@ func (so *stateObject) setError(err error) { } } +func (so *stateObject) markSuicided() { + so.suicided = true +} + // ---------------------------------------------------------------------------- // Getters // ---------------------------------------------------------------------------- From 817f9605aacc971da8ba5856e00a6de8e750ce8c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 3 Oct 2018 21:32:13 -0400 Subject: [PATCH 12/49] Partial stateDB implementation --- state/statedb.go | 545 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 518 insertions(+), 27 deletions(-) diff --git a/state/statedb.go b/state/statedb.go index 50b305af..7f145b9c 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -3,6 +3,8 @@ package state import ( "fmt" "math/big" + "sort" + "sync" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -10,10 +12,18 @@ import ( ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" ) -var _ ethstate.StateDB = (*CommitStateDB)(nil) +var ( + _ ethstate.StateDB = (*CommitStateDB)(nil) + zeroBalance = sdk.ZeroInt().BigInt() +) + +// CommitStateDB implements the Geth state.StateDB interface. Instead of using +// a trie and database for querying and persistence, KVStores and an account +// mapper is used to facilitate state transitions. type CommitStateDB struct { // TODO: Figure out a way to not need to store a context as part of the // structure @@ -21,17 +31,25 @@ type CommitStateDB struct { am auth.AccountMapper storageKey sdk.StoreKey + codeKey sdk.StoreKey // maps that hold 'live' objects, which will get modified while processing a // state transition + // + // TODO: Determine if we need this cache as the KVStore is cache-wrapped stateObjects map[ethcmn.Address]*stateObject stateObjectsDirty map[ethcmn.Address]struct{} + // The refund counter, also used by state transitioning. + refund uint64 + thash, bhash ethcmn.Hash txIndex int logs map[ethcmn.Hash][]*ethtypes.Log logSize uint + preimages map[ethcmn.Hash][]byte + // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs @@ -44,23 +62,502 @@ type CommitStateDB struct { journal *journal validRevisions []ethstate.Revision nextRevisionID int + + // mutex for state deep copying + lock sync.Mutex } -func NewCommitStateDB(ctx sdk.Context) (*CommitStateDB, error) { - // tr, err := db.OpenTrie(root) - // if err != nil { - // return nil, err - // } +// TODO: Make sure storage is prefixed with address!!! +func NewCommitStateDB(ctx sdk.Context) (*CommitStateDB, error) { return &CommitStateDB{ - // stateObjects: make(map[ethcmn.Address]*stateObject), - // stateObjectsDirty: make(map[ethcmn.Address]struct{}), - // logs: make(map[ethcmn.Hash][]*types.Log), - // preimages: make(map[ethcmn.Hash][]byte), - journal: newJournal(), + stateObjects: make(map[ethcmn.Address]*stateObject), + stateObjectsDirty: make(map[ethcmn.Address]struct{}), + logs: make(map[ethcmn.Hash][]*ethtypes.Log), + journal: newJournal(), }, nil } +// ---------------------------------------------------------------------------- +// Setters +// ---------------------------------------------------------------------------- + +// SetBalance sets the balance of an account. +func (csdb *CommitStateDB) SetBalance(addr ethcmn.Address, amount *big.Int) { + so := csdb.GetOrNewStateObject(addr) + if so != nil { + so.SetBalance(amount) + } +} + +// AddBalance adds amount to the account associated with addr. +func (csdb *CommitStateDB) AddBalance(addr ethcmn.Address, amount *big.Int) { + so := csdb.GetOrNewStateObject(addr) + if so != nil { + so.AddBalance(amount) + } +} + +// SubBalance subtracts amount from the account associated with addr. +func (csdb *CommitStateDB) SubBalance(addr ethcmn.Address, amount *big.Int) { + so := csdb.GetOrNewStateObject(addr) + if so != nil { + so.SubBalance(amount) + } +} + +// SetNonce sets the nonce (sequence number) of an account. +func (csdb *CommitStateDB) SetNonce(addr ethcmn.Address, nonce uint64) { + so := csdb.GetOrNewStateObject(addr) + if so != nil { + so.SetNonce(nonce) + } +} + +// SetState sets the storage state with a key, value pair for an account. +func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash) { + so := csdb.GetOrNewStateObject(addr) + if so != nil { + so.SetState(nil, key, value) + } +} + +// SetCode sets the code for a given account. +func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) { + so := csdb.GetOrNewStateObject(addr) + if so != nil { + so.SetCode(crypto.Keccak256Hash(code), code) + } +} + +// AddLog adds a new log to the state and sets the log metadata from the state. +func (csdb *CommitStateDB) AddLog(log *ethtypes.Log) { + csdb.journal.append(addLogChange{txhash: csdb.thash}) + + log.TxHash = csdb.thash + log.BlockHash = csdb.bhash + log.TxIndex = uint(csdb.txIndex) + log.Index = csdb.logSize + csdb.logs[csdb.thash] = append(csdb.logs[csdb.thash], log) + csdb.logSize++ +} + +// AddPreimage records a SHA3 preimage seen by the VM. +func (csdb *CommitStateDB) AddPreimage(hash ethcmn.Hash, preimage []byte) { + if _, ok := csdb.preimages[hash]; !ok { + csdb.journal.append(addPreimageChange{hash: hash}) + + pi := make([]byte, len(preimage)) + copy(pi, preimage) + csdb.preimages[hash] = pi + } +} + +// AddRefund adds gas to the refund counter. +func (csdb *CommitStateDB) AddRefund(gas uint64) { + csdb.journal.append(refundChange{prev: csdb.refund}) + csdb.refund += gas +} + +// SubRefund removes gas from the refund counter. It will panic if the refund +// counter goes below zero. +func (csdb *CommitStateDB) SubRefund(gas uint64) { + csdb.journal.append(refundChange{prev: csdb.refund}) + if gas > csdb.refund { + panic("refund counter below zero") + } + + csdb.refund -= gas +} + +// ---------------------------------------------------------------------------- +// Getters +// ---------------------------------------------------------------------------- + +// GetBalance retrieves the balance from the given address or 0 if object not +// found. +func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int { + so := csdb.getStateObject(addr) + if so != nil { + return so.Balance() + } + + return zeroBalance +} + +// GetNonce returns the nonce (sequence number) for a given account. +func (csdb *CommitStateDB) GetNonce(addr ethcmn.Address) uint64 { + so := csdb.getStateObject(addr) + if so != nil { + return so.Nonce() + } + + return 0 +} + +// GetCode returns the code for a given account. +func (csdb *CommitStateDB) GetCode(addr ethcmn.Address) []byte { + so := csdb.getStateObject(addr) + if so != nil { + return so.Code(nil) + } + + return nil +} + +// GetCodeSize returns the code size for a given account. +func (csdb *CommitStateDB) GetCodeSize(addr ethcmn.Address) int { + so := csdb.getStateObject(addr) + if so == nil { + return 0 + } + + if so.code != nil { + return len(so.code) + } + + // TODO: we may need to cache these lookups directly + return len(so.Code(nil)) +} + +// GetCodeHash returns the code hash for a given account. +func (csdb *CommitStateDB) GetCodeHash(addr ethcmn.Address) ethcmn.Hash { + so := csdb.getStateObject(addr) + if so == nil { + return ethcmn.Hash{} + } + + return ethcmn.BytesToHash(so.CodeHash()) +} + +// GetState retrieves a value from the given account's storage store. +func (csdb *CommitStateDB) GetState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { + so := csdb.getStateObject(addr) + if so != nil { + return so.GetState(nil, hash) + } + + return ethcmn.Hash{} +} + +// GetCommittedState retrieves a value from the given account's committed +// storage. +func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { + so := csdb.getStateObject(addr) + if so != nil { + return so.GetCommittedState(nil, hash) + } + + return ethcmn.Hash{} +} + +// GetLogs returns the current logs for a given hash in the state. +func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) []*ethtypes.Log { + return csdb.logs[hash] +} + +// Logs returns all the current logs in the state. +func (csdb *CommitStateDB) Logs() []*ethtypes.Log { + var logs []*ethtypes.Log + for _, lgs := range csdb.logs { + logs = append(logs, lgs...) + } + + return logs +} + +// GetRefund returns the current value of the refund counter. +func (csdb *CommitStateDB) GetRefund() uint64 { + return csdb.refund +} + +// Preimages returns a list of SHA3 preimages that have been submitted. +func (csdb *CommitStateDB) Preimages() map[ethcmn.Hash][]byte { + return csdb.preimages +} + +// HasSuicided returns if the given account for the specified address has been +// killed. +func (csdb *CommitStateDB) HasSuicided(addr ethcmn.Address) bool { + so := csdb.getStateObject(addr) + if so != nil { + return so.suicided + } + + return false +} + +// StorageTrie returns nil as the state in Ethermint does not use a direct +// storage trie. +func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { + return nil +} + +// ---------------------------------------------------------------------------- +// Persistance +// ---------------------------------------------------------------------------- + +// TODO: Commit writes the state ... +func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, err error) { + // TODO: ... + return +} + +// TODO: ... +func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { + // TODO: ... +} + +// IntermediateRoot computes the current root hash of the state trie. +// It is called in between transactions to get the root hash that +// goes into transaction receipts. +func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash { + // TODO: ... + // csdb.Finalize(deleteEmptyObjects) + // return csdb.trie.Hash() +} + +// ---------------------------------------------------------------------------- +// Snapshotting +// ---------------------------------------------------------------------------- + +// Snapshot returns an identifier for the current revision of the state. +func (csdb *CommitStateDB) Snapshot() int { + id := csdb.nextRevisionID + csdb.nextRevisionID++ + csdb.validRevisions = append(csdb.validRevisions, ethstate.Revision{id, csdb.journal.length()}) + return id +} + +// RevertToSnapshot reverts all state changes made since the given revision. +func (csdb *CommitStateDB) RevertToSnapshot(revID int) { + // find the snapshot in the stack of valid snapshots + idx := sort.Search(len(csdb.validRevisions), func(i int) bool { + return csdb.validRevisions[i].ID >= revID + }) + + if idx == len(csdb.validRevisions) || csdb.validRevisions[idx].ID != revID { + panic(fmt.Errorf("revision ID %v cannot be reverted", revID)) + } + + snapshot := csdb.validRevisions[idx].JournalIndex + + // replay the journal to undo changes and remove invalidated snapshots + csdb.journal.revert(csdb, snapshot) + csdb.validRevisions = csdb.validRevisions[:idx] +} + +// ---------------------------------------------------------------------------- +// Auxiliary +// ---------------------------------------------------------------------------- + +// Suicide marks the given account as suicided and clears the account balance. +// +// The account's state object is still available until the state is committed, +// getStateObject will return a non-nil account after Suicide. +func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool { + so := csdb.getStateObject(addr) + if so == nil { + return false + } + + csdb.journal.append(suicideChange{ + account: &addr, + prev: so.suicided, + prevBalance: sdk.NewIntFromBigInt(so.Balance()), + }) + + so.markSuicided() + so.SetBalance(new(big.Int)) + + return true +} + +// Reset clears out all ephemeral state objects from the state db, but keeps +// the underlying account mapper and store keys to avoid reloading data for the +// next operations. +func (csdb *CommitStateDB) Reset(root ethcmn.Hash) error { + csdb.stateObjects = make(map[ethcmn.Address]*stateObject) + csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) + csdb.thash = ethcmn.Hash{} + csdb.bhash = ethcmn.Hash{} + csdb.txIndex = 0 + csdb.logs = make(map[ethcmn.Hash][]*ethtypes.Log) + csdb.logSize = 0 + csdb.preimages = make(map[ethcmn.Hash][]byte) + + csdb.clearJournalAndRefund() + return nil +} + +func (csdb *CommitStateDB) clearJournalAndRefund() { + csdb.journal = newJournal() + csdb.validRevisions = csdb.validRevisions[:0] + csdb.refund = 0 +} + +// Prepare sets the current transaction hash and index and block hash which is +// used when the EVM emits new state logs. +func (csdb *CommitStateDB) Prepare(thash, bhash ethcmn.Hash, txi int) { + csdb.thash = thash + csdb.bhash = bhash + csdb.txIndex = txi +} + +// CreateAccount explicitly creates a state object. If a state object with the +// address already exists the balance is carried over to the new account. +// +// CreateAccount is called during the EVM CREATE operation. The situation might +// arise that a contract does the following: +// +// 1. sends funds to sha(account ++ (nonce + 1)) +// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) +// +// Carrying over the balance ensures that Ether doesn't disappear. +func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) { + newobj, prevobj := csdb.createObject(addr) + if prevobj != nil { + newobj.setBalance(sdk.NewIntFromBigInt(prevobj.Balance())) + } +} + +// Copy creates a deep, independent copy of the state. +// +// NOTE: Snapshots of the copied state cannot be applied to the copy. +func (csdb *CommitStateDB) Copy() ethstate.StateDB { + csdb.lock.Lock() + defer csdb.lock.Unlock() + + // copy all the basic fields, initialize the memory ones + state := &CommitStateDB{ + ctx: csdb.ctx, + am: csdb.am, + storageKey: csdb.storageKey, + codeKey: csdb.codeKey, + stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)), + stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)), + refund: csdb.refund, + logs: make(map[ethcmn.Hash][]*ethtypes.Log, len(csdb.logs)), + logSize: csdb.logSize, + preimages: make(map[ethcmn.Hash][]byte), + journal: newJournal(), + } + + // copy the dirty states, logs, and preimages + for addr := range csdb.journal.dirties { + // There is a case where an object is in the journal but not in the + // stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we + // need to check for nil. + // + // Ref: https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527 + if object, exist := csdb.stateObjects[addr]; exist { + state.stateObjects[addr] = object.deepCopy(state) + state.stateObjectsDirty[addr] = struct{}{} + } + } + + // Above, we don't copy the actual journal. This means that if the copy is + // copied, the loop above will be a no-op, since the copy's journal is empty. + // Thus, here we iterate over stateObjects, to enable copies of copies. + for addr := range csdb.stateObjectsDirty { + if _, exist := state.stateObjects[addr]; !exist { + state.stateObjects[addr] = csdb.stateObjects[addr].deepCopy(state) + state.stateObjectsDirty[addr] = struct{}{} + } + } + + // copy logs + for hash, logs := range csdb.logs { + cpy := make([]*ethtypes.Log, len(logs)) + for i, l := range logs { + cpy[i] = new(ethtypes.Log) + *cpy[i] = *l + } + state.logs[hash] = cpy + } + + // copy pre-images + for hash, preimage := range csdb.preimages { + state.preimages[hash] = preimage + } + + return state +} + +// ForEachStorage iterates over each storage items, all invokes the provided +// callback on each key, value pair . +func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) { + so := csdb.getStateObject(addr) + if so == nil { + return + } + + store := csdb.ctx.KVStore(csdb.storageKey) + iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes()) + + for ; iter.Valid(); iter.Next() { + key := ethcmn.BytesToHash(iter.Key()) + value := iter.Value() + + if value, dirty := so.dirtyStorage[key]; dirty { + cb(key, value) + continue + } + + cb(key, ethcmn.BytesToHash(value)) + } + + iter.Close() +} + +// GetOrNewStateObject retrieves a state object or create a new state object if +// nil. +func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) ethstate.StateObject { + so := csdb.getStateObject(addr) + if so == nil || so.deleted { + so, _ = csdb.createObject(addr) + } + + return so +} + +// createObject creates a new state object. If there is an existing account with +// the given address, it is overwritten and returned as the second return value. +func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) { + prevObj = csdb.getStateObject(addr) + + acc := csdb.am.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) + newObj = newObject(csdb, acc) + newObj.setNonce(0) // sets the object to dirty + + if prevObj == nil { + csdb.journal.append(createObjectChange{account: &addr}) + } else { + csdb.journal.append(resetObjectChange{prev: prevObj}) + } + + csdb.setStateObject(newObj) + return newObj, prevObj +} + +// Empty returns whether the state object is either non-existent or empty +// according to the EIP161 specification (balance = nonce = code = 0). +func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool { + so := csdb.getStateObject(addr) + return so == nil || so.empty() +} + +// Exist reports whether the given account address exists in the state. Notably, +// this also returns true for suicided accounts. +func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool { + return csdb.getStateObject(addr) != nil +} + +// Error returns the first non-nil error the StateDB encountered. +func (csdb *CommitStateDB) Error() error { + return csdb.dbErr +} + // setError remembers the first non-nil error it is called with. func (csdb *CommitStateDB) setError(err error) { if csdb.dbErr == nil { @@ -68,22 +565,8 @@ func (csdb *CommitStateDB) setError(err error) { } } -// Error returns the first non-nil error the StateDB encountered. -func (csdb *CommitStateDB) Error() error { - return csdb.dbErr -} - -// Retrieve the balance from the given address or 0 if object not found -func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int { - stateObject := csdb.getStateObject(addr) - if stateObject != nil { - return stateObject.Balance() - } - - return common.Big0 -} - -// Retrieve a state object given by the address. Returns nil if not found. +// getStateObject attempts to retrieve a state object given by the address. +// Returns nil and sets an error if not found. func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { // prefer 'live' (cached) objects if obj := csdb.stateObjects[addr]; obj != nil { @@ -94,9 +577,11 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta return obj } + // otherwise, attempt to fetch the account from the account mapper acc := csdb.am.GetAccount(csdb.ctx, addr.Bytes()) if acc == nil { csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes())) + return nil } // insert the state object into the live set @@ -108,3 +593,9 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta func (csdb *CommitStateDB) setStateObject(object *stateObject) { csdb.stateObjects[object.Address()] = object } + +// Database retrieves the low level database supporting the lower level trie +// ops. It is not used in Ethermint, so it returns nil. +func (csdb *CommitStateDB) Database() ethstate.Database { + return nil +} From 2feb5bbb5bbed0e6b624dc9382676fdfb68a9c7e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 12:06:19 -0400 Subject: [PATCH 13/49] Finish initial StateDB implementation --- state/state_object.go | 36 ++++++++++- state/statedb.go | 144 ++++++++++++++++++++++++++++++++---------- types/account.go | 6 +- 3 files changed, 147 insertions(+), 39 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index 95304de5..b4e1a95c 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -8,16 +8,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" auth "github.com/cosmos/cosmos-sdk/x/auth" + tmcrypto "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/crypto" ) var ( _ ethstate.StateObject = (*stateObject)(nil) - emptyCodeHash = crypto.Keccak256(nil) + emptyCodeHash = tmcrypto.Sha256(nil) ) type ( @@ -194,6 +195,33 @@ func (so *stateObject) markSuicided() { so.suicided = true } +// commitState commits all dirty storage to a KVStore. +func (so *stateObject) commitState() { + ctx := so.stateDB.ctx + store := ctx.KVStore(so.stateDB.storageKey) + + for key, value := range so.dirtyStorage { + delete(so.dirtyStorage, key) + + // skip no-op changes, persist actual changes + if value == so.originStorage[key] { + continue + } + + so.originStorage[key] = value + + // delete empty values + if (value == ethcmn.Hash{}) { + store.Delete(key.Bytes()) + continue + } + + store.Set(key.Bytes(), value.Bytes()) + } + + // TODO: Set the account (storage) root (but we probably don't need this) +} + // ---------------------------------------------------------------------------- // Getters // ---------------------------------------------------------------------------- @@ -271,6 +299,8 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e store := ctx.KVStore(so.stateDB.storageKey) rawValue := store.Get(prefixKey.Bytes()) + // TODO: Do we need to RLP split/decode? + if len(rawValue) > 0 { value.SetBytes(rawValue) } @@ -328,5 +358,5 @@ func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { copy(compositeKey, prefix) copy(compositeKey[len(prefix):], key) - return crypto.Keccak256Hash(compositeKey) + return ethcmn.BytesToHash(tmcrypto.Sha256(compositeKey)) } diff --git a/state/statedb.go b/state/statedb.go index 7f145b9c..7cbdc641 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -9,10 +9,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + tmcrypto "github.com/tendermint/tendermint/crypto" + ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" ) var ( @@ -126,7 +127,8 @@ func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash) func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) { so := csdb.GetOrNewStateObject(addr) if so != nil { - so.SetCode(crypto.Keccak256Hash(code), code) + key := ethcmn.BytesToHash(tmcrypto.Sha256(code)) + so.SetCode(key, code) } } @@ -299,22 +301,94 @@ func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { // TODO: Commit writes the state ... func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, err error) { - // TODO: ... + defer csdb.clearJournalAndRefund() + + // remove dirty state object entries based on the journal + for addr := range csdb.journal.dirties { + csdb.stateObjectsDirty[addr] = struct{}{} + } + + // set the state objects + for addr, so := range csdb.stateObjects { + _, isDirty := csdb.stateObjectsDirty[addr] + switch { + case so.suicided || (isDirty && deleteEmptyObjects && so.empty()): + // If the state object has been removed, don't bother syncing it and just + // remove it from the store. + csdb.deleteStateObject(so) + + case isDirty: + // write any contract code associated with the state object + if so.code != nil && so.dirtyCode { + csdb.SetCode(so.Address(), so.code) + so.dirtyCode = false + } + + // update the object in the KVStore + csdb.updateStateObject(so) + } + + delete(csdb.stateObjectsDirty, addr) + } + + // TODO: Get and return the commit/root from the context return } -// TODO: ... +// Finalize finalizes the state objects (accounts) state by setting their state, +// removing the csdb destructed objects and clearing the journal as well as the +// refunds. func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { - // TODO: ... + for addr := range csdb.journal.dirties { + so, exist := csdb.stateObjects[addr] + if !exist { + // ripeMD is 'touched' at block 1714175, in tx: + // 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 + // + // That tx goes out of gas, and although the notion of 'touched' does not + // exist there, the touch-event will still be recorded in the journal. + // Since ripeMD is a special snowflake, it will persist in the journal even + // though the journal is reverted. In this special circumstance, it may + // exist in journal.dirties but not in stateObjects. Thus, we can safely + // ignore it here. + continue + } + + if so.suicided || (deleteEmptyObjects && so.empty()) { + csdb.deleteStateObject(so) + } else { + // Set all the dirty state storage items for the state object in the + // KVStore and finally set the account in the account mapper. + so.commitState() + csdb.updateStateObject(so) + } + + csdb.stateObjectsDirty[addr] = struct{}{} + } + + // invalidate journal because reverting across transactions is not allowed + csdb.clearJournalAndRefund() } -// IntermediateRoot computes the current root hash of the state trie. -// It is called in between transactions to get the root hash that -// goes into transaction receipts. +// IntermediateRoot returns the current root hash of the state. It is called in +// between transactions to get the root hash that goes into transaction +// receipts. func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash { - // TODO: ... - // csdb.Finalize(deleteEmptyObjects) - // return csdb.trie.Hash() + csdb.Finalize(deleteEmptyObjects) + + // TODO: Get and return the commit/root from the context + return ethcmn.Hash{} +} + +// updateStateObject writes the given state object to the store. +func (csdb *CommitStateDB) updateStateObject(so *stateObject) { + csdb.am.SetAccount(csdb.ctx, so.account) +} + +// deleteStateObject removes the given state object from the state store. +func (csdb *CommitStateDB) deleteStateObject(so *stateObject) { + so.deleted = true + csdb.am.RemoveAccount(csdb.ctx, so.account) } // ---------------------------------------------------------------------------- @@ -351,6 +425,30 @@ func (csdb *CommitStateDB) RevertToSnapshot(revID int) { // Auxiliary // ---------------------------------------------------------------------------- +// Database retrieves the low level database supporting the lower level trie +// ops. It is not used in Ethermint, so it returns nil. +func (csdb *CommitStateDB) Database() ethstate.Database { + return nil +} + +// Empty returns whether the state object is either non-existent or empty +// according to the EIP161 specification (balance = nonce = code = 0). +func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool { + so := csdb.getStateObject(addr) + return so == nil || so.empty() +} + +// Exist reports whether the given account address exists in the state. Notably, +// this also returns true for suicided accounts. +func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool { + return csdb.getStateObject(addr) != nil +} + +// Error returns the first non-nil error the StateDB encountered. +func (csdb *CommitStateDB) Error() error { + return csdb.dbErr +} + // Suicide marks the given account as suicided and clears the account balance. // // The account's state object is still available until the state is committed, @@ -540,24 +638,6 @@ func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *s return newObj, prevObj } -// Empty returns whether the state object is either non-existent or empty -// according to the EIP161 specification (balance = nonce = code = 0). -func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool { - so := csdb.getStateObject(addr) - return so == nil || so.empty() -} - -// Exist reports whether the given account address exists in the state. Notably, -// this also returns true for suicided accounts. -func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool { - return csdb.getStateObject(addr) != nil -} - -// Error returns the first non-nil error the StateDB encountered. -func (csdb *CommitStateDB) Error() error { - return csdb.dbErr -} - // setError remembers the first non-nil error it is called with. func (csdb *CommitStateDB) setError(err error) { if csdb.dbErr == nil { @@ -593,9 +673,3 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta func (csdb *CommitStateDB) setStateObject(object *stateObject) { csdb.stateObjects[object.Address()] = object } - -// Database retrieves the low level database supporting the lower level trie -// ops. It is not used in Ethermint, so it returns nil. -func (csdb *CommitStateDB) Database() ethstate.Database { - return nil -} diff --git a/types/account.go b/types/account.go index 67fae188..4d58e431 100644 --- a/types/account.go +++ b/types/account.go @@ -21,7 +21,11 @@ var _ auth.Account = (*Account)(nil) type Account struct { *auth.BaseAccount - Root ethcmn.Hash // merkle root of the storage trie + // merkle root of the storage trie + // + // TODO: good chance we may not need this + Root ethcmn.Hash + CodeHash []byte } From 3c47af91ab40f9476c43c2bebc9d69d8bc9d400c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 12:09:59 -0400 Subject: [PATCH 14/49] Update statedb docs and constructor --- state/statedb.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/state/statedb.go b/state/statedb.go index 7cbdc641..1f050e12 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -49,6 +49,8 @@ type CommitStateDB struct { logs map[ethcmn.Hash][]*ethtypes.Log logSize uint + // TODO: Determine if we actually need this as we do not need preimages in + // the SDK, but it seems to be used elsewhere in Geth. preimages map[ethcmn.Hash][]byte // DB error. @@ -68,13 +70,16 @@ type CommitStateDB struct { lock sync.Mutex } -// TODO: Make sure storage is prefixed with address!!! - -func NewCommitStateDB(ctx sdk.Context) (*CommitStateDB, error) { +// NewCommitStateDB returns a reference to a newly initialized CommitStateDB +// which implements Geth's state.StateDB interface. +func NewCommitStateDB(ctx sdk.Context, am auth.AccountMapper) (*CommitStateDB, error) { return &CommitStateDB{ + ctx: ctx, + am: am, stateObjects: make(map[ethcmn.Address]*stateObject), stateObjectsDirty: make(map[ethcmn.Address]struct{}), logs: make(map[ethcmn.Hash][]*ethtypes.Log), + preimages: make(map[ethcmn.Hash][]byte), journal: newJournal(), }, nil } From 90a3dd3d46d923d6017a6b675a136bcac8de24d1 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 12:12:06 -0400 Subject: [PATCH 15/49] Update NewCommitStateDB constructor to use state keys --- state/statedb.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state/statedb.go b/state/statedb.go index 1f050e12..8c39707e 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -72,10 +72,12 @@ type CommitStateDB struct { // NewCommitStateDB returns a reference to a newly initialized CommitStateDB // which implements Geth's state.StateDB interface. -func NewCommitStateDB(ctx sdk.Context, am auth.AccountMapper) (*CommitStateDB, error) { +func NewCommitStateDB(ctx sdk.Context, am auth.AccountMapper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { return &CommitStateDB{ ctx: ctx, am: am, + storageKey: storageKey, + codeKey: codeKey, stateObjects: make(map[ethcmn.Address]*stateObject), stateObjectsDirty: make(map[ethcmn.Address]struct{}), logs: make(map[ethcmn.Hash][]*ethtypes.Log), From d4fdf1f47a765304d28a1cb3fe75fb4a75f105a1 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 12:15:16 -0400 Subject: [PATCH 16/49] Update Commit godoc --- state/statedb.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/state/statedb.go b/state/statedb.go index 8c39707e..f603401d 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -306,7 +306,10 @@ func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { // Persistance // ---------------------------------------------------------------------------- -// TODO: Commit writes the state ... +// Commit writes the state to the appropriate KVStores. For each state object +// in the cache, it will either be removed, or have it's code set and/or it's +// state (storage) updated. In addition, the state object (account) itself will +// be written. Finally, the root hash (version) will be returned. func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, err error) { defer csdb.clearJournalAndRefund() From 1d6e70d01fd1500f66495e3462f480730a45bcad Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 16:11:39 -0400 Subject: [PATCH 17/49] Update components as may needing deprecation --- core/chain.go | 2 ++ state/database.go | 2 ++ state/trie.go | 2 ++ 3 files changed, 6 insertions(+) diff --git a/core/chain.go b/core/chain.go index 6a9aee66..d6707950 100644 --- a/core/chain.go +++ b/core/chain.go @@ -1,5 +1,7 @@ package core +// TODO: This functionality and implementation may be deprecated + import ( "math/big" diff --git a/state/database.go b/state/database.go index 46e0a2eb..a8a5da68 100644 --- a/state/database.go +++ b/state/database.go @@ -15,6 +15,8 @@ import ( 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. diff --git a/state/trie.go b/state/trie.go index 7d78042f..6c078c4b 100644 --- a/state/trie.go +++ b/state/trie.go @@ -12,6 +12,8 @@ import ( lru "github.com/hashicorp/golang-lru" ) +// TODO: This functionality and implementation may be deprecated + const ( versionLen = 8 ) From 4ca2b80812e901964f94060df06384c094d59a16 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 16:11:50 -0400 Subject: [PATCH 18/49] Update StateDB godoc --- state/statedb.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/state/statedb.go b/state/statedb.go index f603401d..39f5ce2e 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -72,6 +72,12 @@ type CommitStateDB struct { // NewCommitStateDB returns a reference to a newly initialized CommitStateDB // which implements Geth's state.StateDB interface. +// +// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the +// key/value space matters in determining the merkle root. +// +// TODO: Eventually we'll have an EVM module that'll implement a keeper that we +// can pass into this constructor. func NewCommitStateDB(ctx sdk.Context, am auth.AccountMapper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { return &CommitStateDB{ ctx: ctx, From 4171212fcee342e629375b47c8bbdd0af79ee09b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 16:12:13 -0400 Subject: [PATCH 19/49] Register Account interface with amino in types --- types/wire.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/wire.go b/types/wire.go index ad1f2e30..25212484 100644 --- a/types/wire.go +++ b/types/wire.go @@ -3,6 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" ) var typesCodec = wire.NewCodec() @@ -15,6 +16,7 @@ func init() { // codec. func RegisterWire(codec *wire.Codec) { sdk.RegisterWire(codec) + codec.RegisterInterface((*auth.Account)(nil), nil) codec.RegisterConcrete(&Transaction{}, "types/Transaction", nil) codec.RegisterConcrete(&Account{}, "types/Account", nil) } From fb8529d5b99b09586fc115dfda5c45c5e518d312 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 16:12:32 -0400 Subject: [PATCH 20/49] WIP: Import unit test --- importer/importer_test.go | 175 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 importer/importer_test.go diff --git a/importer/importer_test.go b/importer/importer_test.go new file mode 100644 index 00000000..cb966c51 --- /dev/null +++ b/importer/importer_test.go @@ -0,0 +1,175 @@ +package importer + +import ( + "flag" + "fmt" + "os" + "sort" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/ethermint/state" + "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/bank" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + tmlog "github.com/tendermint/tendermint/libs/log" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethcore "github.com/ethereum/go-ethereum/core" +) + +var ( + datadir string + blockchain string + + miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") + genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0") + + accKey = sdk.NewKVStoreKey("acc") + storageKey = sdk.NewKVStoreKey("storage") + codeKey = sdk.NewKVStoreKey("code") + + logger = tmlog.NewNopLogger() +) + +func init() { + flag.StringVar(&datadir, "datadir", "", "test data directory for state storage") + flag.StringVar(&blockchain, "blockchain", "data/blockchain", "ethereum block export file (blocks to import)") + flag.Parse() +} + +func newTestCodec() *wire.Codec { + codec := wire.NewCodec() + + types.RegisterWire(codec) + wire.RegisterCrypto(codec) + + return codec +} + +func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.AccountMapper) { + genBlock := ethcore.DefaultGenesisBlock() + ms := cms.CacheMultiStore() + ctx := sdk.NewContext(ms, abci.Header{}, false, logger) + + stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) + require.NoError(t, err, "failed to create a StateDB instance") + + // sort the addresses and insertion of key/value pairs matters + genAddrs := make([]string, len(genBlock.Alloc)) + i := 0 + for addr := range genBlock.Alloc { + genAddrs[i] = addr.String() + i++ + } + + sort.Strings(genAddrs) + + for _, addrStr := range genAddrs { + addr := ethcmn.HexToAddress(addrStr) + acc := genBlock.Alloc[addr] + + stateDB.AddBalance(addr, acc.Balance) + stateDB.SetCode(addr, acc.Code) + stateDB.SetNonce(addr, acc.Nonce) + + for key, value := range acc.Storage { + stateDB.SetState(addr, key, value) + } + } + + // get balance of one of the genesis account having 200 ETH + b := stateDB.GetBalance(genInvestor) + require.Equal(t, "200000000000000000000", b.String()) + + // commit the stateDB with 'false' to delete empty objects + // + // NOTE: Commit does not yet return the intra merkle root (version) + _, err = stateDB.Commit(false) + require.NoError(t, err) + + // persist multi-store cache state + ms.Write() + + // persist multi-store root state + commitID := cms.Commit() + require.Equal(t, "F162678AD57BBE352BE0CFCFCD90E394C4781D31", fmt.Sprintf("%X", commitID.Hash)) + + // verify account mapper state + genAcc := am.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) + require.NotNil(t, genAcc) + require.Equal(t, sdk.NewIntFromBigInt(b), genAcc.GetCoins().AmountOf(bank.DenomEthereum)) +} + +func TestImportBlocks(t *testing.T) { + if datadir == "" { + datadir = os.TempDir() + } + + db := dbm.NewDB("state", dbm.LevelDBBackend, datadir) + + defer func() { + db.Close() + os.RemoveAll(datadir) + }() + + // create logger, codec and root multi-store + cdc := newTestCodec() + + cms := store.NewCommitMultiStore(db) + + // create account mapper + am := auth.NewAccountMapper( + cdc, + accKey, + types.ProtoBaseAccount, + ) + + // mount stores + keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey} + for _, key := range keys { + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil) + } + + // load latest version (root) + err := cms.LoadLatestVersion() + require.NoError(t, err) + + // set and test genesis block + createAndTestGenesis(t, cms, am) + + // // process blocks + // for block := range blocks { + // // Create a cached-wrapped multi-store based on the commit multi-store and + // // create a new context based off of that. + // ms := cms.CacheMultiStore() + // ctx := sdk.NewContext(ms, abci.Header{}, false, logger) + + // // For each transaction, create a new cache-wrapped multi-store based off of + // // the existing cache-wrapped multi-store in to create a transient store in + // // case processing the tx fails. + // for tx := range block.txs { + // msCache := ms.CacheMultiStore() + // ctx = ctx.WithMultiStore(msCache) + + // // apply tx + + // // check error + // if err != nil { + // msCache.Write() + // } + // } + + // // commit + // ms.Write() + // cms.Commit() + // } +} From f07b741388546d9305ae63a78f764e340ef975f2 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 17:40:30 -0400 Subject: [PATCH 21/49] Implement importer test --- importer/importer_test.go | 167 +++++++++++++++++++++++++++++--------- 1 file changed, 128 insertions(+), 39 deletions(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index cb966c51..3f708995 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -1,34 +1,46 @@ package importer import ( + "bytes" "flag" "fmt" + "io" "os" + "os/signal" + "runtime/pprof" "sort" + "syscall" "testing" - - "github.com/stretchr/testify/require" + "time" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/ethermint/core" "github.com/cosmos/ethermint/state" "github.com/cosmos/ethermint/types" "github.com/cosmos/ethermint/x/bank" + ethcmn "github.com/ethereum/go-ethereum/common" + ethcore "github.com/ethereum/go-ethereum/core" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethvm "github.com/ethereum/go-ethereum/core/vm" + ethparams "github.com/ethereum/go-ethereum/params" + ethrlp "github.com/ethereum/go-ethereum/rlp" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" tmlog "github.com/tendermint/tendermint/libs/log" - - ethcmn "github.com/ethereum/go-ethereum/common" - ethcore "github.com/ethereum/go-ethereum/core" ) var ( - datadir string - blockchain string + flagDataDir string + flagBlockchain string + flagCPUProfile string miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0") @@ -41,8 +53,9 @@ var ( ) func init() { - flag.StringVar(&datadir, "datadir", "", "test data directory for state storage") - flag.StringVar(&blockchain, "blockchain", "data/blockchain", "ethereum block export file (blocks to import)") + flag.StringVar(&flagCPUProfile, "cpu-profile", "", "write CPU profile") + flag.StringVar(&flagDataDir, "datadir", "", "test data directory for state storage") + flag.StringVar(&flagBlockchain, "blockchain", "data/blockchain", "ethereum block export file (blocks to import)") flag.Parse() } @@ -110,20 +123,29 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.Accoun } func TestImportBlocks(t *testing.T) { - if datadir == "" { - datadir = os.TempDir() + if flagDataDir == "" { + flagDataDir = os.TempDir() } - db := dbm.NewDB("state", dbm.LevelDBBackend, datadir) + if flagCPUProfile != "" { + f, err := os.Create(flagCPUProfile) + require.NoError(t, err, "failed to create CPU profile") - defer func() { - db.Close() - os.RemoveAll(datadir) - }() + err = pprof.StartCPUProfile(f) + require.NoError(t, err, "failed to start CPU profile") + } + + db := dbm.NewDB("state", dbm.LevelDBBackend, flagDataDir) + cb := func() { + fmt.Println("cleaning up") + os.RemoveAll(flagDataDir) + pprof.StopCPUProfile() + } + + trapSignal(cb) // create logger, codec and root multi-store cdc := newTestCodec() - cms := store.NewCommitMultiStore(db) // create account mapper @@ -146,30 +168,97 @@ func TestImportBlocks(t *testing.T) { // set and test genesis block createAndTestGenesis(t, cms, am) - // // process blocks - // for block := range blocks { - // // Create a cached-wrapped multi-store based on the commit multi-store and - // // create a new context based off of that. - // ms := cms.CacheMultiStore() - // ctx := sdk.NewContext(ms, abci.Header{}, false, logger) + // open blockchain export file + blockchainInput, err := os.Open(flagBlockchain) + require.Nil(t, err) - // // For each transaction, create a new cache-wrapped multi-store based off of - // // the existing cache-wrapped multi-store in to create a transient store in - // // case processing the tx fails. - // for tx := range block.txs { - // msCache := ms.CacheMultiStore() - // ctx = ctx.WithMultiStore(msCache) + defer blockchainInput.Close() - // // apply tx + // ethereum mainnet config + chainContext := core.NewChainContext() + vmConfig := ethvm.Config{} + chainConfig := ethparams.MainnetChainConfig - // // check error - // if err != nil { - // msCache.Write() - // } - // } + // create RLP stream for exported blocks + stream := ethrlp.NewStream(blockchainInput, 0) + startTime := time.Now() - // // commit - // ms.Write() - // cms.Commit() - // } + var block ethtypes.Block + for { + err = stream.Decode(&block) + if err == io.EOF { + break + } + + require.NoError(t, err, "failed to decode block") + + var ( + usedGas = new(uint64) + gp = new(ethcore.GasPool).AddGas(block.GasLimit()) + ) + + header := block.Header() + chainContext.Coinbase = header.Coinbase + + chainContext.SetHeader(block.NumberU64(), header) + + // Create a cached-wrapped multi-store based on the commit multi-store and + // create a new context based off of that. + ms := cms.CacheMultiStore() + ctx := sdk.NewContext(ms, abci.Header{}, false, logger) + + // stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) + // require.NoError(t, err, "failed to create a StateDB instance") + + // if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { + // ethmisc.ApplyDAOHardFork(stateDB) + // } + + for i, tx := range block.Transactions() { + msCache := ms.CacheMultiStore() + ctx = ctx.WithMultiStore(msCache) + + stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) + require.NoError(t, err, "failed to create a StateDB instance") + + stateDB.Prepare(tx.Hash(), block.Hash(), i) + + txHash := tx.Hash() + if bytes.Equal(txHash[:], ethcmn.FromHex("0xc438cfcc3b74a28741bda361032f1c6362c34aa0e1cedff693f31ec7d6a12717")) { + vmConfig.Tracer = ethvm.NewStructLogger(ðvm.LogConfig{}) + vmConfig.Debug = true + } + + _, _, err = ethcore.ApplyTransaction( + chainConfig, chainContext, nil, gp, stateDB, header, tx, usedGas, vmConfig, + ) + require.NoError(t, err, "failed to apply tx at block %d; tx: %d", block.NumberU64(), tx.Hash()) + + msCache.Write() + } + + // commit + ms.Write() + cms.Commit() + + if block.NumberU64() > 0 && block.NumberU64()%1000 == 0 { + fmt.Printf("processed block: %d (time so far: %v)\n", block.NumberU64(), time.Since(startTime)) + } + } +} + +func trapSignal(cb func()) { + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) + + go func() { + recv := <-c + fmt.Printf("existing; signal: %s\n", recv) + + if cb != nil { + cb() + } + + os.Exit(0) + }() } From ef10c845c7f2c6aad76ef6a89327af0cf10e8786 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 17:51:19 -0400 Subject: [PATCH 22/49] Update importer test to not prune --- importer/importer_test.go | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 3f708995..3a6fa0e2 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -6,10 +6,8 @@ import ( "fmt" "io" "os" - "os/signal" "runtime/pprof" "sort" - "syscall" "testing" "time" @@ -136,13 +134,11 @@ func TestImportBlocks(t *testing.T) { } db := dbm.NewDB("state", dbm.LevelDBBackend, flagDataDir) - cb := func() { + defer func() { fmt.Println("cleaning up") os.RemoveAll(flagDataDir) pprof.StopCPUProfile() - } - - trapSignal(cb) + }() // create logger, codec and root multi-store cdc := newTestCodec() @@ -161,6 +157,8 @@ func TestImportBlocks(t *testing.T) { cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil) } + cms.SetPruning(sdk.PruneNothing) + // load latest version (root) err := cms.LoadLatestVersion() require.NoError(t, err) @@ -232,7 +230,7 @@ func TestImportBlocks(t *testing.T) { _, _, err = ethcore.ApplyTransaction( chainConfig, chainContext, nil, gp, stateDB, header, tx, usedGas, vmConfig, ) - require.NoError(t, err, "failed to apply tx at block %d; tx: %d", block.NumberU64(), tx.Hash()) + require.NoError(t, err, "failed to apply tx at block %d; tx: %X", block.NumberU64(), tx.Hash()) msCache.Write() } @@ -246,19 +244,3 @@ func TestImportBlocks(t *testing.T) { } } } - -func trapSignal(cb func()) { - c := make(chan os.Signal) - signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) - - go func() { - recv := <-c - fmt.Printf("existing; signal: %s\n", recv) - - if cb != nil { - cb() - } - - os.Exit(0) - }() -} From 0c3975ef5c34b021ded95f3d6884a65d30d486e6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 4 Oct 2018 17:52:55 -0400 Subject: [PATCH 23/49] Remove extra code --- importer/importer_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 3a6fa0e2..88346c41 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -1,7 +1,6 @@ package importer import ( - "bytes" "flag" "fmt" "io" @@ -221,12 +220,6 @@ func TestImportBlocks(t *testing.T) { stateDB.Prepare(tx.Hash(), block.Hash(), i) - txHash := tx.Hash() - if bytes.Equal(txHash[:], ethcmn.FromHex("0xc438cfcc3b74a28741bda361032f1c6362c34aa0e1cedff693f31ec7d6a12717")) { - vmConfig.Tracer = ethvm.NewStructLogger(ðvm.LogConfig{}) - vmConfig.Debug = true - } - _, _, err = ethcore.ApplyTransaction( chainConfig, chainContext, nil, gp, stateDB, header, tx, usedGas, vmConfig, ) From f938f74cf08ca8c00efa18f17ab9345bdea832be Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 22 Oct 2018 22:30:13 -0400 Subject: [PATCH 24/49] Fix invalid code commitment --- state/state_object.go | 9 +++++++-- state/statedb.go | 30 ++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index b4e1a95c..45d6108a 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -222,6 +222,13 @@ func (so *stateObject) commitState() { // TODO: Set the account (storage) root (but we probably don't need this) } +// commitCode persists the state object's code to the KVStore. +func (so *stateObject) commitCode() { + ctx := so.stateDB.ctx + store := ctx.KVStore(so.stateDB.codeKey) + store.Set(so.CodeHash(), so.code) +} + // ---------------------------------------------------------------------------- // Getters // ---------------------------------------------------------------------------- @@ -299,8 +306,6 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e store := ctx.KVStore(so.stateDB.storageKey) rawValue := store.Get(prefixKey.Bytes()) - // TODO: Do we need to RLP split/decode? - if len(rawValue) > 0 { value.SetBytes(rawValue) } diff --git a/state/statedb.go b/state/statedb.go index 39f5ce2e..abd318fd 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -327,6 +327,7 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, er // set the state objects for addr, so := range csdb.stateObjects { _, isDirty := csdb.stateObjectsDirty[addr] + switch { case so.suicided || (isDirty && deleteEmptyObjects && so.empty()): // If the state object has been removed, don't bother syncing it and just @@ -336,7 +337,7 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, er case isDirty: // write any contract code associated with the state object if so.code != nil && so.dirtyCode { - csdb.SetCode(so.Address(), so.code) + so.commitCode() so.dirtyCode = false } @@ -415,7 +416,15 @@ func (csdb *CommitStateDB) deleteStateObject(so *stateObject) { func (csdb *CommitStateDB) Snapshot() int { id := csdb.nextRevisionID csdb.nextRevisionID++ - csdb.validRevisions = append(csdb.validRevisions, ethstate.Revision{id, csdb.journal.length()}) + + csdb.validRevisions = append( + csdb.validRevisions, + ethstate.Revision{ + ID: id, + JournalIndex: csdb.journal.length(), + }, + ) + return id } @@ -665,12 +674,12 @@ func (csdb *CommitStateDB) setError(err error) { // Returns nil and sets an error if not found. func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { // prefer 'live' (cached) objects - if obj := csdb.stateObjects[addr]; obj != nil { - if obj.deleted { + if so := csdb.stateObjects[addr]; so != nil { + if so.deleted { return nil } - return obj + return so } // otherwise, attempt to fetch the account from the account mapper @@ -681,11 +690,12 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta } // insert the state object into the live set - obj := newObject(csdb, acc) - csdb.setStateObject(obj) - return obj + so := newObject(csdb, acc) + csdb.setStateObject(so) + + return so } -func (csdb *CommitStateDB) setStateObject(object *stateObject) { - csdb.stateObjects[object.Address()] = object +func (csdb *CommitStateDB) setStateObject(so *stateObject) { + csdb.stateObjects[so.Address()] = so } From 645a0118e233680597210e19740afe982d321b66 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 22 Oct 2018 22:31:09 -0400 Subject: [PATCH 25/49] Remove dup kvPair in ethDB test --- state/ethdb_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/state/ethdb_test.go b/state/ethdb_test.go index 626be049..d27fbc2d 100644 --- a/state/ethdb_test.go +++ b/state/ethdb_test.go @@ -12,12 +12,6 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" ) -type ( - kvPair struct { - key, value []byte - } -) - func newEthereumDB() *EthereumDB { memDB := dbm.NewMemDB() return &EthereumDB{CodeDB: memDB} From 13c627f6cab9112054d8a5418cc490faaa75bfaf Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 22 Oct 2018 22:45:57 -0400 Subject: [PATCH 26/49] Wrap up importer test --- importer/importer_test.go | 72 +++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 88346c41..86a156db 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "io" + "math/big" "os" "runtime/pprof" "sort" @@ -21,6 +22,8 @@ import ( "github.com/cosmos/ethermint/x/bank" ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + ethmisc "github.com/ethereum/go-ethereum/consensus/misc" ethcore "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" ethvm "github.com/ethereum/go-ethereum/core/vm" @@ -47,6 +50,9 @@ var ( codeKey = sdk.NewKVStoreKey("code") logger = tmlog.NewNopLogger() + + rewardBig8 = big.NewInt(8) + rewardBig32 = big.NewInt(32) ) func init() { @@ -203,37 +209,75 @@ func TestImportBlocks(t *testing.T) { // create a new context based off of that. ms := cms.CacheMultiStore() ctx := sdk.NewContext(ms, abci.Header{}, false, logger) + ctx = ctx.WithBlockHeight(int64(block.NumberU64())) - // stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) - // require.NoError(t, err, "failed to create a StateDB instance") + stateDB := createStateDB(t, ctx, am) - // if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { - // ethmisc.ApplyDAOHardFork(stateDB) - // } + if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { + ethmisc.ApplyDAOHardFork(stateDB) + } for i, tx := range block.Transactions() { - msCache := ms.CacheMultiStore() - ctx = ctx.WithMultiStore(msCache) - - stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) - require.NoError(t, err, "failed to create a StateDB instance") - stateDB.Prepare(tx.Hash(), block.Hash(), i) _, _, err = ethcore.ApplyTransaction( chainConfig, chainContext, nil, gp, stateDB, header, tx, usedGas, vmConfig, ) require.NoError(t, err, "failed to apply tx at block %d; tx: %X", block.NumberU64(), tx.Hash()) - - msCache.Write() } - // commit + // apply mining rewards + accumulateRewards(chainConfig, stateDB, header, block.Uncles()) + + // commit stateDB + _, err := stateDB.Commit(chainConfig.IsEIP158(block.Number())) + require.NoError(t, err, "failed to commit StateDB") + + // simulate BaseApp EndBlocker commitment ms.Write() cms.Commit() + // block debugging output if block.NumberU64() > 0 && block.NumberU64()%1000 == 0 { fmt.Printf("processed block: %d (time so far: %v)\n", block.NumberU64(), time.Since(startTime)) } } } + +func createStateDB(t *testing.T, ctx sdk.Context, am auth.AccountMapper) *state.CommitStateDB { + stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) + require.NoError(t, err, "failed to create a StateDB instance") + + return stateDB +} + +// accumulateRewards credits the coinbase of the given block with the mining +// reward. The total reward consists of the static block reward and rewards for +// included uncles. The coinbase of each uncle block is also rewarded. +func accumulateRewards( + config *ethparams.ChainConfig, stateDB *state.CommitStateDB, + header *ethtypes.Header, uncles []*ethtypes.Header, +) { + + // select the correct block reward based on chain progression + blockReward := ethash.FrontierBlockReward + if config.IsByzantium(header.Number) { + blockReward = ethash.ByzantiumBlockReward + } + + // accumulate the rewards for the miner and any included uncles + reward := new(big.Int).Set(blockReward) + r := new(big.Int) + + for _, uncle := range uncles { + r.Add(uncle.Number, rewardBig8) + r.Sub(r, header.Number) + r.Mul(r, blockReward) + r.Div(r, rewardBig8) + stateDB.AddBalance(uncle.Coinbase, r) + r.Div(blockReward, rewardBig32) + reward.Add(reward, r) + } + + stateDB.AddBalance(header.Coinbase, reward) +} From c0489c86dac14d65055de29f61b521e21c9f2430 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 22 Oct 2018 22:46:29 -0400 Subject: [PATCH 27/49] Remove old test importer --- test/importer/doc.go | 8 -- test/importer/importer.go | 247 -------------------------------------- test/importer/log.go | 66 ---------- test/importer/utils.go | 43 ------- test/run.go | 81 ------------- 5 files changed, 445 deletions(-) delete mode 100644 test/importer/doc.go delete mode 100644 test/importer/importer.go delete mode 100644 test/importer/log.go delete mode 100644 test/importer/utils.go delete mode 100644 test/run.go diff --git a/test/importer/doc.go b/test/importer/doc.go deleted file mode 100644 index 0993adc4..00000000 --- a/test/importer/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// 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. -// -// Note: Some code is directly copied from go-ethereum. - -package importer diff --git a/test/importer/importer.go b/test/importer/importer.go deleted file mode 100644 index 700d69eb..00000000 --- a/test/importer/importer.go +++ /dev/null @@ -1,247 +0,0 @@ -package importer - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "fmt" - "io" - "os" - "path" - "time" - - "github.com/cosmos/ethermint/core" - "github.com/cosmos/ethermint/state" - - ethcommon "github.com/ethereum/go-ethereum/common" - ethmisc "github.com/ethereum/go-ethereum/consensus/misc" - ethcore "github.com/ethereum/go-ethereum/core" - ethstate "github.com/ethereum/go-ethereum/core/state" - ethtypes "github.com/ethereum/go-ethereum/core/types" - ethvm "github.com/ethereum/go-ethereum/core/vm" - ethparams "github.com/ethereum/go-ethereum/params" - ethrlp "github.com/ethereum/go-ethereum/rlp" -) - -var ( - miner501 = ethcommon.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") - genInvestor = ethcommon.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0") -) - -// Importer implements a structure to facilitate testing of importing mainnet -// Ethereum blocks and transactions using the Cosmos SDK components and the -// EVM. -type Importer struct { - EthermintDB *state.Database - BlockchainFile string - Datadir string - InterruptCh <-chan bool -} - -// Import performs an import given an Importer that has a Geth stateDB -// implementation and a blockchain exported file. -// nolint -func (imp *Importer) Import() { - // only create genesis if it is a brand new database - if imp.EthermintDB.LatestVersion() == 0 { - // start with empty root hash (i.e. empty state) - gethStateDB, err := ethstate.New(ethcommon.Hash{}, imp.EthermintDB) - if err != nil { - panic(fmt.Sprintf("failed to instantiate geth state.StateDB: %v", err)) - } - - genBlock := ethcore.DefaultGenesisBlock() - for addr, account := range genBlock.Alloc { - gethStateDB.AddBalance(addr, account.Balance) - gethStateDB.SetCode(addr, account.Code) - gethStateDB.SetNonce(addr, account.Nonce) - - for key, value := range account.Storage { - gethStateDB.SetState(addr, key, value) - } - } - - // get balance of one of the genesis account having 200 ETH - b := gethStateDB.GetBalance(genInvestor) - fmt.Printf("balance of %s: %s\n", genInvestor.String(), b) - - // commit the geth stateDB with 'false' to delete empty objects - genRoot, err := gethStateDB.Commit(false) - if err != nil { - panic(err) - } - - commitID := imp.EthermintDB.Commit() - - fmt.Printf("commitID after genesis: %v\n", commitID) - fmt.Printf("genesis state root hash: %x\n", genRoot[:]) - } - - // file with blockchain data exported from geth by using "geth exportdb" - // command. - input, err := os.Open(imp.BlockchainFile) - if err != nil { - panic(err) - } - - defer input.Close() - - // ethereum mainnet config - chainConfig := ethparams.MainnetChainConfig - - // create RLP stream for exported blocks - stream := ethrlp.NewStream(input, 0) - - var ( - block ethtypes.Block - root500 ethcommon.Hash // root hash after block 500 - root501 ethcommon.Hash // root hash after block 501 - ) - - var prevRoot ethcommon.Hash - binary.BigEndian.PutUint64(prevRoot[:8], uint64(imp.EthermintDB.LatestVersion())) - - imp.EthermintDB.Tracing = true - chainContext := core.NewChainContext() - vmConfig := ethvm.Config{} - - n := 0 - startTime := time.Now() - interrupt := false - - var lastSkipped uint64 - for !interrupt { - if err = stream.Decode(&block); err == io.EOF { - err = nil - break - } else if err != nil { - panic(fmt.Errorf("failed to decode at block %d: %s", block.NumberU64(), err)) - } - - // don't import blocks already imported - if block.NumberU64() < uint64(imp.EthermintDB.LatestVersion()) { - lastSkipped = block.NumberU64() - continue - } - - if lastSkipped > 0 { - fmt.Printf("skipped blocks up to %d\n", lastSkipped) - lastSkipped = 0 - } - - header := block.Header() - chainContext.Coinbase = header.Coinbase - chainContext.SetHeader(block.NumberU64(), header) - - gethStateDB, err := ethstate.New(prevRoot, imp.EthermintDB) - if err != nil { - panic(fmt.Errorf("failed to instantiate geth state.StateDB at block %d: %v", block.NumberU64(), err)) - } - - var ( - receipts ethtypes.Receipts - usedGas = new(uint64) - allLogs []*ethtypes.Log - gp = new(ethcore.GasPool).AddGas(block.GasLimit()) - ) - - if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { - ethmisc.ApplyDAOHardFork(gethStateDB) - } - - for i, tx := range block.Transactions() { - gethStateDB.Prepare(tx.Hash(), block.Hash(), i) - - txHash := tx.Hash() - if bytes.Equal(txHash[:], ethcommon.FromHex("0xc438cfcc3b74a28741bda361032f1c6362c34aa0e1cedff693f31ec7d6a12717")) { - vmConfig.Tracer = ethvm.NewStructLogger(ðvm.LogConfig{}) - vmConfig.Debug = true - } - - receipt, _, err := ethcore.ApplyTransaction(chainConfig, chainContext, nil, gp, gethStateDB, header, tx, usedGas, vmConfig) - if vmConfig.Tracer != nil { - w, err := os.Create(path.Join(imp.Datadir, "structlogs.txt")) - if err != nil { - panic(fmt.Sprintf("failed to create file for VM tracing: %v", err)) - } - - encoder := json.NewEncoder(w) - logs := FormatLogs(vmConfig.Tracer.(*ethvm.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 geth stateDB - accumulateRewards(chainConfig, gethStateDB, header, block.Uncles()) - - // commit block in geth - prevRoot, err = gethStateDB.Commit(chainConfig.IsEIP158(block.Number())) - if err != nil { - panic(fmt.Errorf("at block %d: %v", block.NumberU64(), err)) - } - - // commit block in Ethermint - imp.EthermintDB.Commit() - - switch block.NumberU64() { - case 500: - root500 = prevRoot - case 501: - root501 = prevRoot - } - - n++ - if (n % 10000) == 0 { - fmt.Printf("processed %d blocks, time so far: %v\n", n, time.Since(startTime)) - } - - // Check for interrupts - select { - case interrupt = <-imp.InterruptCh: - fmt.Println("interrupted, please wait for cleanup...") - default: - } - } - - fmt.Printf("processed %d blocks\n", n) - - imp.EthermintDB.Tracing = true - - // try to create a new geth stateDB from root of the block 500 - fmt.Printf("root at block 500: %x\n", root500[:]) - - state500, err := ethstate.New(root500, imp.EthermintDB) - if err != nil { - panic(err) - } - - miner501BalanceAt500 := state500.GetBalance(miner501) - - state501, err := ethstate.New(root501, imp.EthermintDB) - if err != nil { - panic(err) - } - - miner501BalanceAt501 := state501.GetBalance(miner501) - - fmt.Printf("investor's balance after block 500: %d\n", state500.GetBalance(genInvestor)) - fmt.Printf("miner of block 501's balance after block 500: %d\n", miner501BalanceAt500) - fmt.Printf("miner of block 501's balance after block 501: %d\n", miner501BalanceAt501) -} diff --git a/test/importer/log.go b/test/importer/log.go deleted file mode 100644 index d8f6c137..00000000 --- a/test/importer/log.go +++ /dev/null @@ -1,66 +0,0 @@ -package importer - -import ( - "fmt" - - ethmath "github.com/ethereum/go-ethereum/common/math" - ethvm "github.com/ethereum/go-ethereum/core/vm" -) - -// StructLogRes stores a structured log emitted by the EVM while replaying a -// transaction in debug mode. -type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error error `json:"error,omitempty"` - Stack *[]string `json:"stack,omitempty"` - Memory *[]string `json:"memory,omitempty"` - Storage *map[string]string `json:"storage,omitempty"` -} - -// FormatLogs formats EVM returned structured logs for json output. -func FormatLogs(logs []ethvm.StructLog) []StructLogRes { - formatted := make([]StructLogRes, len(logs)) - for index, trace := range logs { - formatted[index] = StructLogRes{ - Pc: trace.Pc, - Op: trace.Op.String(), - Gas: trace.Gas, - GasCost: trace.GasCost, - Depth: trace.Depth, - Error: trace.Err, - } - - if trace.Stack != nil { - stack := make([]string, len(trace.Stack)) - for i, stackValue := range trace.Stack { - stack[i] = fmt.Sprintf("%x", ethmath.PaddedBigBytes(stackValue, 32)) - } - - formatted[index].Stack = &stack - } - - if trace.Memory != nil { - memory := make([]string, 0, (len(trace.Memory)+31)/32) - for i := 0; i+32 <= len(trace.Memory); i += 32 { - memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) - } - - formatted[index].Memory = &memory - } - - if trace.Storage != nil { - storage := make(map[string]string) - for i, storageValue := range trace.Storage { - storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) - } - - formatted[index].Storage = &storage - } - } - - return formatted -} diff --git a/test/importer/utils.go b/test/importer/utils.go deleted file mode 100644 index a70e783d..00000000 --- a/test/importer/utils.go +++ /dev/null @@ -1,43 +0,0 @@ -package importer - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/consensus/ethash" - ethstate "github.com/ethereum/go-ethereum/core/state" - ethtypes "github.com/ethereum/go-ethereum/core/types" - ethparams "github.com/ethereum/go-ethereum/params" -) - -// Some weird constants to avoid constant memory allocs for them. -var ( - big8 = big.NewInt(8) - big32 = big.NewInt(32) -) - -// accumulateRewards credits the coinbase of the given block with the mining -// reward. The total reward consists of the static block reward and rewards for -// included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *ethparams.ChainConfig, state ethstate.StateDB, header *ethtypes.Header, uncles []*ethtypes.Header) { - // select the correct block reward based on chain progression - blockReward := ethash.FrontierBlockReward - if config.IsByzantium(header.Number) { - blockReward = ethash.ByzantiumBlockReward - } - - // accumulate the rewards for the miner and any included uncles - reward := new(big.Int).Set(blockReward) - r := new(big.Int) - - for _, uncle := range uncles { - r.Add(uncle.Number, big8) - r.Sub(r, header.Number) - r.Mul(r, blockReward) - r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) - r.Div(blockReward, big32) - reward.Add(reward, r) - } - - state.AddBalance(header.Coinbase, reward) -} diff --git a/test/run.go b/test/run.go deleted file mode 100644 index 06518011..00000000 --- a/test/run.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "os/signal" - "path" - "runtime/pprof" - "syscall" - - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ethermint/state" - "github.com/cosmos/ethermint/test/importer" - "github.com/cosmos/ethermint/types" - - dbm "github.com/tendermint/tendermint/libs/db" -) - -var ( - cpuprofile = flag.String("cpu-profile", "", "write cpu profile `file`") - blockchain = flag.String("blockchain", "data/blockchain", "file containing blocks to load") - datadir = flag.String("datadir", path.Join(os.Getenv("HOME"), ".ethermint"), "directory for ethermint data") - cachesize = flag.Int("cachesize", 1024*1024, "number of key-value pairs for the state stored in memory") -) - -func main() { - flag.Parse() - - if *cpuprofile != "" { - f, err := os.Create(*cpuprofile) - if err != nil { - fmt.Printf("could not create CPU profile: %v\n", err) - return - } - - if err := pprof.StartCPUProfile(f); err != nil { - fmt.Printf("could not start CPU profile: %v\n", err) - return - } - - defer pprof.StopCPUProfile() - } - - sigs := make(chan os.Signal, 1) - interruptCh := make(chan bool, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - - go func() { - <-sigs - interruptCh <- true - }() - - stateDB := dbm.NewDB("state", dbm.LevelDBBackend, *datadir) - codeDB := dbm.NewDB("code", dbm.LevelDBBackend, *datadir) - - cms := store.NewCommitMultiStore(stateDB) - cms.SetPruning(sdk.PruneNothing) - cms.MountStoreWithDB(types.StoreKeyAccount, sdk.StoreTypeIAVL, nil) - cms.MountStoreWithDB(types.StoreKeyStorage, sdk.StoreTypeIAVL, nil) - - if err := cms.LoadLatestVersion(); err != nil { - panic(fmt.Sprintf("failed to load state store: %v", err)) - } - - ethermintDB, err := state.NewDatabase(cms, codeDB, *cachesize) - if err != nil { - panic(fmt.Sprintf("failed to initialize geth Database: %v", err)) - } - - importer := importer.Importer{ - EthermintDB: ethermintDB, - BlockchainFile: *blockchain, - Datadir: *datadir, - InterruptCh: interruptCh, - } - - importer.Import() -} From f82443080baedf5fb14f88aacf1ef4a9b6aecff6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Oct 2018 11:08:24 -0400 Subject: [PATCH 28/49] Remove old deprecated state files --- state/database.go | 208 --------------------------------- state/database_test.go | 110 ------------------ state/ethdb.go | 66 ----------- state/ethdb_test.go | 141 ----------------------- state/test_utils.go | 47 -------- state/trie.go | 210 --------------------------------- state/trie_test.go | 256 ----------------------------------------- x/.keep | 0 8 files changed, 1038 deletions(-) delete mode 100644 state/database.go delete mode 100644 state/database_test.go delete mode 100644 state/ethdb.go delete mode 100644 state/ethdb_test.go delete mode 100644 state/test_utils.go delete mode 100644 state/trie.go delete mode 100644 state/trie_test.go delete mode 100644 x/.keep diff --git a/state/database.go b/state/database.go deleted file mode 100644 index a8a5da68..00000000 --- a/state/database.go +++ /dev/null @@ -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{} -} diff --git a/state/database_test.go b/state/database_test.go deleted file mode 100644 index b300d33d..00000000 --- a/state/database_test.go +++ /dev/null @@ -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) -} diff --git a/state/ethdb.go b/state/ethdb.go deleted file mode 100644 index f05e3fa9..00000000 --- a/state/ethdb.go +++ /dev/null @@ -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() { -} diff --git a/state/ethdb_test.go b/state/ethdb_test.go deleted file mode 100644 index d27fbc2d..00000000 --- a/state/ethdb_test.go +++ /dev/null @@ -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) -} diff --git a/state/test_utils.go b/state/test_utils.go deleted file mode 100644 index 105e7931..00000000 --- a/state/test_utils.go +++ /dev/null @@ -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 -} diff --git a/state/trie.go b/state/trie.go deleted file mode 100644 index 6c078c4b..00000000 --- a/state/trie.go +++ /dev/null @@ -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 -} diff --git a/state/trie_test.go b/state/trie_test.go deleted file mode 100644 index 51a30787..00000000 --- a/state/trie_test.go +++ /dev/null @@ -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() -} diff --git a/x/.keep b/x/.keep deleted file mode 100644 index e69de29b..00000000 From 071d6c9ff934589b63834bec5f95dfcf41f93764 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Oct 2018 11:08:52 -0400 Subject: [PATCH 29/49] Move blockchain export file to /importer --- {data => importer}/blockchain | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename {data => importer}/blockchain (100%) diff --git a/data/blockchain b/importer/blockchain similarity index 100% rename from data/blockchain rename to importer/blockchain From c71b6d35ff5252ff40a135199784498ea10273fd Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Oct 2018 11:09:03 -0400 Subject: [PATCH 30/49] Update importer test --- importer/importer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 86a156db..4b5121a9 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -58,7 +58,7 @@ var ( func init() { flag.StringVar(&flagCPUProfile, "cpu-profile", "", "write CPU profile") flag.StringVar(&flagDataDir, "datadir", "", "test data directory for state storage") - flag.StringVar(&flagBlockchain, "blockchain", "data/blockchain", "ethereum block export file (blocks to import)") + flag.StringVar(&flagBlockchain, "blockchain", "blockchain", "ethereum block export file (blocks to import)") flag.Parse() } From 22eef2aa2029dd27f8441116598405f4f8132b83 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 08:20:59 -0400 Subject: [PATCH 31/49] Add test-import to makefile --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8c03eadc..f44c3adf 100644 --- a/Makefile +++ b/Makefile @@ -147,6 +147,10 @@ test-lint: @echo "--> Running gometalinter..." @gometalinter.v2 --config=gometalinter.json --exclude=vendor ./... +test-import: + @go test ./importer -v --vet=off --run=TestImportBlocks --datadir tmp \ + --blockchain blockchain --timeout=5m + godocs: @echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint" godoc -http=:6060 @@ -162,4 +166,4 @@ format: @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs misspell -w .PHONY: build install update-tools tools deps godocs clean format test-lint \ -test-cli test-race test-unit test +test-cli test-race test-unit test test-import From e47d1a4dc8e1646fab2a2d680c90e2565d185643 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 08:21:14 -0400 Subject: [PATCH 32/49] Fix invalid code hash algo --- state/state_object.go | 7 +++---- state/statedb.go | 6 ++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/state/state_object.go b/state/state_object.go index 45d6108a..a9ae9cf4 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -8,17 +8,16 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" auth "github.com/cosmos/cosmos-sdk/x/auth" - tmcrypto "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ) var ( _ ethstate.StateObject = (*stateObject)(nil) - emptyCodeHash = tmcrypto.Sha256(nil) + emptyCodeHash = ethcrypto.Keccak256(nil) ) type ( @@ -363,5 +362,5 @@ func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { copy(compositeKey, prefix) copy(compositeKey[len(prefix):], key) - return ethcmn.BytesToHash(tmcrypto.Sha256(compositeKey)) + return ethcrypto.Keccak256Hash(compositeKey) } diff --git a/state/statedb.go b/state/statedb.go index abd318fd..cfbe50d9 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -9,11 +9,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - tmcrypto "github.com/tendermint/tendermint/crypto" - ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ) var ( @@ -140,8 +139,7 @@ func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash) func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) { so := csdb.GetOrNewStateObject(addr) if so != nil { - key := ethcmn.BytesToHash(tmcrypto.Sha256(code)) - so.SetCode(key, code) + so.SetCode(ethcrypto.Keccak256Hash(code), code) } } From 9b11fe88bd07f119df860f6ba82cb0c3f3553eb9 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 08:21:34 -0400 Subject: [PATCH 33/49] Update README --- README.md | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 25731bd0..ebc269ea 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,32 @@ [![CircleCI](https://circleci.com/gh/cosmos/ethermint.svg?style=svg)](https://circleci.com/gh/cosmos/ethermint) [![](https://godoc.org/github.com/cosmos/ethermint?status.svg)](http://godoc.org/github.com/cosmos/ethermint) [![Go Report Card](https://goreportcard.com/badge/github.com/cosmos/ethermint)](https://goreportcard.com/report/github.com/cosmos/ethermint) + # Ethermint __**WARNING:**__ Ethermint is under VERY ACTIVE DEVELOPMENT and should be treated as pre-alpha software. This means it is not meant to be run in production, its APIs are subject to change without warning and should not be relied upon, and it should not be used to hold any value. We will remove this warning when we have a release that is stable, secure, and properly tested. -### What is it? +## What is it? `ethermint` will be an implementation of the EVM that runs on top of [`tendermint`](https://github.com/tendermint/tendermint) consensus, a Proof of Stake system. This project has as its primary goals: - [Hard Spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) enablement: This is the ability to take a token from the Ethereum mainnet and "spoon" (shift) the balances over to another network. This feature is intended to make it easy for applications that require more transactions than the Ethereum main chain can provide to move their code over to a compatible chain with much more capacity. -- Web3 Compatibility: In order enable applications to be moved over to an ethermint chain existing tooling (i.e. web3 compatable clients) need to be able to interact with `ethermint`. +- Web3 Compatibility: In order enable applications to be moved over to an ethermint chain existing tooling (i.e. web3 compatible clients) need to be able to interact with `ethermint`. ### Implementation #### Completed + - Have a working implementation that can parse and validate the existing ETH Chain and persist it in a Tendermint store - Implement Ethereum transactions in the CosmosSDK #### Current Work + - Implement web3 compatible API layer - Implement the EVM as a CosmosSDK module - Allow the Ethermint EVM to interact with other [Cosmos SDK modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/app3.md) #### Next Steps + - Hard spoon enablement: The ability to export state from `geth` and import token balances into Ethermint - Ethermint is a functioning Cosmos SDK application and can be deployed as its own zone - Full web3 compatibility will enable existing Ethereum applications to use Ethermint @@ -39,15 +43,32 @@ $ make tools deps build $ make tools deps install ``` -### Using Ethermint to parse Mainnet Ethereum blocks +### Tests -There is an included Ethereum Mainnet blockchain file in `data/blockchain` that provides an easy way to run the demo of parsing Mainnet Ethereum blocks. The dump in `data/` only includes up to block `97638`. To run this, type the following command: +Integration tests are invoked via: ```bash -$ go run test/run.go +$ make test ``` -By default, state will be dumped into `$HOME/.ethermint`. See `--help` for further usage. +To run CLI tests, execute: + +```bash +$ make test-cli +``` + +#### Ethereum Mainnet Import + +There is an included Ethereum mainnet exported blockchain file in `importer/blockchain` +that includes blocks up to height `97638`. To execute and test a full import of +these blocks using the EVM module, execute: + +```bash +$ make test-import +``` + +You may also provide a custom blockchain export file to test importing more blocks +via the `--blockchain` flag. See `TestImportBlocks` for further documentation. ### Community From d017bc7de5d5e45e7357dc4bac186952ad51b0a6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 08:30:52 -0400 Subject: [PATCH 34/49] Update import test --- importer/importer_test.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 4b5121a9..2d4daad9 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -6,8 +6,10 @@ import ( "io" "math/big" "os" + "os/signal" "runtime/pprof" "sort" + "syscall" "testing" "time" @@ -71,6 +73,26 @@ func newTestCodec() *wire.Codec { return codec } +func cleanup() { + fmt.Println("cleaning up test execution...") + os.RemoveAll(flagDataDir) + + if flagCPUProfile != "" { + pprof.StopCPUProfile() + } +} + +func trapSignals() { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-sigs + cleanup() + os.Exit(1) + }() +} + func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.AccountMapper) { genBlock := ethcore.DefaultGenesisBlock() ms := cms.CacheMultiStore() @@ -117,7 +139,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.Accoun // persist multi-store root state commitID := cms.Commit() - require.Equal(t, "F162678AD57BBE352BE0CFCFCD90E394C4781D31", fmt.Sprintf("%X", commitID.Hash)) + require.Equal(t, "9871F7F6A9901BFA9BDFF2269A3351CDD901B3AD", fmt.Sprintf("%X", commitID.Hash)) // verify account mapper state genAcc := am.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) @@ -139,11 +161,8 @@ func TestImportBlocks(t *testing.T) { } db := dbm.NewDB("state", dbm.LevelDBBackend, flagDataDir) - defer func() { - fmt.Println("cleaning up") - os.RemoveAll(flagDataDir) - pprof.StopCPUProfile() - }() + defer cleanup() + trapSignals() // create logger, codec and root multi-store cdc := newTestCodec() From 3ff606fa9db203162d81272a5f14b3ca1ea2420d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 09:45:55 -0400 Subject: [PATCH 35/49] Remove bank module stub --- x/bank/keeper.go | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 x/bank/keeper.go diff --git a/x/bank/keeper.go b/x/bank/keeper.go deleted file mode 100644 index d63b5346..00000000 --- a/x/bank/keeper.go +++ /dev/null @@ -1,7 +0,0 @@ -package bank - -const ( - // DenomEthereum defines the single coin type/denomination supported in - // Ethermint. - DenomEthereum = "ETH" -) From ad97634c240d78257bf47f65641c405674b0fd80 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 09:46:14 -0400 Subject: [PATCH 36/49] Move old state code to x/evm/types --- {state => x/evm/types}/dump.go | 4 ++-- {state => x/evm/types}/journal.go | 2 +- {state => x/evm/types}/state_object.go | 2 +- {state => x/evm/types}/statedb.go | 30 +++++++++++++++----------- 4 files changed, 21 insertions(+), 17 deletions(-) rename {state => x/evm/types}/dump.go (80%) rename {state => x/evm/types}/journal.go (99%) rename {state => x/evm/types}/state_object.go (99%) rename {state => x/evm/types}/statedb.go (95%) diff --git a/state/dump.go b/x/evm/types/dump.go similarity index 80% rename from state/dump.go rename to x/evm/types/dump.go index b4e3ed8b..ed9fd66b 100644 --- a/state/dump.go +++ b/x/evm/types/dump.go @@ -1,10 +1,10 @@ -package state +package types import ( ethstate "github.com/ethereum/go-ethereum/core/state" ) -// RawDump returns a raw state drump. +// RawDump returns a raw state dump. // // TODO: Implement if we need it, especially for the RPC API. func (csdb *CommitStateDB) RawDump() ethstate.Dump { diff --git a/state/journal.go b/x/evm/types/journal.go similarity index 99% rename from state/journal.go rename to x/evm/types/journal.go index 9095c7f4..ab5f2201 100644 --- a/state/journal.go +++ b/x/evm/types/journal.go @@ -1,4 +1,4 @@ -package state +package types import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/state/state_object.go b/x/evm/types/state_object.go similarity index 99% rename from state/state_object.go rename to x/evm/types/state_object.go index a9ae9cf4..d61db967 100644 --- a/state/state_object.go +++ b/x/evm/types/state_object.go @@ -1,4 +1,4 @@ -package state +package types import ( "bytes" diff --git a/state/statedb.go b/x/evm/types/statedb.go similarity index 95% rename from state/statedb.go rename to x/evm/types/statedb.go index cfbe50d9..9dec051b 100644 --- a/state/statedb.go +++ b/x/evm/types/statedb.go @@ -1,4 +1,4 @@ -package state +package types import ( "fmt" @@ -22,11 +22,15 @@ var ( ) // CommitStateDB implements the Geth state.StateDB interface. Instead of using -// a trie and database for querying and persistence, KVStores and an account -// mapper is used to facilitate state transitions. +// a trie and database for querying and persistence, the Keeper uses KVStores +// and an account mapper is used to facilitate state transitions. +// +// TODO: This implementation is subject to change in regards to its statefull +// manner. In otherwords, how this relates to the keeper in this module. type CommitStateDB struct { - // TODO: Figure out a way to not need to store a context as part of the - // structure + // TODO: We need to store the context as part of the structure itself opposed + // to being passed as a parameter (as it should be) in order to implement the + // StateDB interface. Perhaps there is a better way. ctx sdk.Context am auth.AccountMapper @@ -35,8 +39,6 @@ type CommitStateDB struct { // maps that hold 'live' objects, which will get modified while processing a // state transition - // - // TODO: Determine if we need this cache as the KVStore is cache-wrapped stateObjects map[ethcmn.Address]*stateObject stateObjectsDirty map[ethcmn.Address]struct{} @@ -55,7 +57,7 @@ type CommitStateDB struct { // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs - // during a database read is memoized here and will eventually be returned + // during a database read is memo-ized here and will eventually be returned // by StateDB.Commit. dbErr error @@ -74,9 +76,6 @@ type CommitStateDB struct { // // CONTRACT: Stores used for state must be cache-wrapped as the ordering of the // key/value space matters in determining the merkle root. -// -// TODO: Eventually we'll have an EVM module that'll implement a keeper that we -// can pass into this constructor. func NewCommitStateDB(ctx sdk.Context, am auth.AccountMapper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { return &CommitStateDB{ ctx: ctx, @@ -346,7 +345,9 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, er delete(csdb.stateObjectsDirty, addr) } - // TODO: Get and return the commit/root from the context + // NOTE: Ethereum returns the trie merkle root here, but as commitment + // actually happens in the BaseApp at EndBlocker, we do not know the root at + // this time. return } @@ -388,10 +389,13 @@ func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { // IntermediateRoot returns the current root hash of the state. It is called in // between transactions to get the root hash that goes into transaction // receipts. +// +// NOTE: The SDK has not concept or method of getting any intermediate merkle +// root as commitment of the merkle-ized tree doesn't happen until the +// BaseApps' EndBlocker. func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash { csdb.Finalize(deleteEmptyObjects) - // TODO: Get and return the commit/root from the context return ethcmn.Hash{} } From 7537453c83a4af71e6a13d7bfb4d9463f04e503d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 09:46:26 -0400 Subject: [PATCH 37/49] Move default denomination to account type (for now) --- types/account.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/types/account.go b/types/account.go index 4d58e431..70193e45 100644 --- a/types/account.go +++ b/types/account.go @@ -5,13 +5,18 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/ethermint/x/bank" ethcmn "github.com/ethereum/go-ethereum/common" ) var _ auth.Account = (*Account)(nil) +const ( + // DenomDefault defines the single coin type/denomination supported in + // Ethermint. + DenomDefault = "Photon" +) + // ---------------------------------------------------------------------------- // Main Ethermint account // ---------------------------------------------------------------------------- @@ -37,12 +42,12 @@ func ProtoBaseAccount() auth.Account { // Balance returns the balance of an account. func (acc Account) Balance() sdk.Int { - return acc.GetCoins().AmountOf(bank.DenomEthereum) + return acc.GetCoins().AmountOf(DenomDefault) } // SetBalance sets an account's balance. func (acc Account) SetBalance(amt sdk.Int) { - acc.SetCoins(sdk.Coins{sdk.NewCoin(bank.DenomEthereum, amt)}) + acc.SetCoins(sdk.Coins{sdk.NewCoin(DenomDefault, amt)}) } // ---------------------------------------------------------------------------- From 2b9a1f72a05e18c6541af595375c1788bacfd205 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 09:46:36 -0400 Subject: [PATCH 38/49] Update importer test --- importer/importer_test.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 2d4daad9..8852592f 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -19,9 +19,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/ethermint/core" - "github.com/cosmos/ethermint/state" "github.com/cosmos/ethermint/types" - "github.com/cosmos/ethermint/x/bank" + evmtypes "github.com/cosmos/ethermint/x/evm/types" ethcmn "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -98,7 +97,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.Accoun ms := cms.CacheMultiStore() ctx := sdk.NewContext(ms, abci.Header{}, false, logger) - stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) + stateDB, err := evmtypes.NewCommitStateDB(ctx, am, storageKey, codeKey) require.NoError(t, err, "failed to create a StateDB instance") // sort the addresses and insertion of key/value pairs matters @@ -139,12 +138,12 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.Accoun // persist multi-store root state commitID := cms.Commit() - require.Equal(t, "9871F7F6A9901BFA9BDFF2269A3351CDD901B3AD", fmt.Sprintf("%X", commitID.Hash)) + require.Equal(t, "BF58E5FE5A725463C8FEB755F6A6940584E60F0D", fmt.Sprintf("%X", commitID.Hash)) // verify account mapper state genAcc := am.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) require.NotNil(t, genAcc) - require.Equal(t, sdk.NewIntFromBigInt(b), genAcc.GetCoins().AmountOf(bank.DenomEthereum)) + require.Equal(t, sdk.NewIntFromBigInt(b), genAcc.GetCoins().AmountOf(types.DenomDefault)) } func TestImportBlocks(t *testing.T) { @@ -263,8 +262,8 @@ func TestImportBlocks(t *testing.T) { } } -func createStateDB(t *testing.T, ctx sdk.Context, am auth.AccountMapper) *state.CommitStateDB { - stateDB, err := state.NewCommitStateDB(ctx, am, storageKey, codeKey) +func createStateDB(t *testing.T, ctx sdk.Context, am auth.AccountMapper) *evmtypes.CommitStateDB { + stateDB, err := evmtypes.NewCommitStateDB(ctx, am, storageKey, codeKey) require.NoError(t, err, "failed to create a StateDB instance") return stateDB @@ -274,7 +273,7 @@ func createStateDB(t *testing.T, ctx sdk.Context, am auth.AccountMapper) *state. // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. func accumulateRewards( - config *ethparams.ChainConfig, stateDB *state.CommitStateDB, + config *ethparams.ChainConfig, stateDB *evmtypes.CommitStateDB, header *ethtypes.Header, uncles []*ethtypes.Header, ) { From 955a10a47874d06170aeaee7a00590d2fcd3bf90 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 09:46:42 -0400 Subject: [PATCH 39/49] Add EVM module stub --- docs/spec/evm/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/spec/evm/README.md diff --git a/docs/spec/evm/README.md b/docs/spec/evm/README.md new file mode 100644 index 00000000..5dc22940 --- /dev/null +++ b/docs/spec/evm/README.md @@ -0,0 +1,3 @@ +# EVM + +TODO From 3a65d53d35917f5db17d591fd9efc8ea97dcfab9 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:21:51 -0400 Subject: [PATCH 40/49] Remove clutter from ethermint app --- app/ethermint.go | 61 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/app/ethermint.go b/app/ethermint.go index 726e22f7..2b82850e 100644 --- a/app/ethermint.go +++ b/app/ethermint.go @@ -14,7 +14,6 @@ import ( "github.com/pkg/errors" "github.com/cosmos/ethermint/handlers" - "github.com/cosmos/ethermint/state" "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" @@ -37,7 +36,6 @@ type ( *bam.BaseApp codec *wire.Codec - ethDB *state.Database accountKey *sdk.KVStoreKey storageKey *sdk.KVStoreKey @@ -84,30 +82,10 @@ func NewEthermintApp(logger tmlog.Logger, db dbm.DB, sdkAddr ethcmn.Address) *Et tParamsKey: types.StoreKeyTransParams, } - // create Ethereum state database - ethDB, err := state.NewDatabase(cms, db, state.DefaultStoreCacheSize) - if err != nil { - tmcmn.Exit(err.Error()) - } - - app.ethDB = ethDB - // set application keepers and mappers app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount) - app.coinKeeper = bank.NewKeeper(app.accountMapper) app.paramsKeeper = params.NewKeeper(app.codec, app.paramsKey) app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.codec, app.feeCollKey) - app.stakeKeeper = stake.NewKeeper( - app.codec, app.stakeKey, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace), - ) - app.govKeeper = gov.NewKeeper( - app.codec, app.govKey, app.paramsKeeper.Setter(), app.coinKeeper, - app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace), - ) - app.slashingKeeper = slashing.NewKeeper( - app.codec, app.slashingKey, app.stakeKeeper, - app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace), - ) // register message handlers app.Router(). @@ -140,25 +118,13 @@ func NewEthermintApp(logger tmlog.Logger, db dbm.DB, sdkAddr ethcmn.Address) *Et // BeginBlocker signals the beginning of a block. It performs application // updates on the start of every block. func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper) - - return abci.ResponseBeginBlock{ - Tags: tags.ToKVPairs(), - } + return abci.ResponseBeginBlock{} } // EndBlocker signals the end of a block. It performs application updates on // the end of every block. func (app *EthermintApp) EndBlocker(ctx sdk.Context, _ abci.RequestEndBlock) abci.ResponseEndBlock { - tags := gov.EndBlocker(ctx, app.govKeeper) - validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) - - app.slashingKeeper.AddValidators(ctx, validatorUpdates) - - return abci.ResponseEndBlock{ - ValidatorUpdates: validatorUpdates, - Tags: tags, - } + return abci.ResponseEndBlock{} } // initChainer initializes the application blockchain with validators and other @@ -172,25 +138,9 @@ func (app *EthermintApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) panic(errors.Wrap(err, "failed to parse application genesis state")) } - // load the genesis accounts - for _, genAcc := range genesisState.Accounts { - acc := genAcc.ToAccount() - acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx) - app.accountMapper.SetAccount(ctx, acc) - } + // TODO: load the genesis accounts - // load the genesis stake information - validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData) - if err != nil { - panic(errors.Wrap(err, "failed to initialize genesis validators")) - } - - slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) - gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) - - return abci.ResponseInitChain{ - Validators: validators, - } + return abci.ResponseInitChain{} } // CreateCodec creates a new amino wire codec and registers all the necessary @@ -200,9 +150,6 @@ func CreateCodec() *wire.Codec { types.RegisterWire(codec) auth.RegisterWire(codec) - gov.RegisterWire(codec) - slashing.RegisterWire(codec) - stake.RegisterWire(codec) wire.RegisterCrypto(codec) return codec From 7cd5e47eeac47904b9f17f5740b8b0b757835e23 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:22:02 -0400 Subject: [PATCH 41/49] Remove accounts from genesis --- app/genesis.go | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/app/genesis.go b/app/genesis.go index 972ac8ca..0b59fe14 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -2,9 +2,6 @@ package app import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/ethermint/types" ) @@ -12,9 +9,7 @@ type ( // GenesisState defines the application's genesis state. It contains all the // information required and accounts to initialize the blockchain. GenesisState struct { - Accounts []GenesisAccount `json:"accounts"` - StakeData stake.GenesisState `json:"stake"` - GovData gov.GenesisState `json:"gov"` + Accounts []GenesisAccount `json:"accounts"` } // GenesisAccount defines an account to be initialized in the genesis state. @@ -25,23 +20,3 @@ type ( Storage types.Storage `json:"storage,omitempty"` } ) - -// NewGenesisAccount returns a reference to a new initialized genesis account. -func NewGenesisAccount(acc *types.Account) GenesisAccount { - return GenesisAccount{ - Address: acc.GetAddress(), - Coins: acc.GetCoins(), - Code: acc.Code, - Storage: acc.Storage, - } -} - -// ToAccount converts a genesis account to an initialized Ethermint account. -func (ga *GenesisAccount) ToAccount() (acc *types.Account) { - base := auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins.Sort(), - } - - return types.NewAccount(base, ga.Code, ga.Storage) -} From 9a076e95269233e5fecde4f60fdae74cf7b19a0b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:23:12 -0400 Subject: [PATCH 42/49] Other minor updates --- Makefile | 2 +- handlers/ante.go | 6 ------ handlers/ante_test.go | 25 ++----------------------- importer/importer_test.go | 1 + types/wire.go | 2 -- 5 files changed, 4 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index f44c3adf..c3767ce7 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -PACKAGES=$(shell go list ./... | grep -v '/vendor/') +PACKAGES=$(shell go list ./... | grep -Ev 'vendor|importer') COMMIT_HASH := $(shell git rev-parse --short HEAD) BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/ethermint/version.GitCommit=${COMMIT_HASH}" DOCKER_TAG = unstable diff --git a/handlers/ante.go b/handlers/ante.go index c63f33d5..a13ac1ca 100644 --- a/handlers/ante.go +++ b/handlers/ante.go @@ -105,11 +105,6 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (s // TODO: Investigate gas consumption models as the EVM instruction set has its // own and we should probably not charge for additional gas where we don't have // to. - // - // err = acc.SetSequence(seq + 1) - // if err != nil { - // return sdkCtx, sdk.ErrInternal(err.Error()).Result(), true - // } accMapper.SetAccount(sdkCtx, acc) return sdkCtx, sdk.Result{GasWanted: int64(ethTx.Data().GasLimit)}, false @@ -135,7 +130,6 @@ func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMappe signer := ethcmn.BytesToAddress(signerAddrs[i].Bytes()) acc, err := validateSignature(sdkCtx, stdTx, signer, sig, accMapper) - // err.Code() != sdk.CodeOK if err != nil { return sdkCtx, err.Result(), true } diff --git a/handlers/ante_test.go b/handlers/ante_test.go index cff44515..61bc8743 100644 --- a/handlers/ante_test.go +++ b/handlers/ante_test.go @@ -16,6 +16,8 @@ import ( "github.com/tendermint/tendermint/libs/log" ) +// TODO: These tests will radically change as the ante handler develops + func TestEthTxBadSig(t *testing.T) { tx := types.NewTransaction(uint64(0), types.TestAddr1, big.NewInt(10), 1000, big.NewInt(100), []byte{}) @@ -68,29 +70,6 @@ func TestEthTxIncorrectNonce(t *testing.T) { require.Equal(t, sdk.ABCICodeType(0x10003), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) } -func TestEthTxValidTx(t *testing.T) { - tx := types.NewTransaction(0, types.TestAddr1, big.NewInt(50), 1000, big.NewInt(1000), []byte{}) - tx.Sign(types.TestChainID, types.TestPrivKey1) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(types.NewTestCodec(), key, auth.ProtoBaseAccount) - - // set account in accountMapper - acc := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - accMapper.SetAccount(ctx, acc) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, *tx) - - require.False(t, abort, "expected ante handler to not abort") - require.True(t, res.IsOK(), fmt.Sprintf("result not OK on valid Tx: %s", res.Log)) - - // ensure account state updated correctly - seq, _ := accMapper.GetSequence(ctx, types.TestAddr1[:]) - require.Equal(t, int64(1), seq, "account nonce did not increment correctly") -} - func TestEmbeddedTxBadSig(t *testing.T) { testCodec := types.NewTestCodec() testFee := types.NewTestStdFee() diff --git a/importer/importer_test.go b/importer/importer_test.go index 8852592f..5fd30a3e 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -67,6 +67,7 @@ func newTestCodec() *wire.Codec { codec := wire.NewCodec() types.RegisterWire(codec) + auth.RegisterWire(codec) wire.RegisterCrypto(codec) return codec diff --git a/types/wire.go b/types/wire.go index 25212484..ad1f2e30 100644 --- a/types/wire.go +++ b/types/wire.go @@ -3,7 +3,6 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" - "github.com/cosmos/cosmos-sdk/x/auth" ) var typesCodec = wire.NewCodec() @@ -16,7 +15,6 @@ func init() { // codec. func RegisterWire(codec *wire.Codec) { sdk.RegisterWire(codec) - codec.RegisterInterface((*auth.Account)(nil), nil) codec.RegisterConcrete(&Transaction{}, "types/Transaction", nil) codec.RegisterConcrete(&Account{}, "types/Account", nil) } From 5b2496553cee953ea845c6651f13099edd70a9ef Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:49:37 -0400 Subject: [PATCH 43/49] Update to reflect latest SDK changes --- Gopkg.lock | 349 ++++++++++++++++++++++++++++++++------ Gopkg.toml | 26 ++- app/ethermint.go | 34 ++-- handlers/ante.go | 24 +-- handlers/ante_test.go | 235 ------------------------- importer/importer_test.go | 26 +-- types/test_common.go | 17 +- types/tx.go | 18 +- types/wire.go | 16 +- x/evm/types/statedb.go | 16 +- 10 files changed, 391 insertions(+), 370 deletions(-) delete mode 100644 handlers/ante_test.go diff --git a/Gopkg.lock b/Gopkg.lock index 470426e5..d5f6ec0a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,19 +3,27 @@ [[projects]] branch = "master" - digest = "1:60d3b49df18861c92ac49cce49e49f61b3ec927e5b7f39c5ae1128ec5c197b98" + digest = "1:495c7006c2f48b705f0d89fd8449a2ae70622bb748788d9d17caafa65a6769f9" name = "github.com/aristanetworks/goarista" packages = ["monotime"] pruneopts = "T" - revision = "fb622b9b46608fdb39d36447f4d8ef52fe37fc3d" + revision = "33151c4543a79b013e8e6799ef45b2ba88c3cd1c" [[projects]] branch = "master" - digest = "1:b9f5e0f033febe59a62d01e78486c0dd9e4afc9ac5d240aee6ce78a927142e8b" + digest = "1:ad4589ec239820ee99eb01c1ad47ebc5f8e02c4f5103a9b210adff9696d89f36" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "T" + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + branch = "master" + digest = "1:0bd9f11575e82b723837f50e170d010ec29a50aa8ca02a962c439146f03aea55" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "T" - revision = "79e00513b1011888b1e675157ab89f527f901cae" + revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" [[projects]] digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533" @@ -25,29 +33,30 @@ revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" [[projects]] - digest = "1:bc28e755cf6a9fd8e65497514d20c4907973e7a6a6409d30ead3fd37bfeb19a9" + digest = "1:912c494215c339688331953ba09cb9698a4797fe647d93a4e0a117c9c7b960a2" name = "github.com/cosmos/cosmos-sdk" packages = [ "baseapp", + "codec", "store", "types", "version", - "wire", "x/auth", "x/bank", "x/gov", "x/gov/tags", "x/mock", "x/params", + "x/params/subspace", "x/slashing", "x/stake", "x/stake/keeper", + "x/stake/querier", "x/stake/tags", "x/stake/types", ] pruneopts = "T" - revision = "1c38c70468ec721e3a555ba2f3bf5f9da31f0cc9" - version = "v0.24.2" + revision = "075ddce79acb77fe88f849f93fb3036e48ffb555" [[projects]] digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a" @@ -65,6 +74,13 @@ revision = "cbaa98ba5575e67703b32b4b19f73c91f3c4159e" version = "v1.7.1" +[[projects]] + digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b" + name = "github.com/ebuchman/fail-test" + packages = ["."] + pruneopts = "T" + revision = "95f809107225be108efcf10a3509e4ea6ceef3c4" + [[projects]] branch = "master" digest = "1:67d0b50be0549e610017cb91e0b0b745ec0cad7c613bc8e18ff2d1c1fc8825a7" @@ -75,7 +91,7 @@ [[projects]] branch = "ethermint-statedb" - digest = "1:c24d17ef5d37ae7215811cf1cade45822faa232d6bcfbadff30bbeba52225a98" + digest = "1:6fcf36394c3b062f05dcaac00658a3bc49451de7bd289a4c10274c61d6256243" name = "github.com/ethereum/go-ethereum" packages = [ ".", @@ -123,9 +139,17 @@ "trie", ] pruneopts = "T" - revision = "477eb0933b9529f7deeccc233cc815fe34a8ea56" + revision = "411404c27389331f96b2e77bf8bdbbee2f0151f9" source = "github.com/alexanderbez/go-ethereum" +[[projects]] + digest = "1:7fc160b460a6fc506b37fcca68332464c3f2cd57b6e3f111f26c5bbfd2d5518e" + name = "github.com/fsnotify/fsnotify" + packages = ["."] + pruneopts = "T" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + version = "v1.4.7" + [[projects]] digest = "1:0b9c3ad6c948d57a379da9c4e1cdd989b1c73ddc5ec8673f52a9539ce60a109b" name = "github.com/go-kit/kit" @@ -133,6 +157,10 @@ "log", "log/level", "log/term", + "metrics", + "metrics/discard", + "metrics/internal/lv", + "metrics/prometheus", ] pruneopts = "T" revision = "4dc7be5d2d12881735283bcab7352178e190fc71" @@ -193,18 +221,52 @@ revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] - digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db" + digest = "1:3a26588bc48b96825977c1b3df964f8fd842cd6860cc26370588d3563433cf11" + name = "github.com/google/uuid" + packages = ["."] + pruneopts = "T" + revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" + version = "v1.0.0" + +[[projects]] + digest = "1:0ead695774eaa7bf1a284d246febe82054767941de80ab2328a194b088f07026" + name = "github.com/gorilla/websocket" + packages = ["."] + pruneopts = "T" + revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" + version = "v1.2.0" + +[[projects]] + digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5" name = "github.com/hashicorp/golang-lru" packages = [ ".", "simplelru", ] pruneopts = "T" - revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3" + revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" + version = "v0.5.0" [[projects]] - branch = "master" - digest = "1:202e4a1a283dd740ca9d131787e73bb9d69611a01ef86e82ed262e035b0dd792" + digest = "1:071bcbf82c289fba4d3f63c876bf4f0ba7eda625cd60795e0a03ccbf949e517a" + name = "github.com/hashicorp/hcl" + packages = [ + ".", + "hcl/ast", + "hcl/parser", + "hcl/scanner", + "hcl/strconv", + "hcl/token", + "json/parser", + "json/scanner", + "json/token", + ] + pruneopts = "T" + revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" + version = "v1.0.0" + +[[projects]] + digest = "1:a33cc2e4fb12c58430d2aae5834ff6e84cb609da97692e1fe2aa0cd5ebc92623" name = "github.com/huin/goupnp" packages = [ ".", @@ -216,7 +278,8 @@ "ssdp", ] pruneopts = "T" - revision = "1395d1447324cbea88d249fbfcfd70ea878fdfca" + revision = "656e61dfadd241c7cbdd22a023fa81ecb6860ea8" + version = "v1.0.0" [[projects]] digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" @@ -259,12 +322,44 @@ revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] - digest = "1:361de06aa7ae272616cbe71c3994a654cc6316324e30998e650f7765b20c5b33" + digest = "1:53e8c5c79716437e601696140e8b1801aae4204f4ec54a504333702a49572c4f" + name = "github.com/magiconair/properties" + packages = ["."] + pruneopts = "T" + revision = "c2353362d570a7bfa228149c62842019201cfb71" + version = "v1.8.0" + +[[projects]] + digest = "1:a8e3d14801bed585908d130ebfc3b925ba642208e6f30d879437ddfc7bb9b413" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "T" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + pruneopts = "T" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" + +[[projects]] + digest = "1:e5d0bd87abc2781d14e274807a470acd180f0499f8bf5bb18606e9ec22ad9de9" name = "github.com/pborman/uuid" packages = ["."] pruneopts = "T" - revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53" - version = "v1.1" + revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" + version = "v1.2" + +[[projects]] + digest = "1:ccf9949c9c53e85dcb7e2905fc620571422567040925381e6baa62f0b7b850fe" + name = "github.com/pelletier/go-toml" + packages = ["."] + pruneopts = "T" + revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" + version = "v1.2.0" [[projects]] digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" @@ -283,36 +378,121 @@ version = "v1.0.0" [[projects]] - digest = "1:602081d2a289d1f76ea90b806b0c61c19038d76504e9005ccb969864dbaee339" + digest = "1:f4f3858737fd9db5cf3ef8019c918a798a987d4d11f7e531c54dfe70d4708642" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/promhttp", + ] + pruneopts = "T" + revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632" + +[[projects]] + branch = "master" + digest = "1:185cf55b1f44a1bf243558901c3f06efa5c64ba62cfdcbb1bf7bbe8c3fb68561" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "T" + revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" + +[[projects]] + branch = "master" + digest = "1:2d9b03513fadf4adf193b3570f5ef65ee57b658d9f11e901a06d17baf2bdc88b" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "T" + revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" + +[[projects]] + branch = "master" + digest = "1:57bf59ce0c73cef5cc4796a5d64f1ec5b81f6335f242d4a80a62b0a6edc4b77f" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "T" + revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" + +[[projects]] + digest = "1:523d2c2500965d035691347a6d30befd53fde95fad16e0b94bef5d3d2cca8ff7" + name = "github.com/rcrowley/go-metrics" + packages = ["."] + pruneopts = "T" + revision = "e2704e165165ec55d062f5919b4b29494e9fa790" + +[[projects]] + digest = "1:9787d2d3220cbfd444596afd03ab0abcf391df169b789fbe3eae27fa2e426cf6" name = "github.com/rjeczalik/notify" packages = ["."] pruneopts = "T" - revision = "0f065fa99b48b842c3fd3e2c8b194c6f2b69f6b8" - version = "v0.9.1" + revision = "69d839f37b13a8cb7a78366f7633a4071cb43be7" + version = "v0.9.2" [[projects]] - digest = "1:6cae6970d70fc5fe75bf83c48ee33e9c4c561a62d0b033254bee8dd5942b815a" + digest = "1:a8a03bca5a81878daa4958136f3372af00437c61129ca088a430b0b786b9378a" name = "github.com/rs/cors" packages = ["."] pruneopts = "T" - revision = "3fb1b69b103a84de38a19c3c6ec073dd6caa4d3f" - version = "v1.5.0" + revision = "9a47f48565a795472d43519dd49aac781f3034fb" + version = "v1.6.0" [[projects]] - digest = "1:8be8b3743fc9795ec21bbd3e0fc28ff6234018e1a269b0a7064184be95ac13e0" + digest = "1:b7bf9fd95d38ebe6726a63b7d0320611f7c920c64e2c8313eba0cec51926bf55" + name = "github.com/spf13/afero" + packages = [ + ".", + "mem", + ] + pruneopts = "T" + revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" + version = "v1.1.2" + +[[projects]] + digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" + name = "github.com/spf13/cast" + packages = ["."] + pruneopts = "T" + revision = "8965335b8c7107321228e3e3702cab9832751bac" + version = "v1.2.0" + +[[projects]] + digest = "1:52565bd966162d1f4579757f66ce6a7ca9054e7f6b662f0c7c96e4dd228fd017" name = "github.com/spf13/cobra" packages = ["."] pruneopts = "T" - revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" - version = "v0.0.3" + revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" + version = "v0.0.1" [[projects]] - digest = "1:9ba911fe3884995431690e7eb180cf848da0d637ba5f61711783b795d031793f" + digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" + name = "github.com/spf13/jwalterweatherman" + packages = ["."] + pruneopts = "T" + revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" + version = "v1.0.0" + +[[projects]] + digest = "1:0f775ea7a72e30d5574267692aaa9ff265aafd15214a7ae7db26bc77f2ca04dc" name = "github.com/spf13/pflag" packages = ["."] pruneopts = "T" - revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" - version = "v1.0.2" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" + +[[projects]] + digest = "1:a8a1cbf83d6ba47a3421e51b5dd1999e1f64f6175c64295d6b42bdea55312a79" + name = "github.com/spf13/viper" + packages = ["."] + pruneopts = "T" + revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" + version = "v1.0.0" [[projects]] digest = "1:8f39978e4fb2a11d43cc954f2ab458cb38995d4c1557b6d3a7c8cafe0ec2277c" @@ -328,7 +508,7 @@ [[projects]] branch = "master" - digest = "1:ee395d0d8c1719b5a1407f34af93953b4763bacb19a8961aba5b6d312824da41" + digest = "1:ea4a45f31f55c7a42ba3063baa646ac94eb7ee9afe60c1fd2c8b396930222620" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -345,7 +525,14 @@ "leveldb/util", ] pruneopts = "T" - revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd" + revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43" + +[[projects]] + digest = "1:71ffd1fca92b4972ecd588cf13d9929d4f444659788e9128d055a9126498d41d" + name = "github.com/tendermint/btcd" + packages = ["btcec"] + pruneopts = "T" + revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" [[projects]] branch = "master" @@ -360,57 +547,110 @@ revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" [[projects]] - digest = "1:0e2addab3f64ece97ca434b2bf2d4e8cb54a4509904a03be8c81da3fc2ddb245" + digest = "1:25c97d29878b5f821bb17a379469f0f923426188241bf2aa81c18728cdc6927c" name = "github.com/tendermint/go-amino" packages = ["."] pruneopts = "T" - revision = "2106ca61d91029c931fd54968c2bb02dc96b1412" - version = "0.10.1" + revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee" + version = "v0.12.0" [[projects]] - digest = "1:bf042d2f7d1252b9dcae8e694e2f0a9b5294cb357c086fd86dc540d2f32c9fdf" + digest = "1:2ecd824e1615a8becefea26637fe24576f3800260f5dc91ffe44b37bdbd27878" name = "github.com/tendermint/iavl" packages = ["."] pruneopts = "T" - revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9" - version = "v0.9.2" + revision = "3acc91fb8811db2c5409a855ae1f8e441fe98e2d" + version = "v0.11.0" [[projects]] - digest = "1:5a60cb048b401c0263c227baf8778ecaf038be531707057607949540486874ef" + digest = "1:b8bd45120cbea639592420b1d5363f102d819ea89d6239f4dae2a0814c76a6d2" name = "github.com/tendermint/tendermint" packages = [ + "abci/client", + "abci/example/code", + "abci/example/kvstore", "abci/server", "abci/types", + "blockchain", + "cmd/tendermint/commands", + "config", + "consensus", + "consensus/types", "crypto", "crypto/ed25519", "crypto/encoding/amino", "crypto/merkle", + "crypto/multisig", + "crypto/multisig/bitarray", "crypto/secp256k1", "crypto/tmhash", + "evidence", + "libs/autofile", "libs/bech32", + "libs/cli", + "libs/cli/flags", + "libs/clist", "libs/common", "libs/db", + "libs/errors", + "libs/events", + "libs/flowrate", "libs/log", "libs/pubsub", "libs/pubsub/query", + "lite", + "lite/client", + "lite/errors", + "lite/proxy", + "mempool", + "node", + "p2p", + "p2p/conn", + "p2p/pex", + "p2p/upnp", + "privval", + "proxy", + "rpc/client", + "rpc/core", + "rpc/core/types", + "rpc/grpc", + "rpc/lib", + "rpc/lib/client", + "rpc/lib/server", + "rpc/lib/types", + "state", + "state/txindex", + "state/txindex/kv", + "state/txindex/null", "types", + "types/time", + "version", ] pruneopts = "T" - revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e" - version = "v0.23.1-rc0" + revision = "90eda9bfb6e6daeed1c8015df41cb36772d91778" + version = "v0.25.1-rc0" [[projects]] branch = "master" - digest = "1:da29cbeb9d244918393b37243c008ab7128688fb017c966aaf876587c010bcdd" + digest = "1:56a43b9f51e5c5ea734e866b82d57c842b022c795a0611ff5f57f3d7c47de45d" name = "golang.org/x/crypto" packages = [ + "chacha20poly1305", + "curve25519", + "hkdf", + "internal/chacha20", + "internal/subtle", + "nacl/box", + "nacl/secretbox", "pbkdf2", + "poly1305", "ripemd160", + "salsa20/salsa", "scrypt", "ssh/terminal", ] pruneopts = "T" - revision = "182538f80094b6a8efaade63a8fd8e0d9d5843dd" + revision = "0c41d7ab0a0ee717d4590a44bcb987dfd9e183eb" [[projects]] digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7" @@ -425,6 +665,7 @@ "http2/hpack", "idna", "internal/timeseries", + "netutil", "trace", "websocket", ] @@ -433,7 +674,7 @@ [[projects]] branch = "master" - digest = "1:bfa444982d49ce4ca1360599270a94de12a573ccd3bf04493c79bee09da3170b" + digest = "1:8671b7bf5d02bdeb389f8023184c7f92d12fcf0dec9426e055f3433f5fd706b7" name = "golang.org/x/sys" packages = [ "cpu", @@ -441,7 +682,7 @@ "windows", ] pruneopts = "T" - revision = "fa5fdf94c78965f1aa8423f0cc50b8b8d728b05a" + revision = "44b849a8bc13eb42e95e6c6c5e360481b73ec710" [[projects]] digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a" @@ -480,11 +721,11 @@ [[projects]] branch = "master" - digest = "1:e43f1cb3f488a0c2be85939c2a594636f60b442a12a196c778bd2d6c9aca3df7" + digest = "1:849525811c9f6ae1f5bd9b866adb4c9436f4a12d767f48e33bf343596d4aafd7" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] pruneopts = "T" - revision = "11092d34479b07829b72e10713b159248caf5dad" + revision = "94acd270e44e65579b9ee3cdab25034d33fed608" [[projects]] digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770" @@ -528,14 +769,22 @@ pruneopts = "T" revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6" +[[projects]] + digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "T" + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/cosmos/cosmos-sdk/baseapp", + "github.com/cosmos/cosmos-sdk/codec", "github.com/cosmos/cosmos-sdk/store", "github.com/cosmos/cosmos-sdk/types", - "github.com/cosmos/cosmos-sdk/wire", "github.com/cosmos/cosmos-sdk/x/auth", "github.com/cosmos/cosmos-sdk/x/bank", "github.com/cosmos/cosmos-sdk/x/gov", @@ -544,7 +793,6 @@ "github.com/cosmos/cosmos-sdk/x/stake", "github.com/ethereum/go-ethereum/common", "github.com/ethereum/go-ethereum/common/hexutil", - "github.com/ethereum/go-ethereum/common/math", "github.com/ethereum/go-ethereum/consensus", "github.com/ethereum/go-ethereum/consensus/ethash", "github.com/ethereum/go-ethereum/consensus/misc", @@ -554,13 +802,10 @@ "github.com/ethereum/go-ethereum/core/vm", "github.com/ethereum/go-ethereum/crypto", "github.com/ethereum/go-ethereum/crypto/sha3", - "github.com/ethereum/go-ethereum/ethdb", "github.com/ethereum/go-ethereum/params", "github.com/ethereum/go-ethereum/rlp", "github.com/ethereum/go-ethereum/rpc", "github.com/ethereum/go-ethereum/signer/core", - "github.com/ethereum/go-ethereum/trie", - "github.com/hashicorp/golang-lru", "github.com/pkg/errors", "github.com/stretchr/testify/require", "github.com/stretchr/testify/suite", diff --git a/Gopkg.toml b/Gopkg.toml index c177500f..a5c88bd5 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -6,7 +6,9 @@ [[constraint]] name = "github.com/cosmos/cosmos-sdk" - version = "=0.24.2" + # TODO: Remove this once 0.25 has been released + revision = "075ddce79acb77fe88f849f93fb3036e48ffb555" + # version = "=0.24.2" [[constraint]] name = "github.com/hashicorp/golang-lru" @@ -16,14 +18,6 @@ name = "github.com/spf13/cobra" version = "~0.0.1" -[[override]] - name = "github.com/tendermint/iavl" - version = "=v0.9.2" - -[[override]] - name = "github.com/tendermint/tendermint" - version = "=v0.23.1-rc0" - [[constraint]] name = "github.com/stretchr/testify" version = "=1.2.1" @@ -32,9 +26,23 @@ name = "github.com/pkg/errors" version = "=0.8.0" +# dependecy overrides + [[override]] name = "gopkg.in/fatih/set.v0" version = "=0.1.0" +[[override]] + name = "github.com/tendermint/iavl" + version = "=v0.11.0" + +[[override]] + name = "github.com/tendermint/tendermint" + version = "=0.25.1-rc0" + +[[override]] + name = "github.com/tendermint/go-amino" + version = "=v0.12.0" + [prune] go-tests = true diff --git a/app/ethermint.go b/app/ethermint.go index 2b82850e..cd5437db 100644 --- a/app/ethermint.go +++ b/app/ethermint.go @@ -2,9 +2,9 @@ package app import ( bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/gov" @@ -35,7 +35,7 @@ type ( EthermintApp struct { *bam.BaseApp - codec *wire.Codec + cdc *codec.Codec accountKey *sdk.KVStoreKey storageKey *sdk.KVStoreKey @@ -47,7 +47,7 @@ type ( paramsKey *sdk.KVStoreKey tParamsKey *sdk.TransientStoreKey - accountMapper auth.AccountMapper + accountKeeper auth.AccountKeeper feeCollKeeper auth.FeeCollectionKeeper coinKeeper bank.Keeper stakeKeeper stake.Keeper @@ -60,17 +60,17 @@ type ( // NewEthermintApp returns a reference to a new initialized Ethermint // application. func NewEthermintApp(logger tmlog.Logger, db dbm.DB, sdkAddr ethcmn.Address) *EthermintApp { - codec := CreateCodec() + cdc := CreateCodec() cms := store.NewCommitMultiStore(db) baseAppOpts := []func(*bam.BaseApp){ func(bApp *bam.BaseApp) { bApp.SetCMS(cms) }, } - baseApp := bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec, sdkAddr), baseAppOpts...) + baseApp := bam.NewBaseApp(appName, logger, db, types.TxDecoder(cdc, sdkAddr), baseAppOpts...) app := &EthermintApp{ BaseApp: baseApp, - codec: codec, + cdc: cdc, accountKey: types.StoreKeyAccount, storageKey: types.StoreKeyStorage, mainKey: types.StoreKeyMain, @@ -83,9 +83,9 @@ func NewEthermintApp(logger tmlog.Logger, db dbm.DB, sdkAddr ethcmn.Address) *Et } // set application keepers and mappers - app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount) - app.paramsKeeper = params.NewKeeper(app.codec, app.paramsKey) - app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.codec, app.feeCollKey) + app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.accountKey, auth.ProtoBaseAccount) + app.paramsKeeper = params.NewKeeper(app.cdc, app.paramsKey, app.tParamsKey) + app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.feeCollKey) // register message handlers app.Router(). @@ -99,7 +99,7 @@ func NewEthermintApp(logger tmlog.Logger, db dbm.DB, sdkAddr ethcmn.Address) *Et app.SetInitChainer(app.initChainer) app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) - app.SetAnteHandler(handlers.AnteHandler(app.accountMapper, app.feeCollKeeper)) + app.SetAnteHandler(handlers.AnteHandler(app.accountKeeper, app.feeCollKeeper)) app.MountStoresIAVL( app.mainKey, app.accountKey, app.stakeKey, app.slashingKey, @@ -133,7 +133,7 @@ func (app *EthermintApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) var genesisState GenesisState stateJSON := req.AppStateBytes - err := app.codec.UnmarshalJSON(stateJSON, &genesisState) + err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) if err != nil { panic(errors.Wrap(err, "failed to parse application genesis state")) } @@ -145,12 +145,12 @@ func (app *EthermintApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) // CreateCodec creates a new amino wire codec and registers all the necessary // concrete types and interfaces needed for the application. -func CreateCodec() *wire.Codec { - codec := wire.NewCodec() +func CreateCodec() *codec.Codec { + cdc := codec.New() - types.RegisterWire(codec) - auth.RegisterWire(codec) - wire.RegisterCrypto(codec) + types.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) - return codec + return cdc } diff --git a/handlers/ante.go b/handlers/ante.go index a13ac1ca..dd35b4e2 100644 --- a/handlers/ante.go +++ b/handlers/ante.go @@ -21,15 +21,15 @@ const ( // must implementing. Internal ante handlers will be dependant upon the // transaction type. type internalAnteHandler func( - sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper, + sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountKeeper, ) (newCtx sdk.Context, res sdk.Result, abort bool) // AnteHandler is responsible for attempting to route an Ethereum or SDK // transaction to an internal ante handler for performing transaction-level // processing (e.g. fee payment, signature verification) before being passed // onto it's respective handler. -func AnteHandler(accMapper auth.AccountMapper, _ auth.FeeCollectionKeeper) sdk.AnteHandler { - return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { +func AnteHandler(ak auth.AccountKeeper, _ auth.FeeCollectionKeeper) sdk.AnteHandler { + return func(sdkCtx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { var ( handler internalAnteHandler gasLimit int64 @@ -67,13 +67,13 @@ func AnteHandler(accMapper auth.AccountMapper, _ auth.FeeCollectionKeeper) sdk.A } }() - return handler(newCtx, tx, accMapper) + return handler(newCtx, tx, ak) } } // handleEthTx implements an ante handler for an Ethereum transaction. It // validates the signature and if valid returns an OK result. -func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (sdk.Context, sdk.Result, bool) { +func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, ak auth.AccountKeeper) (sdk.Context, sdk.Result, bool) { ethTx, ok := tx.(types.Transaction) if !ok { return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid transaction: %T", tx)).Result(), true @@ -92,7 +92,7 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (s return sdkCtx, sdk.ErrUnauthorized("signature verification failed").Result(), true } - acc := accMapper.GetAccount(sdkCtx, addr.Bytes()) + acc := ak.GetAccount(sdkCtx, addr.Bytes()) // validate the account nonce (referred to as sequence in the AccountMapper) seq := acc.GetSequence() @@ -106,13 +106,13 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (s // own and we should probably not charge for additional gas where we don't have // to. - accMapper.SetAccount(sdkCtx, acc) + ak.SetAccount(sdkCtx, acc) return sdkCtx, sdk.Result{GasWanted: int64(ethTx.Data().GasLimit)}, false } // handleEmbeddedTx implements an ante handler for an SDK transaction. It // validates the signature and if valid returns an OK result. -func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMapper) (sdk.Context, sdk.Result, bool) { +func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, ak auth.AccountKeeper) (sdk.Context, sdk.Result, bool) { stdTx, ok := tx.(auth.StdTx) if !ok { return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid transaction: %T", tx)).Result(), true @@ -129,14 +129,14 @@ func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, accMapper auth.AccountMappe for i, sig := range stdTx.Signatures { signer := ethcmn.BytesToAddress(signerAddrs[i].Bytes()) - acc, err := validateSignature(sdkCtx, stdTx, signer, sig, accMapper) + acc, err := validateSignature(sdkCtx, stdTx, signer, sig, ak) if err != nil { return sdkCtx, err.Result(), true } // TODO: Fees! - accMapper.SetAccount(sdkCtx, acc) + ak.SetAccount(sdkCtx, acc) signerAccs[i] = acc } @@ -163,12 +163,12 @@ func validateStdTxBasic(stdTx auth.StdTx) (err sdk.Error) { func validateSignature( sdkCtx sdk.Context, stdTx auth.StdTx, signer ethcmn.Address, - sig auth.StdSignature, accMapper auth.AccountMapper, + sig auth.StdSignature, ak auth.AccountKeeper, ) (acc auth.Account, sdkErr sdk.Error) { chainID := sdkCtx.ChainID() - acc = accMapper.GetAccount(sdkCtx, signer.Bytes()) + acc = ak.GetAccount(sdkCtx, signer.Bytes()) if acc == nil { return nil, sdk.ErrUnknownAddress(fmt.Sprintf("no account with address %s found", signer)) } diff --git a/handlers/ante_test.go b/handlers/ante_test.go deleted file mode 100644 index 61bc8743..00000000 --- a/handlers/ante_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package handlers - -import ( - "crypto/ecdsa" - "fmt" - "math/big" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/cosmos/ethermint/types" - ethcrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" -) - -// TODO: These tests will radically change as the ante handler develops - -func TestEthTxBadSig(t *testing.T) { - tx := types.NewTransaction(uint64(0), types.TestAddr1, big.NewInt(10), 1000, big.NewInt(100), []byte{}) - - // create bad signature - tx.Sign(big.NewInt(100), types.TestPrivKey2) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(types.NewTestCodec(), key, auth.ProtoBaseAccount) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, *tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x10004), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEthTxInsufficientGas(t *testing.T) { - tx := types.NewTransaction(uint64(0), types.TestAddr1, big.NewInt(0), 0, big.NewInt(0), []byte{}) - tx.Sign(types.TestChainID, types.TestPrivKey1) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(types.NewTestCodec(), key, auth.ProtoBaseAccount) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, *tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x1000c), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEthTxIncorrectNonce(t *testing.T) { - // create transaction with wrong nonce 12 - tx := types.NewTransaction(12, types.TestAddr2, big.NewInt(50), 1000, big.NewInt(1000), []byte("test_bytes")) - tx.Sign(types.TestChainID, types.TestPrivKey1) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, log.NewNopLogger()) - accMapper := auth.NewAccountMapper(types.NewTestCodec(), key, auth.ProtoBaseAccount) - - // set account in accountMapper - acc := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - accMapper.SetAccount(ctx, acc) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, *tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x10003), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEmbeddedTxBadSig(t *testing.T) { - testCodec := types.NewTestCodec() - testFee := types.NewTestStdFee() - - msgs := []sdk.Msg{sdk.NewTestMsg()} - tx := types.NewTestStdTx( - types.TestChainID, msgs, []int64{0}, []int64{0}, []*ecdsa.PrivateKey{types.TestPrivKey1}, testFee, - ) - - // create bad signature - signBytes := types.GetStdTxSignBytes(big.NewInt(100).String(), 1, 1, testFee, msgs, "") - sig, _ := ethcrypto.Sign(signBytes, types.TestPrivKey1) - (tx.(auth.StdTx)).Signatures[0].Signature = sig - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(testCodec, key, auth.ProtoBaseAccount) - - // set account in accountMapper - acc := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - accMapper.SetAccount(ctx, acc) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x10004), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEmbeddedTxInvalidMultiMsg(t *testing.T) { - testCodec := types.NewTestCodec() - testCodec.RegisterConcrete(stake.MsgDelegate{}, "test/MsgDelegate", nil) - - msgs := []sdk.Msg{ - stake.NewMsgDelegate(types.TestAddr1.Bytes(), types.TestAddr2.Bytes(), sdk.NewCoin("steak", sdk.NewInt(50))), - stake.NewMsgDelegate(types.TestAddr2.Bytes(), types.TestAddr2.Bytes(), sdk.NewCoin("steak", sdk.NewInt(50))), - } - - // create transaction with only one signer - tx := types.NewTestStdTx( - types.TestChainID, msgs, []int64{0}, []int64{0}, []*ecdsa.PrivateKey{types.TestPrivKey1}, types.NewTestStdFee(), - ) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(testCodec, key, auth.ProtoBaseAccount) - - // set accounts in accountMapper - acc1 := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - accMapper.SetAccount(ctx, acc1) - - acc2 := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - accMapper.SetAccount(ctx, acc2) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x10004), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEmbeddedTxInvalidAccountNumber(t *testing.T) { - testCodec := types.NewTestCodec() - testCodec.RegisterConcrete(stake.MsgDelegate{}, "test/MsgDelegate", nil) - - msgs := []sdk.Msg{ - stake.NewMsgDelegate(types.TestAddr1.Bytes(), types.TestAddr2.Bytes(), sdk.NewCoin("steak", sdk.NewInt(50))), - } - - // create a transaction with an invalid account number - tx := types.NewTestStdTx( - types.TestChainID, msgs, []int64{3}, []int64{0}, []*ecdsa.PrivateKey{types.TestPrivKey1}, types.NewTestStdFee(), - ) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(testCodec, key, auth.ProtoBaseAccount) - - // set account in accountMapper - acc := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - acc.SetAccountNumber(4) - accMapper.SetAccount(ctx, acc) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x10003), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEmbeddedTxInvalidSequence(t *testing.T) { - testCodec := types.NewTestCodec() - testCodec.RegisterConcrete(stake.MsgDelegate{}, "test/MsgDelegate", nil) - - msgs := []sdk.Msg{ - stake.NewMsgDelegate(types.TestAddr1.Bytes(), types.TestAddr2.Bytes(), sdk.NewCoin("steak", sdk.NewInt(50))), - } - - // create transaction with an invalid sequence (nonce) - tx := types.NewTestStdTx( - types.TestChainID, msgs, []int64{4}, []int64{2}, []*ecdsa.PrivateKey{types.TestPrivKey1}, types.NewTestStdFee(), - ) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(types.NewTestCodec(), key, auth.ProtoBaseAccount) - - // set account in accountMapper - acc := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - acc.SetAccountNumber(4) - acc.SetSequence(3) - accMapper.SetAccount(ctx, acc) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, tx) - - require.True(t, abort, "expected ante handler to abort") - require.Equal(t, sdk.ABCICodeType(0x10003), res.Code, fmt.Sprintf("invalid code returned on bad tx: %s", res.Log)) -} - -func TestEmbeddedTxValid(t *testing.T) { - testCodec := types.NewTestCodec() - testCodec.RegisterConcrete(stake.MsgDelegate{}, "test/MsgDelegate", nil) - - msgs := []sdk.Msg{ - stake.NewMsgDelegate(types.TestAddr1.Bytes(), types.TestAddr2.Bytes(), sdk.NewCoin("steak", sdk.NewInt(50))), - stake.NewMsgDelegate(types.TestAddr2.Bytes(), types.TestAddr2.Bytes(), sdk.NewCoin("steak", sdk.NewInt(50))), - } - - // create a valid transaction - tx := types.NewTestStdTx( - types.TestChainID, msgs, []int64{4, 5}, []int64{3, 1}, - []*ecdsa.PrivateKey{types.TestPrivKey1, types.TestPrivKey2}, types.NewTestStdFee(), - ) - - ms, key := createTestMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: types.TestChainID.String()}, false, nil) - accMapper := auth.NewAccountMapper(types.NewTestCodec(), key, auth.ProtoBaseAccount) - - // set accounts in the accountMapper - acc1 := accMapper.NewAccountWithAddress(ctx, types.TestAddr1.Bytes()) - acc1.SetAccountNumber(4) - acc1.SetSequence(3) - accMapper.SetAccount(ctx, acc1) - - acc2 := accMapper.NewAccountWithAddress(ctx, types.TestAddr2.Bytes()) - acc2.SetAccountNumber(5) - acc2.SetSequence(1) - accMapper.SetAccount(ctx, acc2) - - handler := AnteHandler(accMapper, auth.FeeCollectionKeeper{}) - _, res, abort := handler(ctx, tx) - - require.False(t, abort, "expected ante handler to not abort") - require.True(t, res.IsOK(), fmt.Sprintf("result not OK on valid Tx: %s", res.Log)) - - // Ensure account state updated correctly - seq1, _ := accMapper.GetSequence(ctx, types.TestAddr1.Bytes()) - seq2, _ := accMapper.GetSequence(ctx, types.TestAddr2.Bytes()) - - require.Equal(t, int64(4), seq1, "account nonce did not increment correctly") - require.Equal(t, int64(2), seq2, "account nonce did not increment correctly") -} diff --git a/importer/importer_test.go b/importer/importer_test.go index 5fd30a3e..879de57f 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -13,9 +13,9 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/ethermint/core" @@ -63,14 +63,14 @@ func init() { flag.Parse() } -func newTestCodec() *wire.Codec { - codec := wire.NewCodec() +func newTestCodec() *codec.Codec { + cdc := codec.New() - types.RegisterWire(codec) - auth.RegisterWire(codec) - wire.RegisterCrypto(codec) + types.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) - return codec + return cdc } func cleanup() { @@ -93,12 +93,12 @@ func trapSignals() { }() } -func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.AccountMapper) { +func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.AccountKeeper) { genBlock := ethcore.DefaultGenesisBlock() ms := cms.CacheMultiStore() ctx := sdk.NewContext(ms, abci.Header{}, false, logger) - stateDB, err := evmtypes.NewCommitStateDB(ctx, am, storageKey, codeKey) + stateDB, err := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey) require.NoError(t, err, "failed to create a StateDB instance") // sort the addresses and insertion of key/value pairs matters @@ -142,7 +142,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, am auth.Accoun require.Equal(t, "BF58E5FE5A725463C8FEB755F6A6940584E60F0D", fmt.Sprintf("%X", commitID.Hash)) // verify account mapper state - genAcc := am.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) + genAcc := ak.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) require.NotNil(t, genAcc) require.Equal(t, sdk.NewIntFromBigInt(b), genAcc.GetCoins().AmountOf(types.DenomDefault)) } @@ -169,7 +169,7 @@ func TestImportBlocks(t *testing.T) { cms := store.NewCommitMultiStore(db) // create account mapper - am := auth.NewAccountMapper( + am := auth.NewAccountKeeper( cdc, accKey, types.ProtoBaseAccount, @@ -263,8 +263,8 @@ func TestImportBlocks(t *testing.T) { } } -func createStateDB(t *testing.T, ctx sdk.Context, am auth.AccountMapper) *evmtypes.CommitStateDB { - stateDB, err := evmtypes.NewCommitStateDB(ctx, am, storageKey, codeKey) +func createStateDB(t *testing.T, ctx sdk.Context, ak auth.AccountKeeper) *evmtypes.CommitStateDB { + stateDB, err := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey) require.NoError(t, err, "failed to create a StateDB instance") return stateDB diff --git a/types/test_common.go b/types/test_common.go index 155debc5..80c5cde9 100644 --- a/types/test_common.go +++ b/types/test_common.go @@ -5,14 +5,15 @@ import ( "crypto/ecdsa" "math/big" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" ethcrypto "github.com/ethereum/go-ethereum/crypto" ) +// test variables var ( TestSDKAddr = GenerateEthAddress() TestChainID = big.NewInt(3) @@ -24,15 +25,15 @@ var ( TestAddr2 = PrivKeyToEthAddress(TestPrivKey2) ) -func NewTestCodec() *wire.Codec { - codec := wire.NewCodec() +func NewTestCodec() *codec.Codec { + cdc := codec.New() - RegisterWire(codec) - auth.RegisterWire(codec) - wire.RegisterCrypto(codec) - codec.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil) + RegisterCodec(cdc) + auth.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + cdc.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil) - return codec + return cdc } func NewTestStdFee() auth.StdFee { diff --git a/types/tx.go b/types/tx.go index 00a696c9..016cd83f 100644 --- a/types/tx.go +++ b/types/tx.go @@ -8,8 +8,8 @@ import ( "math/big" "sync/atomic" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" ethcrypto "github.com/ethereum/go-ethereum/crypto" @@ -18,9 +18,12 @@ import ( "github.com/pkg/errors" ) +// TODO: Move to the EVM module + +// message constants const ( - // TypeTxEthereum reflects an Ethereum Transaction type. - TypeTxEthereum = "Ethereum" + TypeTxEthereum = "Ethereum" + RouteTxEthereum = "evm" ) // ---------------------------------------------------------------------------- @@ -238,9 +241,8 @@ func (tx Transaction) VerifySig(chainID *big.Int) (ethcmn.Address, error) { // Type implements the sdk.Msg interface. It returns the type of the // Transaction. -func (tx Transaction) Type() string { - return TypeTxEthereum -} +func (tx Transaction) Type() string { return TypeTxEthereum } +func (tx Transaction) Route() string { return RouteTxEthereum } // ValidateBasic implements the sdk.Msg interface. It performs basic validation // checks of a Transaction. If returns an sdk.Error if validation fails. @@ -283,7 +285,7 @@ func (tx Transaction) hasEmbeddedTx(addr ethcmn.Address) bool { // // CONTRACT: The payload field of an Ethereum transaction must contain a valid // encoded SDK transaction. -func (tx Transaction) GetEmbeddedTx(codec *wire.Codec) (sdk.Tx, sdk.Error) { +func (tx Transaction) GetEmbeddedTx(codec *codec.Codec) (sdk.Tx, sdk.Error) { var etx sdk.Tx err := codec.UnmarshalBinary(tx.data.Payload, &etx) @@ -301,7 +303,7 @@ func (tx Transaction) GetEmbeddedTx(codec *wire.Codec) (sdk.Tx, sdk.Error) { // TxDecoder returns an sdk.TxDecoder that given raw transaction bytes and an // SDK address, attempts to decode them into a Transaction or an EmbeddedTx or // returning an error if decoding fails. -func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder { +func TxDecoder(codec *codec.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx = Transaction{} diff --git a/types/wire.go b/types/wire.go index ad1f2e30..cc3783a8 100644 --- a/types/wire.go +++ b/types/wire.go @@ -1,20 +1,20 @@ package types import ( + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) -var typesCodec = wire.NewCodec() +var typesCodec = codec.New() func init() { - RegisterWire(typesCodec) + RegisterCodec(typesCodec) } -// RegisterWire registers all the necessary types with amino for the given +// RegisterCodec registers all the necessary types with amino for the given // codec. -func RegisterWire(codec *wire.Codec) { - sdk.RegisterWire(codec) - codec.RegisterConcrete(&Transaction{}, "types/Transaction", nil) - codec.RegisterConcrete(&Account{}, "types/Account", nil) +func RegisterCodec(cdc *codec.Codec) { + sdk.RegisterCodec(cdc) + cdc.RegisterConcrete(&Transaction{}, "types/Transaction", nil) + cdc.RegisterConcrete(&Account{}, "types/Account", nil) } diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index 9dec051b..bb367871 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -33,7 +33,7 @@ type CommitStateDB struct { // StateDB interface. Perhaps there is a better way. ctx sdk.Context - am auth.AccountMapper + ak auth.AccountKeeper storageKey sdk.StoreKey codeKey sdk.StoreKey @@ -76,10 +76,10 @@ type CommitStateDB struct { // // CONTRACT: Stores used for state must be cache-wrapped as the ordering of the // key/value space matters in determining the merkle root. -func NewCommitStateDB(ctx sdk.Context, am auth.AccountMapper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { +func NewCommitStateDB(ctx sdk.Context, ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { return &CommitStateDB{ ctx: ctx, - am: am, + ak: ak, storageKey: storageKey, codeKey: codeKey, stateObjects: make(map[ethcmn.Address]*stateObject), @@ -401,13 +401,13 @@ func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash // updateStateObject writes the given state object to the store. func (csdb *CommitStateDB) updateStateObject(so *stateObject) { - csdb.am.SetAccount(csdb.ctx, so.account) + csdb.ak.SetAccount(csdb.ctx, so.account) } // deleteStateObject removes the given state object from the state store. func (csdb *CommitStateDB) deleteStateObject(so *stateObject) { so.deleted = true - csdb.am.RemoveAccount(csdb.ctx, so.account) + csdb.ak.RemoveAccount(csdb.ctx, so.account) } // ---------------------------------------------------------------------------- @@ -556,7 +556,7 @@ func (csdb *CommitStateDB) Copy() ethstate.StateDB { // copy all the basic fields, initialize the memory ones state := &CommitStateDB{ ctx: csdb.ctx, - am: csdb.am, + ak: csdb.ak, storageKey: csdb.storageKey, codeKey: csdb.codeKey, stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)), @@ -651,7 +651,7 @@ func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) ethstate.Sta func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) { prevObj = csdb.getStateObject(addr) - acc := csdb.am.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) + acc := csdb.ak.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) newObj = newObject(csdb, acc) newObj.setNonce(0) // sets the object to dirty @@ -685,7 +685,7 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta } // otherwise, attempt to fetch the account from the account mapper - acc := csdb.am.GetAccount(csdb.ctx, addr.Bytes()) + acc := csdb.ak.GetAccount(csdb.ctx, addr.Bytes()) if acc == nil { csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes())) return nil From ccc6ae69840bea9647caf3a250980888c3cfe154 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:53:23 -0400 Subject: [PATCH 44/49] Fix linting errors --- types/tx.go | 5 ++++- types/utils.go | 2 +- x/evm/types/statedb.go | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/types/tx.go b/types/tx.go index 016cd83f..8a7abf28 100644 --- a/types/tx.go +++ b/types/tx.go @@ -241,7 +241,10 @@ func (tx Transaction) VerifySig(chainID *big.Int) (ethcmn.Address, error) { // Type implements the sdk.Msg interface. It returns the type of the // Transaction. -func (tx Transaction) Type() string { return TypeTxEthereum } +func (tx Transaction) Type() string { return TypeTxEthereum } + +// Route implements the sdk.Msg interface. It returns the route of the +// Transaction. func (tx Transaction) Route() string { return RouteTxEthereum } // ValidateBasic implements the sdk.Msg interface. It performs basic validation diff --git a/types/utils.go b/types/utils.go index 7135a583..ae4c7c09 100644 --- a/types/utils.go +++ b/types/utils.go @@ -25,7 +25,7 @@ func GenerateEthAddress() ethcmn.Address { // PrivKeyToEthAddress generates an Ethereum address given an ECDSA private key. func PrivKeyToEthAddress(p *ecdsa.PrivateKey) ethcmn.Address { - return ethcrypto.PubkeyToAddress(ecdsa.PublicKey(p.PublicKey)) + return ethcrypto.PubkeyToAddress(p.PublicKey) } // ValidateSigner attempts to validate a signer for a given slice of bytes over diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index bb367871..ceda0375 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -306,7 +306,7 @@ func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { } // ---------------------------------------------------------------------------- -// Persistance +// Persistence // ---------------------------------------------------------------------------- // Commit writes the state to the appropriate KVStores. For each state object From e6380ec8628d0bc32124bda3f818754e5774fedb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:57:29 -0400 Subject: [PATCH 45/49] update dev tools; fix linting --- app/ethermint.go | 2 +- handlers/ante.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/ethermint.go b/app/ethermint.go index cd5437db..c683d59b 100644 --- a/app/ethermint.go +++ b/app/ethermint.go @@ -129,7 +129,7 @@ func (app *EthermintApp) EndBlocker(ctx sdk.Context, _ abci.RequestEndBlock) abc // initChainer initializes the application blockchain with validators and other // state data from TendermintCore. -func (app *EthermintApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { +func (app *EthermintApp) initChainer(_ sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { var genesisState GenesisState stateJSON := req.AppStateBytes diff --git a/handlers/ante.go b/handlers/ante.go index dd35b4e2..2b95feaf 100644 --- a/handlers/ante.go +++ b/handlers/ante.go @@ -29,7 +29,7 @@ type internalAnteHandler func( // processing (e.g. fee payment, signature verification) before being passed // onto it's respective handler. func AnteHandler(ak auth.AccountKeeper, _ auth.FeeCollectionKeeper) sdk.AnteHandler { - return func(sdkCtx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { + return func(sdkCtx sdk.Context, tx sdk.Tx, _ bool) (newCtx sdk.Context, res sdk.Result, abort bool) { var ( handler internalAnteHandler gasLimit int64 From 40b715efd430bfa8c1257204c177ec038221bb12 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 10:58:13 -0400 Subject: [PATCH 46/49] Run test-import on CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 86411426..62e1c273 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,4 +15,4 @@ jobs: - run: name: "Run tests" - command: make test-lint test-unit + command: make test-lint test-unit test-import From 9dccb2ed0c0781425d6f109c4c4d07a55b40c36a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 11:02:51 -0400 Subject: [PATCH 47/49] Update lock file --- Gopkg.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index d5f6ec0a..02a6d308 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -91,7 +91,7 @@ [[projects]] branch = "ethermint-statedb" - digest = "1:6fcf36394c3b062f05dcaac00658a3bc49451de7bd289a4c10274c61d6256243" + digest = "1:00d8053ec8370727a6ecb12cdbfc0b3ce08bce2f8ac2aa34d57402ff09243517" name = "github.com/ethereum/go-ethereum" packages = [ ".", @@ -139,7 +139,7 @@ "trie", ] pruneopts = "T" - revision = "411404c27389331f96b2e77bf8bdbbee2f0151f9" + revision = "0a57b29f0c8e6dc27901fae1c91d3758723b81eb" source = "github.com/alexanderbez/go-ethereum" [[projects]] @@ -674,7 +674,7 @@ [[projects]] branch = "master" - digest = "1:8671b7bf5d02bdeb389f8023184c7f92d12fcf0dec9426e055f3433f5fd706b7" + digest = "1:d6d9bf31934efd450aeb0a07061dce8a0e84bc80e4aae05841dd588325832a40" name = "golang.org/x/sys" packages = [ "cpu", @@ -682,7 +682,7 @@ "windows", ] pruneopts = "T" - revision = "44b849a8bc13eb42e95e6c6c5e360481b73ec710" + revision = "5cd93ef61a7c8f0f858690154eb6de2e69415fa1" [[projects]] digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a" From d9bb42a81858e0993576b9206152f02f03549134 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 11:05:39 -0400 Subject: [PATCH 48/49] Fix importer hash check --- importer/importer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importer/importer_test.go b/importer/importer_test.go index 879de57f..206c90d9 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -139,7 +139,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.Accoun // persist multi-store root state commitID := cms.Commit() - require.Equal(t, "BF58E5FE5A725463C8FEB755F6A6940584E60F0D", fmt.Sprintf("%X", commitID.Hash)) + require.Equal(t, "12D4DB63083D5B01824A35BB70BF671686D60532", fmt.Sprintf("%X", commitID.Hash)) // verify account mapper state genAcc := ak.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) From 0dedf51ed8ab688c3f67e63c6152e858ab8f7c3e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Oct 2018 11:06:27 -0400 Subject: [PATCH 49/49] Update dep comments --- Gopkg.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gopkg.toml b/Gopkg.toml index a5c88bd5..5ba05cc7 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,6 +1,7 @@ [[constraint]] name = "github.com/ethereum/go-ethereum" - # TODO: Remove this forked source and branch + # TODO: Remove this forked source and branch once Turbo-Geth is ready as a + # client. source = "github.com/alexanderbez/go-ethereum" branch = "ethermint-statedb"