diff --git a/CMakeLists.txt b/CMakeLists.txt index 156d0e5c5..ba25e74ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ function(createDefaultCacheConfig) set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on") set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)") set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.") + set(JUSTTESTS OFF CACHE BOOL "Build only for tests.") endfunction() @@ -49,7 +50,7 @@ function(configureProject) add_definitions(-DETH_FATDB) endif() - if (HEADLESS) + if (HEADLESS OR JUSTTESTS) add_definitions(-DETH_HEADLESS) endif() endfunction() @@ -153,8 +154,11 @@ if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) endif () add_subdirectory(libsolidity) -add_subdirectory(lllc) -add_subdirectory(solc) + +if (NOT JUSTTESTS) + add_subdirectory(lllc) + add_subdirectory(solc) +endif() if (JSONRPC) add_subdirectory(libweb3jsonrpc) @@ -171,25 +175,30 @@ add_subdirectory(libethereum) add_subdirectory(libwebthree) add_subdirectory(test) -add_subdirectory(eth) -if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") - add_subdirectory(exp) -endif () +if (NOT JUSTTESTS) -# TODO check msvc -if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) - add_subdirectory(neth) -endif () + add_subdirectory(eth) + + if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") + add_subdirectory(exp) + endif () + + # TODO check msvc + if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) + add_subdirectory(neth) + endif () + + if (NOT HEADLESS) + + add_subdirectory(libnatspec) + add_subdirectory(libjsqrc) + add_subdirectory(alethzero) + add_subdirectory(third) + add_subdirectory(mix) + + endif() -if (NOT HEADLESS) - - add_subdirectory(libnatspec) - add_subdirectory(libjsqrc) - add_subdirectory(alethzero) - add_subdirectory(third) - add_subdirectory(mix) - endif() enable_testing() diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 24fac9c0a..3113b5427 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -65,16 +65,16 @@ public: FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } /// Explicitly construct, copying from a byte array. - explicit FixedHash(bytes const& _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); else if (_t != FailIfDifferent) { m_data.fill(0); auto c = std::min(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } + explicit FixedHash(bytes const& _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } } /// Explicitly construct, copying from a byte array. - explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); else if (_t != FailIfDifferent) { m_data.fill(0); auto c = std::min(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } + explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } } /// Explicitly construct, copying from a bytes in memory with given pointer. explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } /// Explicitly construct, copying from a string. - explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : dev::asBytes(_s)) {} + explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s) : dev::asBytes(_s), _ht) {} /// Convert to arithmetic type. operator Arith() const { return fromBigEndian(m_data); } diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 42633f6f1..2c5f07e51 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -23,7 +23,7 @@ public: vector_ref(typename std::conditional::value, std::vector::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {} #ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ - vector_ref(leveldb::Slice const& _s): m_data(_s.data()), m_count(_s.size() / sizeof(_T)) {} + vector_ref(leveldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {} #endif explicit operator bool() const { return m_data && m_count; } diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h index 816e393a3..ecda3b6ec 100644 --- a/libdevcrypto/MemoryDB.h +++ b/libdevcrypto/MemoryDB.h @@ -51,14 +51,18 @@ public: bool kill(h256 _h); void purge(); - bytes lookupAux(h256 _h) const { return asBytes(lookup(h256(sha3(_h).ref().cropped(16), h256::AlignRight))); } - void insertAux(h256 _h, bytesConstRef _v) { return insert(h256(sha3(_h).ref().cropped(16), h256::AlignRight), _v); } + bytes lookupAux(h256 _h) const { auto h = aux(_h); return m_aux.count(h) ? m_aux.at(h) : bytes(); } + void insertAux(h256 _h, bytesConstRef _v) { m_auxKey = aux(_h); m_aux[m_auxKey] = _v.toBytes(); } std::set keys() const; protected: + static h256 aux(h256 _k) { return h256(sha3(_k).ref().cropped(0, 24), h256::AlignLeft); } + std::map m_over; std::map m_refCount; + h256 m_auxKey; + std::map m_aux; mutable bool m_enforceRefs = false; }; diff --git a/libdevcrypto/OverlayDB.cpp b/libdevcrypto/OverlayDB.cpp index 9e110bb84..4f237becb 100644 --- a/libdevcrypto/OverlayDB.cpp +++ b/libdevcrypto/OverlayDB.cpp @@ -19,6 +19,7 @@ * @date 2014 */ +#include #include #include "OverlayDB.h" using namespace std; @@ -51,11 +52,23 @@ void OverlayDB::commit() if (m_refCount[i.first]) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); } + if (m_auxKey && m_aux.count(m_auxKey)) + m_db->Put(m_writeOptions, m_auxKey.ref(), bytesConstRef(&m_aux[m_auxKey])); m_over.clear(); m_refCount.clear(); } } +bytes OverlayDB::lookupAux(h256 _h) const +{ + bytes ret = MemoryDB::lookupAux(_h); + if (!ret.empty()) + return ret; + std::string v; + m_db->Get(m_readOptions, _h.ref(), &v); + return asBytes(v); +} + void OverlayDB::rollback() { m_over.clear(); diff --git a/libdevcrypto/OverlayDB.h b/libdevcrypto/OverlayDB.h index e9bd53343..d027afbd4 100644 --- a/libdevcrypto/OverlayDB.h +++ b/libdevcrypto/OverlayDB.h @@ -51,6 +51,8 @@ public: bool exists(h256 _h) const; void kill(h256 _h); + bytes lookupAux(h256 _h) const; + private: using MemoryDB::clear; diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index 583bfbf6e..348cab59e 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -408,7 +408,7 @@ public: Super::setRoot(h256(Super::db()->lookupAux(m_secure.root()))); } - h256 root() const { return m_secure.root(); } + h256 root() const { const_cast(this)->syncRoot(); return m_secure.root(); } void insert(bytesConstRef _key, bytesConstRef _value) { Super::insert(_key, _value); m_secure.insert(_key, _value); syncRoot(); } void remove(bytesConstRef _key) { Super::remove(_key); m_secure.remove(_key); syncRoot(); } diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index eba10e321..ad41780e9 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -33,7 +33,7 @@ namespace eth { const unsigned c_protocolVersion = 55; -const unsigned c_databaseVersion = 5 + +const unsigned c_databaseVersion = 6 + #if ETH_FATDB 1000 #else diff --git a/libethereum/Account.h b/libethereum/Account.h index 3f3a5d548..2cc962baa 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -76,28 +76,40 @@ public: ContractConception }; + /// Changedness of account to create. + enum Changedness + { + /// Account starts as though it has been changed. + Changed, + /// Account starts as though it has not been changed. + Unchanged + }; + /// Construct a dead Account. Account() {} /// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a /// contract account in the /// conception phase, where the code is not yet known. - Account(u256 _balance, NewAccountType _t): m_isAlive(true), m_balance(_balance), m_codeHash(_t == NormalCreation ? EmptySHA3 : c_contractConceptionCodeHash) {} + Account(u256 _balance, NewAccountType _t, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_balance(_balance), m_codeHash(_t == NormalCreation ? EmptySHA3 : c_contractConceptionCodeHash) {} /// Explicit constructor for wierd cases of construction of a normal account. - Account(u256 _nonce, u256 _balance): m_isAlive(true), m_nonce(_nonce), m_balance(_balance) {} + Account(u256 _nonce, u256 _balance, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance) {} /// Explicit constructor for wierd cases of construction or a contract account. - Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash): m_isAlive(true), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); } + Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash, Changedness _c): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); } /// Kill this account. Useful for the suicide opcode. Following this call, isAlive() returns false. - void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = EmptyTrie; m_balance = 0; m_nonce = 0; } + void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = EmptyTrie; m_balance = 0; m_nonce = 0; changed(); } /// @returns true iff this object represents an account in the state. Returns false if this object /// represents an account that should no longer exist in the trie (an account that never existed or was /// suicided). bool isAlive() const { return m_isAlive; } + /// @returns true if the account is unchanged from creation. + bool isDirty() const { return !m_isUnchanged; } + /// @returns the balance of this account. Can be altered in place. u256& balance() { return m_balance; } @@ -106,7 +118,7 @@ public: u256 const& balance() const { return m_balance; } /// Increments the balance of this account by the given amount. It's a bigint, so can be negative. - void addBalance(bigint _i) { m_balance = (u256)((bigint)m_balance + _i); } + void addBalance(bigint _i) { if (!_i) return; m_balance = (u256)((bigint)m_balance + _i); changed(); } /// @returns the nonce of the account. Can be altered in place. u256& nonce() { return m_nonce; } @@ -115,7 +127,7 @@ public: u256 const& nonce() const { return m_nonce; } /// Increment the nonce of the account by one. - void incNonce() { m_nonce++; } + void incNonce() { m_nonce++; changed(); } /// @returns the root of the trie (whose nodes are stored in the state db externally to this class) @@ -127,7 +139,7 @@ public: /// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing /// to the trie later. - void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; } + void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; changed(); } /// @returns true if we are in the contract-conception state and setCode is valid to call. bool isFreshCode() const { return m_codeHash == c_contractConceptionCodeHash; } @@ -140,8 +152,8 @@ public: h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; } /// Sets the code of the account. Must only be called when isFreshCode() returns true. - void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; } - void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; } + void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); } + void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); } /// @returns true if the account's code is available through code(). bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); } @@ -154,9 +166,15 @@ public: bytes const& code() const { assert(codeCacheValid()); return m_codeCache; } private: + /// Note that we've altered the account. + void changed() { m_isUnchanged = false; } + /// Is this account existant? If not, it represents a deleted account. bool m_isAlive = false; + /// True if we've not made any alteration to the account having been given it's properties directly. + bool m_isUnchanged = false; + /// Account's nonce. u256 m_nonce = 0; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 29095076f..f3a204fea 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -19,6 +19,8 @@ * @date 2014 */ +#include + #include "BlockChain.h" #include @@ -43,14 +45,17 @@ namespace js = json_spirit; std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) { string cmp = toBigEndianString(_bc.currentHash()); - auto it = _bc.m_extrasDB->NewIterator(_bc.m_readOptions); + auto it = _bc.m_db->NewIterator(_bc.m_readOptions); for (it->SeekToFirst(); it->Valid(); it->Next()) if (it->key().ToString() != "best") { - string rlpString = it->value().ToString(); - RLP r(rlpString); - BlockDetails d(r); - _out << toHex(it->key().ToString()) << ": " << d.number << " @ " << d.parent << (cmp == it->key().ToString() ? " BEST" : "") << std::endl; + try { + BlockInfo d(bytesConstRef(it->value())); + _out << toHex(it->key().ToString()) << ": " << d.number << " @ " << d.parentHash << (cmp == it->key().ToString() ? " BEST" : "") << std::endl; + } + catch (...) { + cwarn << "Invalid DB entry:" << toHex(it->key().ToString()) << " -> " << toHex(bytesConstRef(it->value())); + } } delete it; return _out; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 6aedc4c24..014d0f91e 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -224,7 +224,7 @@ void State::ensureCached(std::map& _cache, Address _a, bool _r if (state.isNull()) s = Account(0, Account::NormalCreation); else - s = Account(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash()); + s = Account(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash(), Account::Unchanged); bool ok; tie(it, ok) = _cache.insert(make_pair(_a, s)); } @@ -625,7 +625,7 @@ void State::uncommitToMine() if (!m_transactions.size()) m_state.setRoot(m_previousBlock.stateRoot); else - m_state.setRoot(m_receipts[m_receipts.size() - 1].stateRoot()); + m_state.setRoot(m_receipts.back().stateRoot()); m_db = m_lastTx; paranoia("Uncommited to mine", true); m_currentBlock.sha3Uncles = h256(); @@ -890,7 +890,7 @@ Address State::newContract(u256 _balance, bytes const& _code) auto it = m_cache.find(ret); if (it == m_cache.end()) { - m_cache[ret] = Account(0, _balance, EmptyTrie, h); + m_cache[ret] = Account(0, _balance, EmptyTrie, h, Account::Changed); return ret; } } @@ -1144,7 +1144,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) _out << "--- " << _s.rootHash() << std::endl; std::set
d; std::set
dtr; - auto trie = TrieDB(const_cast(&_s.m_db), _s.rootHash()); + auto trie = SecureTrieDB(const_cast(&_s.m_db), _s.rootHash()); for (auto i: trie) d.insert(i.first), dtr.insert(i.first); for (auto i: _s.m_cache) diff --git a/libethereum/State.h b/libethereum/State.h index 37af0cf0e..a496a4a03 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -328,40 +328,43 @@ template void commit(std::map const& _cache, DB& _db, SecureTrieDB& _state) { for (auto const& i: _cache) - if (!i.second.isAlive()) - _state.remove(i.first); - else + if (i.second.isDirty()) { - RLPStream s(4); - s << i.second.nonce() << i.second.balance(); - - if (i.second.storageOverlay().empty()) - { - assert(i.second.baseRoot()); - s.append(i.second.baseRoot()); - } + if (!i.second.isAlive()) + _state.remove(i.first); else { - SecureTrieDB storageDB(&_db, i.second.baseRoot()); - for (auto const& j: i.second.storageOverlay()) - if (j.second) - storageDB.insert(j.first, rlp(j.second)); - else - storageDB.remove(j.first); - assert(storageDB.root()); - s.append(storageDB.root()); - } - - if (i.second.isFreshCode()) - { - h256 ch = sha3(i.second.code()); - _db.insert(ch, &i.second.code()); - s << ch; + RLPStream s(4); + s << i.second.nonce() << i.second.balance(); + + if (i.second.storageOverlay().empty()) + { + assert(i.second.baseRoot()); + s.append(i.second.baseRoot()); + } + else + { + SecureTrieDB storageDB(&_db, i.second.baseRoot()); + for (auto const& j: i.second.storageOverlay()) + if (j.second) + storageDB.insert(j.first, rlp(j.second)); + else + storageDB.remove(j.first); + assert(storageDB.root()); + s.append(storageDB.root()); + } + + if (i.second.isFreshCode()) + { + h256 ch = sha3(i.second.code()); + _db.insert(ch, &i.second.code()); + s << ch; + } + else + s << i.second.codeHash(); + + _state.insert(i.first, &s.out()); } - else - s << i.second.codeHash(); - - _state.insert(i.first, &s.out()); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7ddfdb40d..292f62a03 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,7 +26,7 @@ target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) target_link_libraries(testeth secp256k1) target_link_libraries(testeth solidity) -if (NOT HEADLESS) +if (NOT HEADLESS AND NOT JUSTTESTS) target_link_libraries(testeth webthree) target_link_libraries(testeth natspec) endif() diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 65ff5084f..5b7b0415e 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -20,6 +20,7 @@ * State test functions. */ +#include #include #include #include @@ -29,7 +30,21 @@ using namespace std; using namespace dev; using namespace dev::eth; -int stateTest() +namespace dev +{ +namespace test +{ + +int stateTest(); + +BOOST_AUTO_TEST_SUITE(StateIntegration) + +BOOST_AUTO_TEST_CASE(Basic) +{ + State s; +} + +BOOST_AUTO_TEST_CASE(Complex) { cnote << "Testing State..."; @@ -37,14 +52,15 @@ int stateTest() KeyPair myMiner = sha3("Gav's Miner"); // KeyPair you = sha3("123"); - Defaults::setDBPath(boost::filesystem::temp_directory_path().string()); + Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count())); OverlayDB stateDB = State::openDB(); CanonBlockChain bc; - State s(myMiner.address(), stateDB); - cout << bc; + State s(myMiner.address(), stateDB); + cout << s; + // Sync up - this won't do much until we use the last state. s.sync(bc); @@ -52,7 +68,7 @@ int stateTest() // Mine to get some ether! s.commitToMine(bc); - while (!s.mine(100).completed) {} + while (!s.mine(100, true).completed) {} s.completeMine(); bc.attemptImport(s.blockData(), stateDB); @@ -65,7 +81,7 @@ int stateTest() // Inject a transaction to transfer funds from miner to me. bytes tx; { - Transaction t(1000, 0, 0, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); + Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); assert(t.sender() == myMiner.address()); tx = t.rlp(); } @@ -76,7 +92,7 @@ int stateTest() // Mine to get some ether and set in stone. s.commitToMine(bc); s.commitToMine(bc); - while (!s.mine(50).completed) { s.commitToMine(bc); } + while (!s.mine(100, true).completed) {} s.completeMine(); bc.attemptImport(s.blockData(), stateDB); @@ -85,7 +101,9 @@ int stateTest() s.sync(bc); cout << s; - - return 0; } +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/trie.cpp b/test/trie.cpp index 4b676fb9a..aa453925d 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -52,6 +52,26 @@ using dev::operator <<; BOOST_AUTO_TEST_SUITE(TrieTests) +BOOST_AUTO_TEST_CASE(fat_trie) +{ + h256 r; + MemoryDB fm; + { + FatGenericTrieDB ft(&fm); + ft.init(); + ft.insert(h256("69", h256::FromHex, h256::AlignRight).ref(), h256("414243", h256::FromHex, h256::AlignRight).ref()); + for (auto i: ft) + cnote << i.first << i.second; + r = ft.root(); + } + { + FatGenericTrieDB ft(&fm); + ft.setRoot(r); + for (auto i: ft) + cnote << i.first << i.second; + } +} + BOOST_AUTO_TEST_CASE(trie_test_anyorder) { string testPath = test::getTestPath();