Browse Source

TrieDB framework and insertion.

LevelDB overlay and map backends.
cl-refactor
Gav Wood 11 years ago
parent
commit
04743c50f7
  1. 2
      libethereum/BlockInfo.cpp
  2. 22
      libethereum/Common.h
  3. 8
      libethereum/RLP.cpp
  4. 14
      libethereum/State.cpp
  5. 12
      libethereum/State.h
  6. 179
      libethereum/Trie.cpp
  7. 469
      libethereum/Trie.h
  8. 33
      test/main.cpp

2
libethereum/BlockInfo.cpp

@ -92,7 +92,7 @@ void BlockInfo::populate(bytesConstRef _block)
// check it hashes according to proof of work.
Dagger d(headerHashWithoutNonce());
if (d.eval(nonce) >= difficulty)
if (d.eval(nonce) >= (u256)(((bigint)1 << 256) / difficulty))
throw InvalidNonce();
}

22
libethereum/Common.h

@ -56,16 +56,16 @@ using u160s = std::vector<u160>;
using u256Set = std::set<u256>;
using u160Set = std::set<u160>;
template <class _T, class _Out> inline void toBigEndian(_T _val, _Out& o_out);
template <class _T, class _In> inline _T fromBigEndian(_In const& _bytes);
template <class T, class Out> inline void toBigEndian(T _val, Out& o_out);
template <class T, class In> inline T fromBigEndian(In const& _bytes);
template <unsigned _N>
template <unsigned N>
class FixedHash
{
using Arith = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<_N * 8, _N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
using Arith = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
public:
enum { size = _N };
enum { size = N };
FixedHash() { m_data.fill(0); }
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); }
@ -84,10 +84,20 @@ public:
byte* data() { return m_data.data(); }
byte const* data() const { return m_data.data(); }
bytes asBytes() const { return bytes(data(), data() + 32); }
private:
std::array<byte, _N> m_data;
std::array<byte, N> m_data;
};
template <unsigned N>
inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> const& _h)
{
for (unsigned i = 0; i < N; ++i)
_out << std::hex << std::setfill('0') << std::setw(2) << (int)_h[i];
return _out;
}
using h256 = FixedHash<32>;
using h160 = FixedHash<20>;
using h256s = std::vector<h256>;

8
libethereum/RLP.cpp

@ -45,6 +45,14 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin)
{
auto pl = _parent.payload();
m_lastItem = pl.cropped(0, RLP(pl).actualSize());
uint t = 0;
for (uint i = 0; i < _parent.itemCount(); ++i)
t += _parent[i].actualSize();
if (pl.size() != t)
cout << _parent.itemCount() << " " << asHex(pl);
assert(pl.size() == t);
m_remaining = pl.size() - m_lastItem.size();
}
else

14
libethereum/State.cpp

@ -43,15 +43,19 @@ u256 const State::c_newContractFee = 0;
u256 const State::c_txFee = 0;
u256 const State::c_blockReward = 0;
State::State(Address _coinbaseAddress): m_ourAddress(_coinbaseAddress)
State::State(Address _coinbaseAddress): m_state(&m_db), m_ourAddress(_coinbaseAddress)
{
secp256k1_start();
m_previousBlock = BlockInfo::genesis();
m_currentBlock.coinbaseAddress = m_ourAddress;
ldb::Options o;
ldb::DB::Open(o, "state", &m_db);
m_state.open(m_db, m_currentBlock.stateRoot, &m_over);
ldb::DB* db = nullptr;
ldb::DB::Open(o, "state", &db);
m_db.setDB(db);
m_state.init();
m_state.setRoot(m_currentBlock.stateRoot);
}
void State::sync(BlockChain const& _bc)
@ -336,13 +340,13 @@ u256 State::contractMemory(Address _id, u256 _memory) const
RLP rlp(m_state[_id]);
if (rlp.itemCount() != 3)
throw InvalidContractAddress();
return fromBigEndian<u256>(TrieDB<h256>(m_db, rlp[2].toHash<h256>(), (std::map<h256, std::string>*)&m_over)[_memory]);
return fromBigEndian<u256>(TrieDB<h256, Overlay>(const_cast<Overlay*>(&m_db), rlp[2].toHash<h256>())[_memory]);
}
void State::setContractMemory(Address _contract, u256 _memory, u256 _value)
{
RLP rlp(m_state[_contract]);
TrieDB<h256> c(m_db, &m_over);
TrieDB<h256, Overlay> c(&m_db);
std::string s = toBigEndianString(_value);
if (rlp.itemCount() == 3)
{

12
libethereum/State.h

@ -154,16 +154,8 @@ private:
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
/// Commit all pending state modifications for archival. This cannot be undone.
void commit() { for (auto const& i: m_over) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); m_over.clear(); }
/// Rollback all pending state modifictions.
void rollback() { m_over.clear(); }
ldb::DB* m_db; ///< The DB, holding all of our Tries' backend data.
ldb::WriteOptions m_writeOptions;
std::map<h256, std::string> m_over; ///< The current overlay onto the state DB.
TrieDB<Address> m_state; ///< Our state tree.
Overlay m_db; ///< Our overlay for the state tree.
TrieDB<Address, Overlay> m_state; ///< Our state tree, as an Overlay DB.
std::map<h256, Transaction> m_transactions; ///< The current list of transactions that we've included in the state.
BlockInfo m_previousBlock; ///< The previous block's information.

179
libethereum/Trie.cpp

@ -665,17 +665,34 @@ void Trie::remove(std::string const& _key)
}
}
h256 const GenericTrieDB::c_null = sha3(RLPNull);
uint sharedNibbles(bytesConstRef _a, uint _ab, uint _ae, bytesConstRef _b, uint _bb, uint _be)
{
uint ret = 0;
for (uint ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi, ++ret) {}
return ret;
}
bool isLeaf(RLP const& _twoItem)
{
assert(_twoItem.isList() && _twoItem.itemCount() == 2);
auto pl = _twoItem[0].payload();
return (pl[0] & 0x20);
}
inline byte nibble(bytesConstRef _data, uint _i)
NibbleSlice keyOf(RLP const& _twoItem)
{
return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4);
assert(_twoItem.isList() && _twoItem.itemCount() == 2);
auto pl = _twoItem[0].payload();
if (pl[0] & 0x10)
return NibbleSlice(pl, 1);
else
return NibbleSlice(pl, 2);
}
std::string hexPrefixEncode(bytesConstRef _data, bool _terminated, int _beginNibble, int _endNibble)
std::string hexPrefixEncode(bytesConstRef _data, bool _terminated, int _beginNibble, int _endNibble, uint _offset)
{
uint begin = _beginNibble;
uint end = _endNibble < 0 ? _data.size() * 2 + 1 + _endNibble : _endNibble;
uint begin = _beginNibble + _offset;
uint end = (_endNibble < 0 ? (_data.size() * 2 - _offset) + 1 + _endNibble : _endNibble) + _offset;
bool odd = (end - begin) & 1;
std::string ret(1, ((_terminated ? 2 : 0) | (odd ? 1 : 0)) * 16);
@ -693,145 +710,61 @@ std::string hexPrefixEncode(bytesConstRef _data, bool _terminated, int _beginNib
return ret;
}
uint sharedNibbles(bytesConstRef _a, uint _ab, uint _ae, bytesConstRef _b, uint _bb, uint _be)
std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _terminated)
{
uint ret = 0;
for (uint ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi) {}
return ret;
}
uint begin1 = _o1;
uint end1 = _d1.size() * 2 - _o1;
uint begin2 = _o2;
uint end2 = _d2.size() * 2 - _o2;
// alters given RLP such that the given key[_begin:_end]/value exists under it, and puts the new RLP into _s.
// _s's size could be anything.
void GenericTrieDB::mergeHelper(RLPStream& _s, RLP const& _replace, bytesConstRef _key, bytesConstRef _value, uint _begin, uint _end, uint _nodeBegin)
{
/* if (_replace.isNull() || _replace.isEmpty())
{
_s.insertList(2);
_s << hexPrefixEncode(_key, true, _begin, _end);
_s.appendString(_value);
}
else if (_replace.itemCount() == 2)
bool odd = (end1 - begin1 + end2 - begin2) & 1;
std::string ret(1, ((_terminated ? 2 : 0) | (odd ? 1 : 0)) * 16);
ret.reserve((end1 - begin1 + end2 - begin2) / 2 + 1);
uint d = odd ? 1 : 2;
for (auto i = begin1; i < end1; ++i, ++d)
{
// split [k,v] into branch
auto pl = _replace[0].payload();
auto plb = ((pl[0] & 0x10) ? 1 : 2) + _nodeBegin;
auto ple = pl.size() - plb;
uint pivot = sharedNibbles(_key, _begin, _end, pl, plb, ple);
// special case for pivot == first nibble, pivot == last nibble. if it's anything else, then it's recursive.
if (pivot == ple - plb && !(pl[0] & 0x20)) // key begins with node-partial and is non-terminated - leave it as the child node's problem.
{
// share this non-termed 2-item node - merge in below.
_s.insertList(2);
_s << _replace[0];
mergeNode(_s, _replace[1], _key, _value, _begin + pivot, _end, 0);
}
else if (pivot == ple - plb && pivot == _end - _begin && (pl[0] & 0x20)) // key and value exactly the same and is terminated - just replace value.
{
_s.insertList(2);
_s << hexPrefixEncode(_key, true, _begin, _end);
_s.appendString(_value);
}
byte n = nibble(_d1, i);
if (d & 1) // odd
ret.back() |= n; // or the nibble onto the back
else
{
if (pivot == 0)
{
// branch since the pivot is right here.
_s.insertList(17);
auto n = plb == ple ? 16 : nibble(pl, plb);
auto m = _begin == _end ? 16 : nibble(_key, _begin);
for (auto i = 0; i < 16; ++i)
if (i == n)
remerge(_s, _replace, _nodeBegin);
else if (i == m)
mergeItem(_s, RLP(), _key, _value, _begin, _end);
else
_s << "";
if (n == 16)
{
assert(pl[0] & 0x20);
// this should be terminated - if it's not and we share the entire 2-node partial key's worth of nibbles, then why didn't we skip over the 2-item node and merge directly into the branch below?
_s.appendRaw(_replace[1]);
}
else if (m == 16)
_s << _value;
else
s << "";
}
else
{
_s.insertList(2);
_s << hexPrefixEncode(_key, false, _begin, _begin + pivot);
mergeItem(_s, _replace, _key, _value, _begin + pivot, _end, _nodeBegin + pivot);
}
}
ret.push_back(n << 4); // push the nibble on to the back << 4
}
else if (_replace.itemCount() == 17)
for (auto i = begin2; i < end2; ++i, ++d)
{
// insert into branch
_s.insertList(17);
if (_begin == _end)
{
// as value
for (auto i = 0; i < 16; ++i)
_s << _replace[i];
_s << _value;
}
byte n = nibble(_d2, i);
if (d & 1) // odd
ret.back() |= n; // or the nibble onto the back
else
{
// underneath
auto n = nibble(_key, _begin);
for (auto i = 0; i < 17; ++i)
if (n == i)
insertItem(s, _replace[i], _key, _value, _begin, _end);
else
_s << _replace[i];
}
}*/
ret.push_back(n << 4); // push the nibble on to the back << 4
}
return ret;
}
// Inserts the given item into an RLPStream, either inline (if RLP < 32) or creating a node and inserting the hash.
// Assumes we are _nodeBegin of the way through the key of the _replace.
void GenericTrieDB::mergeItem(RLPStream& _s, RLP const& _replace, bytesConstRef _k, bytesConstRef _v, uint _begin, uint _end, uint _nodeBegin)
std::string hexPrefixEncode(NibbleSlice _s, bool _leaf, int _begin, int _end)
{
/*
RLPStream s;
mergeHelper(s, (_replace.isString() || _replace.isInt()) ? RLP(node(_replace.toHash())) : _replace, _k, _v, _begin, _end, _nodeBegin);
streamNode(s, s.out());
// Kill old node.
if ((_replace.isString() || _replace.isInt()) && _nodeBegin == 0)
killNode(_replace.toHash());
*/
return hexPrefixEncode(_s.data, _leaf, _begin, _end, _s.offset);
}
void GenericTrieDB::streamNode(RLPStream& _s, bytes const& _b)
std::string hexPrefixEncode(NibbleSlice _s1, NibbleSlice _s2, bool _leaf)
{
if (_b.size() < 32)
_s.appendRaw(_b);
else
_s << insertNode(&_b);
return hexPrefixEncode(_s1.data, _s1.offset, _s2.data, _s2.offset, _leaf);
}
void GenericTrieDB::insert(bytesConstRef _key, bytesConstRef _value)
bool NibbleSlice::contains(NibbleSlice _k) const
{
string rv = node(m_root);
killNode(m_root);
RLPStream s;
mergeHelper(s, RLP(rv), _key, _value, 0, _key.size() * 2, 0);
m_root = insertNode(&s.out());
return shared(_k) == _k.size();
}
void GenericTrieDB::remove(bytesConstRef _key)
bool NibbleSlice::operator==(NibbleSlice _k) const
{
return _k.size() == size() && shared(_k) == _k.size();
}
std::string GenericTrieDB::at(bytesConstRef _key) const
uint NibbleSlice::shared(NibbleSlice _k) const
{
return std::string();
return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size());
}
}

469
libethereum/Trie.h

@ -32,6 +32,7 @@ namespace eth
bytes rlp256(StringMap const& _s);
h256 hash256(StringMap const& _s);
h256 hash256(u256Map const& _s);
std::string hexPrefixEncode(bytes const& _hexVector, bool _terminated = false, int _begin = 0, int _end = -1);
class TrieNode;
@ -75,23 +76,109 @@ public:
virtual std::string at(h256 _key) const = 0;
};*/
inline byte nibble(bytesConstRef _data, uint _i)
{
return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4);
}
struct NibbleSlice
{
bytesConstRef data;
uint offset;
NibbleSlice(bytesConstRef _d = bytesConstRef(), uint _o = 0): data(_d), offset(_o) {}
byte operator[](uint _index) const { return nibble(data, offset + _index); }
uint size() const { return data.size() * 2 - offset; }
NibbleSlice mid(uint _index) const { return NibbleSlice(data, offset + _index); }
uint shared(NibbleSlice _s) const;
bool contains(NibbleSlice _s) const;
bool operator==(NibbleSlice _s) const;
bool operator!=(NibbleSlice _s) const { return !operator==(_s); }
};
inline std::ostream& operator<<(std::ostream& _out, NibbleSlice const& _m)
{
for (uint i = 0; i < _m.size(); ++i)
_out << std::hex << (int)_m[i];
return _out;
}
class DBFace
{
public:
virtual std::string node(h256 _h) const = 0;
virtual void insertNode(h256 _h, bytesConstRef _v) = 0;
virtual void killNode(h256 _h) = 0;
};
class BasicMap
{
public:
BasicMap() {}
std::map<h256, std::string> const& get() const { return m_over; }
std::string lookup(h256 _h) const { auto it = m_over.find(_h); if (it != m_over.end()) return it->second; return std::string(); }
void insert(h256 _h, bytesConstRef _v) { m_over[_h] = _v.toString(); m_refCount[_h]++; }
void kill(h256 _h) { if (!--m_refCount[_h]) m_over.erase(_h); }
protected:
std::map<h256, std::string> m_over;
std::map<h256, uint> m_refCount;
};
inline std::ostream& operator<<(std::ostream& _out, BasicMap const& _m)
{
for (auto i: _m.get())
{
_out << i.first << ": ";
::operator<<(_out, RLP(i.second));
_out << " " << asHex(i.second);
_out << std::endl;
}
return _out;
}
class Overlay: public BasicMap
{
public:
Overlay(ldb::DB* _db = nullptr): m_db(_db) {}
ldb::DB* db() const { return m_db; }
void setDB(ldb::DB* _db, bool _clearOverlay = true) { m_db = _db; if (_clearOverlay) m_over.clear(); }
void commit() { for (auto const& i: m_over) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); m_over.clear(); m_refCount.clear(); }
void rollback() { m_over.clear(); m_refCount.clear(); }
std::string lookup(h256 _h) const { std::string ret = BasicMap::lookup(_h); if (ret.empty()) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); return ret; }
private:
ldb::DB* m_db = nullptr;
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
};
/**
* @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree.
* This version uses an LDB backend - TODO: split off m_db & m_over into opaque key/value map layer and allow caching & testing without DB.
* TODO: Implement!
* TODO: Init function that inserts the SHA(emptyRLP) -> emptyRLP into the DB and sets m_root to SHA(emptyRLP).
*/
template <class DB>
class GenericTrieDB
{
public:
GenericTrieDB() {}
GenericTrieDB(ldb::DB* _db, std::map<h256, std::string>* _overlay = nullptr): GenericTrieDB() { open(_db, c_null, _overlay); }
GenericTrieDB(ldb::DB* _db, h256 _root, std::map<h256, std::string>* _overlay = nullptr): GenericTrieDB() { open(_db, _root, _overlay); }
GenericTrieDB(DB* _db): m_db(_db) {}
GenericTrieDB(DB* _db, h256 _root): m_root(_root), m_db(_db) {}
~GenericTrieDB() {}
void open(ldb::DB* _db, h256 _root, std::map<h256, std::string>* _overlay = nullptr) { m_root = _root; m_db = _db; m_over = _overlay; }
void open(DB* _db, h256 _root) { m_root = _root; m_db = _db; }
void init();
void setRoot(h256 _root) { m_root = _root; }
h256 root() const { return m_root; }
void debugPrint() {}
@ -103,40 +190,362 @@ public:
// TODO: iterators.
private:
void streamNode(RLPStream& _s, bytes const& _b);
void mergeHelper(RLPStream& _s, RLP const& _replace, bytesConstRef _key, bytesConstRef _value, uint _begin, uint _end, uint _nodeBegin = 0);
void mergeItem(RLPStream& _s, RLP const& _replace, bytesConstRef _k, bytesConstRef _v, uint _begin, uint _end, uint _nodeBegin = 0);
std::string node(h256 _h) const { if (_h == c_null) return std::string(); if (m_over) { auto it = m_over->find(_h); if (it != m_over->end()) return it->second; } std::string ret; if (m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)&m_root, 32), &ret); return ret; }
void insertNode(h256 _h, bytesConstRef _v) const { (*m_over)[_h] = _v.toString(); }
h256 insertNode(bytesConstRef _v) const { auto h = sha3(_v); (*m_over)[h] = _v.toString(); return h; }
void killNode(h256 _h) const { m_over->erase(_h); } // only from overlay - no killing from DB proper.
static const h256 c_null;
h256 m_root = c_null;
ldb::DB* m_db = nullptr;
std::map<h256, std::string>* m_over = nullptr;
ldb::ReadOptions m_readOptions;
RLPStream& streamNode(RLPStream& _s, bytes const& _b);
std::string atAux(RLP const& _here, NibbleSlice _key) const;
void mergeAtAux(RLPStream& _out, RLP const& _replace, NibbleSlice _key, bytesConstRef _value);
bytes mergeAt(RLP const& _replace, NibbleSlice _k, bytesConstRef _v);
// in1: null (DEL) -- OR -- [_k, V] (DEL)
// out1: [_k, _s]
// in2: [V0, ..., V15, S16] (DEL) AND _k == {}
// out2: [V0, ..., V15, _s]
bytes place(RLP const& _orig, NibbleSlice _k, bytesConstRef _s);
// in1: [K, S] (DEL)
// out1: null
// in2: [V0, ..., V15, S] (DEL)
// out2: [V0, ..., V15, null]
bytes remove(RLP const& _orig);
// in: [K1 & K2, V] (DEL) : nibbles(K1) == _s, 0 < _s < nibbles(K1 & K2)
// out: [K1, H] ; [K2, V] => H (INS) (being [K1, [K2, V]] (INS) if necessary)
bytes cleve(RLP const& _orig, uint _s);
// in: [K1, H] (DEL) ; H <= [K2, V] (DEL) (being [K1, [K2, V]] (DEL) if necessary)
// out: [K1 & K2, V]
bytes graft(RLP const& _orig);
// in: [V0, ... V15, S] (DEL) : (exists unique i: !!Vi AND !S "out1") OR (all i: !Vi AND !!S "out2")
// out1: [k{i}, Vi]
// out2: [k{}, S]
bytes merge(RLP const& _orig);
// in: [k{}, S] (DEL)
// out: [null ** 16, S]
// -- OR --
// in: [k{i}, V] (DEL)
// out: [null ** i, V, null ** (16 - i)]
bytes branch(RLP const& _orig);
std::string node(h256 _h) const { return m_db->lookup(_h); }
void insertNode(h256 _h, bytesConstRef _v) { m_db->insert(_h, _v); }
void killNode(h256 _h) { m_db->kill(_h); }
h256 insertNode(bytesConstRef _v) { auto h = sha3(_v); insertNode(h, _v); return h; }
void killNode(RLP const& _d) { if (_d.data().size() >= 32) killNode(sha3(_d.data())); }
h256 m_root;
DB* m_db = nullptr;
};
template <class KeyType>
class TrieDB: public GenericTrieDB
template <class KeyType, class DB>
class TrieDB: public GenericTrieDB<DB>
{
public:
TrieDB() {}
TrieDB(ldb::DB* _db, std::map<h256, std::string>* _overlay = nullptr): GenericTrieDB(_db, _overlay) {}
TrieDB(ldb::DB* _db, h256 _root, std::map<h256, std::string>* _overlay = nullptr): GenericTrieDB() { open(_db, _root, _overlay); }
TrieDB(DB* _db): GenericTrieDB<DB>(_db) {}
TrieDB(DB* _db, h256 _root): GenericTrieDB<DB>(_db, _root) {}
std::string operator[](KeyType _k) const { return at(_k); }
std::string at(KeyType _k) const { return GenericTrieDB::at(bytesConstRef((byte const*)&_k, sizeof(KeyType))); }
void insert(KeyType _k, bytesConstRef _value) { GenericTrieDB::insert(bytesConstRef((byte const*)&_k, sizeof(KeyType)), _value); }
std::string at(KeyType _k) const { return GenericTrieDB<DB>::at(bytesConstRef((byte const*)&_k, sizeof(KeyType))); }
void insert(KeyType _k, bytesConstRef _value) { GenericTrieDB<DB>::insert(bytesConstRef((byte const*)&_k, sizeof(KeyType)), _value); }
void insert(KeyType _k, bytes const& _value) { insert(_k, bytesConstRef(&_value)); }
void remove(KeyType _k) { GenericTrieDB::remove(bytesConstRef((byte const*)&_k, sizeof(KeyType))); }
void remove(KeyType _k) { GenericTrieDB<DB>::remove(bytesConstRef((byte const*)&_k, sizeof(KeyType))); }
};
}
// Template implementations...
namespace eth
{
uint sharedNibbles(bytesConstRef _a, uint _ab, uint _ae, bytesConstRef _b, uint _bb, uint _be);
bool isLeaf(RLP const& _twoItem);
NibbleSlice keyOf(RLP const& _twoItem);
std::string hexPrefixEncode(bytesConstRef _data, bool _terminated, int _beginNibble, int _endNibble, uint _offset);
std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _terminated);
std::string hexPrefixEncode(NibbleSlice _s, bool _leaf, int _begin = 0, int _end = -1);
std::string hexPrefixEncode(NibbleSlice _s1, NibbleSlice _s2, bool _leaf);
template <class DB> void GenericTrieDB<DB>::init()
{
m_root = insertNode(&RLPNull);
}
template <class DB> void GenericTrieDB<DB>::insert(bytesConstRef _key, bytesConstRef _value)
{
std::string rv = node(m_root);
killNode(m_root);
bytes b = mergeAt(RLP(rv), NibbleSlice(_key), _value);
m_root = insertNode(&b);
}
template <class DB> void GenericTrieDB<DB>::remove(bytesConstRef _key)
{
}
template <class DB> std::string GenericTrieDB<DB>::at(bytesConstRef _key) const
{
return atAux(RLP(node(m_root)), _key);
}
template <class DB> std::string GenericTrieDB<DB>::atAux(RLP const& _here, NibbleSlice _key) const
{
if (_here.isEmpty())
// not found.
return std::string();
assert(_here.isList() && (_here.itemCount() == 2 || _here.itemCount() == 17));
if (_here.itemCount() == 2)
{
auto k = keyOf(_here);
if (_key == k && isLeaf(_here))
// reached leaf and it's us
return _here[1].toString();
else if (_key.contains(k) && !isLeaf(_here))
// not yet at leaf and it might yet be us. onwards...
return atAux(_here[1].isList() ? _here[1] : RLP(node(_here[1].toHash<h256>())), _key.mid(k.size()));
else
// not us.
return std::string();
}
else
{
if (_key.size() == 0)
return _here[16].toString();
auto n = _here[_key[0]];
if (n.isEmpty())
return std::string();
else
return atAux(n.isList() ? n : RLP(node(n.toHash<h256>())), _key.mid(1));
}
}
template <class DB> bytes GenericTrieDB<DB>::mergeAt(RLP const& _orig, NibbleSlice _k, bytesConstRef _v)
{
// 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.
// Empty - just insert here
if (_orig.isEmpty())
return place(_orig, _k, _v);
assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17));
if (_orig.itemCount() == 2)
{
// pair...
NibbleSlice k = keyOf(_orig);
// exactly our node - place value in directly.
if (k == _k && isLeaf(_orig))
return place(_orig, _k, _v);
// partial key is our key - move down.
if (_k.contains(k))
{
killNode(sha3(_orig.data()));
RLPStream s(2);
s.appendRaw(_orig[0]);
mergeAtAux(s, _orig[1], _k.mid(k.size()), _v);
return s.out();
}
auto sh = _k.shared(k);
// std::cout << _k << " sh " << k << " = " << sh << std::endl;
if (sh)
// shared stuff - cleve at disagreement.
return mergeAt(RLP(cleve(_orig, sh)), _k, _v);
else
// nothing shared - if we can branch, branch, otherwise cleve again.
return mergeAt(RLP(branch(_orig)), _k, _v);
}
else
{
// branch...
// exactly our node - place value.
if (_k.size() == 0)
return place(_orig, _k, _v);
// Kill the node.
killNode(sha3(_orig.data()));
// not exactly our node - delve to next level at the correct index.
byte n = _k[0];
RLPStream r(17);
for (byte i = 0; i < 17; ++i)
if (i == n)
mergeAtAux(r, _orig[i], _k.mid(1), _v);
else
r.appendRaw(_orig[i]);
return r.out();
}
}
template <class DB> void GenericTrieDB<DB>::mergeAtAux(RLPStream& _out, RLP const& _orig, NibbleSlice _k, bytesConstRef _v)
{
bytes b = mergeAt(_orig, _k, _v);
killNode(_orig);
streamNode(_out, b);
}
// in1: null -- OR -- [_k, V]
// out1: [_k, _s]
// in2: [V0, ..., V15, S16] AND _k == {}
// out2: [V0, ..., V15, _s]
template <class DB> bytes GenericTrieDB<DB>::place(RLP const& _orig, NibbleSlice _k, bytesConstRef _s)
{
killNode(_orig);
if (_orig.isEmpty())
return RLPStream(2).appendString(hexPrefixEncode(_k, true)).appendString(_s).out();
assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17));
if (_orig.itemCount() == 2)
return RLPStream(2).appendRaw(_orig[0]).appendString(_s).out();
auto s = RLPStream(17);
for (uint i = 0; i < 16; ++i)
s.appendRaw(_orig[i]);
s.appendString(_s);
return s.out();
}
// in1: [K, S] (DEL)
// out1: null
// in2: [V0, ..., V15, S] (DEL)
// out2: [V0, ..., V15, null] iff exists i: !!Vi -- OR -- null otherwise
template <class DB> bytes GenericTrieDB<DB>::remove(RLP const& _orig)
{
killNode(_orig);
assert(_orig.isList() && (_orig.itemCount() == 2 || _orig.itemCount() == 17));
if (_orig.itemCount() == 2)
return RLPNull;
RLPStream r(17);
for (uint i = 0; i < 16; ++i)
r.appendRaw(_orig[i]);
r.appendString("");
return r.out();
}
template <class DB> RLPStream& GenericTrieDB<DB>::streamNode(RLPStream& _s, bytes const& _b)
{
if (_b.size() < 32)
_s.appendRaw(_b);
else
_s.append(insertNode(&_b));
return _s;
}
// in: [K1 & K2, V] (DEL) : nibbles(K1) == _s, 0 < _s < nibbles(K1 & K2)
// out: [K1, H] (INS) ; [K2, V] => H (INS) (being [K1, [K2, V]] if necessary)
template <class DB> bytes GenericTrieDB<DB>::cleve(RLP const& _orig, uint _s)
{
killNode(_orig);
assert(_orig.isList() && _orig.itemCount() == 2);
auto k = keyOf(_orig);
assert(_s && _s < k.size());
RLPStream bottom(2);
bottom.appendString(hexPrefixEncode(k, isLeaf(_orig), _s));
bottom.appendRaw(_orig[1]);
RLPStream top(2);
top.appendString(hexPrefixEncode(k, false, 0, _s));
streamNode(top, bottom.out());
return top.out();
}
// in: [K1, H] (DEL) ; H <= [K2, V] (DEL) (being [K1, [K2, V]] (DEL) if necessary)
// out: [K1 & K2, V] (INS)
template <class DB> bytes GenericTrieDB<DB>::graft(RLP const& _orig)
{
assert(_orig.isList() && _orig.itemCount() == 2);
std::string s;
RLP n;
if (_orig[1].isList())
n = _orig[1];
else
{
// remove second item from the trie.
s = node(_orig[1].toHash<h256>());
killNode(_orig[1]);
n = RLP(s);
}
assert(n.itemCount() == 2);
return (RLPStream(2) << hexPrefixEncode(keyOf(_orig), keyOf(n), isLeaf(n)) << n[1]).out();
}
// in: [V0, ... V15, S] (DEL) : (exists unique i: !!Vi AND !S "out1") OR (all i: !Vi AND !!S "out2")
// out1: [k{i}, Vi] (INS)
// out2: [k{}, S] (INS)
template <class DB> bytes GenericTrieDB<DB>::merge(RLP const& _orig)
{
assert(_orig.isList() && _orig.itemCount() == 17);
RLPStream s(2);
if (_orig[16].isEmpty())
{
for (byte i = 0; i < 16; ++i)
if (!_orig[i].isEmpty())
{
s << hexPrefixEncode(bytesConstRef(&i, 1), false, 1, 2, 0) << _orig[i];
return s.out();
}
assert(false);
}
else
{
s << hexPrefixEncode(bytes(), true) << _orig[16];
return s.out();
}
}
// in: [k{}, S] (DEL)
// out: [null ** 16, S] (INS)
// -- OR --
// in: [k{i}, S] (DEL)
// out: [null ** i, H, null ** (16 - i)], H <= [k{}, S] (INS)
// -- OR --
// in: [k{i}, N] (DEL)
// out: [null ** i, N, null ** (16 - i)] (INS)
template <class DB> bytes GenericTrieDB<DB>::branch(RLP const& _orig)
{
assert(_orig.isList() && _orig.itemCount() == 2);
auto k = keyOf(_orig);
RLPStream r(17);
if (k.size() == 0)
{
assert(isLeaf(_orig));
for (uint i = 0; i < 16; ++i)
r << "";
r << _orig[1];
}
else
{
byte b = k[0];
for (uint i = 0; i < 16; ++i)
if (i == b)
if (isLeaf(_orig) || k.size() > 1)
{
RLPStream bottom(2);
bottom.appendString(hexPrefixEncode(k.mid(1), isLeaf(_orig)));
bottom.appendRaw(_orig[1]);
streamNode(r, bottom.out());
}
else
r << _orig[1];
else
r << "";
r << "";
}
return r.out();
}
}

33
test/main.cpp

@ -113,6 +113,37 @@ int main()
cout << "SENDER: " << hex << low160(eth::sha3(bytesConstRef(&pubkey).cropped(1))) << endl;
}
*/
{
BasicMap m;
GenericTrieDB<BasicMap> t(&m);
t.init(); // initialise as empty tree.
cout << m;
cout << t.root() << endl;
cout << hash256(StringMap()) << endl;
t.insert(string("test"), string("test"));
cout << m;
cout << t.root() << endl;
cout << hash256({{"test", "test"}}) << endl;
t.insert(string("te"), string("test"));
cout << m;
cout << t.root() << endl;
cout << hash256({{"test", "test"}, {"te", "test"}}) << endl;
}
{
BasicMap m;
GenericTrieDB<BasicMap> t(&m);
t.init(); // initialise as empty tree.
t.insert(string("a"), string("A"));
t.insert(string("b"), string("B"));
cout << m;
cout << t.root() << endl;
cout << hash256({{"b", "B"}, {"a", "A"}}) << endl;
cout << RLP(rlp256({{"b", "B"}, {"a", "A"}})) << endl;
}
return 0;
cout << escaped(asString(rlp256({{"b", "B"}, {"a", "A"}})), false) << " == " << RLP(rlp256({{"b", "B"}, {"a", "A"}})) << endl;
cout << escaped(asString(rlp256({{"test", "test"}})), false) << " == " << RLP(rlp256({{"test", "test"}})) << endl;
cout << asHex(rlp256({{"test", "test"}, {"te", "test"}})) << endl;
@ -204,7 +235,7 @@ int main()
assert(asString(rlp("dog")) == "\x43""dog");
// 2-item list
RLP twoItemList("\x82\x0f\x43""dog");
RLP twoItemList((byte const*)"\x82\x0f\x43""dog", 6);
assert(twoItemList.itemCount() == 2);
assert(twoItemList[0] == 15);
assert(twoItemList[1] == "dog");

Loading…
Cancel
Save