From f666853724fe06d775c999529e752f953f0e99cc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 19 Jan 2014 13:32:01 +0000 Subject: [PATCH] Repotted a lot of the Trie stuff. --- libethereum/AddressState.cpp | 6 - libethereum/AddressState.h | 11 - libethereum/MemTrie.cpp | 478 +++++++++++++++++++ libethereum/MemTrie.h | 53 +++ libethereum/State.cpp | 1 - libethereum/State.h | 2 +- libethereum/Trie.cpp | 789 ------------------------------- libethereum/TrieCommon.cpp | 127 +++++ libethereum/TrieCommon.h | 102 ++++ libethereum/TrieDB.cpp | 29 ++ libethereum/{Trie.h => TrieDB.h} | 73 +-- libethereum/TrieHash.cpp | 197 ++++++++ libethereum/TrieHash.h | 33 ++ test/main.cpp | 12 +- 14 files changed, 1030 insertions(+), 883 deletions(-) create mode 100644 libethereum/MemTrie.cpp create mode 100644 libethereum/MemTrie.h delete mode 100644 libethereum/Trie.cpp create mode 100644 libethereum/TrieCommon.cpp create mode 100644 libethereum/TrieCommon.h create mode 100644 libethereum/TrieDB.cpp rename libethereum/{Trie.h => TrieDB.h} (90%) create mode 100644 libethereum/TrieHash.cpp create mode 100644 libethereum/TrieHash.h diff --git a/libethereum/AddressState.cpp b/libethereum/AddressState.cpp index b54836956..49adf882a 100644 --- a/libethereum/AddressState.cpp +++ b/libethereum/AddressState.cpp @@ -19,13 +19,7 @@ * @date 2014 */ -#include "Trie.h" #include "AddressState.h" using namespace std; using namespace eth; -h256 AddressState::memoryHash() const -{ - return hash256(m_memory); -} - diff --git a/libethereum/AddressState.h b/libethereum/AddressState.h index c84a6ac40..bcd644319 100644 --- a/libethereum/AddressState.h +++ b/libethereum/AddressState.h @@ -46,17 +46,6 @@ public: std::map& memory() { assert(m_type == AddressType::Contract); return m_memory; } std::map const& memory() const { assert(m_type == AddressType::Contract); return m_memory; } - h256 memoryHash() const; - - std::string toString() const - { - if (m_type == AddressType::Normal) - return asString(rlpList(m_balance, toCompactBigEndianString(m_nonce))); - if (m_type == AddressType::Contract) - return asString(rlpList(m_balance, toCompactBigEndianString(m_nonce), memoryHash())); - return ""; - } - private: AddressType m_type; u256 m_balance; diff --git a/libethereum/MemTrie.cpp b/libethereum/MemTrie.cpp new file mode 100644 index 000000000..7b269d682 --- /dev/null +++ b/libethereum/MemTrie.cpp @@ -0,0 +1,478 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file MemTrie.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" +#include "TrieCommon.h" +#include "MemTrie.h" +using namespace std; +using namespace eth; + +namespace eth +{ + +#define ENABLE_DEBUG_PRINT 0 + +/*/ +#define APPEND_CHILD appendString +/*/ +#define APPEND_CHILD appendRaw +/**/ + +class MemTrieNode +{ +public: + MemTrieNode() {} + virtual ~MemTrieNode() {} + + virtual std::string const& at(bytesConstRef _key) const = 0; + virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) = 0; + virtual MemTrieNode* remove(bytesConstRef _key) = 0; + void putRLP(RLPStream& _parentStream) const; + +#if ENABLE_DEBUG_PRINT + void debugPrint(std::string const& _indent = "") const { std::cerr << std::hex << hash256() << ":" << std::endl; debugPrintBody(_indent); } +#endif + + /// 256-bit hash of the node - this is a SHA-3/256 hash of the RLP of the node. + h256 hash256() const { RLPStream s; makeRLP(s); return eth::sha3(s.out()); } + bytes rlp() const { RLPStream s; makeRLP(s); return s.out(); } + void mark() { m_hash256 = h256(); } + +protected: + virtual void makeRLP(RLPStream& _intoStream) const = 0; + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent = "") const = 0; +#endif + + static MemTrieNode* newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2); + +private: + mutable h256 m_hash256; +}; + +static const std::string c_nullString; + +class TrieExtNode: public MemTrieNode +{ +public: + TrieExtNode(bytesConstRef _bytes): m_ext(_bytes.begin(), _bytes.end()) {} + + bytes m_ext; +}; + +class TrieBranchNode: public MemTrieNode +{ +public: + TrieBranchNode(std::string const& _value): m_value(_value) + { + memset(m_nodes.data(), 0, sizeof(MemTrieNode*) * 16); + } + + TrieBranchNode(byte _i1, MemTrieNode* _n1, std::string const& _value = std::string()): m_value(_value) + { + memset(m_nodes.data(), 0, sizeof(MemTrieNode*) * 16); + m_nodes[_i1] = _n1; + } + + TrieBranchNode(byte _i1, MemTrieNode* _n1, byte _i2, MemTrieNode* _n2) + { + memset(m_nodes.data(), 0, sizeof(MemTrieNode*) * 16); + m_nodes[_i1] = _n1; + m_nodes[_i2] = _n2; + } + + virtual ~TrieBranchNode() + { + for (auto i: m_nodes) + delete i; + } + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent) const + { + + if (m_value.size()) + std::cerr << _indent << "@: " << m_value << std::endl; + for (auto i = 0; i < 16; ++i) + if (m_nodes[i]) + { + std::cerr << _indent << std::hex << i << ": "; + m_nodes[i]->debugPrint(_indent + " "); + } + } +#endif + + virtual std::string const& at(bytesConstRef _key) const override; + virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) override; + virtual MemTrieNode* remove(bytesConstRef _key) override; + virtual void makeRLP(RLPStream& _parentStream) const override; + +private: + /// @returns (byte)-1 when no active branches, 16 when multiple active and the index of the active branch otherwise. + byte activeBranch() const; + + MemTrieNode* rejig(); + + std::array m_nodes; + std::string m_value; +}; + +class TrieLeafNode: public TrieExtNode +{ +public: + TrieLeafNode(bytesConstRef _key, std::string const& _value): TrieExtNode(_key), m_value(_value) {} + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent) const + { + assert(m_value.size()); + std::cerr << _indent; + if (m_ext.size()) + std::cerr << asHex(m_ext, 1) << ": "; + else + std::cerr << "@: "; + std::cerr << m_value << std::endl; + } +#endif + + virtual std::string const& at(bytesConstRef _key) const override { return contains(_key) ? m_value : c_nullString; } + virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) override; + virtual MemTrieNode* remove(bytesConstRef _key) override; + virtual void makeRLP(RLPStream& _parentStream) const override; + +private: + bool contains(bytesConstRef _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } + + std::string m_value; +}; + +class TrieInfixNode: public TrieExtNode +{ +public: + TrieInfixNode(bytesConstRef _key, MemTrieNode* _next): TrieExtNode(_key), m_next(_next) {} + virtual ~TrieInfixNode() { delete m_next; } + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent) const + { + std::cerr << _indent << asHex(m_ext, 1) << ": "; + m_next->debugPrint(_indent + " "); + } +#endif + + virtual std::string const& at(bytesConstRef _key) const override { assert(m_next); return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; } + virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) override; + virtual MemTrieNode* remove(bytesConstRef _key) override; + virtual void makeRLP(RLPStream& _parentStream) const override; + +private: + bool contains(bytesConstRef _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } + + MemTrieNode* m_next; +}; + +void MemTrieNode::putRLP(RLPStream& _parentStream) const +{ + RLPStream s; + makeRLP(s); + if (s.out().size() < 32) + _parentStream.APPEND_CHILD(s.out()); + else + _parentStream << eth::sha3(s.out()); +} + +void TrieBranchNode::makeRLP(RLPStream& _intoStream) const +{ + _intoStream.appendList(17); + for (auto i: m_nodes) + if (i) + i->putRLP(_intoStream); + else + _intoStream << ""; + _intoStream << m_value; +} + +void TrieLeafNode::makeRLP(RLPStream& _intoStream) const +{ + _intoStream.appendList(2) << hexPrefixEncode(m_ext, true) << m_value; +} + +void TrieInfixNode::makeRLP(RLPStream& _intoStream) const +{ + assert(m_next); + _intoStream.appendList(2); + _intoStream << hexPrefixEncode(m_ext, false); + m_next->putRLP(_intoStream); +} + +MemTrieNode* MemTrieNode::newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2) +{ + uint prefix = commonPrefix(_k1, _k2); + + MemTrieNode* ret; + if (_k1.size() == prefix) + ret = new TrieBranchNode(_k2[prefix], new TrieLeafNode(_k2.cropped(prefix + 1), _v2), _v1); + else if (_k2.size() == prefix) + ret = new TrieBranchNode(_k1[prefix], new TrieLeafNode(_k1.cropped(prefix + 1), _v1), _v2); + else // both continue after split + ret = new TrieBranchNode(_k1[prefix], new TrieLeafNode(_k1.cropped(prefix + 1), _v1), _k2[prefix], new TrieLeafNode(_k2.cropped(prefix + 1), _v2)); + + if (prefix) + // have shared prefix - split. + ret = new TrieInfixNode(_k1.cropped(0, prefix), ret); + + return ret; +} + +std::string const& TrieBranchNode::at(bytesConstRef _key) const +{ + if (_key.empty()) + return m_value; + else if (m_nodes[_key[0]] != nullptr) + return m_nodes[_key[0]]->at(_key.cropped(1)); + return c_nullString; +} + +MemTrieNode* TrieBranchNode::insert(bytesConstRef _key, std::string const& _value) +{ + assert(_value.size()); + mark(); + if (_key.empty()) + m_value = _value; + else + if (!m_nodes[_key[0]]) + m_nodes[_key[0]] = new TrieLeafNode(_key.cropped(1), _value); + else + m_nodes[_key[0]] = m_nodes[_key[0]]->insert(_key.cropped(1), _value); + return this; +} + +MemTrieNode* TrieBranchNode::remove(bytesConstRef _key) +{ + if (_key.empty()) + if (m_value.size()) + { + m_value.clear(); + return rejig(); + } + else {} + else if (m_nodes[_key[0]] != nullptr) + { + m_nodes[_key[0]] = m_nodes[_key[0]]->remove(_key.cropped(1)); + return rejig(); + } + return this; +} + +MemTrieNode* TrieBranchNode::rejig() +{ + mark(); + byte n = activeBranch(); + + if (n == (byte)-1 && m_value.size()) + { + // switch to leaf + auto r = new TrieLeafNode(bytesConstRef(), m_value); + delete this; + return r; + } + else if (n < 16 && m_value.empty()) + { + // only branching to n... + if (auto b = dynamic_cast(m_nodes[n])) + { + // switch to infix + m_nodes[n] = nullptr; + delete this; + return new TrieInfixNode(bytesConstRef(&n, 1), b); + } + else + { + auto x = dynamic_cast(m_nodes[n]); + assert(x); + // include in child + pushFront(x->m_ext, n); + m_nodes[n] = nullptr; + delete this; + return x; + } + } + + return this; +} + +byte TrieBranchNode::activeBranch() const +{ + byte n = (byte)-1; + for (int i = 0; i < 16; ++i) + if (m_nodes[i] != nullptr) + { + if (n == (byte)-1) + n = (byte)i; + else + return 16; + } + return n; +} + +MemTrieNode* TrieInfixNode::insert(bytesConstRef _key, std::string const& _value) +{ + assert(_value.size()); + mark(); + if (contains(_key)) + { + m_next = m_next->insert(_key.cropped(m_ext.size()), _value); + return this; + } + else + { + uint prefix = commonPrefix(_key, m_ext); + if (prefix) + { + // one infix becomes two infixes, then insert into the second + // instead of pop_front()... + trimFront(m_ext, prefix); + + return new TrieInfixNode(_key.cropped(0, prefix), insert(_key.cropped(prefix), _value)); + } + else + { + // split here. + auto f = m_ext[0]; + trimFront(m_ext, 1); + MemTrieNode* n = m_ext.empty() ? m_next : this; + if (n != this) + { + m_next = nullptr; + delete this; + } + TrieBranchNode* ret = new TrieBranchNode(f, n); + ret->insert(_key, _value); + return ret; + } + } +} + +MemTrieNode* TrieInfixNode::remove(bytesConstRef _key) +{ + if (contains(_key)) + { + mark(); + m_next = m_next->remove(_key.cropped(m_ext.size())); + if (auto p = dynamic_cast(m_next)) + { + // merge with child... + m_ext.reserve(m_ext.size() + p->m_ext.size()); + for (auto i: p->m_ext) + m_ext.push_back(i); + p->m_ext = m_ext; + p->mark(); + m_next = nullptr; + delete this; + return p; + } + if (!m_next) + { + delete this; + return nullptr; + } + } + return this; +} + +MemTrieNode* TrieLeafNode::insert(bytesConstRef _key, std::string const& _value) +{ + assert(_value.size()); + mark(); + if (contains(_key)) + { + m_value = _value; + return this; + } + else + { + // create new trie. + auto n = MemTrieNode::newBranch(_key, _value, bytesConstRef(&m_ext), m_value); + delete this; + return n; + } +} + +MemTrieNode* TrieLeafNode::remove(bytesConstRef _key) +{ + if (contains(_key)) + { + delete this; + return nullptr; + } + return this; +} + +MemTrie::~MemTrie() +{ + delete m_root; +} + +h256 MemTrie::hash256() const +{ + return m_root ? m_root->hash256() : eth::sha3(RLPNull); +} + +bytes MemTrie::rlp() const +{ + return m_root ? m_root->rlp() : RLPNull; +} + +void MemTrie::debugPrint() +{ +#if ENABLE_DEBUG_PRINT + if (m_root) + m_root->debugPrint(); +#endif +} + +std::string const& MemTrie::at(std::string const& _key) const +{ + if (!m_root) + return c_nullString; + auto h = toHex(_key); + return m_root->at(bytesConstRef(&h)); +} + +void MemTrie::insert(std::string const& _key, std::string const& _value) +{ + if (_value.empty()) + remove(_key); + auto h = toHex(_key); + m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(bytesConstRef(&h), _value); +} + +void MemTrie::remove(std::string const& _key) +{ + if (m_root) + { + auto h = toHex(_key); + m_root = m_root->remove(&h); + } +} + +} diff --git a/libethereum/MemTrie.h b/libethereum/MemTrie.h new file mode 100644 index 000000000..b1cf93963 --- /dev/null +++ b/libethereum/MemTrie.h @@ -0,0 +1,53 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file MemTrie.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" + +namespace eth +{ + +class MemTrieNode; + +/** + * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. + */ +class MemTrie +{ +public: + MemTrie(): m_root(nullptr) {} + ~MemTrie(); + + h256 hash256() const; + bytes rlp() const; + + void debugPrint(); + + std::string const& at(std::string const& _key) const; + void insert(std::string const& _key, std::string const& _value); + void remove(std::string const& _key); + +private: + MemTrieNode* m_root; +}; + +} diff --git a/libethereum/State.cpp b/libethereum/State.cpp index b59c93cb9..54ce6deb4 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -35,7 +35,6 @@ #endif #include #include -#include "Trie.h" #include "BlockChain.h" #include "Instruction.h" #include "Exceptions.h" diff --git a/libethereum/State.h b/libethereum/State.h index 786830322..310c63fef 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -31,7 +31,7 @@ #include "BlockInfo.h" #include "AddressState.h" #include "Transaction.h" -#include "Trie.h" +#include "TrieDB.h" #include "Dagger.h" namespace eth diff --git a/libethereum/Trie.cpp b/libethereum/Trie.cpp deleted file mode 100644 index 8551e73e0..000000000 --- a/libethereum/Trie.cpp +++ /dev/null @@ -1,789 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Foobar is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Foobar. If not, see . -*/ -/** @file Trie.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "Common.h" -#include "Trie.h" -using namespace std; -using namespace eth; - -namespace eth -{ - -#define ENABLE_DEBUG_PRINT 0 - -/*/ -#define APPEND_CHILD appendString -/*/ -#define APPEND_CHILD appendRaw -/**/ - -#if ENABLE_DEBUG_PRINT -bool g_hashDebug = false; -#endif - -/* - * Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1 - * [0,0,1,2,3,4,5] 0x10012345 - * [0,1,2,3,4,5] 0x00012345 - * [1,2,3,4,5] 0x112345 - * [0,0,1,2,3,4] 0x00001234 - * [0,1,2,3,4] 0x101234 - * [1,2,3,4] 0x001234 - * [0,0,1,2,3,4,5,T] 0x30012345 - * [0,0,1,2,3,4,T] 0x20001234 - * [0,1,2,3,4,5,T] 0x20012345 - * [1,2,3,4,5,T] 0x312345 - * [1,2,3,4,T] 0x201234 - */ - -std::string hexPrefixEncode(bytes const& _hexVector, bool _terminated, int _begin, int _end) -{ - uint begin = _begin; - uint end = _end < 0 ? _hexVector.size() + 1 + _end : _end; - bool termed = _terminated; - bool odd = ((end - begin) % 2) != 0; - - std::string ret(1, ((termed ? 2 : 0) | (odd ? 1 : 0)) * 16); - if (odd) - { - ret[0] |= _hexVector[begin]; - ++begin; - } - for (uint i = begin; i < end; i += 2) - ret += _hexVector[i] * 16 + _hexVector[i + 1]; - return ret; -} - -void hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen, RLPStream& _rlp); - -void hash256rlp(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen, RLPStream& _rlp) -{ -#if ENABLE_DEBUG_PRINT - static std::string s_indent; - if (_preLen) - s_indent += " "; -#endif - - if (_begin == _end) - _rlp << ""; // NULL - else if (std::next(_begin) == _end) - { - // only one left - terminate with the pair. - _rlp.appendList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; -#if ENABLE_DEBUG_PRINT - if (g_hashDebug) - std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha3(_rlp.out()) << std::endl; -#endif - } - else - { - // find the number of common prefix nibbles shared - // i.e. the minimum number of nibbles shared at the beginning between the first hex string and each successive. - uint sharedPre = (uint)-1; - uint c = 0; - for (auto i = std::next(_begin); i != _end && sharedPre; ++i, ++c) - { - uint x = std::min(sharedPre, std::min((uint)_begin->first.size(), (uint)i->first.size())); - uint shared = _preLen; - for (; shared < x && _begin->first[shared] == i->first[shared]; ++shared) {} - sharedPre = std::min(shared, sharedPre); - } - if (sharedPre > _preLen) - { - // if they all have the same next nibble, we also want a pair. -#if ENABLE_DEBUG_PRINT - if (g_hashDebug) - std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; -#endif - _rlp.appendList(2) << hexPrefixEncode(_begin->first, false, _preLen, (int)sharedPre); - hash256aux(_s, _begin, _end, (unsigned)sharedPre, _rlp); -#if ENABLE_DEBUG_PRINT - if (g_hashDebug) - std::cerr << s_indent << "= " << hex << sha3(_rlp.out()) << std::endl; -#endif - } - else - { - // otherwise enumerate all 16+1 entries. - _rlp.appendList(17); - auto b = _begin; - if (_preLen == b->first.size()) - { -#if ENABLE_DEBUG_PRINT - if (g_hashDebug) - std::cerr << s_indent << "@: " << b->second << std::endl; -#endif - ++b; - } - for (auto i = 0; i < 16; ++i) - { - auto n = b; - for (; n != _end && n->first[_preLen] == i; ++n) {} - if (b == n) - _rlp << ""; - else - { -#if ENABLE_DEBUG_PRINT - if (g_hashDebug) - std::cerr << s_indent << std::hex << i << ": " << std::endl; -#endif - hash256aux(_s, b, n, _preLen + 1, _rlp); - } - b = n; - } - if (_preLen == _begin->first.size()) - _rlp << _begin->second; - else - _rlp << ""; - -#if ENABLE_DEBUG_PRINT - if (g_hashDebug) - std::cerr << s_indent << "= " << hex << sha3(_rlp.out()) << std::endl; -#endif - } - } -#if ENABLE_DEBUG_PRINT - if (_preLen) - s_indent.resize(s_indent.size() - 2); -#endif -} - -void hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen, RLPStream& _rlp) -{ - RLPStream rlp; - hash256rlp(_s, _begin, _end, _preLen, rlp); - if (rlp.out().size() < 32) - { - // RECURSIVE RLP -#if ENABLE_DEBUG_PRINT - cerr << "[INLINE: " << dec << rlp.out().size() << " < 32]" << endl; -#endif - _rlp.APPEND_CHILD(rlp.out()); - } - else - { -#if ENABLE_DEBUG_PRINT - cerr << "[HASH: " << dec << rlp.out().size() << " >= 32]" << endl; -#endif - _rlp << sha3(rlp.out()); - } -} - -h256 hash256(StringMap const& _s) -{ - // build patricia tree. - if (_s.empty()) - return sha3(RLPNull); - HexMap hexMap; - for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(i->first)] = i->second; - RLPStream s; - hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); - return sha3(s.out()); -} - -bytes rlp256(StringMap const& _s) -{ - // build patricia tree. - if (_s.empty()) - return RLPNull; - HexMap hexMap; - for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(i->first)] = i->second; - RLPStream s; - hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); - return s.out(); -} - -h256 hash256(u256Map const& _s) -{ - // build patricia tree. - if (_s.empty()) - return sha3(RLPNull); - HexMap hexMap; - for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(toBigEndianString(i->first))] = asString(rlp(i->second)); - RLPStream s; - hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); - return sha3(s.out()); -} - -class TrieNode -{ -public: - TrieNode() {} - virtual ~TrieNode() {} - - virtual std::string const& at(bytesConstRef _key) const = 0; - virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) = 0; - virtual TrieNode* remove(bytesConstRef _key) = 0; - void putRLP(RLPStream& _parentStream) const; - -#if ENABLE_DEBUG_PRINT - void debugPrint(std::string const& _indent = "") const { std::cerr << std::hex << hash256() << ":" << std::endl; debugPrintBody(_indent); } -#endif - - /// 256-bit hash of the node - this is a SHA-3/256 hash of the RLP of the node. - h256 hash256() const { RLPStream s; makeRLP(s); return eth::sha3(s.out()); } - bytes rlp() const { RLPStream s; makeRLP(s); return s.out(); } - void mark() { m_hash256 = h256(); } - -protected: - virtual void makeRLP(RLPStream& _intoStream) const = 0; - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent = "") const = 0; -#endif - - static TrieNode* newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2); - -private: - mutable h256 m_hash256; -}; - -static const std::string c_nullString; - -class TrieExtNode: public TrieNode -{ -public: - TrieExtNode(bytesConstRef _bytes): m_ext(_bytes.begin(), _bytes.end()) {} - - bytes m_ext; -}; - -class TrieBranchNode: public TrieNode -{ -public: - TrieBranchNode(std::string const& _value): m_value(_value) - { - memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); - } - - TrieBranchNode(byte _i1, TrieNode* _n1, std::string const& _value = std::string()): m_value(_value) - { - memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); - m_nodes[_i1] = _n1; - } - - TrieBranchNode(byte _i1, TrieNode* _n1, byte _i2, TrieNode* _n2) - { - memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); - m_nodes[_i1] = _n1; - m_nodes[_i2] = _n2; - } - - virtual ~TrieBranchNode() - { - for (auto i: m_nodes) - delete i; - } - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent) const - { - - if (m_value.size()) - std::cerr << _indent << "@: " << m_value << std::endl; - for (auto i = 0; i < 16; ++i) - if (m_nodes[i]) - { - std::cerr << _indent << std::hex << i << ": "; - m_nodes[i]->debugPrint(_indent + " "); - } - } -#endif - - virtual std::string const& at(bytesConstRef _key) const override; - virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) override; - virtual TrieNode* remove(bytesConstRef _key) override; - virtual void makeRLP(RLPStream& _parentStream) const override; - -private: - /// @returns (byte)-1 when no active branches, 16 when multiple active and the index of the active branch otherwise. - byte activeBranch() const; - - TrieNode* rejig(); - - std::array m_nodes; - std::string m_value; -}; - -class TrieLeafNode: public TrieExtNode -{ -public: - TrieLeafNode(bytesConstRef _key, std::string const& _value): TrieExtNode(_key), m_value(_value) {} - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent) const - { - assert(m_value.size()); - std::cerr << _indent; - if (m_ext.size()) - std::cerr << asHex(m_ext, 1) << ": "; - else - std::cerr << "@: "; - std::cerr << m_value << std::endl; - } -#endif - - virtual std::string const& at(bytesConstRef _key) const override { return contains(_key) ? m_value : c_nullString; } - virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) override; - virtual TrieNode* remove(bytesConstRef _key) override; - virtual void makeRLP(RLPStream& _parentStream) const override; - -private: - bool contains(bytesConstRef _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } - - std::string m_value; -}; - -class TrieInfixNode: public TrieExtNode -{ -public: - TrieInfixNode(bytesConstRef _key, TrieNode* _next): TrieExtNode(_key), m_next(_next) {} - virtual ~TrieInfixNode() { delete m_next; } - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent) const - { - std::cerr << _indent << asHex(m_ext, 1) << ": "; - m_next->debugPrint(_indent + " "); - } -#endif - - virtual std::string const& at(bytesConstRef _key) const override { assert(m_next); return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; } - virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) override; - virtual TrieNode* remove(bytesConstRef _key) override; - virtual void makeRLP(RLPStream& _parentStream) const override; - -private: - bool contains(bytesConstRef _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } - - TrieNode* m_next; -}; - -void TrieNode::putRLP(RLPStream& _parentStream) const -{ - RLPStream s; - makeRLP(s); - if (s.out().size() < 32) - _parentStream.APPEND_CHILD(s.out()); - else - _parentStream << eth::sha3(s.out()); -} - -void TrieBranchNode::makeRLP(RLPStream& _intoStream) const -{ - _intoStream.appendList(17); - for (auto i: m_nodes) - if (i) - i->putRLP(_intoStream); - else - _intoStream << ""; - _intoStream << m_value; -} - -void TrieLeafNode::makeRLP(RLPStream& _intoStream) const -{ - _intoStream.appendList(2) << hexPrefixEncode(m_ext, true) << m_value; -} - -void TrieInfixNode::makeRLP(RLPStream& _intoStream) const -{ - assert(m_next); - _intoStream.appendList(2); - _intoStream << hexPrefixEncode(m_ext, false); - m_next->putRLP(_intoStream); -} - -TrieNode* TrieNode::newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2) -{ - uint prefix = commonPrefix(_k1, _k2); - - TrieNode* ret; - if (_k1.size() == prefix) - ret = new TrieBranchNode(_k2[prefix], new TrieLeafNode(_k2.cropped(prefix + 1), _v2), _v1); - else if (_k2.size() == prefix) - ret = new TrieBranchNode(_k1[prefix], new TrieLeafNode(_k1.cropped(prefix + 1), _v1), _v2); - else // both continue after split - ret = new TrieBranchNode(_k1[prefix], new TrieLeafNode(_k1.cropped(prefix + 1), _v1), _k2[prefix], new TrieLeafNode(_k2.cropped(prefix + 1), _v2)); - - if (prefix) - // have shared prefix - split. - ret = new TrieInfixNode(_k1.cropped(0, prefix), ret); - - return ret; -} - -std::string const& TrieBranchNode::at(bytesConstRef _key) const -{ - if (_key.empty()) - return m_value; - else if (m_nodes[_key[0]] != nullptr) - return m_nodes[_key[0]]->at(_key.cropped(1)); - return c_nullString; -} - -TrieNode* TrieBranchNode::insert(bytesConstRef _key, std::string const& _value) -{ - assert(_value.size()); - mark(); - if (_key.empty()) - m_value = _value; - else - if (!m_nodes[_key[0]]) - m_nodes[_key[0]] = new TrieLeafNode(_key.cropped(1), _value); - else - m_nodes[_key[0]] = m_nodes[_key[0]]->insert(_key.cropped(1), _value); - return this; -} - -TrieNode* TrieBranchNode::remove(bytesConstRef _key) -{ - if (_key.empty()) - if (m_value.size()) - { - m_value.clear(); - return rejig(); - } - else {} - else if (m_nodes[_key[0]] != nullptr) - { - m_nodes[_key[0]] = m_nodes[_key[0]]->remove(_key.cropped(1)); - return rejig(); - } - return this; -} - -TrieNode* TrieBranchNode::rejig() -{ - mark(); - byte n = activeBranch(); - - if (n == (byte)-1 && m_value.size()) - { - // switch to leaf - auto r = new TrieLeafNode(bytesConstRef(), m_value); - delete this; - return r; - } - else if (n < 16 && m_value.empty()) - { - // only branching to n... - if (auto b = dynamic_cast(m_nodes[n])) - { - // switch to infix - m_nodes[n] = nullptr; - delete this; - return new TrieInfixNode(bytesConstRef(&n, 1), b); - } - else - { - auto x = dynamic_cast(m_nodes[n]); - assert(x); - // include in child - pushFront(x->m_ext, n); - m_nodes[n] = nullptr; - delete this; - return x; - } - } - - return this; -} - -byte TrieBranchNode::activeBranch() const -{ - byte n = (byte)-1; - for (int i = 0; i < 16; ++i) - if (m_nodes[i] != nullptr) - { - if (n == (byte)-1) - n = (byte)i; - else - return 16; - } - return n; -} - -TrieNode* TrieInfixNode::insert(bytesConstRef _key, std::string const& _value) -{ - assert(_value.size()); - mark(); - if (contains(_key)) - { - m_next = m_next->insert(_key.cropped(m_ext.size()), _value); - return this; - } - else - { - uint prefix = commonPrefix(_key, m_ext); - if (prefix) - { - // one infix becomes two infixes, then insert into the second - // instead of pop_front()... - trimFront(m_ext, prefix); - - return new TrieInfixNode(_key.cropped(0, prefix), insert(_key.cropped(prefix), _value)); - } - else - { - // split here. - auto f = m_ext[0]; - trimFront(m_ext, 1); - TrieNode* n = m_ext.empty() ? m_next : this; - if (n != this) - { - m_next = nullptr; - delete this; - } - TrieBranchNode* ret = new TrieBranchNode(f, n); - ret->insert(_key, _value); - return ret; - } - } -} - -TrieNode* TrieInfixNode::remove(bytesConstRef _key) -{ - if (contains(_key)) - { - mark(); - m_next = m_next->remove(_key.cropped(m_ext.size())); - if (auto p = dynamic_cast(m_next)) - { - // merge with child... - m_ext.reserve(m_ext.size() + p->m_ext.size()); - for (auto i: p->m_ext) - m_ext.push_back(i); - p->m_ext = m_ext; - p->mark(); - m_next = nullptr; - delete this; - return p; - } - if (!m_next) - { - delete this; - return nullptr; - } - } - return this; -} - -TrieNode* TrieLeafNode::insert(bytesConstRef _key, std::string const& _value) -{ - assert(_value.size()); - mark(); - if (contains(_key)) - { - m_value = _value; - return this; - } - else - { - // create new trie. - auto n = TrieNode::newBranch(_key, _value, bytesConstRef(&m_ext), m_value); - delete this; - return n; - } -} - -TrieNode* TrieLeafNode::remove(bytesConstRef _key) -{ - if (contains(_key)) - { - delete this; - return nullptr; - } - return this; -} - -Trie::~Trie() -{ - delete m_root; -} - -h256 Trie::hash256() const -{ - return m_root ? m_root->hash256() : eth::sha3(RLPNull); -} - -bytes Trie::rlp() const -{ - return m_root ? m_root->rlp() : RLPNull; -} - -void Trie::debugPrint() -{ -#if ENABLE_DEBUG_PRINT - if (m_root) - m_root->debugPrint(); -#endif -} - -std::string const& Trie::at(std::string const& _key) const -{ - if (!m_root) - return c_nullString; - auto h = toHex(_key); - return m_root->at(bytesConstRef(&h)); -} - -void Trie::insert(std::string const& _key, std::string const& _value) -{ - if (_value.empty()) - remove(_key); - auto h = toHex(_key); - m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(bytesConstRef(&h), _value); -} - -void Trie::remove(std::string const& _key) -{ - if (m_root) - { - auto h = toHex(_key); - m_root = m_root->remove(&h); - } -} - -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); -} - -NibbleSlice keyOf(RLP const& _twoItem) -{ - return keyOf(_twoItem[0].payload()); -} - -NibbleSlice keyOf(bytesConstRef _hpe) -{ - if (!_hpe.size()) - return NibbleSlice(_hpe, 0); - if (_hpe[0] & 0x10) - return NibbleSlice(_hpe, 1); - else - return NibbleSlice(_hpe, 2); -} - -std::string hexPrefixEncode(bytesConstRef _data, bool _terminated, int _beginNibble, int _endNibble, uint _offset) -{ - 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); - ret.reserve((end - begin) / 2 + 1); - - uint d = odd ? 1 : 2; - for (auto i = begin; i < end; ++i, ++d) - { - byte n = nibble(_data, i); - if (d & 1) // odd - ret.back() |= n; // or the nibble onto the back - else - ret.push_back(n << 4); // push the nibble on to the back << 4 - } - return ret; -} - -std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _terminated) -{ - uint begin1 = _o1; - uint end1 = _d1.size() * 2; - uint begin2 = _o2; - uint end2 = _d2.size() * 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) - { - byte n = nibble(_d1, i); - if (d & 1) // odd - ret.back() |= n; // or the nibble onto the back - else - ret.push_back(n << 4); // push the nibble on to the back << 4 - } - for (auto i = begin2; i < end2; ++i, ++d) - { - byte n = nibble(_d2, i); - if (d & 1) // odd - ret.back() |= n; // or the nibble onto the back - else - ret.push_back(n << 4); // push the nibble on to the back << 4 - } - return ret; -} - -std::string hexPrefixEncode(NibbleSlice _s, bool _leaf, int _begin, int _end) -{ - return hexPrefixEncode(_s.data, _leaf, _begin, _end, _s.offset); -} - -std::string hexPrefixEncode(NibbleSlice _s1, NibbleSlice _s2, bool _leaf) -{ - return hexPrefixEncode(_s1.data, _s1.offset, _s2.data, _s2.offset, _leaf); -} - -bool NibbleSlice::contains(NibbleSlice _k) const -{ - return shared(_k) == _k.size(); -} - -bool NibbleSlice::operator==(NibbleSlice _k) const -{ - return _k.size() == size() && shared(_k) == _k.size(); -} - -uint NibbleSlice::shared(NibbleSlice _k) const -{ - return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); -} - -byte uniqueInUse(RLP const& _orig, byte _except) -{ - byte used = 255; - for (unsigned i = 0; i < 17; ++i) - if (i != _except && !_orig[i].isEmpty()) - { - if (used == 255) - used = i; - else - return 255; - } - return used; -} - -} diff --git a/libethereum/TrieCommon.cpp b/libethereum/TrieCommon.cpp new file mode 100644 index 000000000..956fafc29 --- /dev/null +++ b/libethereum/TrieCommon.cpp @@ -0,0 +1,127 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file TrieCommon.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "TrieCommon.h" + +namespace eth +{ + +/* + * Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1 + * NOTE: the "termination marker" and "leaf-node" specifier are completely equivalent. + * [0,0,1,2,3,4,5] 0x10012345 + * [0,1,2,3,4,5] 0x00012345 + * [1,2,3,4,5] 0x112345 + * [0,0,1,2,3,4] 0x00001234 + * [0,1,2,3,4] 0x101234 + * [1,2,3,4] 0x001234 + * [0,0,1,2,3,4,5,T] 0x30012345 + * [0,0,1,2,3,4,T] 0x20001234 + * [0,1,2,3,4,5,T] 0x20012345 + * [1,2,3,4,5,T] 0x312345 + * [1,2,3,4,T] 0x201234 + */ + +std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf, int _begin, int _end) +{ + uint begin = _begin; + uint end = _end < 0 ? _hexVector.size() + 1 + _end : _end; + bool odd = ((end - begin) % 2) != 0; + + std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16); + if (odd) + { + ret[0] |= _hexVector[begin]; + ++begin; + } + for (uint i = begin; i < end; i += 2) + ret += _hexVector[i] * 16 + _hexVector[i + 1]; + return ret; +} + +std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, uint _offset) +{ + 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, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16); + ret.reserve((end - begin) / 2 + 1); + + uint d = odd ? 1 : 2; + for (auto i = begin; i < end; ++i, ++d) + { + byte n = nibble(_data, i); + if (d & 1) // odd + ret.back() |= n; // or the nibble onto the back + else + ret.push_back(n << 4); // push the nibble on to the back << 4 + } + return ret; +} + +std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _leaf) +{ + uint begin1 = _o1; + uint end1 = _d1.size() * 2; + uint begin2 = _o2; + uint end2 = _d2.size() * 2; + + bool odd = (end1 - begin1 + end2 - begin2) & 1; + + std::string ret(1, ((_leaf ? 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) + { + byte n = nibble(_d1, i); + if (d & 1) // odd + ret.back() |= n; // or the nibble onto the back + else + ret.push_back(n << 4); // push the nibble on to the back << 4 + } + for (auto i = begin2; i < end2; ++i, ++d) + { + byte n = nibble(_d2, i); + if (d & 1) // odd + ret.back() |= n; // or the nibble onto the back + else + ret.push_back(n << 4); // push the nibble on to the back << 4 + } + return ret; +} + +byte uniqueInUse(RLP const& _orig, byte _except) +{ + byte used = 255; + for (unsigned i = 0; i < 17; ++i) + if (i != _except && !_orig[i].isEmpty()) + { + if (used == 255) + used = i; + else + return 255; + } + return used; +} + +} diff --git a/libethereum/TrieCommon.h b/libethereum/TrieCommon.h new file mode 100644 index 000000000..36766e2d1 --- /dev/null +++ b/libethereum/TrieCommon.h @@ -0,0 +1,102 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file TrieCommon.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" +#include "RLP.h" + +namespace eth +{ + +inline byte nibble(bytesConstRef _data, uint _i) +{ + return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4); +} + +inline 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; +} + +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); } + + bool contains(NibbleSlice _k) const { return shared(_k) == _k.size(); } + uint shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); } + bool operator==(NibbleSlice _k) const { return _k.size() == size() && shared(_k) == _k.size(); } + 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; +} + +inline bool isLeaf(RLP const& _twoItem) +{ + assert(_twoItem.isList() && _twoItem.itemCount() == 2); + auto pl = _twoItem[0].payload(); + return (pl[0] & 0x20); +} + +inline NibbleSlice keyOf(bytesConstRef _hpe) +{ + if (!_hpe.size()) + return NibbleSlice(_hpe, 0); + if (_hpe[0] & 0x10) + return NibbleSlice(_hpe, 1); + else + return NibbleSlice(_hpe, 2); +} + +inline NibbleSlice keyOf(RLP const& _twoItem) +{ + return keyOf(_twoItem[0].payload()); +} + +byte uniqueInUse(RLP const& _orig, byte _except); +std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf = false, int _begin = 0, int _end = -1); +std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, uint _offset); +std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _leaf); + +inline std::string hexPrefixEncode(NibbleSlice _s, bool _leaf, int _begin = 0, int _end = -1) +{ + return hexPrefixEncode(_s.data, _leaf, _begin, _end, _s.offset); +} + +inline std::string hexPrefixEncode(NibbleSlice _s1, NibbleSlice _s2, bool _leaf) +{ + return hexPrefixEncode(_s1.data, _s1.offset, _s2.data, _s2.offset, _leaf); +} + +} diff --git a/libethereum/TrieDB.cpp b/libethereum/TrieDB.cpp new file mode 100644 index 000000000..58b69d2b5 --- /dev/null +++ b/libethereum/TrieDB.cpp @@ -0,0 +1,29 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file TrieDB.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" +#include "TrieDB.h" +using namespace std; +using namespace eth; + +namespace eth +{ +} diff --git a/libethereum/Trie.h b/libethereum/TrieDB.h similarity index 90% rename from libethereum/Trie.h rename to libethereum/TrieDB.h index 198d3fe46..595f92e9d 100644 --- a/libethereum/Trie.h +++ b/libethereum/TrieDB.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . */ -/** @file Trie.h +/** @file TrieDB.h * @author Gav Wood * @date 2014 */ @@ -22,70 +22,13 @@ #pragma once #include -#include "RLP.h" +#include +#include "TrieCommon.h" namespace ldb = leveldb; 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; - -/** - * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. - */ -class Trie -{ -public: - Trie(): m_root(nullptr) {} - ~Trie(); - - h256 hash256() const; - bytes rlp() const; - - void debugPrint(); - - std::string const& at(std::string const& _key) const; - void insert(std::string const& _key, std::string const& _value); - void remove(std::string const& _key); - -private: - TrieNode* m_root; -}; - -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: @@ -150,16 +93,6 @@ private: #pragma warning(disable:4100) // disable warnings so it compiles #endif -uint sharedNibbles(bytesConstRef _a, uint _ab, uint _ae, bytesConstRef _b, uint _bb, uint _be); -bool isLeaf(RLP const& _twoItem); -byte uniqueInUse(RLP const& _orig, byte _except); -NibbleSlice keyOf(RLP const& _twoItem); -NibbleSlice keyOf(bytesConstRef _hpe); -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); - /** * @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. diff --git a/libethereum/TrieHash.cpp b/libethereum/TrieHash.cpp new file mode 100644 index 000000000..19cb2248f --- /dev/null +++ b/libethereum/TrieHash.cpp @@ -0,0 +1,197 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file TrieHash.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" +#include "TrieCommon.h" +#include "TrieHash.h" +using namespace std; +using namespace eth; + +namespace eth +{ + +/*/ +#define APPEND_CHILD appendString +/*/ +#define APPEND_CHILD appendRaw +/**/ + +#define ENABLE_DEBUG_PRINT 0 + +#if ENABLE_DEBUG_PRINT +bool g_hashDebug = false; +#endif + +void hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen, RLPStream& _rlp); + +void hash256rlp(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen, RLPStream& _rlp) +{ +#if ENABLE_DEBUG_PRINT + static std::string s_indent; + if (_preLen) + s_indent += " "; +#endif + + if (_begin == _end) + _rlp << ""; // NULL + else if (std::next(_begin) == _end) + { + // only one left - terminate with the pair. + _rlp.appendList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha3(_rlp.out()) << std::endl; +#endif + } + else + { + // find the number of common prefix nibbles shared + // i.e. the minimum number of nibbles shared at the beginning between the first hex string and each successive. + uint sharedPre = (uint)-1; + uint c = 0; + for (auto i = std::next(_begin); i != _end && sharedPre; ++i, ++c) + { + uint x = std::min(sharedPre, std::min((uint)_begin->first.size(), (uint)i->first.size())); + uint shared = _preLen; + for (; shared < x && _begin->first[shared] == i->first[shared]; ++shared) {} + sharedPre = std::min(shared, sharedPre); + } + if (sharedPre > _preLen) + { + // if they all have the same next nibble, we also want a pair. +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; +#endif + _rlp.appendList(2) << hexPrefixEncode(_begin->first, false, _preLen, (int)sharedPre); + hash256aux(_s, _begin, _end, (unsigned)sharedPre, _rlp); +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << "= " << hex << sha3(_rlp.out()) << std::endl; +#endif + } + else + { + // otherwise enumerate all 16+1 entries. + _rlp.appendList(17); + auto b = _begin; + if (_preLen == b->first.size()) + { +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << "@: " << b->second << std::endl; +#endif + ++b; + } + for (auto i = 0; i < 16; ++i) + { + auto n = b; + for (; n != _end && n->first[_preLen] == i; ++n) {} + if (b == n) + _rlp << ""; + else + { +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << std::hex << i << ": " << std::endl; +#endif + hash256aux(_s, b, n, _preLen + 1, _rlp); + } + b = n; + } + if (_preLen == _begin->first.size()) + _rlp << _begin->second; + else + _rlp << ""; + +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << "= " << hex << sha3(_rlp.out()) << std::endl; +#endif + } + } +#if ENABLE_DEBUG_PRINT + if (_preLen) + s_indent.resize(s_indent.size() - 2); +#endif +} + +void hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen, RLPStream& _rlp) +{ + RLPStream rlp; + hash256rlp(_s, _begin, _end, _preLen, rlp); + if (rlp.out().size() < 32) + { + // RECURSIVE RLP +#if ENABLE_DEBUG_PRINT + cerr << "[INLINE: " << dec << rlp.out().size() << " < 32]" << endl; +#endif + _rlp.APPEND_CHILD(rlp.out()); + } + else + { +#if ENABLE_DEBUG_PRINT + cerr << "[HASH: " << dec << rlp.out().size() << " >= 32]" << endl; +#endif + _rlp << sha3(rlp.out()); + } +} + +h256 hash256(StringMap const& _s) +{ + // build patricia tree. + if (_s.empty()) + return sha3(RLPNull); + HexMap hexMap; + for (auto i = _s.rbegin(); i != _s.rend(); ++i) + hexMap[toHex(i->first)] = i->second; + RLPStream s; + hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); + return sha3(s.out()); +} + +bytes rlp256(StringMap const& _s) +{ + // build patricia tree. + if (_s.empty()) + return RLPNull; + HexMap hexMap; + for (auto i = _s.rbegin(); i != _s.rend(); ++i) + hexMap[toHex(i->first)] = i->second; + RLPStream s; + hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); + return s.out(); +} + +h256 hash256(u256Map const& _s) +{ + // build patricia tree. + if (_s.empty()) + return sha3(RLPNull); + HexMap hexMap; + for (auto i = _s.rbegin(); i != _s.rend(); ++i) + hexMap[toHex(toBigEndianString(i->first))] = asString(rlp(i->second)); + RLPStream s; + hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); + return sha3(s.out()); +} + +} diff --git a/libethereum/TrieHash.h b/libethereum/TrieHash.h new file mode 100644 index 000000000..a6d4f98cd --- /dev/null +++ b/libethereum/TrieHash.h @@ -0,0 +1,33 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file TrieHash.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" + +namespace eth +{ + +bytes rlp256(StringMap const& _s); +h256 hash256(StringMap const& _s); +h256 hash256(u256Map const& _s); + +} diff --git a/test/main.cpp b/test/main.cpp index 85d0e768b..f1fa9afbc 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -26,7 +26,9 @@ #include #include "Dagger.h" #include "RLP.h" -#include "Trie.h" +#include "TrieHash.h" +#include "TrieDB.h" +#include "MemTrie.h" #include "State.h" using namespace std; using namespace std::chrono; @@ -162,13 +164,13 @@ int main() cout << RLP(rlp256({{"b", "B"}, {"a", "A"}})) << endl; } { - Trie t; + MemTrie t; t.insert("dog", "puppy"); cout << hex << t.hash256() << endl; cout << RLP(t.rlp()) << endl; } { - Trie t; + MemTrie t; t.insert("bed", "d"); t.insert("be", "e"); cout << hex << t.hash256() << endl; @@ -176,7 +178,7 @@ int main() } { cout << hex << hash256({{"dog", "puppy"}, {"doe", "reindeer"}}) << endl; - Trie t; + MemTrie t; t.insert("dog", "puppy"); t.insert("doe", "reindeer"); cout << hex << t.hash256() << endl; @@ -187,7 +189,7 @@ int main() BasicMap m; GenericTrieDB d(&m); d.init(); // initialise as empty tree. - Trie t; + MemTrie t; StringMap s; auto add = [&](char const* a, char const* b)