diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index c6e9a5374..181664337 100644 --- a/libethereum/BlockInfo.cpp +++ b/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(); } diff --git a/libethereum/Common.h b/libethereum/Common.h index 01918dea0..a550bc6ae 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -56,16 +56,16 @@ using u160s = std::vector; using u256Set = std::set; using u160Set = std::set; -template inline void toBigEndian(_T _val, _Out& o_out); -template inline _T fromBigEndian(_In const& _bytes); +template inline void toBigEndian(T _val, Out& o_out); +template inline T fromBigEndian(In const& _bytes); -template +template class FixedHash { - using Arith = boost::multiprecision::number>; + using Arith = boost::multiprecision::number>; 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 m_data; + std::array m_data; }; +template +inline std::ostream& operator<<(std::ostream& _out, FixedHash 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; diff --git a/libethereum/RLP.cpp b/libethereum/RLP.cpp index 40cc74859..581687f75 100644 --- a/libethereum/RLP.cpp +++ b/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 diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 485e18b1a..856ad44a9 100644 --- a/libethereum/State.cpp +++ b/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(TrieDB(m_db, rlp[2].toHash(), (std::map*)&m_over)[_memory]); + return fromBigEndian(TrieDB(const_cast(&m_db), rlp[2].toHash())[_memory]); } void State::setContractMemory(Address _contract, u256 _memory, u256 _value) { RLP rlp(m_state[_contract]); - TrieDB c(m_db, &m_over); + TrieDB c(&m_db); std::string s = toBigEndianString(_value); if (rlp.itemCount() == 3) { diff --git a/libethereum/State.h b/libethereum/State.h index 9ed967c4e..41e05c31a 100644 --- a/libethereum/State.h +++ b/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 m_over; ///< The current overlay onto the state DB. - TrieDB
m_state; ///< Our state tree. + Overlay m_db; ///< Our overlay for the state tree. + TrieDB m_state; ///< Our state tree, as an Overlay DB. std::map m_transactions; ///< The current list of transactions that we've included in the state. BlockInfo m_previousBlock; ///< The previous block's information. diff --git a/libethereum/Trie.cpp b/libethereum/Trie.cpp index 23d8f3ab0..93bf1c79c 100644 --- a/libethereum/Trie.cpp +++ b/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()); } - } diff --git a/libethereum/Trie.h b/libethereum/Trie.h index db3812929..29be644f2 100644 --- a/libethereum/Trie.h +++ b/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 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 m_over; + std::map 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 GenericTrieDB { public: - GenericTrieDB() {} - GenericTrieDB(ldb::DB* _db, std::map* _overlay = nullptr): GenericTrieDB() { open(_db, c_null, _overlay); } - GenericTrieDB(ldb::DB* _db, h256 _root, std::map* _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* _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* 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 TrieDB: public GenericTrieDB +template +class TrieDB: public GenericTrieDB { public: - TrieDB() {} - TrieDB(ldb::DB* _db, std::map* _overlay = nullptr): GenericTrieDB(_db, _overlay) {} - TrieDB(ldb::DB* _db, h256 _root, std::map* _overlay = nullptr): GenericTrieDB() { open(_db, _root, _overlay); } + TrieDB(DB* _db): GenericTrieDB(_db) {} + TrieDB(DB* _db, h256 _root): GenericTrieDB(_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::at(bytesConstRef((byte const*)&_k, sizeof(KeyType))); } + void insert(KeyType _k, bytesConstRef _value) { GenericTrieDB::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::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 void GenericTrieDB::init() +{ + m_root = insertNode(&RLPNull); +} + +template void GenericTrieDB::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 void GenericTrieDB::remove(bytesConstRef _key) +{ +} + +template std::string GenericTrieDB::at(bytesConstRef _key) const +{ + return atAux(RLP(node(m_root)), _key); +} + +template std::string GenericTrieDB::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())), _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())), _key.mid(1)); + } +} + +template bytes GenericTrieDB::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 void GenericTrieDB::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 bytes GenericTrieDB::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 bytes GenericTrieDB::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 RLPStream& GenericTrieDB::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 bytes GenericTrieDB::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 bytes GenericTrieDB::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()); + 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 bytes GenericTrieDB::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 bytes GenericTrieDB::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(); +} +} diff --git a/test/main.cpp b/test/main.cpp index 35f3fc20d..bab0d2809 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -113,6 +113,37 @@ int main() cout << "SENDER: " << hex << low160(eth::sha3(bytesConstRef(&pubkey).cropped(1))) << endl; } */ + + { + BasicMap m; + GenericTrieDB 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 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");