Browse Source

Correct order of nonce/balance. PROTOCOL CHANGE! NEW CHAIN!

Extra paranoia for trie.
Trie fixes.
Trie tests.
Version bump.
cl-refactor
Gav Wood 11 years ago
parent
commit
9f1191370a
  1. 2
      libethcore/CommonEth.cpp
  2. 6
      libethereum/AddressState.h
  3. 2
      libethereum/BlockChain.cpp
  4. 143
      libethereum/State.cpp
  5. 5
      libethereum/State.h
  6. 2
      libethsupport/Common.h
  7. 4
      libethsupport/CommonData.cpp
  8. 11
      libethsupport/TrieDB.cpp
  9. 91
      libethsupport/TrieDB.h
  10. 2
      libethsupport/vector_ref.h
  11. 35
      test/main.cpp
  12. 104
      test/trie.cpp

2
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<pair<u256, string>> g_units =
{

6
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;

2
libethereum/BlockChain.cpp

@ -82,7 +82,7 @@ std::map<Address, AddressState> 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;

143
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<u256>() : 0;
return r ? s ? s->balance() : r[1].toInt<u256>() : 0;
}
u256 nonce() const
{
return r ? s ? s->nonce() : r[1].toInt<u256>() : 0;
return r ? s ? s->nonce() : r[0].toInt<u256>() : 0;
}
bytes code() const
@ -323,7 +348,7 @@ map<Address, u256> 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<u256>();
ret[i.first] = RLP(i.second)[1].toInt<u256>();
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<h256, Overlay> storageDB(&m_db, r[2].toHash<h256>());
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<h256>() != EmptySHA3 && m_db.lookup(r[3].toHash<h256>()).empty())
return false;
}
}
{
EnforceRefs r(m_db, true);
for (auto const& i: m_state)
{
RLP r(i.second);
TrieDB<h256, Overlay> storageDB(&m_db, r[2].toHash<h256>());
try
for (auto const& i: m_state)
{
RLP r(i.second);
TrieDB<h256, Overlay> storageDB(const_cast<Overlay*>(&m_db), r[2].toHash<h256>()); // 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<h256>() != EmptySHA3 && m_db.lookup(r[3].toHash<h256>()).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<u256>() && cache->nonce() == r[1].toInt<u256>()))
if (cache && r && cache->nonce() == r[0].toInt<u256>() && cache->balance() == r[1].toInt<u256>())
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<u256>()) << " #:" << (cache ? cache->nonce() : r[1].toInt<u256>()) << contout.str() << std::endl;
_out << lead << i << ": " << std::dec << (cache ? cache->nonce() : r[0].toInt<u256>()) << " #:" << (cache ? cache->balance() : r[1].toInt<u256>()) << 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<u256, Diff<u256>> const& i: _s.storage)

5
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<Address, Overlay> m_state; ///< Our state tree, as an Overlay DB.
@ -338,7 +339,7 @@ void commit(std::map<Address, AddressState> const& _cache, DB& _db, TrieDB<Addre
else
{
RLPStream s(4);
s << i.second.balance() << i.second.nonce();
s << i.second.nonce() << i.second.balance();
if (i.second.storage().empty())
s.append(i.second.oldRoot(), false, true);

2
libethsupport/Common.h

@ -24,7 +24,7 @@
#pragma once
// define version
#define ETH_VERSION 0.5.9
#define ETH_VERSION 0.5.10
// way to many uint to size_t warnings in 32 bit build
#ifdef _M_IX86

4
libethsupport/CommonData.cpp

@ -51,8 +51,8 @@ std::string eth::escaped(std::string const& _s, bool _all)
std::string eth::randomWord()
{
static std::mt19937_64 s_eng(0);
std::string ret(std::uniform_int_distribution<int>(4, 10)(s_eng), ' ');
char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
std::string ret(std::uniform_int_distribution<int>(1, 5)(s_eng), ' ');
char const n[] = "qwertyuiop";//asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
std::uniform_int_distribution<int> d(0, sizeof(n) - 2);
for (char& c: ret)
c = n[d(s_eng)];

11
libethsupport/TrieDB.cpp

@ -29,6 +29,17 @@ namespace eth
const h256 c_shaNull = sha3(rlp(""));
std::map<h256, std::string> BasicMap::get() const
{
if (!m_enforceRefs)
return m_over;
std::map<h256, std::string> 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);

91
libethsupport/TrieDB.h

@ -43,7 +43,7 @@ public:
BasicMap() {}
void clear() { m_over.clear(); }
std::map<h256, std::string> const& get() const { return m_over; }
std::map<h256, std::string> get() const;
std::string lookup(h256 _h) const;
bool exists(h256 _h) const;
@ -57,7 +57,7 @@ protected:
std::map<h256, std::string> m_over;
std::map<h256, uint> 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<h256>& _keyMask) const
void descendKey(h256 _k, std::set<h256>& _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<h256>& _keyMask) const
void descendEntry(RLP const& _r, std::set<h256>& _keyMask, bool _wasExt, std::ostream* _out, int _indent) const
{
if (_r.isData() && _r.size() == 32)
descendKey(_r.toHash<h256>(), _keyMask);
descendKey(_r.toHash<h256>(), _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<h256>& _keyMask) const
void descendList(RLP const& _r, std::set<h256>& _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<h256> check() const
std::set<h256> leftOvers(std::ostream* _out = nullptr) const
{
std::set<h256> 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 <class DB> std::string GenericTrieDB<DB>::deref(RLP const& _n) const
template <class DB> bytes GenericTrieDB<DB>::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 <class DB> bytes GenericTrieDB<DB>::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 <class DB> bytes GenericTrieDB<DB>::deleteAt(RLP const& _orig, NibbleSl
template <class DB> bool GenericTrieDB<DB>::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<h256>().abridged() : std::string());
#endif
bytes b = _orig.isEmpty() ? bytes() : deleteAt(_orig.isList() ? _orig : RLP(node(_orig.toHash<h256>())), _k);
if (!b.size()) // not found - no change.
@ -780,7 +809,10 @@ template <class DB> bool GenericTrieDB<DB>::deleteAtAux(RLPStream& _out, RLP con
template <class DB> bytes GenericTrieDB<DB>::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 <class DB> bytes GenericTrieDB<DB>::place(RLP const& _orig, NibbleSlice
// out2: [V0, ..., V15, null] iff exists i: !!Vi -- OR -- null otherwise
template <class DB> bytes GenericTrieDB<DB>::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 <class DB> RLPStream& GenericTrieDB<DB>::streamNode(RLPStream& _s, byte
template <class DB> bytes GenericTrieDB<DB>::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 <class DB> bytes GenericTrieDB<DB>::cleve(RLP const& _orig, uint _s)
template <class DB> bytes GenericTrieDB<DB>::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 <class DB> bytes GenericTrieDB<DB>::graft(RLP const& _orig)
template <class DB> bytes GenericTrieDB<DB>::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 <class DB> bytes GenericTrieDB<DB>::merge(RLP const& _orig, byte _i)
template <class DB> bytes GenericTrieDB<DB>::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);

2
libethsupport/vector_ref.h

@ -33,7 +33,7 @@ public:
bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); }
std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); }
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>((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 <class _T2> 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; }

35
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<BasicMap> 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;

104
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<pair<string, string>> 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<BasicMap> 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<BasicMap> 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<BasicMap> d(&m);
BasicMap dm;
EnforceRefs e(dm, true);
GenericTrieDB<BasicMap> 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<BasicMap> 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;
}

Loading…
Cancel
Save