Gav Wood
11 years ago
14 changed files with 1030 additions and 883 deletions
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MemTrie.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @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<MemTrieNode*, 16> 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<TrieBranchNode*>(m_nodes[n])) |
|||
{ |
|||
// switch to infix
|
|||
m_nodes[n] = nullptr; |
|||
delete this; |
|||
return new TrieInfixNode(bytesConstRef(&n, 1), b); |
|||
} |
|||
else |
|||
{ |
|||
auto x = dynamic_cast<TrieExtNode*>(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<TrieExtNode*>(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); |
|||
} |
|||
} |
|||
|
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MemTrie.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @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; |
|||
}; |
|||
|
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Trie.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @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<TrieNode*, 16> 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<TrieBranchNode*>(m_nodes[n])) |
|||
{ |
|||
// switch to infix
|
|||
m_nodes[n] = nullptr; |
|||
delete this; |
|||
return new TrieInfixNode(bytesConstRef(&n, 1), b); |
|||
} |
|||
else |
|||
{ |
|||
auto x = dynamic_cast<TrieExtNode*>(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<TrieExtNode*>(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; |
|||
} |
|||
|
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file TrieCommon.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @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; |
|||
} |
|||
|
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file TrieCommon.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @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); |
|||
} |
|||
|
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file TrieDB.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "Common.h" |
|||
#include "TrieDB.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
namespace eth |
|||
{ |
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file TrieHash.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @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()); |
|||
} |
|||
|
|||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file TrieHash.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include "Common.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
bytes rlp256(StringMap const& _s); |
|||
h256 hash256(StringMap const& _s); |
|||
h256 hash256(u256Map const& _s); |
|||
|
|||
} |
Loading…
Reference in new issue