Browse Source

Repotted a lot of the Trie stuff.

cl-refactor
Gav Wood 11 years ago
parent
commit
f666853724
  1. 6
      libethereum/AddressState.cpp
  2. 11
      libethereum/AddressState.h
  3. 478
      libethereum/MemTrie.cpp
  4. 53
      libethereum/MemTrie.h
  5. 1
      libethereum/State.cpp
  6. 2
      libethereum/State.h
  7. 789
      libethereum/Trie.cpp
  8. 127
      libethereum/TrieCommon.cpp
  9. 102
      libethereum/TrieCommon.h
  10. 29
      libethereum/TrieDB.cpp
  11. 73
      libethereum/TrieDB.h
  12. 197
      libethereum/TrieHash.cpp
  13. 33
      libethereum/TrieHash.h
  14. 12
      test/main.cpp

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

11
libethereum/AddressState.h

@ -46,17 +46,6 @@ public:
std::map<u256, u256>& memory() { assert(m_type == AddressType::Contract); return m_memory; }
std::map<u256, u256> 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;

478
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 <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);
}
}
}

53
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 <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
libethereum/State.cpp

@ -35,7 +35,6 @@
#endif
#include <time.h>
#include <random>
#include "Trie.h"
#include "BlockChain.h"
#include "Instruction.h"
#include "Exceptions.h"

2
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

789
libethereum/Trie.cpp

@ -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;
}
}

127
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 <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;
}
}

102
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 <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);
}
}

29
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 <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
{
}

73
libethereum/Trie.h → 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 <http://www.gnu.org/licenses/>.
*/
/** @file Trie.h
/** @file TrieDB.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
@ -22,70 +22,13 @@
#pragma once
#include <map>
#include "RLP.h"
#include <leveldb/db.h>
#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.

197
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 <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());
}
}

33
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 <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);
}

12
test/main.cpp

@ -26,7 +26,9 @@
#include <secp256k1.h>
#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<BasicMap> d(&m);
d.init(); // initialise as empty tree.
Trie t;
MemTrie t;
StringMap s;
auto add = [&](char const* a, char const* b)

Loading…
Cancel
Save