From f481ff655f375767524ac30f986df6d77f490c83 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 2 Jul 2020 12:20:15 +0200 Subject: [PATCH 01/29] evm: fix non-determinism --- x/evm/types/journal.go | 75 ++++++++++++++++++++++--- x/evm/types/state_object.go | 107 +++++++++++++++++++----------------- x/evm/types/statedb.go | 100 ++++++++++++++++++++------------- 3 files changed, 186 insertions(+), 96 deletions(-) diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go index 45a470336..c3bd079e7 100644 --- a/x/evm/types/journal.go +++ b/x/evm/types/journal.go @@ -22,14 +22,24 @@ type journalEntry interface { // 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[ethcmn.Address]int // Dirty accounts and the number of changes + entries []journalEntry // Current changes tracked by the journal + dirties []dirty // Dirty accounts and the number of changes + addressToIndex map[ethcmn.Address]int // map from address to the index of the dirties slice +} + +// dirty represents a single key value pair of the journal dirties, where the +// key correspons to the account address and the value to the number of +// changes for that account. +type dirty struct { + address ethcmn.Address + changes int } // newJournal create a new initialized journal. func newJournal() *journal { return &journal{ - dirties: make(map[ethcmn.Address]int), + dirties: []dirty{}, + addressToIndex: make(map[ethcmn.Address]int), } } @@ -37,7 +47,7 @@ func newJournal() *journal { func (j *journal) append(entry journalEntry) { j.entries = append(j.entries, entry) if addr := entry.dirtied(); addr != nil { - j.dirties[*addr]++ + j.addDirty(*addr) } } @@ -50,8 +60,9 @@ func (j *journal) revert(statedb *CommitStateDB, snapshot int) { // 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.substractDirty(*addr) + if j.getDirty(*addr) == 0 { + j.deleteDirty(*addr) } } } @@ -62,7 +73,7 @@ func (j *journal) revert(statedb *CommitStateDB, snapshot int) { // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD // precompile consensus exception. func (j *journal) dirty(addr ethcmn.Address) { - j.dirties[addr]++ + j.addDirty(addr) } // length returns the current number of entries in the journal. @@ -70,6 +81,48 @@ func (j *journal) length() int { return len(j.entries) } +// append inserts a new modification entry to the end of the change journal. +func (j *journal) getDirty(addr ethcmn.Address) int { + idx, found := j.addressToIndex[addr] + if !found { + return 0 + } + + return j.dirties[idx].changes +} + +func (j *journal) addDirty(addr ethcmn.Address) { + idx, found := j.addressToIndex[addr] + if !found { + return + } + + dirty := j.dirties[idx] + dirty.changes++ + j.dirties[idx] = dirty +} + +func (j *journal) substractDirty(addr ethcmn.Address) { + idx, found := j.addressToIndex[addr] + if !found { + return + } + + dirty := j.dirties[idx] + dirty.changes-- + j.dirties[idx] = dirty +} + +func (j *journal) deleteDirty(addr ethcmn.Address) { + idx, found := j.addressToIndex[addr] + if !found { + return + } + + j.dirties = append(j.dirties[:idx], j.dirties[idx+1:]...) + delete(j.addressToIndex, addr) +} + type ( // Changes to the account trie. createObjectChange struct { @@ -128,8 +181,14 @@ type ( ) func (ch createObjectChange) revert(s *CommitStateDB) { - delete(s.stateObjects, *ch.account) delete(s.stateObjectsDirty, *ch.account) + idx, exists := s.addressToIndex[*ch.account] + if !exists { + // perform no-op + return + } + // reemove from the slice + s.stateObjects = append(s.stateObjects[:idx], s.stateObjects[idx+1:]...) } func (ch createObjectChange) dirtied() *ethcmn.Address { diff --git a/x/evm/types/state_object.go b/x/evm/types/state_object.go index c07adcab8..00abe393f 100644 --- a/x/evm/types/state_object.go +++ b/x/evm/types/state_object.go @@ -23,57 +23,56 @@ var ( emptyCodeHash = ethcrypto.Keccak256(nil) ) -type ( - // StateObject interface for interacting with state object - StateObject interface { - GetCommittedState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash - GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash - SetState(db ethstate.Database, key, value ethcmn.Hash) - - Code(db ethstate.Database) []byte - SetCode(codeHash ethcmn.Hash, code []byte) - CodeHash() []byte - - AddBalance(amount *big.Int) - SubBalance(amount *big.Int) - SetBalance(amount *big.Int) - - Balance() *big.Int - ReturnGas(gas *big.Int) - Address() ethcmn.Address - - SetNonce(nonce uint64) - Nonce() uint64 - } - // stateObject represents an Ethereum account which is being modified. +// StateObject interface for interacting with state object +type StateObject interface { + GetCommittedState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash + GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash + SetState(db ethstate.Database, key, value ethcmn.Hash) + + Code(db ethstate.Database) []byte + SetCode(codeHash ethcmn.Hash, code []byte) + CodeHash() []byte + + AddBalance(amount *big.Int) + SubBalance(amount *big.Int) + SetBalance(amount *big.Int) + + Balance() *big.Int + ReturnGas(gas *big.Int) + Address() ethcmn.Address + + SetNonce(nonce uint64) + Nonce() uint64 +} + +// 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. +type stateObject struct { + code types.Code // contract bytecode, which gets set when code is loaded + // 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 + stateDB *CommitStateDB + account *types.EthAccount + balance sdk.Int + originStorage types.Storage // Storage cache of original entries to dedup rewrites + dirtyStorage types.Storage // Storage entries that need to be flushed to disk + address ethcmn.Address + // cache flags // - // 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 { - code types.Code // contract bytecode, which gets set when code is loaded - // 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 - stateDB *CommitStateDB - account *types.EthAccount - balance sdk.Int - originStorage types.Storage // Storage cache of original entries to dedup rewrites - dirtyStorage types.Storage // Storage entries that need to be flushed to disk - address ethcmn.Address - // 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 - } -) + // 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 +} func newStateObject(db *CommitStateDB, accProto authexported.Account, balance sdk.Int) *stateObject { ethermintAccount, ok := accProto.(*types.EthAccount) @@ -402,3 +401,11 @@ func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { return ethcrypto.Keccak256Hash(compositeKey) } + +// stateEntry represents a single key value pair from the StateDB's stateObject mappindg. +// This is to prevent non determinism at genesis initialization or export. +type stateEntry struct { + // address key of the state object + address ethcmn.Address + stateObject *stateObject +} diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index 4f3f745ec..6e85916b6 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -45,9 +45,10 @@ type CommitStateDB struct { accountKeeper AccountKeeper bankKeeper BankKeeper - // maps that hold 'live' objects, which will get modified while processing a + // array that hold 'live' objects, which will get modified while processing a // state transition - stateObjects map[ethcmn.Address]*stateObject + stateObjects []stateEntry + addressToIndex map[ethcmn.Address]int // map from address to the index of the state objects slice stateObjectsDirty map[ethcmn.Address]struct{} // The refund counter, also used by state transitioning. @@ -91,7 +92,8 @@ func NewCommitStateDB( storeKey: storeKey, accountKeeper: ak, bankKeeper: bk, - stateObjects: make(map[ethcmn.Address]*stateObject), + stateObjects: []stateEntry{}, + addressToIndex: make(map[ethcmn.Address]int), stateObjectsDirty: make(map[ethcmn.Address]struct{}), preimages: make(map[ethcmn.Hash][]byte), journal: newJournal(), @@ -389,34 +391,34 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error) defer csdb.clearJournalAndRefund() // remove dirty state object entries based on the journal - for addr := range csdb.journal.dirties { - csdb.stateObjectsDirty[addr] = struct{}{} + for _, dirty := range csdb.journal.dirties { + csdb.stateObjectsDirty[dirty.address] = struct{}{} } // set the state objects - for addr, so := range csdb.stateObjects { - _, isDirty := csdb.stateObjectsDirty[addr] + for _, stateEntry := range csdb.stateObjects { + _, isDirty := csdb.stateObjectsDirty[stateEntry.address] switch { - case so.suicided || (isDirty && deleteEmptyObjects && so.empty()): + case stateEntry.stateObject.suicided || (isDirty && deleteEmptyObjects && stateEntry.stateObject.empty()): // If the state object has been removed, don't bother syncing it and just // remove it from the store. - csdb.deleteStateObject(so) + csdb.deleteStateObject(stateEntry.stateObject) case isDirty: // write any contract code associated with the state object - if so.code != nil && so.dirtyCode { - so.commitCode() - so.dirtyCode = false + if stateEntry.stateObject.code != nil && stateEntry.stateObject.dirtyCode { + stateEntry.stateObject.commitCode() + stateEntry.stateObject.dirtyCode = false } // update the object in the KVStore - if err := csdb.updateStateObject(so); err != nil { + if err := csdb.updateStateObject(stateEntry.stateObject); err != nil { return ethcmn.Hash{}, err } } - delete(csdb.stateObjectsDirty, addr) + delete(csdb.stateObjectsDirty, stateEntry.address) } // NOTE: Ethereum returns the trie merkle root here, but as commitment @@ -429,8 +431,8 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error) // removing the csdb destructed objects and clearing the journal as well as the // refunds. func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error { - for addr := range csdb.journal.dirties { - so, exist := csdb.stateObjects[addr] + for _, dirty := range csdb.journal.dirties { + idx, exist := csdb.addressToIndex[dirty.address] if !exist { // ripeMD is 'touched' at block 1714175, in tx: // 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 @@ -444,18 +446,19 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error { continue } - if so.suicided || (deleteEmptyObjects && so.empty()) { - csdb.deleteStateObject(so) + stateEntry := csdb.stateObjects[idx] + if stateEntry.stateObject.suicided || (deleteEmptyObjects && stateEntry.stateObject.empty()) { + csdb.deleteStateObject(stateEntry.stateObject) } 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() - if err := csdb.updateStateObject(so); err != nil { + stateEntry.stateObject.commitState() + if err := csdb.updateStateObject(stateEntry.stateObject); err != nil { return err } } - csdb.stateObjectsDirty[addr] = struct{}{} + csdb.stateObjectsDirty[dirty.address] = struct{}{} } // invalidate journal because reverting across transactions is not allowed @@ -587,7 +590,8 @@ func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool { // the underlying account mapper and store keys to avoid reloading data for the // next operations. func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error { - csdb.stateObjects = make(map[ethcmn.Address]*stateObject) + csdb.stateObjects = []stateEntry{} + csdb.addressToIndex = make(map[ethcmn.Address]int) csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) csdb.thash = ethcmn.Hash{} csdb.bhash = ethcmn.Hash{} @@ -601,27 +605,28 @@ func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error { // UpdateAccounts updates the nonce and coin balances of accounts func (csdb *CommitStateDB) UpdateAccounts() { - for addr, so := range csdb.stateObjects { - currAcc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes())) + for _, stateEntry := range csdb.stateObjects { + currAcc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(stateEntry.address.Bytes())) emintAcc, ok := currAcc.(*emint.EthAccount) if !ok { continue } balance := csdb.bankKeeper.GetBalance(csdb.ctx, emintAcc.GetAddress(), emint.DenomDefault) - if so.Balance() != balance.Amount.BigInt() && balance.IsValid() { - so.balance = balance.Amount + if stateEntry.stateObject.Balance() != balance.Amount.BigInt() && balance.IsValid() { + stateEntry.stateObject.balance = balance.Amount } - if so.Nonce() != emintAcc.GetSequence() { - so.account = emintAcc + if stateEntry.stateObject.Nonce() != emintAcc.GetSequence() { + stateEntry.stateObject.account = emintAcc } } } // ClearStateObjects clears cache of state objects to handle account changes outside of the EVM func (csdb *CommitStateDB) ClearStateObjects() { - csdb.stateObjects = make(map[ethcmn.Address]*stateObject) + csdb.stateObjects = []stateEntry{} + csdb.addressToIndex = make(map[ethcmn.Address]int) csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) } @@ -669,7 +674,8 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { storeKey: csdb.storeKey, accountKeeper: csdb.accountKeeper, bankKeeper: csdb.bankKeeper, - stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)), + stateObjects: make([]stateEntry, len(csdb.journal.dirties)), + addressToIndex: make(map[ethcmn.Address]int, len(csdb.journal.dirties)), stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)), refund: csdb.refund, logSize: csdb.logSize, @@ -678,15 +684,18 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { } // copy the dirty states, logs, and preimages - for addr := range csdb.journal.dirties { + for _, dirty := 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{}{} + if idx, exist := csdb.addressToIndex[dirty.address]; exist { + state.stateObjects[idx] = stateEntry{ + address: dirty.address, + stateObject: csdb.stateObjects[idx].stateObject.deepCopy(state), + } + state.stateObjectsDirty[dirty.address] = struct{}{} } } @@ -694,8 +703,11 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { // 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) + if idx, exist := csdb.addressToIndex[addr]; !exist { + state.stateObjects[idx] = stateEntry{ + address: addr, + stateObject: csdb.stateObjects[idx].stateObject.deepCopy(state), + } state.stateObjectsDirty[addr] = struct{}{} } } @@ -784,8 +796,13 @@ func (csdb *CommitStateDB) setError(err error) { // 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) { + idx, found := csdb.addressToIndex[addr] + if !found { + return nil + } + // prefer 'live' (cached) objects - if so := csdb.stateObjects[addr]; so != nil { + if so := csdb.stateObjects[idx].stateObject; so != nil { if so.deleted { return nil } @@ -810,7 +827,14 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta } func (csdb *CommitStateDB) setStateObject(so *stateObject) { - csdb.stateObjects[so.Address()] = so + se := stateEntry{ + address: so.Address(), + stateObject: so, + } + + idx := len(csdb.stateObjects) + csdb.stateObjects = append(csdb.stateObjects, se) + csdb.addressToIndex[se.address] = idx } // RawDump returns a raw state dump. From e139d96d0925eed31967b688d70c3ce691bedb97 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 2 Jul 2020 16:12:24 +0200 Subject: [PATCH 02/29] fixes --- x/evm/genesis_test.go | 4 ++-- x/evm/types/journal.go | 11 +++++++++-- x/evm/types/statedb.go | 34 ++++++++++++++++++---------------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go index 9992bb343..0bc5b0946 100644 --- a/x/evm/genesis_test.go +++ b/x/evm/genesis_test.go @@ -44,10 +44,10 @@ func (suite *EvmTestSuite) TestContractExportImport() { suite.T().Logf("contract addr 0x%x", address) // clear keeper code and re-initialize - suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx).SetCode(address, nil) + suite.app.EvmKeeper.SetCode(suite.ctx, address, nil) _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, genState) - resCode := suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx).GetCode(address) + resCode := suite.app.EvmKeeper.GetCode(suite.ctx, address) suite.Require().Equal(deployedEnsFactoryCode, resCode) } diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go index c3bd079e7..7365f18b8 100644 --- a/x/evm/types/journal.go +++ b/x/evm/types/journal.go @@ -81,7 +81,8 @@ func (j *journal) length() int { return len(j.entries) } -// append inserts a new modification entry to the end of the change journal. +// getDirty returns the dirty count for a given address. If the address is not +// found it returns 0. func (j *journal) getDirty(addr ethcmn.Address) int { idx, found := j.addressToIndex[addr] if !found { @@ -91,6 +92,8 @@ func (j *journal) getDirty(addr ethcmn.Address) int { return j.dirties[idx].changes } +// addDirty adds 1 to the dirty count of an address. It performs a no-op if the +// address is not found. func (j *journal) addDirty(addr ethcmn.Address) { idx, found := j.addressToIndex[addr] if !found { @@ -102,6 +105,8 @@ func (j *journal) addDirty(addr ethcmn.Address) { j.dirties[idx] = dirty } +// substractDirty substracts 1 to the dirty count of an address. It performs a +// no-op if the address is not found. func (j *journal) substractDirty(addr ethcmn.Address) { idx, found := j.addressToIndex[addr] if !found { @@ -113,6 +118,8 @@ func (j *journal) substractDirty(addr ethcmn.Address) { j.dirties[idx] = dirty } +// deleteDirty deletes a dirty entry from the jounal's dirties slice. If the +// entry is not found it performs a no-op. func (j *journal) deleteDirty(addr ethcmn.Address) { idx, found := j.addressToIndex[addr] if !found { @@ -187,7 +194,7 @@ func (ch createObjectChange) revert(s *CommitStateDB) { // perform no-op return } - // reemove from the slice + // remove from the slice s.stateObjects = append(s.stateObjects[:idx], s.stateObjects[idx+1:]...) } diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index 6e85916b6..b63e2940c 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -703,11 +703,8 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { // 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 idx, exist := csdb.addressToIndex[addr]; !exist { - state.stateObjects[idx] = stateEntry{ - address: addr, - stateObject: csdb.stateObjects[idx].stateObject.deepCopy(state), - } + if idx, exist := state.addressToIndex[addr]; !exist { + state.setStateObject(csdb.stateObjects[idx].stateObject.deepCopy(state)) state.stateObjectsDirty[addr] = struct{}{} } } @@ -797,17 +794,15 @@ func (csdb *CommitStateDB) setError(err error) { // Returns nil and sets an error if not found. func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { idx, found := csdb.addressToIndex[addr] - if !found { - return nil - } + if found { + // prefer 'live' (cached) objects + if so := csdb.stateObjects[idx].stateObject; so != nil { + if so.deleted { + return nil + } - // prefer 'live' (cached) objects - if so := csdb.stateObjects[idx].stateObject; so != nil { - if so.deleted { - return nil + return so } - - return so } // otherwise, attempt to fetch the account from the account mapper @@ -827,14 +822,21 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta } func (csdb *CommitStateDB) setStateObject(so *stateObject) { + idx, found := csdb.addressToIndex[so.Address()] + if found { + // update the existing object + csdb.stateObjects[idx].stateObject = so + return + } + + // append the new state object to the stateObjects slice se := stateEntry{ address: so.Address(), stateObject: so, } - idx := len(csdb.stateObjects) csdb.stateObjects = append(csdb.stateObjects, se) - csdb.addressToIndex[se.address] = idx + csdb.addressToIndex[se.address] = len(csdb.stateObjects) - 1 } // RawDump returns a raw state dump. From 9601ed171533596fbb4e4e4491ff428df625fae6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 2 Jul 2020 17:05:12 +0200 Subject: [PATCH 03/29] typo --- x/evm/types/journal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go index 7365f18b8..4120dc2c0 100644 --- a/x/evm/types/journal.go +++ b/x/evm/types/journal.go @@ -105,7 +105,7 @@ func (j *journal) addDirty(addr ethcmn.Address) { j.dirties[idx] = dirty } -// substractDirty substracts 1 to the dirty count of an address. It performs a +// substractDirty subtracts 1 to the dirty count of an address. It performs a // no-op if the address is not found. func (j *journal) substractDirty(addr ethcmn.Address) { idx, found := j.addressToIndex[addr] From a64afb61551e088fbfd6ad923592ca5dd536b9ae Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 7 Jul 2020 15:07:14 +0200 Subject: [PATCH 04/29] fix tests --- x/evm/types/journal.go | 41 ++++++++++++------------ x/evm/types/journal_test.go | 62 +++++++++++++++++++++++++++++++++++++ x/evm/types/statedb.go | 62 ++++++++++++++++++------------------- x/evm/types/statedb_test.go | 4 +++ 4 files changed, 118 insertions(+), 51 deletions(-) create mode 100644 x/evm/types/journal_test.go diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go index 4120dc2c0..b145e6266 100644 --- a/x/evm/types/journal.go +++ b/x/evm/types/journal.go @@ -22,9 +22,9 @@ type journalEntry interface { // 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 []dirty // Dirty accounts and the number of changes - addressToIndex map[ethcmn.Address]int // map from address to the index of the dirties slice + entries []journalEntry // Current changes tracked by the journal + dirties []dirty // Dirty accounts and the number of changes + addressToJournalIndex map[ethcmn.Address]int // map from address to the index of the dirties slice } // dirty represents a single key value pair of the journal dirties, where the @@ -38,8 +38,8 @@ type dirty struct { // newJournal create a new initialized journal. func newJournal() *journal { return &journal{ - dirties: []dirty{}, - addressToIndex: make(map[ethcmn.Address]int), + dirties: []dirty{}, + addressToJournalIndex: make(map[ethcmn.Address]int), } } @@ -84,7 +84,7 @@ func (j *journal) length() int { // getDirty returns the dirty count for a given address. If the address is not // found it returns 0. func (j *journal) getDirty(addr ethcmn.Address) int { - idx, found := j.addressToIndex[addr] + idx, found := j.addressToJournalIndex[addr] if !found { return 0 } @@ -92,42 +92,43 @@ func (j *journal) getDirty(addr ethcmn.Address) int { return j.dirties[idx].changes } -// addDirty adds 1 to the dirty count of an address. It performs a no-op if the -// address is not found. +// addDirty adds 1 to the dirty count of an address. If the dirty entry is not +// found it creates it. func (j *journal) addDirty(addr ethcmn.Address) { - idx, found := j.addressToIndex[addr] + idx, found := j.addressToJournalIndex[addr] if !found { - return + j.dirties = append(j.dirties, dirty{address: addr, changes: 0}) + idx = len(j.dirties) - 1 + j.addressToJournalIndex[addr] = idx } - dirty := j.dirties[idx] - dirty.changes++ - j.dirties[idx] = dirty + j.dirties[idx].changes++ } // substractDirty subtracts 1 to the dirty count of an address. It performs a // no-op if the address is not found. func (j *journal) substractDirty(addr ethcmn.Address) { - idx, found := j.addressToIndex[addr] + idx, found := j.addressToJournalIndex[addr] if !found { return } - dirty := j.dirties[idx] - dirty.changes-- - j.dirties[idx] = dirty + if j.dirties[idx].changes == 0 { + return + } + j.dirties[idx].changes-- } // deleteDirty deletes a dirty entry from the jounal's dirties slice. If the // entry is not found it performs a no-op. func (j *journal) deleteDirty(addr ethcmn.Address) { - idx, found := j.addressToIndex[addr] + idx, found := j.addressToJournalIndex[addr] if !found { return } j.dirties = append(j.dirties[:idx], j.dirties[idx+1:]...) - delete(j.addressToIndex, addr) + delete(j.addressToJournalIndex, addr) } type ( @@ -189,7 +190,7 @@ type ( func (ch createObjectChange) revert(s *CommitStateDB) { delete(s.stateObjectsDirty, *ch.account) - idx, exists := s.addressToIndex[*ch.account] + idx, exists := s.addressToObjectIndex[*ch.account] if !exists { // perform no-op return diff --git a/x/evm/types/journal_test.go b/x/evm/types/journal_test.go new file mode 100644 index 000000000..f38fd9254 --- /dev/null +++ b/x/evm/types/journal_test.go @@ -0,0 +1,62 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + ethcmn "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/crypto" +) + +type JournalTestSuite struct { + suite.Suite + + address ethcmn.Address + journal *journal +} + +func (suite *JournalTestSuite) TestJournal_append() { + suite.journal.append(createObjectChange{ + account: &suite.address, + }) + suite.Require().Len(suite.journal.entries, 1) + suite.Require().Equal(1, suite.journal.dirties[0].changes) + + suite.journal.append(balanceChange{ + account: &suite.address, + prev: sdk.ZeroInt(), + }) + + suite.Require().Len(suite.journal.entries, 2) + suite.Require().Equal(2, suite.journal.dirties[0].changes) +} + +func (suite *JournalTestSuite) TestJournal_substractDirty() { + suite.journal.substractDirty(suite.address) + suite.Require().Equal(0, suite.journal.getDirty(suite.address)) + + suite.journal.addDirty(suite.address) + suite.Require().Equal(1, suite.journal.getDirty(suite.address)) + + suite.journal.substractDirty(suite.address) + suite.Require().Equal(0, suite.journal.getDirty(suite.address)) + + suite.journal.substractDirty(suite.address) + suite.Require().Equal(0, suite.journal.getDirty(suite.address)) +} + +func (suite *JournalTestSuite) SetupTest() { + privkey, err := crypto.GenerateKey() + suite.Require().NoError(err) + + suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) + suite.journal = newJournal() +} + +func TestJournalTestSuite(t *testing.T) { + suite.Run(t, new(JournalTestSuite)) +} diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index b63e2940c..dc9636bd0 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -47,9 +47,9 @@ type CommitStateDB struct { // array that hold 'live' objects, which will get modified while processing a // state transition - stateObjects []stateEntry - addressToIndex map[ethcmn.Address]int // map from address to the index of the state objects slice - stateObjectsDirty map[ethcmn.Address]struct{} + stateObjects []stateEntry + addressToObjectIndex map[ethcmn.Address]int // map from address to the index of the state objects slice + stateObjectsDirty map[ethcmn.Address]struct{} // The refund counter, also used by state transitioning. refund uint64 @@ -88,15 +88,15 @@ func NewCommitStateDB( ctx sdk.Context, storeKey sdk.StoreKey, ak AccountKeeper, bk BankKeeper, ) *CommitStateDB { return &CommitStateDB{ - ctx: ctx, - storeKey: storeKey, - accountKeeper: ak, - bankKeeper: bk, - stateObjects: []stateEntry{}, - addressToIndex: make(map[ethcmn.Address]int), - stateObjectsDirty: make(map[ethcmn.Address]struct{}), - preimages: make(map[ethcmn.Hash][]byte), - journal: newJournal(), + ctx: ctx, + storeKey: storeKey, + accountKeeper: ak, + bankKeeper: bk, + stateObjects: []stateEntry{}, + addressToObjectIndex: make(map[ethcmn.Address]int), + stateObjectsDirty: make(map[ethcmn.Address]struct{}), + preimages: make(map[ethcmn.Hash][]byte), + journal: newJournal(), } } @@ -432,7 +432,7 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error) // refunds. func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error { for _, dirty := range csdb.journal.dirties { - idx, exist := csdb.addressToIndex[dirty.address] + idx, exist := csdb.addressToObjectIndex[dirty.address] if !exist { // ripeMD is 'touched' at block 1714175, in tx: // 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 @@ -591,7 +591,7 @@ func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool { // next operations. func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error { csdb.stateObjects = []stateEntry{} - csdb.addressToIndex = make(map[ethcmn.Address]int) + csdb.addressToObjectIndex = make(map[ethcmn.Address]int) csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) csdb.thash = ethcmn.Hash{} csdb.bhash = ethcmn.Hash{} @@ -626,7 +626,7 @@ func (csdb *CommitStateDB) UpdateAccounts() { // ClearStateObjects clears cache of state objects to handle account changes outside of the EVM func (csdb *CommitStateDB) ClearStateObjects() { csdb.stateObjects = []stateEntry{} - csdb.addressToIndex = make(map[ethcmn.Address]int) + csdb.addressToObjectIndex = make(map[ethcmn.Address]int) csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) } @@ -670,17 +670,17 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { // copy all the basic fields, initialize the memory ones state := &CommitStateDB{ - ctx: csdb.ctx, - storeKey: csdb.storeKey, - accountKeeper: csdb.accountKeeper, - bankKeeper: csdb.bankKeeper, - stateObjects: make([]stateEntry, len(csdb.journal.dirties)), - addressToIndex: make(map[ethcmn.Address]int, len(csdb.journal.dirties)), - stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)), - refund: csdb.refund, - logSize: csdb.logSize, - preimages: make(map[ethcmn.Hash][]byte), - journal: newJournal(), + ctx: csdb.ctx, + storeKey: csdb.storeKey, + accountKeeper: csdb.accountKeeper, + bankKeeper: csdb.bankKeeper, + stateObjects: make([]stateEntry, len(csdb.journal.dirties)), + addressToObjectIndex: make(map[ethcmn.Address]int, len(csdb.journal.dirties)), + stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)), + refund: csdb.refund, + logSize: csdb.logSize, + preimages: make(map[ethcmn.Hash][]byte), + journal: newJournal(), } // copy the dirty states, logs, and preimages @@ -690,7 +690,7 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { // need to check for nil. // // Ref: https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527 - if idx, exist := csdb.addressToIndex[dirty.address]; exist { + if idx, exist := csdb.addressToObjectIndex[dirty.address]; exist { state.stateObjects[idx] = stateEntry{ address: dirty.address, stateObject: csdb.stateObjects[idx].stateObject.deepCopy(state), @@ -703,7 +703,7 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB { // 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 idx, exist := state.addressToIndex[addr]; !exist { + if idx, exist := state.addressToObjectIndex[addr]; !exist { state.setStateObject(csdb.stateObjects[idx].stateObject.deepCopy(state)) state.stateObjectsDirty[addr] = struct{}{} } @@ -793,7 +793,7 @@ func (csdb *CommitStateDB) setError(err error) { // 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) { - idx, found := csdb.addressToIndex[addr] + idx, found := csdb.addressToObjectIndex[addr] if found { // prefer 'live' (cached) objects if so := csdb.stateObjects[idx].stateObject; so != nil { @@ -822,7 +822,7 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta } func (csdb *CommitStateDB) setStateObject(so *stateObject) { - idx, found := csdb.addressToIndex[so.Address()] + idx, found := csdb.addressToObjectIndex[so.Address()] if found { // update the existing object csdb.stateObjects[idx].stateObject = so @@ -836,7 +836,7 @@ func (csdb *CommitStateDB) setStateObject(so *stateObject) { } csdb.stateObjects = append(csdb.stateObjects, se) - csdb.addressToIndex[se.address] = len(csdb.stateObjects) - 1 + csdb.addressToObjectIndex[se.address] = len(csdb.stateObjects) - 1 } // RawDump returns a raw state dump. diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go index 94bfabfbd..aff7b725d 100644 --- a/x/evm/types/statedb_test.go +++ b/x/evm/types/statedb_test.go @@ -60,3 +60,7 @@ func (suite *StateDBTestSuite) TestBloomFilter() { suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress)) suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2)))) } + +// func TestStateDBTestSuite(t *testing.T) { +// suite.Run(t, new(StateDBTestSuite)) +// } From cacc374f4cfe205a8c9f13d39d9763a84aa667d8 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 7 Jul 2020 17:02:13 +0200 Subject: [PATCH 05/29] local testnet command --- .gitignore | 2 + Makefile | 100 +++--- cmd/emintd/main.go | 2 +- cmd/emintd/testnet.go | 376 +++++++++++++++++++++++ docker-compose.yml | 70 +++++ networks/local/Makefile | 4 + networks/local/ethermint_node/Dockerfile | 23 ++ networks/local/ethermint_node/wrapper.sh | 25 ++ 8 files changed, 541 insertions(+), 61 deletions(-) create mode 100644 cmd/emintd/testnet.go create mode 100644 docker-compose.yml create mode 100644 networks/local/Makefile create mode 100644 networks/local/ethermint_node/Dockerfile create mode 100755 networks/local/ethermint_node/wrapper.sh diff --git a/.gitignore b/.gitignore index 527a1e29f..196a43267 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ dist tools-stamp proto-tools-stamp golangci-lint +local_testnet +keyring_test_cosmos # Testing coverage.txt diff --git a/Makefile b/Makefile index 6ac45fa39..78ba62ab5 100644 --- a/Makefile +++ b/Makefile @@ -21,27 +21,32 @@ ETHERMINT_DAEMON_BINARY = emintd ETHERMINT_CLI_BINARY = emintcli GO_MOD=GO111MODULE=on BINDIR ?= $(GOPATH)/bin +BUILDDIR ?= $(CURDIR)/build SIMAPP = github.com/cosmos/ethermint/app RUNSIM = $(BINDIR)/runsim all: tools verify install -####################### -### Build / Install ### -####################### +############################################################################### +### Build ### +############################################################################### + +build: go.sum + go build -mod=readonly ./... + +build-ethermint: go.sum + mkdir -p $(BUILDDIR) + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILDDIR) ./cmd/$(ETHERMINT_DAEMON_BINARY) + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILDDIR) ./cmd/$(ETHERMINT_CLI_BINARY) + +build-ethermint-linux: go.sum + GOOS=linux GOARCH=amd64 CGO_ENABLED=1 $(MAKE) build-ethermint -build: -ifeq ($(OS),Windows_NT) - ${GO_MOD} go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY).exe ./cmd/emintd - ${GO_MOD} go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY).exe ./cmd/emintcli -else - ${GO_MOD} go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY) ./cmd/emintd/ - ${GO_MOD} go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY) ./cmd/emintcli/ -endif +.PHONY: build build-ethermint build-ethermint-linux install: - ${GO_MOD} go install $(BUILD_FLAGS) ./cmd/emintd - ${GO_MOD} go install $(BUILD_FLAGS) ./cmd/emintcli + ${GO_MOD} go install $(BUILD_FLAGS) ./cmd/$(ETHERMINT_DAEMON_BINARY) + ${GO_MOD} go install $(BUILD_FLAGS) ./cmd/$(ETHERMINT_CLI_BINARY) clean: @rm -rf ./build ./vendor @@ -91,48 +96,6 @@ $(RUNSIM): @(cd /tmp && go get github.com/cosmos/tools/cmd/runsim@v1.0.0) tools: $(RUNSIM) -ifdef GOLINT_CHECK - @echo "Golint is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing golint" - ${GO_MOD} go get -v $(GOLINT) -endif -ifdef GOCILINT_CHECK - @echo "golangci-lint is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing golangci-lint" - ${GO_MOD} go get -v $(GOCILINT) -endif -ifdef UNCONVERT_CHECK - @echo "Unconvert is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing unconvert" - ${GO_MOD} go get -v $(UNCONVERT) -endif -ifdef INEFFASSIGN_CHECK - @echo "Ineffassign is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing ineffassign" - ${GO_MOD} go get -v $(INEFFASSIGN) -endif -ifdef MISSPELL_CHECK - @echo "misspell is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing misspell" - ${GO_MOD} go get -v $(MISSPELL) -endif -ifdef ERRCHECK_CHECK - @echo "errcheck is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing errcheck" - ${GO_MOD} go get -v $(ERRCHECK) -endif -ifdef UNPARAM_CHECK - @echo "unparam is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing unparam" - ${GO_MOD} go get -v $(UNPARAM) -endif ####################### @@ -266,8 +229,8 @@ test-sim-nondeterminism: test-sim-custom-genesis-fast: @echo "Running custom genesis simulation..." - @echo "By default, ${HOME}/.emintd/config/genesis.json will be used." - @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.emintd/config/genesis.json \ + @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." + @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json \ -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h test-sim-import-export: runsim @@ -280,8 +243,8 @@ test-sim-after-import: runsim test-sim-custom-genesis-multi-seed: runsim @echo "Running multi-seed custom genesis simulation..." - @echo "By default, ${HOME}/.emintd/config/genesis.json will be used." - @$(BINDIR)/runsim -Jobs=4 -Genesis=${HOME}/.emintd/config/genesis.json 400 5 TestFullAppSimulation + @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." + @$(BINDIR)/runsim -Jobs=4 -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json 400 5 TestFullAppSimulation test-sim-multi-seed-long: runsim @echo "Running multi-seed application simulation. This may take awhile!" @@ -310,4 +273,21 @@ docs-serve: docs-build: @cd docs && \ npm install && \ - npm run build \ No newline at end of file + npm run build + +############################################################################### +### Localnet ### +############################################################################### + +build-docker-local-ethermint: + @$(MAKE) -C networks/local + +# Run a 4-node testnet locally +localnet-start: build-ethermint-linux localnet-stop + @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z ethermint/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi + docker-compose up -d + +localnet-stop: + docker-compose down + +.PHONY: build-docker-local-ethermint localnet-start localnet-stop diff --git a/cmd/emintd/main.go b/cmd/emintd/main.go index aa0d38fbe..a601bad6e 100644 --- a/cmd/emintd/main.go +++ b/cmd/emintd/main.go @@ -74,7 +74,7 @@ func main() { app.DefaultNodeHome, app.DefaultCLIHome, ), genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics), - + testnetCmd(ctx, cdc, app.ModuleBasics, bank.GenesisBalancesIterator{}), // AddGenesisAccountCmd allows users to add accounts to the genesis file AddGenesisAccountCmd(ctx, cdc, appCodec, app.DefaultNodeHome, app.DefaultCLIHome), flags.NewCompletionCmd(rootCmd, true), diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go new file mode 100644 index 000000000..c3e401c46 --- /dev/null +++ b/cmd/emintd/testnet.go @@ -0,0 +1,376 @@ +package main + +// DONTCOVER + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + + "github.com/spf13/cobra" + tmconfig "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + tmos "github.com/tendermint/tendermint/libs/os" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmtypes "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "github.com/cosmos/cosmos-sdk/client/flags" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/cosmos/ethermint/types" +) + +var ( + flagNodeDirPrefix = "node-dir-prefix" + flagNumValidators = "v" + flagOutputDir = "output-dir" + flagNodeDaemonHome = "node-daemon-home" + flagNodeCLIHome = "node-cli-home" + flagStartingIPAddress = "starting-ip-address" +) + +// get cmd to initialize all files for tendermint testnet and application +func testnetCmd(ctx *server.Context, cdc *codec.Codec, + mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, +) *cobra.Command { + + cmd := &cobra.Command{ + Use: "testnet", + Short: "Initialize files for a Ethermint testnet", + Long: `testnet will create "v" number of directories and populate each with +necessary files (private validator, genesis, config, etc.). + +Note, strict routability for addresses is turned off in the config file.`, + + Example: "simd testnet --v 4 --keyring-backend= test --output-dir ./output --starting-ip-address 192.168.10.2", + RunE: func(cmd *cobra.Command, _ []string) error { + config := ctx.Config + + outputDir, _ := cmd.Flags().GetString(flagOutputDir) + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) + minGasPrices, _ := cmd.Flags().GetString(server.FlagMinGasPrices) + nodeDirPrefix, _ := cmd.Flags().GetString(flagNodeDirPrefix) + nodeDaemonHome, _ := cmd.Flags().GetString(flagNodeDaemonHome) + nodeCLIHome, _ := cmd.Flags().GetString(flagNodeCLIHome) + startingIPAddress, _ := cmd.Flags().GetString(flagStartingIPAddress) + numValidators, _ := cmd.Flags().GetInt(flagNumValidators) + + return InitTestnet( + cmd, config, cdc, mbm, genBalIterator, outputDir, chainID, minGasPrices, + nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, keyringBackend, numValidators, + ) + }, + } + + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") + cmd.Flags().StringP(flagOutputDir, "o", "./local_testnet", "Directory to store initialization data for the testnet") + cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") + cmd.Flags().String(flagNodeDaemonHome, "emintd", "Home directory of the node's daemon configuration") + cmd.Flags().String(flagNodeCLIHome, "emintcli", "Home directory of the node's cli configuration") + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", types.DenomDefault), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photon,0.001stake)") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + + return cmd +} + +const nodeDirPerm = 0755 + +// Initialize the testnet +func InitTestnet( + cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec, + mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, + outputDir, chainID, minGasPrices, nodeDirPrefix, nodeDaemonHome, + nodeCLIHome, startingIPAddress, keyringBackend string, numValidators int, +) error { + + if chainID == "" { + chainID = fmt.Sprintf("%d", tmrand.Int63()) + } + + nodeIDs := make([]string, numValidators) + valPubKeys := make([]crypto.PubKey, numValidators) + + simappConfig := srvconfig.DefaultConfig() + simappConfig.MinGasPrices = minGasPrices + + var ( + genAccounts []authexported.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + inBuf := bufio.NewReader(cmd.InOrStdin()) + // generate private keys, node IDs, and initial transactions + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + clientDir := filepath.Join(outputDir, nodeDirName, nodeCLIHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + + config.SetRoot(nodeDir) + config.RPC.ListenAddress = "tcp://0.0.0.0:26657" + + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + if err := os.MkdirAll(clientDir, nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + config.Moniker = nodeDirName + + ip, err := getIP(i, startingIPAddress) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(config) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + genFiles = append(genFiles, config.GenesisFile()) + + kb, err := keyring.NewKeyring( + sdk.KeyringServiceName(), + keyringBackend, + clientDir, + inBuf, + ) + if err != nil { + return err + } + + keyPass := clientkeys.DefaultKeyPass + addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, keyPass, true) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + info := map[string]string{"secret": secret} + + cliPrint, err := json.Marshal(info) + if err != nil { + return err + } + + // save private key seed words + if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint); err != nil { + return err + } + + accTokens := sdk.TokensFromConsensusPower(1000) + accStakingTokens := sdk.TokensFromConsensusPower(500) + coins := sdk.Coins{ + sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), + sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), + } + + genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: coins.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + valTokens := sdk.TokensFromConsensusPower(100) + msg := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + + tx := authtypes.NewStdTx([]sdk.Msg{msg}, authtypes.StdFee{}, []authtypes.StdSignature{}, memo) //nolint:staticcheck // SA1019: authtypes.StdFee is deprecated + txBldr := authtypes.NewTxBuilderFromCLI(inBuf).WithChainID(chainID).WithMemo(memo).WithKeybase(kb) + + signedTx, err := txBldr.SignStdTx(nodeDirName, clientkeys.DefaultKeyPass, tx, false) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + txBytes, err := cdc.MarshalJSON(signedTx) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + // gather gentxs folder + if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), simappConfig) + } + + if err := initGenFiles(cdc, mbm, chainID, genAccounts, genBalances, genFiles, numValidators); err != nil { + return err + } + + err := collectGenFiles( + cdc, config, chainID, nodeIDs, valPubKeys, numValidators, + outputDir, nodeDirPrefix, nodeDaemonHome, genBalIterator, + ) + if err != nil { + return err + } + + cmd.PrintErrf("Successfully initialized %d node directories\n", numValidators) + return nil +} + +func initGenFiles( + cdc codec.JSONMarshaler, mbm module.BasicManager, chainID string, + genAccounts []authexported.GenesisAccount, genBalances []banktypes.Balance, + genFiles []string, numValidators int, +) error { + + appGenState := mbm.DefaultGenesis(cdc) + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState) + + authGenState.Accounts = genAccounts + appGenState[authtypes.ModuleName] = cdc.MustMarshalJSON(authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = genBalances + appGenState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankGenState) + + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) + if err != nil { + return err + } + + genDoc := tmtypes.GenesisDoc{ + ChainID: chainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < numValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + return nil +} + +func collectGenFiles( + cdc *codec.Codec, config *tmconfig.Config, chainID string, + nodeIDs []string, valPubKeys []crypto.PubKey, + numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string, + genBalIterator banktypes.GenesisBalancesIterator, +) error { + + var appState json.RawMessage + genTime := tmtime.Now() + + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + config.Moniker = nodeDirName + + config.SetRoot(nodeDir) + + nodeID, valPubKey := nodeIDs[i], valPubKeys[i] + initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, nodeID, valPubKey) + + genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile()) + if err != nil { + return err + } + + nodeAppState, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genBalIterator) + if err != nil { + return err + } + + if appState == nil { + // set the canonical application state (they should not differ) + appState = nodeAppState + } + + genFile := config.GenesisFile() + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func getIP(i int, startingIPAddr string) (ip string, err error) { + if len(startingIPAddr) == 0 { + ip, err = server.ExternalIP() + if err != nil { + return "", err + } + return ip, nil + } + return calculateIP(startingIPAddr, i) +} + +func calculateIP(ip string, i int) (string, error) { + ipv4 := net.ParseIP(ip).To4() + if ipv4 == nil { + return "", fmt.Errorf("%v: non ipv4 address", ip) + } + + for j := 0; j < i; j++ { + ipv4[3]++ + } + + return ipv4.String(), nil +} + +func writeFile(name string, dir string, contents []byte) error { + writePath := filepath.Join(dir) + file := filepath.Join(writePath, name) + + err := tmos.EnsureDir(writePath, 0755) + if err != nil { + return err + } + + err = tmos.WriteFile(file, contents, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..e4f77c91f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,70 @@ +version: "3" + +services: + emintdnode0: + container_name: emintdnode0 + image: "ethermint/node" + ports: + - "26656-26657:26656-26657" + - "1317:1317" + environment: + - ID=0 + - LOG=${LOG:-emintd.log} + volumes: + - ./build:/emintd:Z + networks: + localnet: + ipv4_address: 192.168.10.2 + + emintdnode1: + container_name: emintdnode1 + image: "ethermint/node" + ports: + - "26659-26660:26656-26657" + - "1318:1317" + environment: + - ID=1 + - LOG=${LOG:-emintd.log} + volumes: + - ./build:/emintd:Z + networks: + localnet: + ipv4_address: 192.168.10.3 + + emintdnode2: + container_name: emintdnode2 + image: "ethermint/node" + environment: + - ID=2 + - LOG=${LOG:-emintd.log} + ports: + - "26661-26662:26656-26657" + - "1319:1317" + volumes: + - ./build:/emintd:Z + networks: + localnet: + ipv4_address: 192.168.10.4 + + emintdnode3: + container_name: emintdnode3 + image: "ethermint/node" + environment: + - ID=3 + - LOG=${LOG:-emintd.log} + ports: + - "26663-26664:26656-26657" + - "1320:1317" + volumes: + - ./build:/emintd:Z + networks: + localnet: + ipv4_address: 192.168.10.5 + +networks: + localnet: + driver: bridge + ipam: + driver: default + config: + - subnet: 192.168.10.0/16 diff --git a/networks/local/Makefile b/networks/local/Makefile new file mode 100644 index 000000000..87b3916a4 --- /dev/null +++ b/networks/local/Makefile @@ -0,0 +1,4 @@ +all: + docker build --tag ethermint/node node + +.PHONY: all diff --git a/networks/local/ethermint_node/Dockerfile b/networks/local/ethermint_node/Dockerfile new file mode 100644 index 000000000..5ff870d7d --- /dev/null +++ b/networks/local/ethermint_node/Dockerfile @@ -0,0 +1,23 @@ +#FROM alpine:3.10.2 +# +#RUN apk update && \ +# apk upgrade && \ +# apk --no-cache add curl jq file + +# Changed from Alpine to Ubuntu because the keyring PR is linking to libc +# Alpine uses muslc instead of libc + +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get -y install curl jq file + +VOLUME [ /emintd ] +WORKDIR /emintd +EXPOSE 26656 26657 1317 +ENTRYPOINT ["/usr/bin/wrapper.sh"] +CMD ["start"] +STOPSIGNAL SIGTERM + +COPY wrapper.sh /usr/bin/wrapper.sh diff --git a/networks/local/ethermint_node/wrapper.sh b/networks/local/ethermint_node/wrapper.sh new file mode 100755 index 000000000..fdc71b7f9 --- /dev/null +++ b/networks/local/ethermint_node/wrapper.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +BINARY=/emintd/${BINARY:-emintd} +ID=${ID:-0} +LOG=${LOG:-emintd.log} + +if ! [ -f "${BINARY}" ]; then + echo "The binary $(basename "${BINARY}") cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'emintd'" + exit 1 +fi + +BINARY_CHECK="$(file "$BINARY" | grep 'ELF 64-bit LSB executable, x86-64')" + +if [ -z "${BINARY_CHECK}" ]; then + echo "Binary needs to be OS linux, ARCH amd64" + exit 1 +fi + +export EMINTD="/emintd/node${ID}/emintd" + +if [ -d "$(dirname "${EMINTD}"/"${LOG}")" ]; then + "${BINARY}" --home "${EMINTD}" "$@" | tee "${EMINTD}/${LOG}" +else + "${BINARY}" --home "${EMINTD}" "$@" +fi From 9d35880e916ea0ee0703aa12920b253e2d9800f1 Mon Sep 17 00:00:00 2001 From: Holechain Date: Thu, 9 Jul 2020 23:42:06 +0800 Subject: [PATCH 06/29] fix testnet cmd (#383) fix export-eth-key generate eth type account in genesis.json file --- cmd/emintd/testnet.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go index c3e401c46..1a7ddda85 100644 --- a/cmd/emintd/testnet.go +++ b/cmd/emintd/testnet.go @@ -6,6 +6,8 @@ import ( "bufio" "encoding/json" "fmt" + emintCrypto "github.com/cosmos/ethermint/crypto" + ethcrypto "github.com/ethereum/go-ethereum/crypto" "net" "os" "path/filepath" @@ -58,7 +60,7 @@ necessary files (private validator, genesis, config, etc.). Note, strict routability for addresses is turned off in the config file.`, - Example: "simd testnet --v 4 --keyring-backend= test --output-dir ./output --starting-ip-address 192.168.10.2", + Example: "simd testnet --v 4 --keyring-backend test --output-dir ./output --starting-ip-address 192.168.10.2", RunE: func(cmd *cobra.Command, _ []string) error { config := ctx.Config @@ -161,11 +163,16 @@ func InitTestnet( keyringBackend, clientDir, inBuf, + keyring.WithKeygenFunc(ethermintKeygenFunc), ) if err != nil { return err } + cmd.Printf( + "Password for account '%s' :\n", nodeDirName, + ) + keyPass := clientkeys.DefaultKeyPass addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, keyPass, true) if err != nil { @@ -193,7 +200,10 @@ func InitTestnet( } genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: coins.Sort()}) - genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + genAccounts = append(genAccounts, types.EthAccount{ + BaseAccount: authtypes.NewBaseAccount(addr, nil, 0, 0), + CodeHash: ethcrypto.Keccak256(nil), + }) valTokens := sdk.TokensFromConsensusPower(100) msg := stakingtypes.NewMsgCreateValidator( @@ -374,3 +384,7 @@ func writeFile(name string, dir string, contents []byte) error { return nil } + +func ethermintKeygenFunc(bz []byte, algo keyring.SigningAlgo) (crypto.PrivKey, error) { + return emintCrypto.PrivKeySecp256k1(bz), nil +} From f21b88ec0c7f1cfbf2ac33ab18265963ecbe050c Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 9 Jul 2020 18:00:35 +0200 Subject: [PATCH 07/29] fixes --- cmd/emintcli/keys.go | 12 +++--------- cmd/emintd/testnet.go | 26 +++++++++++--------------- crypto/keys.go | 12 ++++++++++++ 3 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 crypto/keys.go diff --git a/cmd/emintcli/keys.go b/cmd/emintcli/keys.go index eb4299659..fd14a34ad 100644 --- a/cmd/emintcli/keys.go +++ b/cmd/emintcli/keys.go @@ -4,14 +4,12 @@ import ( "bufio" "io" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/client/flags" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" - emintCrypto "github.com/cosmos/ethermint/crypto" + "github.com/cosmos/ethermint/crypto" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -54,7 +52,7 @@ func keyCommands() *cobra.Command { func getKeybase(transient bool, buf io.Reader) (keyring.Keybase, error) { if transient { - return keyring.NewInMemory(keyring.WithKeygenFunc(ethermintKeygenFunc)), nil + return keyring.NewInMemory(keyring.WithKeygenFunc(crypto.EthermintKeygenFunc)), nil } return keyring.NewKeyring( @@ -62,7 +60,7 @@ func getKeybase(transient bool, buf io.Reader) (keyring.Keybase, error) { viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf, - keyring.WithKeygenFunc(ethermintKeygenFunc)) + keyring.WithKeygenFunc(crypto.EthermintKeygenFunc)) } func runAddCmd(cmd *cobra.Command, args []string) error { @@ -74,7 +72,3 @@ func runAddCmd(cmd *cobra.Command, args []string) error { return clientkeys.RunAddCmd(cmd, args, kb, inBuf) } - -func ethermintKeygenFunc(bz []byte, algo keyring.SigningAlgo) (crypto.PrivKey, error) { - return emintCrypto.PrivKeySecp256k1(bz), nil -} diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go index 1a7ddda85..42e774143 100644 --- a/cmd/emintd/testnet.go +++ b/cmd/emintd/testnet.go @@ -6,15 +6,14 @@ import ( "bufio" "encoding/json" "fmt" - emintCrypto "github.com/cosmos/ethermint/crypto" - ethcrypto "github.com/ethereum/go-ethereum/crypto" "net" "os" "path/filepath" + ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/spf13/cobra" tmconfig "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" + tmcrypto "github.com/tendermint/tendermint/crypto" tmos "github.com/tendermint/tendermint/libs/os" tmrand "github.com/tendermint/tendermint/libs/rand" tmtypes "github.com/tendermint/tendermint/types" @@ -35,6 +34,7 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/ethermint/crypto" "github.com/cosmos/ethermint/types" ) @@ -47,6 +47,8 @@ var ( flagStartingIPAddress = "starting-ip-address" ) +const nodeDirPerm = 0755 + // get cmd to initialize all files for tendermint testnet and application func testnetCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, @@ -94,9 +96,7 @@ Note, strict routability for addresses is turned off in the config file.`, return cmd } -const nodeDirPerm = 0755 - -// Initialize the testnet +// InitTestnet initializes the testnet configuration func InitTestnet( cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, @@ -109,7 +109,7 @@ func InitTestnet( } nodeIDs := make([]string, numValidators) - valPubKeys := make([]crypto.PubKey, numValidators) + valPubKeys := make([]tmcrypto.PubKey, numValidators) simappConfig := srvconfig.DefaultConfig() simappConfig.MinGasPrices = minGasPrices @@ -163,7 +163,7 @@ func InitTestnet( keyringBackend, clientDir, inBuf, - keyring.WithKeygenFunc(ethermintKeygenFunc), + keyring.WithKeygenFunc(crypto.EthermintKeygenFunc), ) if err != nil { return err @@ -196,7 +196,7 @@ func InitTestnet( accStakingTokens := sdk.TokensFromConsensusPower(500) coins := sdk.Coins{ sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), - sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), + sdk.NewCoin(types.DenomDefault, accStakingTokens), } genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: coins.Sort()}) @@ -209,7 +209,7 @@ func InitTestnet( msg := stakingtypes.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + sdk.NewCoin(types.DenomDefault, valTokens), stakingtypes.NewDescription(nodeDirName, "", "", "", ""), stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), sdk.OneInt(), @@ -299,7 +299,7 @@ func initGenFiles( func collectGenFiles( cdc *codec.Codec, config *tmconfig.Config, chainID string, - nodeIDs []string, valPubKeys []crypto.PubKey, + nodeIDs []string, valPubKeys []tmcrypto.PubKey, numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, ) error { @@ -384,7 +384,3 @@ func writeFile(name string, dir string, contents []byte) error { return nil } - -func ethermintKeygenFunc(bz []byte, algo keyring.SigningAlgo) (crypto.PrivKey, error) { - return emintCrypto.PrivKeySecp256k1(bz), nil -} diff --git a/crypto/keys.go b/crypto/keys.go new file mode 100644 index 000000000..c3cdb7931 --- /dev/null +++ b/crypto/keys.go @@ -0,0 +1,12 @@ +package crypto + +import ( + "github.com/cosmos/cosmos-sdk/crypto/keyring" + tmcrypto "github.com/tendermint/tendermint/crypto" +) + +// EthermintKeygenFunc is the key generation function to generate secp256k1 ToECDSA +// from ethereum. +func EthermintKeygenFunc(bz []byte, algo keyring.SigningAlgo) (tmcrypto.PrivKey, error) { + return PrivKeySecp256k1(bz), nil +} From c36f498a93b648f25e7b83588ccc859592b5b4a7 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 9 Jul 2020 18:50:56 +0200 Subject: [PATCH 08/29] update docker --- Dockerfile | 22 ++-- Makefile | 121 +++++++----------- docker-compose.yml | 8 +- .../Dockerfile | 0 .../wrapper.sh | 8 +- 5 files changed, 67 insertions(+), 92 deletions(-) rename networks/local/{ethermint_node => ethermintnode}/Dockerfile (100%) rename networks/local/{ethermint_node => ethermintnode}/wrapper.sh (70%) diff --git a/Dockerfile b/Dockerfile index aad531549..d4c13c045 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,22 @@ FROM golang:alpine AS build-env -# Set up dependencies -ENV PACKAGES git build-base +# Install minimum necessary dependencies +ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 +RUN apk add --no-cache $PACKAGES # Set working directory for the build -WORKDIR /go/src/github.com/Chainsafe/ethermint - -# Install dependencies -RUN apk add --update $PACKAGES +WORKDIR /go/src/github.com/ChainSafe/ethermint # Add source files COPY . . -# Make the binary -RUN make build +# build Cosmos SDK, remove packages +RUN make build-ethermint && \ + cp ./build/emint* /go/bin + # Final image -FROM alpine +FROM alpine:edge # Install ca-certificates RUN apk add --update ca-certificates @@ -26,5 +26,7 @@ WORKDIR /root COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintd /usr/bin/emintd COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintcli /usr/bin/emintcli -# Run emintd by default +EXPOSE 26656 26657 1317 + +# Run emintd by default, omit entrypoint to ease using container with emintcli CMD ["emintd"] diff --git a/Makefile b/Makefile index 78ba62ab5..c8ff35ccb 100644 --- a/Makefile +++ b/Makefile @@ -61,30 +61,9 @@ verify: ${GO_MOD} go mod verify -############################ -### Tools / Dependencies ### -############################ - -########################################################## -### TODO: Move tool depedencies to a separate makefile ### -########################################################## - -GOLINT = github.com/tendermint/lint/golint -GOCILINT = github.com/golangci/golangci-lint/cmd/golangci-lint -UNCONVERT = github.com/mdempsky/unconvert -INEFFASSIGN = github.com/gordonklaus/ineffassign -MISSPELL = github.com/client9/misspell/cmd/misspell -ERRCHECK = github.com/kisielk/errcheck -UNPARAM = mvdan.cc/unparam - -GOLINT_CHECK := $(shell command -v golint 2> /dev/null) -GOCILINT_CHECK := $(shell command -v golangci-lint 2> /dev/null) -UNCONVERT_CHECK := $(shell command -v unconvert 2> /dev/null) -INEFFASSIGN_CHECK := $(shell command -v ineffassign 2> /dev/null) -MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null) -ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null) -UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null) - +############################################################################### +### Tools & Dependencies ### +############################################################################### # Install the runsim binary with a temporary workaround of entering an outside # directory as the "go get" command ignores the -mod option and will polute the @@ -97,10 +76,9 @@ $(RUNSIM): tools: $(RUNSIM) - -####################### -### Testing / Misc. ### -####################### +############################################################################### +### Tests & Simulation ### +############################################################################### test: test-unit @@ -117,9 +95,40 @@ test-import: test-rpc: ./scripts/integration-test-all.sh -q 1 -z 1 -s 2 -godocs: - @echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint" - godoc -http=:6060 +test-sim-nondeterminism: + @echo "Running non-determinism test..." + @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h + +test-sim-custom-genesis-fast: + @echo "Running custom genesis simulation..." + @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." + @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json \ + -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h + +test-sim-import-export: runsim + @echo "Running Ethermint import/export simulation. This may take several minutes..." + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 25 5 TestAppImportExport + +test-sim-after-import: runsim + @echo "Running Ethermint simulation-after-import. This may take several minutes..." + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 25 5 TestAppSimulationAfterImport + +test-sim-custom-genesis-multi-seed: runsim + @echo "Running multi-seed custom genesis simulation..." + @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." + @$(BINDIR)/runsim -Jobs=4 -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json 400 5 TestFullAppSimulation + +test-sim-multi-seed-long: runsim + @echo "Running multi-seed application simulation. This may take awhile!" + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 500 50 TestFullAppSimulation + +test-sim-multi-seed-short: runsim + @echo "Running multi-seed application simulation. This may take awhile!" + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 50 10 TestFullAppSimulation + +.PHONY: runsim test-sim-nondeterminism test-sim-custom-genesis-fast test-sim-fast sim-import-export \ + test-sim-simulation-after-import test-sim-custom-genesis-multi-seed test-sim-multi-seed docker: docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . @@ -218,50 +227,10 @@ proto-update-deps: .PHONY: proto-all proto-gen proto-lint proto-check-breaking proto-update-deps -####################### -### Simulations ### -####################### -test-sim-nondeterminism: - @echo "Running non-determinism test..." - @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h - -test-sim-custom-genesis-fast: - @echo "Running custom genesis simulation..." - @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." - @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json \ - -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h - -test-sim-import-export: runsim - @echo "Running Ethermint import/export simulation. This may take several minutes..." - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 25 5 TestAppImportExport - -test-sim-after-import: runsim - @echo "Running Ethermint simulation-after-import. This may take several minutes..." - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 25 5 TestAppSimulationAfterImport - -test-sim-custom-genesis-multi-seed: runsim - @echo "Running multi-seed custom genesis simulation..." - @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." - @$(BINDIR)/runsim -Jobs=4 -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json 400 5 TestFullAppSimulation - -test-sim-multi-seed-long: runsim - @echo "Running multi-seed application simulation. This may take awhile!" - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 500 50 TestFullAppSimulation - -test-sim-multi-seed-short: runsim - @echo "Running multi-seed application simulation. This may take awhile!" - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 50 10 TestFullAppSimulation - -.PHONY: runsim test-sim-nondeterminism test-sim-custom-genesis-fast test-sim-fast sim-import-export \ - test-sim-simulation-after-import test-sim-custom-genesis-multi-seed test-sim-multi-seed \ - - - -####################### -### Documentation ### -####################### +############################################################################### +### Documentation ### +############################################################################### # Start docs site at localhost:8080 docs-serve: @@ -275,6 +244,10 @@ docs-build: npm install && \ npm run build +godocs: + @echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint" + godoc -http=:6060 + ############################################################################### ### Localnet ### ############################################################################### diff --git a/docker-compose.yml b/docker-compose.yml index e4f77c91f..88c0fff58 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: emintdnode0: container_name: emintdnode0 - image: "ethermint/node" + image: "ethermint/ethermintnode" ports: - "26656-26657:26656-26657" - "1317:1317" @@ -18,7 +18,7 @@ services: emintdnode1: container_name: emintdnode1 - image: "ethermint/node" + image: "ethermint/ethermintnode" ports: - "26659-26660:26656-26657" - "1318:1317" @@ -33,7 +33,7 @@ services: emintdnode2: container_name: emintdnode2 - image: "ethermint/node" + image: "ethermint/ethermintnode" environment: - ID=2 - LOG=${LOG:-emintd.log} @@ -48,7 +48,7 @@ services: emintdnode3: container_name: emintdnode3 - image: "ethermint/node" + image: "ethermint/ethermintnode" environment: - ID=3 - LOG=${LOG:-emintd.log} diff --git a/networks/local/ethermint_node/Dockerfile b/networks/local/ethermintnode/Dockerfile similarity index 100% rename from networks/local/ethermint_node/Dockerfile rename to networks/local/ethermintnode/Dockerfile diff --git a/networks/local/ethermint_node/wrapper.sh b/networks/local/ethermintnode/wrapper.sh similarity index 70% rename from networks/local/ethermint_node/wrapper.sh rename to networks/local/ethermintnode/wrapper.sh index fdc71b7f9..5ed589487 100755 --- a/networks/local/ethermint_node/wrapper.sh +++ b/networks/local/ethermintnode/wrapper.sh @@ -16,10 +16,10 @@ if [ -z "${BINARY_CHECK}" ]; then exit 1 fi -export EMINTD="/emintd/node${ID}/emintd" +export EMINTDHOME="/emintd/node${ID}/emintd" -if [ -d "$(dirname "${EMINTD}"/"${LOG}")" ]; then - "${BINARY}" --home "${EMINTD}" "$@" | tee "${EMINTD}/${LOG}" +if [ -d "$(dirname "${EMINTDHOME}"/"${LOG}")" ]; then + "${BINARY}" --home "${EMINTDHOME}" "$@" | tee "${EMINTDHOME}/${LOG}" else - "${BINARY}" --home "${EMINTD}" "$@" + "${BINARY}" --home "${EMINTDHOME}" "$@" fi From 3e1b09bcb8d4a5f98c2a1f06bff589a965835c42 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 13 Jul 2020 22:08:59 +0200 Subject: [PATCH 09/29] minor changes --- .codecov.yml | 2 ++ .gitignore | 1 + 2 files changed, 3 insertions(+) diff --git a/.codecov.yml b/.codecov.yml index 2517bd53f..74317c508 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -53,6 +53,8 @@ flags: ignore: - "docs" - "*.md" + - "cmd" + - "x/faucet" - "**/*.pb.go" - "types/*.pb.go" - "x/**/*.pb.go" diff --git a/.gitignore b/.gitignore index 196a43267..5d9020fad 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ .glide/ vendor build +bin tools/bin/* docs/_build docs/tutorial From b4c20e15dd600f89a1a15225f5b2a9ab3e6e0906 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jul 2020 09:50:23 +0200 Subject: [PATCH 10/29] fix build-docker-local-ethermint --- Makefile | 2 +- networks/local/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c8ff35ccb..74ff63f50 100644 --- a/Makefile +++ b/Makefile @@ -257,7 +257,7 @@ build-docker-local-ethermint: # Run a 4-node testnet locally localnet-start: build-ethermint-linux localnet-stop - @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z ethermint/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi + @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi docker-compose up -d localnet-stop: diff --git a/networks/local/Makefile b/networks/local/Makefile index 87b3916a4..f5c867a89 100644 --- a/networks/local/Makefile +++ b/networks/local/Makefile @@ -1,4 +1,4 @@ all: - docker build --tag ethermint/node node + docker build --tag emintd/node ethermintnode .PHONY: all From f004dc975864b9cc02be5a6e7864c9f351cececd Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jul 2020 10:48:49 +0200 Subject: [PATCH 11/29] fix dockerfile --- Dockerfile | 12 +++++------- Makefile | 13 ++++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index d4c13c045..ed639541c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM golang:alpine AS build-env # Install minimum necessary dependencies -ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 +ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev RUN apk add --no-cache $PACKAGES # Set working directory for the build @@ -10,10 +10,8 @@ WORKDIR /go/src/github.com/ChainSafe/ethermint # Add source files COPY . . -# build Cosmos SDK, remove packages -RUN make build-ethermint && \ - cp ./build/emint* /go/bin - +# build Ethermint +RUN make build-ethermint-linux # Final image FROM alpine:edge @@ -23,8 +21,8 @@ RUN apk add --update ca-certificates WORKDIR /root # Copy over binaries from the build-env -COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintd /usr/bin/emintd -COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintcli /usr/bin/emintcli +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintd /usr/bin/emintd +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintcli /usr/bin/emintcli EXPOSE 26656 26657 1317 diff --git a/Makefile b/Makefile index 74ff63f50..1bbc39c2a 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,11 @@ verify: @echo "--> Verifying dependencies have not been modified" ${GO_MOD} go mod verify +docker: + docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . + docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest + docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} + ############################################################################### ### Tools & Dependencies ### @@ -130,12 +135,6 @@ test-sim-multi-seed-short: runsim .PHONY: runsim test-sim-nondeterminism test-sim-custom-genesis-fast test-sim-fast sim-import-export \ test-sim-simulation-after-import test-sim-custom-genesis-multi-seed test-sim-multi-seed -docker: - docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . - docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest - docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} - - .PHONY: build install update-tools tools godocs clean format lint \ test-cli test-race test-unit test test-import @@ -256,7 +255,7 @@ build-docker-local-ethermint: @$(MAKE) -C networks/local # Run a 4-node testnet locally -localnet-start: build-ethermint-linux localnet-stop +localnet-start: localnet-stop @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi docker-compose up -d From 03929493d10faebdb8a72cd66ba7df340cec3206 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jul 2020 10:50:18 +0200 Subject: [PATCH 12/29] update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1bbc39c2a..802d7fcf5 100644 --- a/Makefile +++ b/Makefile @@ -255,7 +255,7 @@ build-docker-local-ethermint: @$(MAKE) -C networks/local # Run a 4-node testnet locally -localnet-start: localnet-stop +localnet-start: build-ethermint-linux localnet-stop @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi docker-compose up -d From cf3d14a5403f1a6a44cbcf0e302b14bf25669b92 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jul 2020 14:46:46 +0200 Subject: [PATCH 13/29] update denoms --- cmd/emintd/testnet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go index 42e774143..83d5867ca 100644 --- a/cmd/emintd/testnet.go +++ b/cmd/emintd/testnet.go @@ -195,7 +195,7 @@ func InitTestnet( accTokens := sdk.TokensFromConsensusPower(1000) accStakingTokens := sdk.TokensFromConsensusPower(500) coins := sdk.Coins{ - sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), + sdk.NewCoin(sdk.DefaultBondDenom, accTokens), sdk.NewCoin(types.DenomDefault, accStakingTokens), } From 049559293dee382f6a34a155515fb790b591a56d Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 14 Jul 2020 16:05:18 +0200 Subject: [PATCH 14/29] update genesis file --- cmd/emintd/testnet.go | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go index 83d5867ca..06946bc66 100644 --- a/cmd/emintd/testnet.go +++ b/cmd/emintd/testnet.go @@ -30,8 +30,11 @@ import ( authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/ethermint/crypto" @@ -193,13 +196,13 @@ func InitTestnet( } accTokens := sdk.TokensFromConsensusPower(1000) - accStakingTokens := sdk.TokensFromConsensusPower(500) - coins := sdk.Coins{ + accStakingTokens := sdk.TokensFromConsensusPower(5000) + coins := sdk.NewCoins( sdk.NewCoin(sdk.DefaultBondDenom, accTokens), sdk.NewCoin(types.DenomDefault, accStakingTokens), - } + ) - genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: coins.Sort()}) + genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: coins}) genAccounts = append(genAccounts, types.EthAccount{ BaseAccount: authtypes.NewBaseAccount(addr, nil, 0, 0), CodeHash: ethcrypto.Keccak256(nil), @@ -277,6 +280,30 @@ func initGenFiles( bankGenState.Balances = genBalances appGenState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankGenState) + var stakingGenState stakingtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[stakingtypes.ModuleName], &stakingGenState) + + stakingGenState.Params.BondDenom = types.DenomDefault + appGenState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingGenState) + + var govGenState govtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[govtypes.ModuleName], &govGenState) + + govGenState.DepositParams.MinDeposit[0].Denom = types.DenomDefault + appGenState[govtypes.ModuleName] = cdc.MustMarshalJSON(govGenState) + + var mintGenState minttypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[minttypes.ModuleName], &mintGenState) + + mintGenState.Params.MintDenom = types.DenomDefault + appGenState[minttypes.ModuleName] = cdc.MustMarshalJSON(mintGenState) + + var crisisGenState crisistypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[crisistypes.ModuleName], &crisisGenState) + + crisisGenState.ConstantFee.Denom = types.DenomDefault + appGenState[crisistypes.ModuleName] = cdc.MustMarshalJSON(crisisGenState) + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) if err != nil { return err From ab6c56477d610eb5f34b400fc3ab7c8a8871fac8 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 15 Jul 2020 18:54:46 +0200 Subject: [PATCH 15/29] update makefile --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 802d7fcf5..402203689 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,10 @@ docker: docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} + # update old container + docker rm ethermint + # create a new container from the latest image + docker create --name ethermint -t -i cosmos/ethermint:latest ethermint ############################################################################### @@ -255,7 +259,7 @@ build-docker-local-ethermint: @$(MAKE) -C networks/local # Run a 4-node testnet locally -localnet-start: build-ethermint-linux localnet-stop +localnet-start: docker localnet-stop @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi docker-compose up -d From 592dde752180acde8182f7115a8863284bd6c087 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 15 Jul 2020 19:12:33 +0200 Subject: [PATCH 16/29] fix docker-compose.yml images --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 88c0fff58..4b3f2e41c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: emintdnode0: container_name: emintdnode0 - image: "ethermint/ethermintnode" + image: "cosmos/ethermint:latest" ports: - "26656-26657:26656-26657" - "1317:1317" @@ -18,7 +18,7 @@ services: emintdnode1: container_name: emintdnode1 - image: "ethermint/ethermintnode" + image: "cosmos/ethermint:latest" ports: - "26659-26660:26656-26657" - "1318:1317" @@ -33,7 +33,7 @@ services: emintdnode2: container_name: emintdnode2 - image: "ethermint/ethermintnode" + image: "cosmos/ethermint:latest" environment: - ID=2 - LOG=${LOG:-emintd.log} @@ -48,7 +48,7 @@ services: emintdnode3: container_name: emintdnode3 - image: "ethermint/ethermintnode" + image: "cosmos/ethermint:latest" environment: - ID=3 - LOG=${LOG:-emintd.log} From de3f05d66b5e9769bbbc204af160012c4f16af1b Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 21 Jul 2020 10:02:02 +0200 Subject: [PATCH 17/29] fix localnet execution (#398) --- Dockerfile | 7 ++++--- Makefile | 10 ++++++++-- docker-compose.yml | 8 ++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index ed639541c..ce86afdf5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ -FROM golang:alpine AS build-env +FROM golang:stretch as build-env # Install minimum necessary dependencies -ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev -RUN apk add --no-cache $PACKAGES +ENV PACKAGES curl make git libc-dev bash gcc +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y $PACKAGES # Set working directory for the build WORKDIR /go/src/github.com/ChainSafe/ethermint diff --git a/Makefile b/Makefile index 802d7fcf5..13f3992e8 100644 --- a/Makefile +++ b/Makefile @@ -255,8 +255,14 @@ build-docker-local-ethermint: @$(MAKE) -C networks/local # Run a 4-node testnet locally -localnet-start: build-ethermint-linux localnet-stop - @if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi +localnet-start: localnet-stop + mkdir -p ./build/ + docker rmi -f ethermint-build-linux + docker build --rm -t ethermint-build-linux . ; \ + container_id=$$(docker create ethermint-build-linux) ; \ + docker cp $${container_id}:/usr/bin/emintd ./build/ ; \ + docker cp $${container_id}:/usr/bin/emintcli ./build/ + if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi docker-compose up -d localnet-stop: diff --git a/docker-compose.yml b/docker-compose.yml index 88c0fff58..e0e49b37f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: emintdnode0: container_name: emintdnode0 - image: "ethermint/ethermintnode" + image: "emintd/node" ports: - "26656-26657:26656-26657" - "1317:1317" @@ -18,7 +18,7 @@ services: emintdnode1: container_name: emintdnode1 - image: "ethermint/ethermintnode" + image: "emintd/node" ports: - "26659-26660:26656-26657" - "1318:1317" @@ -33,7 +33,7 @@ services: emintdnode2: container_name: emintdnode2 - image: "ethermint/ethermintnode" + image: "emintd/node" environment: - ID=2 - LOG=${LOG:-emintd.log} @@ -48,7 +48,7 @@ services: emintdnode3: container_name: emintdnode3 - image: "ethermint/ethermintnode" + image: "emintd/node" environment: - ID=3 - LOG=${LOG:-emintd.log} From 184c9abf4e331839a54718541bad66302dbcffb8 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jul 2020 11:44:52 +0200 Subject: [PATCH 18/29] finish documentation --- Makefile | 4 + cmd/emintd/testnet.go | 2 +- docs/quickstart/installation.md | 16 ++- docs/quickstart/run_node.md | 41 +----- docs/quickstart/testnet.md | 212 +++++++++++++++++++++++++++-- docs/quickstart/validator-setup.md | 4 +- 6 files changed, 229 insertions(+), 50 deletions(-) diff --git a/Makefile b/Makefile index 6b457343f..286eae06e 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,10 @@ docker: docker rm ethermint # create a new container from the latest image docker create --name ethermint -t -i cosmos/ethermint:latest ethermint + # move the binaries to the ./build directory + mkdir -p ./build/ + docker cp ethermint:/usr/bin/emintd ./build/ ; \ + docker cp ethermint:/usr/bin/emintcli ./build/ ############################################################################### diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go index 06946bc66..5e165c559 100644 --- a/cmd/emintd/testnet.go +++ b/cmd/emintd/testnet.go @@ -65,7 +65,7 @@ necessary files (private validator, genesis, config, etc.). Note, strict routability for addresses is turned off in the config file.`, - Example: "simd testnet --v 4 --keyring-backend test --output-dir ./output --starting-ip-address 192.168.10.2", + Example: "emintd testnet --v 4 --keyring-backend test --output-dir ./output --starting-ip-address 192.168.10.2", RunE: func(cmd *cobra.Command, _ []string) error { config := ctx.Config diff --git a/docs/quickstart/installation.md b/docs/quickstart/installation.md index 2bf55efdd..01d16ed08 100644 --- a/docs/quickstart/installation.md +++ b/docs/quickstart/installation.md @@ -21,9 +21,21 @@ emintd -h emintcli -h ``` - +## Docker - +You can build Ethermint using Docker by running: + +```bash +make docker +``` + +This will install the binaries on the `./build` directory. Now, check that the binaries have been +successfuly installed: + +```bash +emintd -h +emintcli -h +``` ## Releases diff --git a/docs/quickstart/run_node.md b/docs/quickstart/run_node.md index 43cad4e64..5ef321a1c 100644 --- a/docs/quickstart/run_node.md +++ b/docs/quickstart/run_node.md @@ -10,7 +10,7 @@ Run a local node and start the REST and JSON-RPC clients {synopsis} - [Installation](./installation.md) {prereq} -## Script deployment +## Automated deployment Run the local node with faucet enabled: @@ -29,43 +29,10 @@ In another terminal window or tab, run the Ethereum JSON-RPC server as well as t emintcli rest-server --laddr "tcp://localhost:8545" --unlock-key mykey --chain-id 8 ``` -## Manual setup +## Manual deployment -These instructions are for setting up a brand new full node from scratch. - -First, initialize the node and create the necessary config files: - -```bash -emintd init -``` - -::: warning -Monikers can contain only ASCII characters. Using Unicode characters will render your node unreachable. -::: - -You can edit this `moniker` later, in the `$(HOME)/.emintd/config/config.toml` file: - -```toml -# A custom human readable name for this node -moniker = "" -``` - -You can edit the `$HOME/.emintd/config/app.toml` file in order to enable the anti spam mechanism and reject incoming transactions with less than the minimum gas prices: - -```toml -# This is a TOML config file. -# For more information, see https://github.com/toml-lang/toml - -##### main base config options ##### - -# The minimum gas prices a validator is willing to accept for processing a -# transaction. A transaction's fees must meet the minimum of any denomination -# specified in this config (e.g. 10uatom). - -minimum-gas-prices = "" -``` - -Your full node is now initiallized. +The instructions for setting up a brand new full node from scratch are the the same as running a +[single node local testnet](./testnet.md#single-node-local-manual-testnet). ## Start node diff --git a/docs/quickstart/testnet.md b/docs/quickstart/testnet.md index e625b5bc0..f4056a11f 100644 --- a/docs/quickstart/testnet.md +++ b/docs/quickstart/testnet.md @@ -4,19 +4,215 @@ order: 3 # Testnet -Learn how to deploy a local testnet or connect to an existing one {synopsis} +Learn how to deploy a local testnet or connect to an existing public one {synopsis} ## Pre-requisite Readings -- [Run Node](./run_node.md) {prereq} +- [Install Ethermint](./installation.md) {prereq} +- [Install Docker](https://docs.docker.com/engine/installation/) {prereq} +- [Install docker-compose](https://docs.docker.com/compose/install/) {prereq} + -## Genesis and Seeds +## Single-node, Local, Manual Testnet -### Copy the Genesis File +This guide helps you create a single validator node that runs a network locally for testing and other development related uses. + +### Initialize node + +```bash +$MONIKER=testing +$KEY=mykey +$CHAINID=8 + +emintd init $MONIKER --chain-id=$CHAINID +``` + +::: warning +Monikers can contain only ASCII characters. Using Unicode characters will render your node unreachable. +::: + +You can edit this `moniker` later, in the `$(HOME)/.emintd/config/config.toml` file: + +```toml +# A custom human readable name for this node +moniker = "" +``` + +You can edit the `$HOME/.emintd/config/app.toml` file in order to enable the anti spam mechanism and reject incoming transactions with less than the minimum gas prices: + +```toml +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# The minimum gas prices a validator is willing to accept for processing a +# transaction. A transaction's fees must meet the minimum of any denomination +# specified in this config (e.g. 10photon). + +minimum-gas-prices = "" +``` + +### Genesis Procedure + +```bash +# Create a key to hold your account +emintcli keys add $KEY + +# Add that key into the genesis.app_state.accounts array in the genesis file +# NOTE: this command lets you set the number of coins. Make sure this account has some coins +# with the genesis.app_state.staking.params.bond_denom denom, the default is staking +emintd add-genesis-account $(emintcli keys show validator -a) 1000000000stake,10000000000photon + +# Generate the transaction that creates your validator +emintd gentx --name $KEY + +# Add the generated bonding transaction to the genesis file +emintd collect-gentxs + +# Finally, check the correctness of the genesis.json file +emintd validate-genesis +``` + +### Run Testnet + +Now its safe to start the daemon: + +```bash +emintd start +``` + +## Multi-node, Local, Automated Testnet + +### Build & Run Testnet + +To build start a 4 node testnet run: + +```bash +make localnet-start +``` + +This command creates a 4-node network using the `emintdnode` Docker image. +The ports for each node are found in this table: + +| Node ID | P2P Port | REST/RPC Port | +|--------------|----------|---------------| +| `emintnode0` | `26656` | `26657` | +| `emintnode1` | `26659` | `26660` | +| `emintnode2` | `26661` | `26662` | +| `emintnode3` | `26663` | `26664` | + +To update the binary, just rebuild it and restart the nodes: + +```bash +make localnet-start +``` + +### Stop Testnet + +Once you are done, execute: + +```bash +make localnet-stop +``` + +### Configuration + +The `make localnet-start` creates files for a 4-node testnet in `./build` by +calling the `emintd testnet` command. This outputs a handful of files in the +`./build` directory: + +```bash +tree -L 3 build/ + +build/ +├── emintcli +├── emintd +├── gentxs +│   ├── node0.json +│   ├── node1.json +│   ├── node2.json +│   └── node3.json +├── node0 +│   ├── emintcli +│   │   ├── key_seed.json +│   │   └── keyring-test-cosmos +│   └── emintd +│   ├── config +│   ├── data +│   └── emintd.log +├── node1 +│   ├── emintcli +│   │   ├── key_seed.json +│   │   └── keyring-test-cosmos +│   └── emintd +│   ├── config +│   ├── data +│   └── emintd.log +├── node2 +│   ├── emintcli +│   │   ├── key_seed.json +│   │   └── keyring-test-cosmos +│   └── emintd +│   ├── config +│   ├── data +│   └── emintd.log +└── node3 + ├── emintcli + │   ├── key_seed.json + │   └── keyring-test-cosmos + └── emintd + ├── config + ├── data + └── emintd.log +``` + +Each `./build/nodeN` directory is mounted to the `/emintd` directory in each container. + +### Logging + +Logs are saved under each `./build/nodeN/emintd/gaia.log`. You can also watch logs +directly via Docker, for example: + +```bash +docker logs -f emintdnode0 +``` + +### Keys & Accounts + +To interact with `emintcli` and start querying state or creating txs, you use the +`emintcli` directory of any given node as your `home`, for example: + +```bash +emintcli keys list --home ./build/node0/emintcli +``` + +Now that accounts exists, you may create new accounts and send those accounts +funds! + +::: tip +**Note**: Each node's seed is located at `./build/nodeN/emintcli/key_seed.json` and can be restored to the CLI using the `emintcli keys add --restore` command +::: + +### Special Binaries + +If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume. For example: + +```bash +# Run with custom binary +BINARY=ethermint make localnet-start +``` + +## Multi-node, Public, Manual Testnet + +If you are looking to connect to a persistent public testnet. You will need to manually configure your node. + +### Genesis and Seeds + +#### Copy the Genesis File - ::: tip -If you want to start a network from scratch, you will need to start the genesis procedure. +If you want to start a network from scratch, you will need to start the [genesis procedure](#genesis-procedure) by creating a `genesis.json` and submit + collect the genesis transactions from the [validators](./validator-setup.md). ::: If you want to connect to an existing testnet, fetch the testnet's `genesis.json` file and copy it into the `emintd`'s config directory (i.e `$HOME/.emintd/config/genesis.json`). @@ -27,13 +223,13 @@ Then verify the correctness of the genesis configuration file: emintd validate-genesis ``` -### Add Seed Nodes +#### Add Seed Nodes Your node needs to know how to find peers. You'll need to add healthy seed nodes to `$HOME/.emintd/config/config.toml`. If those seeds aren't working, you can find more seeds and persistent peers on an existing explorer. For more information on seeds and peers, you can the Tendermint [P2P documentation](https://docs.tendermint.com/master/spec/p2p/peer.html). -### Start testnet +#### Start testnet The final step is to [start the nodes](./run_node.md#start-node). Once enough voting power (+2/3) from the genesis validators is up-and-running, the testnet will start producing blocks. diff --git a/docs/quickstart/validator-setup.md b/docs/quickstart/validator-setup.md index acc968873..7bb6c0190 100644 --- a/docs/quickstart/validator-setup.md +++ b/docs/quickstart/validator-setup.md @@ -56,7 +56,7 @@ When specifying commission parameters, the `commission-max-change-rate` is used You can confirm that you are in the validator set by using a third party explorer. -## Genesis transactions +## Genesis Transactions A genesis transaction (aka `gentx`) is a JSON file carrying a self-delegation from a validator. All genesis transactions are collected by a genesis coordinator and validated against an initial `genesis.json` file. @@ -106,7 +106,7 @@ encoded `address` in the `~/.emintd/config/priv_validator.json` file. To be in the validator set, you need to have more total voting power than the 100th validator. ::: -## Halting Your Validator +## Halt Your Validator Node When attempting to perform routine maintenance or planning for an upcoming coordinated upgrade, it can be useful to have your validator systematically and gracefully halt the chain and shutdown the node. From 505c17bacd82a73434fa9ea3dcb81a00608e9a0d Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jul 2020 11:53:57 +0200 Subject: [PATCH 19/29] changelog and comment rpc tests workflow --- .github/workflows/test.yml | 34 +++++++++++++++++----------------- CHANGELOG.md | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fe47c9296..0970dbbd8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -176,23 +176,23 @@ jobs: file: ./coverage.txt fail_ci_if_error: true if: "env.GIT_DIFF != ''" - - rpc-tests: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v1 - id: git_diff - with: - SUFFIX_FILTER: | - .go - .mod - .sum - - name: rpc-test - run: | - make test-rpc - if: "env.GIT_DIFF != ''" + # FIXME: no tests currently running + # rpc-tests: + # runs-on: ubuntu-latest + # timeout-minutes: 10 + # steps: + # - uses: actions/checkout@v2 + # - uses: technote-space/get-diff-action@v1 + # id: git_diff + # with: + # SUFFIX_FILTER: | + # .go + # .mod + # .sum + # - name: rpc-test + # run: | + # make test-rpc + # if: "env.GIT_DIFF != ''" # TODO: remove tmp dir to fix this # test-importer: # runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eb12d621..11f2da662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (build) [\#378](https://github.com/ChainSafe/ethermint/pull/378) Create multi-node, local, automated testnet setup with `make localnet-start`. * (rpc) [\#330](https://github.com/ChainSafe/ethermint/issues/330) Implement `PublicFilterAPI`'s `EventSystem` which subscribes to Tendermint events upon `Filter` creation. * (rpc) [\#231](https://github.com/ChainSafe/ethermint/issues/231) Implement `NewBlockFilter` in rpc/filters.go which instantiates a polling block filter * Polls for new blocks via `BlockNumber` rpc call; if block number changes, it requests the new block via `GetBlockByNumber` rpc call and adds it to its internal list of blocks From fe11029fdcdfdee7abee7cdd9118b7844d5c2878 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 21 Jul 2020 12:00:44 +0200 Subject: [PATCH 20/29] update codecov --- README.md | 4 ++-- .codecov.yml => codecov.yml | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename .codecov.yml => codecov.yml (100%) diff --git a/README.md b/README.md index 051b35157..c2a8da72e 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ parent: Go report card - - Code Coverage + + Code Coverage
diff --git a/.codecov.yml b/codecov.yml similarity index 100% rename from .codecov.yml rename to codecov.yml From 7b573e5875709030a5c5079ae8856a4e63b61802 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 22 Jul 2020 09:28:17 +0200 Subject: [PATCH 21/29] update testnet docs --- docs/quickstart/testnet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart/testnet.md b/docs/quickstart/testnet.md index f4056a11f..d7b3b81a8 100644 --- a/docs/quickstart/testnet.md +++ b/docs/quickstart/testnet.md @@ -171,7 +171,7 @@ Each `./build/nodeN` directory is mounted to the `/emintd` directory in each con ### Logging -Logs are saved under each `./build/nodeN/emintd/gaia.log`. You can also watch logs +Logs are saved under each `./build/nodeN/emintd/emintd.log`. You can also watch logs directly via Docker, for example: ```bash From d7bea0d4890353b683748318ec03021e17111ac0 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 22 Jul 2020 09:48:22 +0200 Subject: [PATCH 22/29] fix docker-compose execution --- .gitignore | 1 - cmd/emintd/testnet.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5d9020fad..0a394be4a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ dist tools-stamp proto-tools-stamp golangci-lint -local_testnet keyring_test_cosmos # Testing diff --git a/cmd/emintd/testnet.go b/cmd/emintd/testnet.go index 5e165c559..8190ebb10 100644 --- a/cmd/emintd/testnet.go +++ b/cmd/emintd/testnet.go @@ -87,7 +87,7 @@ Note, strict routability for addresses is turned off in the config file.`, } cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") - cmd.Flags().StringP(flagOutputDir, "o", "./local_testnet", "Directory to store initialization data for the testnet") + cmd.Flags().StringP(flagOutputDir, "o", "./build", "Directory to store initialization data for the testnet") cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") cmd.Flags().String(flagNodeDaemonHome, "emintd", "Home directory of the node's daemon configuration") cmd.Flags().String(flagNodeCLIHome, "emintcli", "Home directory of the node's cli configuration") From 79fd45de534fc084e329bc9c63198d99dfe9f8f6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 22 Jul 2020 09:53:42 +0200 Subject: [PATCH 23/29] update docs --- docs/quickstart/testnet.md | 59 +++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/quickstart/testnet.md b/docs/quickstart/testnet.md index d7b3b81a8..cdc4d9708 100644 --- a/docs/quickstart/testnet.md +++ b/docs/quickstart/testnet.md @@ -82,9 +82,11 @@ Now its safe to start the daemon: emintd start ``` +You can then stop the node using Ctrl+C. + ## Multi-node, Local, Automated Testnet -### Build & Run Testnet +### Build Testnet & Start Testnet To build start a 4 node testnet run: @@ -108,6 +110,61 @@ To update the binary, just rebuild it and restart the nodes: make localnet-start ``` +Start the testnet using Docker compose: + +```bash +docker-compose up +``` + +You should see the logs from each node. First, you will see a few of them producing blocks while the others are trying to connect to their peers: + +```bash +emintdnode0 is up-to-date +emintdnode3 is up-to-date +emintdnode1 is up-to-date +emintdnode2 is up-to-date +Attaching to emintdnode0, emintdnode3, emintdnode1, emintdnode2 +emintdnode3 | I[2020-07-22|07:47:07.437] starting ABCI with Tendermint module=main +emintdnode3 | E[2020-07-22|07:47:07.967] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 11186e7dd7aa0dfc96048b492c4320f734a1940c@192.168.10.4:26656" +emintdnode3 | E[2020-07-22|07:47:07.967] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 254941abdd04466361c89d2f1f145cf71434c2c9@192.168.10.3:26656" +emintdnode3 | E[2020-07-22|07:47:07.967] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address fe56e598bacdcf57165f0cd9f9e54d4ee041616f@192.168.10.2:26656" +emintdnode3 | I[2020-07-22|07:47:13.297] Executed block module=state height=1 validTxs=0 invalidTxs=0 +emintdnode3 | I[2020-07-22|07:47:13.299] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD +emintdnode3 | I[2020-07-22|07:47:18.605] Executed block module=state height=2 validTxs=0 invalidTxs=0 +emintdnode3 | I[2020-07-22|07:47:18.609] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA +emintdnode3 | I[2020-07-22|07:47:23.936] Executed block module=state height=3 validTxs=0 invalidTxs=0 +emintdnode3 | I[2020-07-22|07:47:23.939] Committed state module=state height=3 txs=0 appHash=6B042D4D833132DC649332810EA388FED8D3DDD211DA12BA515BC411354A819E +emintdnode0 | I[2020-07-22|07:47:07.444] starting ABCI with Tendermint module=main +emintdnode0 | E[2020-07-22|07:47:07.933] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 11186e7dd7aa0dfc96048b492c4320f734a1940c@192.168.10.4:26656" +emintdnode0 | E[2020-07-22|07:47:07.943] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 254941abdd04466361c89d2f1f145cf71434c2c9@192.168.10.3:26656" +emintdnode0 | E[2020-07-22|07:47:07.943] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 668f4d838fcf8832a387295bc73bdbf265bf5026@192.168.10.5:26656" +emintdnode0 | I[2020-07-22|07:47:13.396] Executed block module=state height=1 validTxs=0 invalidTxs=0 +emintdnode0 | I[2020-07-22|07:47:13.398] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD +emintdnode0 | I[2020-07-22|07:47:18.608] Executed block module=state height=2 validTxs=0 invalidTxs=0 +emintdnode0 | I[2020-07-22|07:47:18.618] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA +emintdnode0 | I[2020-07-22|07:47:23.939] Executed block module=state height=3 validTxs=0 invalidTxs=0 +emintdnode0 | I[2020-07-22|07:47:23.940] Committed state module=state height=3 txs=0 appHash=6B042D4D833132DC649332810EA388FED8D3DDD211DA12BA515BC411354A819E +emintdnode2 | I[2020-07-22|07:47:07.386] starting ABCI with Tendermint module=main +emintdnode2 | E[2020-07-22|07:47:07.903] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 254941abdd04466361c89d2f1f145cf71434c2c9@192.168.10.3:26656" +emintdnode2 | E[2020-07-22|07:47:07.903] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 668f4d838fcf8832a387295bc73bdbf265bf5026@192.168.10.5:26656" +emintdnode2 | E[2020-07-22|07:47:07.903] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address fe56e598bacdcf57165f0cd9f9e54d4ee041616f@192.168.10.2:26656" +emintdnode2 | I[2020-07-22|07:47:13.290] Executed block module=state height=1 validTxs=0 invalidTxs=0 +emintdnode2 | I[2020-07-22|07:47:13.293] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD +emintdnode2 | I[2020-07-22|07:47:18.605] Executed block module=state height=2 validTxs=0 invalidTxs=0 +emintdnode2 | I[2020-07-22|07:47:18.608] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA +emintdnode2 | I[2020-07-22|07:47:23.933] Executed block module=state height=3 validTxs=0 invalidTxs=0 +emintdnode2 | I[2020-07-22|07:47:23.936] Committed state module=state height=3 txs=0 appHash=6B042D4D833132DC649332810EA388FED8D3DDD211DA12BA515BC411354A819E +emintdnode1 | I[2020-07-22|07:47:07.358] starting ABCI with Tendermint module=main +emintdnode1 | E[2020-07-22|07:47:07.904] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 11186e7dd7aa0dfc96048b492c4320f734a1940c@192.168.10.4:26656" +emintdnode1 | E[2020-07-22|07:47:07.905] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 668f4d838fcf8832a387295bc73bdbf265bf5026@192.168.10.5:26656" +emintdnode1 | E[2020-07-22|07:47:07.905] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address fe56e598bacdcf57165f0cd9f9e54d4ee041616f@192.168.10.2:26656" +emintdnode1 | E[2020-07-22|07:47:07.948] Error dialing peer module=p2p err="dial tcp 192.168.10.5:26656: connect: connection refused" +emintdnode1 | I[2020-07-22|07:47:13.396] Executed block module=state height=1 validTxs=0 invalidTxs=0 +emintdnode1 | I[2020-07-22|07:47:13.400] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD +emintdnode1 | I[2020-07-22|07:47:18.614] Executed block module=state height=2 validTxs=0 invalidTxs=0 +emintdnode1 | I[2020-07-22|07:47:18.616] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA +``` + ### Stop Testnet Once you are done, execute: From a9983c314bbc0e4f5386c61974b1c3baf2309698 Mon Sep 17 00:00:00 2001 From: Daniel Choi Date: Wed, 29 Jul 2020 10:35:33 -0700 Subject: [PATCH 24/29] fix errors and make testnet work (#403) * fix errors and make testnet work * Update Dockerfile Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * wip - fix db * fixes emintd nodes and syncs nodes * starts daemon and rpc server in bg Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze --- Dockerfile | 13 +++++++------ Makefile | 6 +++--- docker-compose.yml | 24 ++++++++++++++++-------- scripts/start.sh | 5 +++++ 4 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 scripts/start.sh diff --git a/Dockerfile b/Dockerfile index ce86afdf5..451fcc817 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,17 +15,18 @@ COPY . . RUN make build-ethermint-linux # Final image -FROM alpine:edge +FROM golang:1.14 as final -# Install ca-certificates -RUN apk add --update ca-certificates -WORKDIR /root +WORKDIR / + +RUN apt-get update # Copy over binaries from the build-env COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintd /usr/bin/emintd COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintcli /usr/bin/emintcli +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/scripts/start.sh / -EXPOSE 26656 26657 1317 +EXPOSE 26656 26657 1317 8545 # Run emintd by default, omit entrypoint to ease using container with emintcli -CMD ["emintd"] +ENTRYPOINT ["/bin/bash", "-c"] \ No newline at end of file diff --git a/Makefile b/Makefile index 286eae06e..17f767727 100644 --- a/Makefile +++ b/Makefile @@ -268,9 +268,9 @@ localnet-start: localnet-stop docker rmi -f ethermint-build-linux docker build --rm -t ethermint-build-linux . ; \ container_id=$$(docker create ethermint-build-linux) ; \ - docker cp $${container_id}:/usr/bin/emintd ./build/ ; \ - docker cp $${container_id}:/usr/bin/emintcli ./build/ - if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/$(ETHERMINT_DAEMON_BINARY):Z emintd/node testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi + docker container start $${container_id} ; + + if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermint-build-linux "emintd testnet --v 4 -o /ethermint --starting-ip-address 192.168.10.2 --keyring-backend=test"; fi docker-compose up -d localnet-stop: diff --git a/docker-compose.yml b/docker-compose.yml index e0e49b37f..22363ed64 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,63 +3,71 @@ version: "3" services: emintdnode0: container_name: emintdnode0 - image: "emintd/node" + image: "ethermint-build-linux" ports: - "26656-26657:26656-26657" - "1317:1317" + - "8545:8545" environment: - ID=0 - LOG=${LOG:-emintd.log} volumes: - - ./build:/emintd:Z + - ./build:/ethermint:Z networks: localnet: ipv4_address: 192.168.10.2 + entrypoint: "emintd --home ethermint/node0/emintd/ start" emintdnode1: container_name: emintdnode1 - image: "emintd/node" + image: "ethermint-build-linux" ports: - "26659-26660:26656-26657" - "1318:1317" + - "8546:8545" environment: - ID=1 - LOG=${LOG:-emintd.log} volumes: - - ./build:/emintd:Z + - ./build:/ethermint:Z networks: localnet: ipv4_address: 192.168.10.3 + entrypoint: "emintd --home ethermint/node1/emintd/ start" emintdnode2: container_name: emintdnode2 - image: "emintd/node" + image: "ethermint-build-linux" environment: - ID=2 - LOG=${LOG:-emintd.log} ports: - "26661-26662:26656-26657" - "1319:1317" + - "8547:8545" volumes: - - ./build:/emintd:Z + - ./build:/ethermint:Z networks: localnet: ipv4_address: 192.168.10.4 + entrypoint: "emintd --home ethermint/node2/emintd/ start" emintdnode3: container_name: emintdnode3 - image: "emintd/node" + image: "ethermint-build-linux" environment: - ID=3 - LOG=${LOG:-emintd.log} ports: - "26663-26664:26656-26657" - "1320:1317" + - "8548:8545" volumes: - - ./build:/emintd:Z + - ./build:/ethermint:Z networks: localnet: ipv4_address: 192.168.10.5 + entrypoint: "emintd --home ethermint/node3/emintd/ start" networks: localnet: diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100644 index 000000000..4eca5c744 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,5 @@ +#!/bin/sh +emintd --home /ethermint/node$ID/emintd/ start > emintd.log & +sleep 5 +emintcli rest-server --laddr "tcp://localhost:8545" --chain-id 7305661614933169792 --trace > emintcli.log & +tail -f /dev/null \ No newline at end of file From ced5dc9fe5beee463b872d8e7191079fba1bc277 Mon Sep 17 00:00:00 2001 From: araskachoi Date: Wed, 29 Jul 2020 10:54:24 -0700 Subject: [PATCH 25/29] update entrypoint and docs --- docker-compose.yml | 8 ++-- docs/quickstart/testnet.md | 77 +++++++++++++++----------------------- 2 files changed, 35 insertions(+), 50 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 22363ed64..0b93cf8e8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: networks: localnet: ipv4_address: 192.168.10.2 - entrypoint: "emintd --home ethermint/node0/emintd/ start" + entrypoint: "bash start.sh" emintdnode1: container_name: emintdnode1 @@ -33,7 +33,7 @@ services: networks: localnet: ipv4_address: 192.168.10.3 - entrypoint: "emintd --home ethermint/node1/emintd/ start" + entrypoint: "bash start.sh" emintdnode2: container_name: emintdnode2 @@ -50,7 +50,7 @@ services: networks: localnet: ipv4_address: 192.168.10.4 - entrypoint: "emintd --home ethermint/node2/emintd/ start" + entrypoint: "bash start.sh" emintdnode3: container_name: emintdnode3 @@ -67,7 +67,7 @@ services: networks: localnet: ipv4_address: 192.168.10.5 - entrypoint: "emintd --home ethermint/node3/emintd/ start" + entrypoint: "bash start.sh" networks: localnet: diff --git a/docs/quickstart/testnet.md b/docs/quickstart/testnet.md index cdc4d9708..841b33dc6 100644 --- a/docs/quickstart/testnet.md +++ b/docs/quickstart/testnet.md @@ -113,58 +113,43 @@ make localnet-start Start the testnet using Docker compose: ```bash -docker-compose up +docker-compose up -d ``` -You should see the logs from each node. First, you will see a few of them producing blocks while the others are trying to connect to their peers: +You will see: ```bash -emintdnode0 is up-to-date -emintdnode3 is up-to-date -emintdnode1 is up-to-date -emintdnode2 is up-to-date -Attaching to emintdnode0, emintdnode3, emintdnode1, emintdnode2 -emintdnode3 | I[2020-07-22|07:47:07.437] starting ABCI with Tendermint module=main -emintdnode3 | E[2020-07-22|07:47:07.967] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 11186e7dd7aa0dfc96048b492c4320f734a1940c@192.168.10.4:26656" -emintdnode3 | E[2020-07-22|07:47:07.967] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 254941abdd04466361c89d2f1f145cf71434c2c9@192.168.10.3:26656" -emintdnode3 | E[2020-07-22|07:47:07.967] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address fe56e598bacdcf57165f0cd9f9e54d4ee041616f@192.168.10.2:26656" -emintdnode3 | I[2020-07-22|07:47:13.297] Executed block module=state height=1 validTxs=0 invalidTxs=0 -emintdnode3 | I[2020-07-22|07:47:13.299] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD -emintdnode3 | I[2020-07-22|07:47:18.605] Executed block module=state height=2 validTxs=0 invalidTxs=0 -emintdnode3 | I[2020-07-22|07:47:18.609] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA -emintdnode3 | I[2020-07-22|07:47:23.936] Executed block module=state height=3 validTxs=0 invalidTxs=0 -emintdnode3 | I[2020-07-22|07:47:23.939] Committed state module=state height=3 txs=0 appHash=6B042D4D833132DC649332810EA388FED8D3DDD211DA12BA515BC411354A819E -emintdnode0 | I[2020-07-22|07:47:07.444] starting ABCI with Tendermint module=main -emintdnode0 | E[2020-07-22|07:47:07.933] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 11186e7dd7aa0dfc96048b492c4320f734a1940c@192.168.10.4:26656" -emintdnode0 | E[2020-07-22|07:47:07.943] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 254941abdd04466361c89d2f1f145cf71434c2c9@192.168.10.3:26656" -emintdnode0 | E[2020-07-22|07:47:07.943] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 668f4d838fcf8832a387295bc73bdbf265bf5026@192.168.10.5:26656" -emintdnode0 | I[2020-07-22|07:47:13.396] Executed block module=state height=1 validTxs=0 invalidTxs=0 -emintdnode0 | I[2020-07-22|07:47:13.398] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD -emintdnode0 | I[2020-07-22|07:47:18.608] Executed block module=state height=2 validTxs=0 invalidTxs=0 -emintdnode0 | I[2020-07-22|07:47:18.618] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA -emintdnode0 | I[2020-07-22|07:47:23.939] Executed block module=state height=3 validTxs=0 invalidTxs=0 -emintdnode0 | I[2020-07-22|07:47:23.940] Committed state module=state height=3 txs=0 appHash=6B042D4D833132DC649332810EA388FED8D3DDD211DA12BA515BC411354A819E -emintdnode2 | I[2020-07-22|07:47:07.386] starting ABCI with Tendermint module=main -emintdnode2 | E[2020-07-22|07:47:07.903] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 254941abdd04466361c89d2f1f145cf71434c2c9@192.168.10.3:26656" -emintdnode2 | E[2020-07-22|07:47:07.903] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 668f4d838fcf8832a387295bc73bdbf265bf5026@192.168.10.5:26656" -emintdnode2 | E[2020-07-22|07:47:07.903] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address fe56e598bacdcf57165f0cd9f9e54d4ee041616f@192.168.10.2:26656" -emintdnode2 | I[2020-07-22|07:47:13.290] Executed block module=state height=1 validTxs=0 invalidTxs=0 -emintdnode2 | I[2020-07-22|07:47:13.293] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD -emintdnode2 | I[2020-07-22|07:47:18.605] Executed block module=state height=2 validTxs=0 invalidTxs=0 -emintdnode2 | I[2020-07-22|07:47:18.608] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA -emintdnode2 | I[2020-07-22|07:47:23.933] Executed block module=state height=3 validTxs=0 invalidTxs=0 -emintdnode2 | I[2020-07-22|07:47:23.936] Committed state module=state height=3 txs=0 appHash=6B042D4D833132DC649332810EA388FED8D3DDD211DA12BA515BC411354A819E -emintdnode1 | I[2020-07-22|07:47:07.358] starting ABCI with Tendermint module=main -emintdnode1 | E[2020-07-22|07:47:07.904] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 11186e7dd7aa0dfc96048b492c4320f734a1940c@192.168.10.4:26656" -emintdnode1 | E[2020-07-22|07:47:07.905] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 668f4d838fcf8832a387295bc73bdbf265bf5026@192.168.10.5:26656" -emintdnode1 | E[2020-07-22|07:47:07.905] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address fe56e598bacdcf57165f0cd9f9e54d4ee041616f@192.168.10.2:26656" -emintdnode1 | E[2020-07-22|07:47:07.948] Error dialing peer module=p2p err="dial tcp 192.168.10.5:26656: connect: connection refused" -emintdnode1 | I[2020-07-22|07:47:13.396] Executed block module=state height=1 validTxs=0 invalidTxs=0 -emintdnode1 | I[2020-07-22|07:47:13.400] Committed state module=state height=1 txs=0 appHash=C96935712F41C25AE97AA35C88636CDEC2080D34A4E15520DD6958D5E35BFBFD -emintdnode1 | I[2020-07-22|07:47:18.614] Executed block module=state height=2 validTxs=0 invalidTxs=0 -emintdnode1 | I[2020-07-22|07:47:18.616] Committed state module=state height=2 txs=0 appHash=ABB24CA318A9B41D7F35AAEEA198EF91DBF2A263B2E271C6161FF4563B188DEA +... +Creating network "chainsafe-ethermint_localnet" with driver "bridge" +Creating emintdnode0 ... done +Creating emintdnode2 ... done +Creating emintdnode1 ... done +Creating emintdnode3 ... done ``` +In order to see the logs of a particular node, you can use the command: +```bash +docker exec emintdnode0 tail emintd.log +``` + +The logs will look like: + +```bash +I[2020-07-29|17:33:52.452] starting ABCI with Tendermint module=main +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 272a247b837653cf068d39efd4c407ffbd9a0e6f@192.168.10.5:26656" +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 3e05d3637b7ebf4fc0948bbef01b54d670aa810a@192.168.10.4:26656" +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 689f8606ede0b26ad5b79ae244c14cc67ab4efe7@192.168.10.3:26656" +I[2020-07-29|17:33:58.828] Executed block module=state height=88 validTxs=0 invalidTxs=0 +I[2020-07-29|17:33:58.830] Committed state module=state height=88 txs=0 appHash=90CC5FA53CF8B5EC49653A14DA20888AD81C92FCF646F04D501453FD89FCC791 +I[2020-07-29|17:34:04.032] Executed block module=state height=89 validTxs=0 invalidTxs=0 +I[2020-07-29|17:34:04.034] Committed state module=state height=89 txs=0 appHash=0B54C4DB1A0DACB1EEDCD662B221C048C826D309FD2A2F31FF26BAE8D2D7D8D7 +I[2020-07-29|17:34:09.381] Executed block module=state height=90 validTxs=0 invalidTxs=0 +I[2020-07-29|17:34:09.383] Committed state module=state height=90 txs=0 appHash=75FD1EE834F0669D5E717C812F36B21D5F20B3CCBB45E8B8D415CB9C4513DE51 +I[2020-07-29|17:34:14.700] Executed block module=state height=91 validTxs=0 invalidTxs=0 +``` + +** disregard the `Can't add peer's address to addrbook` warning. As long as the blocks are being produced and the block hashes are the same for each node, there should not be any issues. + ### Stop Testnet Once you are done, execute: From 43f396efcd9d8acadae28abf87e41ecf79d92137 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 30 Jul 2020 11:50:49 +0200 Subject: [PATCH 26/29] update logs --- docs/quickstart/testnet.md | 77 ++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/docs/quickstart/testnet.md b/docs/quickstart/testnet.md index 841b33dc6..24b38c45a 100644 --- a/docs/quickstart/testnet.md +++ b/docs/quickstart/testnet.md @@ -104,19 +104,13 @@ The ports for each node are found in this table: | `emintnode2` | `26661` | `26662` | | `emintnode3` | `26663` | `26664` | -To update the binary, just rebuild it and restart the nodes: +To update the binary, just rebuild it and restart the nodes ```bash make localnet-start ``` -Start the testnet using Docker compose: - -```bash -docker-compose up -d -``` - -You will see: +The command above command will run containers in the background using Docker compose. You will see the network being created: ```bash ... @@ -127,28 +121,6 @@ Creating emintdnode1 ... done Creating emintdnode3 ... done ``` -In order to see the logs of a particular node, you can use the command: -```bash -docker exec emintdnode0 tail emintd.log -``` - -The logs will look like: - -```bash -I[2020-07-29|17:33:52.452] starting ABCI with Tendermint module=main -E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 272a247b837653cf068d39efd4c407ffbd9a0e6f@192.168.10.5:26656" -E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 3e05d3637b7ebf4fc0948bbef01b54d670aa810a@192.168.10.4:26656" -E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 689f8606ede0b26ad5b79ae244c14cc67ab4efe7@192.168.10.3:26656" -I[2020-07-29|17:33:58.828] Executed block module=state height=88 validTxs=0 invalidTxs=0 -I[2020-07-29|17:33:58.830] Committed state module=state height=88 txs=0 appHash=90CC5FA53CF8B5EC49653A14DA20888AD81C92FCF646F04D501453FD89FCC791 -I[2020-07-29|17:34:04.032] Executed block module=state height=89 validTxs=0 invalidTxs=0 -I[2020-07-29|17:34:04.034] Committed state module=state height=89 txs=0 appHash=0B54C4DB1A0DACB1EEDCD662B221C048C826D309FD2A2F31FF26BAE8D2D7D8D7 -I[2020-07-29|17:34:09.381] Executed block module=state height=90 validTxs=0 invalidTxs=0 -I[2020-07-29|17:34:09.383] Committed state module=state height=90 txs=0 appHash=75FD1EE834F0669D5E717C812F36B21D5F20B3CCBB45E8B8D415CB9C4513DE51 -I[2020-07-29|17:34:14.700] Executed block module=state height=91 validTxs=0 invalidTxs=0 -``` - -** disregard the `Can't add peer's address to addrbook` warning. As long as the blocks are being produced and the block hashes are the same for each node, there should not be any issues. ### Stop Testnet @@ -213,8 +185,49 @@ Each `./build/nodeN` directory is mounted to the `/emintd` directory in each con ### Logging -Logs are saved under each `./build/nodeN/emintd/emintd.log`. You can also watch logs -directly via Docker, for example: +In order to see the logs of a particular node you can use the following command: + +```bash +# node 0: daemon logs +docker exec emintdnode0 tail emintd.log + +# node 0: REST & RPC logs +docker exec emintdnode0 tail emintcli.log +``` + +The logs for the daemon will look like: + +```bash +I[2020-07-29|17:33:52.452] starting ABCI with Tendermint module=main +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 272a247b837653cf068d39efd4c407ffbd9a0e6f@192.168.10.5:26656" +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 3e05d3637b7ebf4fc0948bbef01b54d670aa810a@192.168.10.4:26656" +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 689f8606ede0b26ad5b79ae244c14cc67ab4efe7@192.168.10.3:26656" +I[2020-07-29|17:33:58.828] Executed block module=state height=88 validTxs=0 invalidTxs=0 +I[2020-07-29|17:33:58.830] Committed state module=state height=88 txs=0 appHash=90CC5FA53CF8B5EC49653A14DA20888AD81C92FCF646F04D501453FD89FCC791 +I[2020-07-29|17:34:04.032] Executed block module=state height=89 validTxs=0 invalidTxs=0 +I[2020-07-29|17:34:04.034] Committed state module=state height=89 txs=0 appHash=0B54C4DB1A0DACB1EEDCD662B221C048C826D309FD2A2F31FF26BAE8D2D7D8D7 +I[2020-07-29|17:34:09.381] Executed block module=state height=90 validTxs=0 invalidTxs=0 +I[2020-07-29|17:34:09.383] Committed state module=state height=90 txs=0 appHash=75FD1EE834F0669D5E717C812F36B21D5F20B3CCBB45E8B8D415CB9C4513DE51 +I[2020-07-29|17:34:14.700] Executed block module=state height=91 validTxs=0 invalidTxs=0 +``` + +::: tip +You can disregard the `Can't add peer's address to addrbook` warning. As long as the blocks are +being produced and the app hashes are the same for each node, there should not be any issues. +::: + +Whereas the logs for the REST & RPC server would look like: + +```bash +I[2020-07-30|09:39:17.488] Starting application REST service (chain-id: "7305661614933169792")... module=rest-server +I[2020-07-30|09:39:17.488] Starting RPC HTTP server on 127.0.0.1:8545 module=rest-server +... +``` + +#### Follow Logs + +You can also watch logs as they are produced via Docker with the `--follow` (`-f`) flag, for +example: ```bash docker logs -f emintdnode0 From 1a220c4b9d3f042c2871aa89075585c83dbf6744 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 31 Jul 2020 19:51:00 +0200 Subject: [PATCH 27/29] try fix rpc --- scripts/integration-test-all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/integration-test-all.sh b/scripts/integration-test-all.sh index 4560a49ef..bfcf7a4a4 100755 --- a/scripts/integration-test-all.sh +++ b/scripts/integration-test-all.sh @@ -63,7 +63,7 @@ fi # Compile ethermint echo "compiling ethermint" -make build +make build-ethermint # PID array declaration arr=() From 28442eec912cc21749aae8c33d629a969519f559 Mon Sep 17 00:00:00 2001 From: araskachoi Date: Fri, 31 Jul 2020 11:49:50 -0700 Subject: [PATCH 28/29] build docker image --- Dockerfile | 36 ++++++++++---------- Makefile | 10 +++--- docker-compose.yml | 8 ++--- networks/local/ethermintnode/Dockerfile | 45 +++++++++++++++---------- networks/local/ethermintnode/wrapper.sh | 25 -------------- 5 files changed, 53 insertions(+), 71 deletions(-) delete mode 100755 networks/local/ethermintnode/wrapper.sh diff --git a/Dockerfile b/Dockerfile index 451fcc817..8a65041ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,30 @@ -FROM golang:stretch as build-env +FROM golang:alpine AS build-env -# Install minimum necessary dependencies -ENV PACKAGES curl make git libc-dev bash gcc -RUN apt-get update && apt-get upgrade -y && \ - apt-get install -y $PACKAGES +# Set up dependencies +ENV PACKAGES git build-base # Set working directory for the build -WORKDIR /go/src/github.com/ChainSafe/ethermint +WORKDIR /go/src/github.com/Chainsafe/ethermint + +# Install dependencies +RUN apk add --update $PACKAGES # Add source files COPY . . -# build Ethermint -RUN make build-ethermint-linux +# Make the binary +RUN make build # Final image -FROM golang:1.14 as final - -WORKDIR / +FROM alpine -RUN apt-get update +# Install ca-certificates +RUN apk add --update ca-certificates +WORKDIR /root # Copy over binaries from the build-env -COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintd /usr/bin/emintd -COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintcli /usr/bin/emintcli -COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/scripts/start.sh / - -EXPOSE 26656 26657 1317 8545 +COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintd /usr/bin/emintd +COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintcli /usr/bin/emintcli -# Run emintd by default, omit entrypoint to ease using container with emintcli -ENTRYPOINT ["/bin/bash", "-c"] \ No newline at end of file +# Run emintd by default +CMD ["emintd"] \ No newline at end of file diff --git a/Makefile b/Makefile index 063d86465..5c33e7872 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,9 @@ docker: docker cp ethermint:/usr/bin/emintd ./build/ ; \ docker cp ethermint:/usr/bin/emintcli ./build/ +docker-localnet: + # build the image + docker build -f ./networks/local/ethermintnode/Dockerfile . -t emintd/node ############################################################################### ### Tools & Dependencies ### @@ -266,12 +269,9 @@ build-docker-local-ethermint: # Run a 4-node testnet locally localnet-start: localnet-stop mkdir -p ./build/ - docker rmi -f ethermint-build-linux - docker build --rm -t ethermint-build-linux . ; \ - container_id=$$(docker create ethermint-build-linux) ; \ - docker container start $${container_id} ; + @$(MAKE) docker-localnet - if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermint-build-linux "emintd testnet --v 4 -o /ethermint --starting-ip-address 192.168.10.2 --keyring-backend=test"; fi + if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/ethermint:Z emintd/node "emintd testnet --v 4 -o /ethermint --starting-ip-address 192.168.10.2 --keyring-backend=test"; fi docker-compose up -d localnet-stop: diff --git a/docker-compose.yml b/docker-compose.yml index 0b93cf8e8..861a13bf3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: emintdnode0: container_name: emintdnode0 - image: "ethermint-build-linux" + image: "emintd/node" ports: - "26656-26657:26656-26657" - "1317:1317" @@ -20,7 +20,7 @@ services: emintdnode1: container_name: emintdnode1 - image: "ethermint-build-linux" + image: "emintd/node" ports: - "26659-26660:26656-26657" - "1318:1317" @@ -37,7 +37,7 @@ services: emintdnode2: container_name: emintdnode2 - image: "ethermint-build-linux" + image: "emintd/node" environment: - ID=2 - LOG=${LOG:-emintd.log} @@ -54,7 +54,7 @@ services: emintdnode3: container_name: emintdnode3 - image: "ethermint-build-linux" + image: "emintd/node" environment: - ID=3 - LOG=${LOG:-emintd.log} diff --git a/networks/local/ethermintnode/Dockerfile b/networks/local/ethermintnode/Dockerfile index 5ff870d7d..451fcc817 100644 --- a/networks/local/ethermintnode/Dockerfile +++ b/networks/local/ethermintnode/Dockerfile @@ -1,23 +1,32 @@ -#FROM alpine:3.10.2 -# -#RUN apk update && \ -# apk upgrade && \ -# apk --no-cache add curl jq file +FROM golang:stretch as build-env -# Changed from Alpine to Ubuntu because the keyring PR is linking to libc -# Alpine uses muslc instead of libc +# Install minimum necessary dependencies +ENV PACKAGES curl make git libc-dev bash gcc +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y $PACKAGES -FROM ubuntu:18.04 +# Set working directory for the build +WORKDIR /go/src/github.com/ChainSafe/ethermint -RUN apt-get update && \ - apt-get -y upgrade && \ - apt-get -y install curl jq file +# Add source files +COPY . . -VOLUME [ /emintd ] -WORKDIR /emintd -EXPOSE 26656 26657 1317 -ENTRYPOINT ["/usr/bin/wrapper.sh"] -CMD ["start"] -STOPSIGNAL SIGTERM +# build Ethermint +RUN make build-ethermint-linux -COPY wrapper.sh /usr/bin/wrapper.sh +# Final image +FROM golang:1.14 as final + +WORKDIR / + +RUN apt-get update + +# Copy over binaries from the build-env +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintd /usr/bin/emintd +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/emintcli /usr/bin/emintcli +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/scripts/start.sh / + +EXPOSE 26656 26657 1317 8545 + +# Run emintd by default, omit entrypoint to ease using container with emintcli +ENTRYPOINT ["/bin/bash", "-c"] \ No newline at end of file diff --git a/networks/local/ethermintnode/wrapper.sh b/networks/local/ethermintnode/wrapper.sh deleted file mode 100755 index 5ed589487..000000000 --- a/networks/local/ethermintnode/wrapper.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env sh - -BINARY=/emintd/${BINARY:-emintd} -ID=${ID:-0} -LOG=${LOG:-emintd.log} - -if ! [ -f "${BINARY}" ]; then - echo "The binary $(basename "${BINARY}") cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'emintd'" - exit 1 -fi - -BINARY_CHECK="$(file "$BINARY" | grep 'ELF 64-bit LSB executable, x86-64')" - -if [ -z "${BINARY_CHECK}" ]; then - echo "Binary needs to be OS linux, ARCH amd64" - exit 1 -fi - -export EMINTDHOME="/emintd/node${ID}/emintd" - -if [ -d "$(dirname "${EMINTDHOME}"/"${LOG}")" ]; then - "${BINARY}" --home "${EMINTDHOME}" "$@" | tee "${EMINTDHOME}/${LOG}" -else - "${BINARY}" --home "${EMINTDHOME}" "$@" -fi From 0f776c05fdbc7b1b8298c2d4fa6a0a84b015dce7 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Fri, 31 Jul 2020 23:37:02 +0200 Subject: [PATCH 29/29] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8a65041ac..aad531549 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,4 +27,4 @@ COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintd /usr/b COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/emintcli /usr/bin/emintcli # Run emintd by default -CMD ["emintd"] \ No newline at end of file +CMD ["emintd"]