From 9f1191370a3c3c7890dc8dd23dc47c99b51c93f0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 30 May 2014 00:29:38 +0200 Subject: [PATCH] Correct order of nonce/balance. PROTOCOL CHANGE! NEW CHAIN! Extra paranoia for trie. Trie fixes. Trie tests. Version bump. --- libethcore/CommonEth.cpp | 2 +- libethereum/AddressState.h | 6 +- libethereum/BlockChain.cpp | 2 +- libethereum/State.cpp | 143 ++++++++++++++++++----------------- libethereum/State.h | 5 +- libethsupport/Common.h | 2 +- libethsupport/CommonData.cpp | 4 +- libethsupport/TrieDB.cpp | 11 +++ libethsupport/TrieDB.h | 91 +++++++++++++++++----- libethsupport/vector_ref.h | 2 +- test/main.cpp | 35 --------- test/trie.cpp | 104 ++++++++++++++++++------- 12 files changed, 243 insertions(+), 164 deletions(-) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 4efb0f4eb..e0301619d 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -28,7 +28,7 @@ using namespace eth; //#define ETH_ADDRESS_DEBUG 1 -const unsigned eth::c_protocolVersion = 17; +const unsigned eth::c_protocolVersion = 18; static const vector> g_units = { diff --git a/libethereum/AddressState.h b/libethereum/AddressState.h index 5bfb6b0ab..3984dd498 100644 --- a/libethereum/AddressState.h +++ b/libethereum/AddressState.h @@ -32,8 +32,8 @@ namespace eth class AddressState { public: - AddressState(): m_isAlive(false), m_balance(0), m_nonce(0) {} - AddressState(u256 _balance, u256 _nonce, h256 _contractRoot, h256 _codeHash): m_isAlive(true), m_balance(_balance), m_nonce(_nonce), m_storageRoot(_contractRoot), m_codeHash(_codeHash) {} + AddressState(): m_isAlive(false), m_nonce(0), m_balance(0) {} + AddressState(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash): m_isAlive(true), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) {} void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = h256(); m_balance = 0; m_nonce = 0; } bool isAlive() const { return m_isAlive; } @@ -61,8 +61,8 @@ public: private: bool m_isAlive; bool m_gotCode; - u256 m_balance; u256 m_nonce; + u256 m_balance; /// The base storage root. Used with the state DB to give a base to the storage. m_storageOverlay is overlaid on this and takes precedence for all values set. h256 m_storageRoot; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index f10070507..b7db36910 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -82,7 +82,7 @@ std::map const& eth::genesisState() "6c386a4b26f73c802f34673f7248bb118f97424a", "e4157b34ea9615cfbde6b4fda419828124b70c78" })) - s_ret[Address(fromHex(i))] = AddressState(u256(1) << 200, 0, h256(), EmptySHA3); + s_ret[Address(fromHex(i))] = AddressState(0, u256(1) << 200, h256(), EmptySHA3); } return s_ret; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 40b587d8d..2d6b6b76d 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -66,13 +66,20 @@ State::State(Address _coinbaseAddress, Overlay const& _db): // Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly. m_state.init(); + + paranoia("beginning of normal construction."); + eth::commit(genesisState(), m_db, m_state); m_db.commit(); + paranoia("after DB commit of normal construction."); + m_previousBlock = BlockChain::genesis(); resetCurrent(); assert(m_state.root() == m_previousBlock.stateRoot); + + paranoia("end of normal construction."); } State::State(State const& _s): @@ -87,10 +94,27 @@ State::State(State const& _s): m_ourAddress(_s.m_ourAddress), m_blockReward(_s.m_blockReward) { + paranoia("after state cloning (copy cons)."); +} + +void State::paranoia(std::string const& _when) const +{ +#if ETH_PARANOIA + // TODO: variable on context; just need to work out when there should be no leftovers + // [in general this is hard since contract alteration will result in nodes in the DB that are no directly part of the state DB]. + if (!isTrieGood(false)) + { + cwarn << "BAD TRIE" << _when; + throw InvalidTrie(); + } +#else + (void)_when; +#endif } State& State::operator=(State const& _s) { + paranoia("prior state cloning (assignment op)"); m_db = _s.m_db; m_state.open(&m_db, _s.m_state.root()); m_transactions = _s.m_transactions; @@ -101,6 +125,7 @@ State& State::operator=(State const& _s) m_currentBlock = _s.m_currentBlock; m_ourAddress = _s.m_ourAddress; m_blockReward = _s.m_blockReward; + paranoia("after state cloning (assignment op)"); return *this; } @@ -115,12 +140,12 @@ struct CachedAddressState u256 balance() const { - return r ? s ? s->balance() : r[0].toInt() : 0; + return r ? s ? s->balance() : r[1].toInt() : 0; } u256 nonce() const { - return r ? s ? s->nonce() : r[1].toInt() : 0; + return r ? s ? s->nonce() : r[0].toInt() : 0; } bytes code() const @@ -323,7 +348,7 @@ map State::addresses() const ret[i.first] = i.second.balance(); for (auto const& i: m_state) if (m_cache.find(i.first) == m_cache.end()) - ret[i.first] = RLP(i.second)[0].toInt(); + ret[i.first] = RLP(i.second)[1].toInt(); return ret; } @@ -345,6 +370,8 @@ void State::resetCurrent() // TODO: check. m_state.setRoot(m_currentBlock.stateRoot); + + paranoia("begin resetCurrent"); } bool State::cull(TransactionQueue& _tq) const @@ -529,23 +556,12 @@ u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, boo if (_fullCommit) { -#if ETH_PARANOIA - if (!isTrieGood()) - { - cwarn << "INVALID TRIE prior to database commit!"; - throw InvalidTrie(); - } -#endif + paranoia("immediately before database commit"); + // Commit the new trie to disk. m_db.commit(); -#if ETH_PARANOIA - if (!isTrieGood()) - { - cwarn << "INVALID TRIE immediately after database commit!"; - throw InvalidTrie(); - } -#endif + paranoia("immediately after database commit"); m_previousBlock = m_currentBlock; } else @@ -840,44 +856,37 @@ bytes const& State::code(Address _contract) const return m_cache[_contract].code(); } -bool State::isTrieGood() +bool State::isTrieGood(bool _requireNoLeftOvers) const { - { - EnforceRefs r(m_db, false); - for (auto const& i: m_state) + for (int e = 0; e < 2; ++e) + try { - RLP r(i.second); - TrieDB storageDB(&m_db, r[2].toHash()); - try + EnforceRefs r(m_db, !!e); + auto lo = m_state.leftOvers(); + if (!lo.empty() && _requireNoLeftOvers) { - for (auto const& j: storageDB) { (void)j; } - } - catch (InvalidTrie) - { - cwarn << "BAD TRIE [unenforced refs]"; + cwarn << "LEFTOVERS" << (e ? "[enforced" : "[unenforced") << "refs]"; + cnote << "Left:" << lo; + cnote << "Keys:" << m_db.keys(); + m_state.debugStructure(cerr); return false; } - if (r[3].toHash() != EmptySHA3 && m_db.lookup(r[3].toHash()).empty()) - return false; - } - } - { - EnforceRefs r(m_db, true); - for (auto const& i: m_state) - { - RLP r(i.second); - TrieDB storageDB(&m_db, r[2].toHash()); - try + for (auto const& i: m_state) { + RLP r(i.second); + TrieDB storageDB(const_cast(&m_db), r[2].toHash()); // promise not to alter Overlay. for (auto const& j: storageDB) { (void)j; } - } - catch (InvalidTrie) - { - cwarn << "BAD TRIE [enforced refs]"; - return false; + if (!e && r[3].toHash() != EmptySHA3 && m_db.lookup(r[3].toHash()).empty()) + return false; } } - } + catch (InvalidTrie) + { + cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]"; + cnote << m_db.keys(); + m_state.debugStructure(cerr); + return false; + } return true; } @@ -887,13 +896,7 @@ u256 State::execute(bytesConstRef _rlp) commit(); // get an updated hash #endif -#if ETH_PARANOIA - if (!isTrieGood()) - { - cwarn << "BAD TRIE before execution begins."; - throw InvalidTrie(); - } -#endif + paranoia("start of execution."); State old(*this); auto h = rootHash(); @@ -921,19 +924,19 @@ u256 State::execute(bytesConstRef _rlp) commit(); #if ETH_PARANOIA - if (e.t().receiveAddress) - { - EnforceRefs r(m_db, true); - assert(!storageRoot(e.t().receiveAddress) || m_db.lookup(storageRoot(e.t().receiveAddress)).size()); - } - ctrace << "Executed; now" << rootHash(); ctrace << old.diff(*this); - if (!isTrieGood()) + paranoia("after execution commit."); + + if (e.t().receiveAddress) { - cwarn << "BAD TRIE immediately after execution."; - throw InvalidTrie(); + EnforceRefs r(m_db, true); + if (storageRoot(e.t().receiveAddress) && m_db.lookup(storageRoot(e.t().receiveAddress)).empty()) + { + cwarn << "TRIE immediately after execution; no node for receiveAddress"; + throw InvalidTrie(); + } } #endif @@ -1098,7 +1101,7 @@ std::ostream& eth::operator<<(std::ostream& _out, State const& _s) else { string lead = (cache ? r ? " * " : " + " : " "); - if (cache && r && (cache->balance() == r[0].toInt() && cache->nonce() == r[1].toInt())) + if (cache && r && cache->nonce() == r[0].toInt() && cache->balance() == r[1].toInt()) lead = " . "; stringstream contout; @@ -1142,7 +1145,7 @@ std::ostream& eth::operator<<(std::ostream& _out, State const& _s) } else contout << " [SIMPLE]"; - _out << lead << i << ": " << std::dec << (cache ? cache->balance() : r[0].toInt()) << " #:" << (cache ? cache->nonce() : r[1].toInt()) << contout.str() << std::endl; + _out << lead << i << ": " << std::dec << (cache ? cache->nonce() : r[0].toInt()) << " #:" << (cache ? cache->balance() : r[1].toInt()) << contout.str() << std::endl; } } return _out; @@ -1167,18 +1170,18 @@ std::ostream& eth::operator<<(std::ostream& _out, AccountDiff const& _s) if (!_s.exist.to()) return _out; - if (_s.balance) - { - _out << std::dec << _s.balance.to() << " "; - if (_s.balance.from()) - _out << "(" << std::showpos << (((bigint)_s.balance.to()) - ((bigint)_s.balance.from())) << std::noshowpos << ") "; - } if (_s.nonce) { _out << std::dec << "#" << _s.nonce.to() << " "; if (_s.nonce.from()) _out << "(" << std::showpos << (((bigint)_s.nonce.to()) - ((bigint)_s.nonce.from())) << std::noshowpos << ") "; } + if (_s.balance) + { + _out << std::dec << _s.balance.to() << " "; + if (_s.balance.from()) + _out << "(" << std::showpos << (((bigint)_s.balance.to()) - ((bigint)_s.balance.from())) << std::noshowpos << ") "; + } if (_s.code) _out << "$" << std::hex << _s.code.to() << " (" << _s.code.from() << ") "; for (pair> const& i: _s.storage) diff --git a/libethereum/State.h b/libethereum/State.h index eac800447..d40d51325 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -297,7 +297,8 @@ private: /// @returns gas used by transactions thus far executed. u256 gasUsed() const { return m_transactions.size() ? m_transactions.back().gasUsed : 0; } - bool isTrieGood(); + bool isTrieGood(bool _requireNoLeftOvers) const; + void paranoia(std::string const& _when) const; Overlay m_db; ///< Our overlay for the state tree. TrieDB m_state; ///< Our state tree, as an Overlay DB. @@ -338,7 +339,7 @@ void commit(std::map const& _cache, DB& _db, TrieDB(4, 10)(s_eng), ' '); - char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; + std::string ret(std::uniform_int_distribution(1, 5)(s_eng), ' '); + char const n[] = "qwertyuiop";//asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; std::uniform_int_distribution d(0, sizeof(n) - 2); for (char& c: ret) c = n[d(s_eng)]; diff --git a/libethsupport/TrieDB.cpp b/libethsupport/TrieDB.cpp index d3a8e3710..0c9ca6206 100644 --- a/libethsupport/TrieDB.cpp +++ b/libethsupport/TrieDB.cpp @@ -29,6 +29,17 @@ namespace eth const h256 c_shaNull = sha3(rlp("")); +std::map BasicMap::get() const +{ + if (!m_enforceRefs) + return m_over; + std::map ret; + for (auto const& i: m_refCount) + if (i.second) + ret.insert(*m_over.find(i.first)); + return ret; +} + std::string BasicMap::lookup(h256 _h) const { auto it = m_over.find(_h); diff --git a/libethsupport/TrieDB.h b/libethsupport/TrieDB.h index ab42fec17..8b430ea31 100644 --- a/libethsupport/TrieDB.h +++ b/libethsupport/TrieDB.h @@ -43,7 +43,7 @@ public: BasicMap() {} void clear() { m_over.clear(); } - std::map const& get() const { return m_over; } + std::map get() const; std::string lookup(h256 _h) const; bool exists(h256 _h) const; @@ -57,7 +57,7 @@ protected: std::map m_over; std::map m_refCount; - bool m_enforceRefs = false; + mutable bool m_enforceRefs = false; }; inline std::ostream& operator<<(std::ostream& _out, BasicMap const& _m) @@ -103,11 +103,11 @@ private: class EnforceRefs { public: - EnforceRefs(BasicMap& _o, bool _r): m_o(_o), m_r(_o.m_enforceRefs) { _o.m_enforceRefs = _r; } + EnforceRefs(BasicMap const& _o, bool _r): m_o(_o), m_r(_o.m_enforceRefs) { _o.m_enforceRefs = _r; } ~EnforceRefs() { m_o.m_enforceRefs = m_r; } private: - BasicMap& m_o; + BasicMap const& m_o; bool m_r; }; @@ -160,48 +160,69 @@ public: void debugPrint() {} - void descendKey(h256 _k, std::set& _keyMask) const + void descendKey(h256 _k, std::set& _keyMask, bool _wasExt, std::ostream* _out, int _indent = 0) const { _keyMask.erase(_k); - if (_k == m_root && _k == EmptySHA3) // root allowed to be empty + if (_k == m_root && _k == c_shaNull) // root allowed to be empty return; - descend(RLP(node(_k)), _keyMask); + descendList(RLP(node(_k)), _keyMask, _wasExt, _out, _indent); // if not, it must be a list } - void descendEntry(RLP const& _r, std::set& _keyMask) const + void descendEntry(RLP const& _r, std::set& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const { if (_r.isData() && _r.size() == 32) - descendKey(_r.toHash(), _keyMask); + descendKey(_r.toHash(), _keyMask, _wasExt, _out, _indent); else if (_r.isList()) - descend(_r, _keyMask); + descendList(_r, _keyMask, _wasExt, _out, _indent); else throw InvalidTrie(); } - void descend(RLP const& _r, std::set& _keyMask) const + void descendList(RLP const& _r, std::set& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const { - if (_r.isList() && _r.size() == 2) + if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out)) { + if (_out) + (*_out) << std::string(_indent * 2, ' ') << (_wasExt ? "!2 " : "2 ") << sha3(_r.data()).abridged() << ": " << _r << "\n"; if (!isLeaf(_r)) // don't go down leaves - descendEntry(_r[1], _keyMask); + descendEntry(_r[1], _keyMask, true, _out, _indent + 1); } - else if (_r.isList() && _r.size() == 17) + else if (_r.isList() && _r.itemCount() == 17) { + if (_out) + (*_out) << std::string(_indent * 2, ' ') << "17 " << sha3(_r.data()).abridged() << ": " << _r << "\n"; for (unsigned i = 0; i < 16; ++i) if (!_r[i].isEmpty()) // 16 branches are allowed to be empty - descendEntry(_r[i], _keyMask); + descendEntry(_r[i], _keyMask, false, _out, _indent + 1); } else throw InvalidTrie(); } - std::set check() const + std::set leftOvers(std::ostream* _out = nullptr) const { std::set k = m_db->keys(); - descendKey(m_root, k); + descendKey(m_root, k, false, _out); return k; } + void debugStructure(std::ostream& _out) const + { + leftOvers(&_out); + } + + bool check(bool _requireNoLeftOvers) const + { + try + { + return leftOvers().empty() || !_requireNoLeftOvers; + } + catch (...) + { + return false; + } + } + std::string at(bytesConstRef _key) const; void insert(bytesConstRef _key, bytesConstRef _value); void remove(bytesConstRef _key); @@ -670,6 +691,10 @@ template std::string GenericTrieDB::deref(RLP const& _n) const template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSlice _k) { +#if ETH_PARANOIA + tdebug << "deleteAt " << _orig << _k << sha3(_orig.data()).abridged(); +#endif + // The caller will make sure that the bytes are inserted properly. // - This might mean inserting an entry into m_over // We will take care to ensure that (our reference to) _orig is killed. @@ -717,7 +742,7 @@ template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSl byte used = uniqueInUse(_orig, 16); if (used != 255) - if (_orig[used].isList() && _orig[used].itemCount() == 2) + if (isTwoItemNode(_orig[used])) return graft(RLP(merge(_orig, used))); else return merge(_orig, used); @@ -764,6 +789,10 @@ template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSl template bool GenericTrieDB::deleteAtAux(RLPStream& _out, RLP const& _orig, NibbleSlice _k) { +#if ETH_PARANOIA + tdebug << "deleteAtAux " << _orig << _k << sha3(_orig.data()).abridged() << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash().abridged() : std::string()); +#endif + bytes b = _orig.isEmpty() ? bytes() : deleteAt(_orig.isList() ? _orig : RLP(node(_orig.toHash())), _k); if (!b.size()) // not found - no change. @@ -780,7 +809,10 @@ template bool GenericTrieDB::deleteAtAux(RLPStream& _out, RLP con template bytes GenericTrieDB::place(RLP const& _orig, NibbleSlice _k, bytesConstRef _s) { -// ::operator<<(std::cout << "place ", _orig) << ", " << _k << ", " << _s.toString() << std::endl; +#if ETH_PARANOIA + tdebug << "place " << _orig << _k; +#endif + killNode(_orig); if (_orig.isEmpty()) @@ -803,6 +835,10 @@ template bytes GenericTrieDB::place(RLP const& _orig, NibbleSlice // out2: [V0, ..., V15, null] iff exists i: !!Vi -- OR -- null otherwise template bytes GenericTrieDB::remove(RLP const& _orig) { +#if ETH_PARANOIA + tdebug << "kill " << _orig; +#endif + killNode(_orig); assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17)); @@ -826,7 +862,9 @@ template RLPStream& GenericTrieDB::streamNode(RLPStream& _s, byte template bytes GenericTrieDB::cleve(RLP const& _orig, uint _s) { -// ::operator<<(std::cout << "cleve ", _orig) << ", " << _s << std::endl; +#if ETH_PARANOIA + tdebug << "cleve " << _orig << _s; +#endif killNode(_orig); assert(_orig.isList() && _orig.itemCount() == 2); @@ -845,6 +883,10 @@ template bytes GenericTrieDB::cleve(RLP const& _orig, uint _s) template bytes GenericTrieDB::graft(RLP const& _orig) { +#if ETH_PARANOIA + tdebug << "graft " << _orig; +#endif + assert(_orig.isList() && _orig.itemCount() == 2); std::string s; RLP n; @@ -868,6 +910,10 @@ template bytes GenericTrieDB::graft(RLP const& _orig) template bytes GenericTrieDB::merge(RLP const& _orig, byte _i) { +#if ETH_PARANOIA + tdebug << "merge " << _orig << (int)_i; +#endif + assert(_orig.isList() && _orig.itemCount() == 17); RLPStream s(2); if (_i != 16) @@ -883,9 +929,12 @@ template bytes GenericTrieDB::merge(RLP const& _orig, byte _i) template bytes GenericTrieDB::branch(RLP const& _orig) { -// ::operator<<(std::cout << "branch ", _orig) << std::endl; +#if ETH_PARANOIA + tdebug << "branch " << _orig; +#endif assert(_orig.isList() && _orig.itemCount() == 2); + killNode(_orig); auto k = keyOf(_orig); RLPStream r(17); diff --git a/libethsupport/vector_ref.h b/libethsupport/vector_ref.h index fde47e661..66f479f8c 100644 --- a/libethsupport/vector_ref.h +++ b/libethsupport/vector_ref.h @@ -33,7 +33,7 @@ public: bool contentsEqual(std::vector const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } std::vector toVector() const { return std::vector(m_data, m_data + m_count); } std::vector toBytes() const { return std::vector((unsigned char const*)m_data, m_data + m_count * sizeof(_T)); } - std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count); } + std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } template operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); } _T* data() const { return m_data; } diff --git a/test/main.cpp b/test/main.cpp index 42e673446..b85f00b03 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -42,41 +42,6 @@ using namespace eth; BOOST_AUTO_TEST_CASE(basic_tests) { - cdebug << "Stress-testing Trie..."; - { - BasicMap m; - EnforceRefs e(m, true); - GenericTrieDB d(&m); - d.init(); // initialise as empty tree. - MemTrie t; - assert(d.check().empty()); - for (int a = 0; a < 100; ++a) - { - StringMap m; - for (int i = 0; i < 100; ++i) - { - auto k = randomWord(); - auto v = toString(i); - m.insert(make_pair(k, v)); - t.insert(k, v); - d.insert(k, v); - assert(hash256(m) == t.hash256()); - assert(hash256(m) == d.root()); - assert(d.check().empty()); - } - while (!m.empty()) - { - auto k = m.begin()->first; - d.remove(k); - t.remove(k); - m.erase(k); - assert(hash256(m) == t.hash256()); - assert(hash256(m) == d.root()); - assert(d.check().empty()); - } - } - } - /* RLPStream s; BlockInfo::genesis().fillStream(s, false); std::cout << RLP(s.out()) << std::endl; diff --git a/test/trie.cpp b/test/trie.cpp index f3ff3ef4a..59823f20b 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -49,7 +49,6 @@ namespace eth BOOST_AUTO_TEST_CASE(trie_tests) { cnote << "Testing Trie..."; - js::mValue v; string s = asString(contents("../../../tests/trietest.json")); BOOST_REQUIRE_MESSAGE( s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?"); @@ -57,7 +56,7 @@ BOOST_AUTO_TEST_CASE(trie_tests) for (auto& i: v.get_obj()) { js::mObject& o = i.second.get_obj(); - cnote << i.first; +// cnote << i.first; vector> ss; for (auto& i: o["in"].get_obj()) ss.push_back(make_pair(i.first, i.second.get_str())); @@ -67,9 +66,14 @@ BOOST_AUTO_TEST_CASE(trie_tests) BasicMap m; GenericTrieDB t(&m); t.init(); + BOOST_REQUIRE(t.check(true)); for (auto const& k: ss) + { +// cdebug << k.first << k.second; t.insert(k.first, k.second); - BOOST_REQUIRE(!o["root"].is_null()); + BOOST_REQUIRE(t.check(true)); + } + BOOST_REQUIRE(!o["root"].is_null()); BOOST_CHECK(o["root"].get_str() == toHex(t.root().asArray()) ); } } @@ -81,8 +85,9 @@ inline h256 stringMapHash256(StringMap const& _s) return hash256(_s); } -int trieTest() +BOOST_AUTO_TEST_CASE(moreTrieTests) { + cnote << "Testing Trie more..."; #if 0 // More tests... { @@ -153,6 +158,7 @@ int trieTest() cout << RLP(t.rlp()) << endl; cout << toHex(t.rlp()) << endl; } +#endif { BasicMap m; GenericTrieDB d(&m); @@ -166,20 +172,21 @@ int trieTest() t.insert(a, b); s[a] = b; - cout << endl << "-------------------------------" << endl; + /*cout << endl << "-------------------------------" << endl; cout << a << " -> " << b << endl; cout << d; cout << m; cout << d.root() << endl; - cout << hash256(s) << endl; + cout << hash256(s) << endl;*/ - assert(t.hash256() == hash256(s)); - assert(d.root() == hash256(s)); + BOOST_REQUIRE(d.check(true)); + BOOST_REQUIRE_EQUAL(t.hash256(), hash256(s)); + BOOST_REQUIRE_EQUAL(d.root(), hash256(s)); for (auto const& i: s) { (void)i; - assert(t.at(i.first) == i.second); - assert(d.at(i.first) == i.second); + BOOST_REQUIRE_EQUAL(t.at(i.first), i.second); + BOOST_REQUIRE_EQUAL(d.at(i.first), i.second); } }; @@ -189,22 +196,23 @@ int trieTest() t.remove(a); d.remove(string(a)); - cout << endl << "-------------------------------" << endl; + /*cout << endl << "-------------------------------" << endl; cout << "X " << a << endl; cout << d; cout << m; cout << d.root() << endl; - cout << hash256(s) << endl; + cout << hash256(s) << endl;*/ - assert(t.at(a).empty()); - assert(d.at(string(a)).empty()); - assert(t.hash256() == hash256(s)); - assert(d.root() == hash256(s)); + BOOST_REQUIRE(d.check(true)); + BOOST_REQUIRE(t.at(a).empty()); + BOOST_REQUIRE(d.at(string(a)).empty()); + BOOST_REQUIRE_EQUAL(t.hash256(), hash256(s)); + BOOST_REQUIRE_EQUAL(d.root(), hash256(s)); for (auto const& i: s) { (void)i; - assert(t.at(i.first) == i.second); - assert(d.at(i.first) == i.second); + BOOST_REQUIRE_EQUAL(t.at(i.first), i.second); + BOOST_REQUIRE_EQUAL(d.at(i.first), i.second); } }; @@ -219,36 +227,78 @@ int trieTest() remove("doge"); remove("doe"); } -#endif +} + +BOOST_AUTO_TEST_CASE(trieStess) +{ + cnote << "Stress-testing Trie..."; { BasicMap m; - GenericTrieDB d(&m); + BasicMap dm; + EnforceRefs e(dm, true); + GenericTrieDB d(&dm); d.init(); // initialise as empty tree. MemTrie t; + BOOST_REQUIRE(d.check(true)); for (int a = 0; a < 20; ++a) { StringMap m; - for (int i = 0; i < 20; ++i) + for (int i = 0; i < 50; ++i) { auto k = randomWord(); auto v = toString(i); - m.insert(make_pair(k, v)); + m[k] = v; t.insert(k, v); d.insert(k, v); - assert(hash256(m) == t.hash256()); - assert(hash256(m) == d.root()); + BOOST_REQUIRE_EQUAL(hash256(m), t.hash256()); + BOOST_REQUIRE_EQUAL(hash256(m), d.root()); + BOOST_REQUIRE(d.check(true)); } while (!m.empty()) { auto k = m.begin()->first; + auto v = m.begin()->second; d.remove(k); t.remove(k); m.erase(k); - assert(hash256(m) == t.hash256()); - assert(hash256(m) == d.root()); + if (!d.check(true)) + { + cwarn << m; + for (auto i: d) + cwarn << i.first.toString() << i.second.toString(); + + BasicMap dm2; + EnforceRefs e2(dm2, true); + GenericTrieDB d2(&dm2); + d2.init(); // initialise as empty tree. + for (auto i: d) + d2.insert(i.first, i.second); + + cwarn << "Good:" << d2.root(); +// for (auto i: dm2.get()) +// cwarn << i.first.abridged() << ": " << RLP(i.second); + d2.debugStructure(cerr); + cwarn << "Broken:" << d.root(); // Leaves an extension -> extension (3c1... -> 742...) +// for (auto i: dm.get()) +// cwarn << i.first.abridged() << ": " << RLP(i.second); + d.debugStructure(cerr); + + d2.insert(k, v); + cwarn << "Pres:" << d2.root(); +// for (auto i: dm2.get()) +// cwarn << i.first.abridged() << ": " << RLP(i.second); + d2.debugStructure(cerr); + g_logVerbosity = 99; + d2.remove(k); + g_logVerbosity = 4; + + cwarn << "Good?" << d2.root(); + } + BOOST_REQUIRE(d.check(true)); + BOOST_REQUIRE_EQUAL(hash256(m), t.hash256()); + BOOST_REQUIRE_EQUAL(hash256(m), d.root()); } } } - return 0; }