mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	[10:59:28] Vitalik Buterin: block.parent.gas_limit * 1023 / 1024 <= block.gas_limit <= block.parent.gas_limit * 1025/1024
		
			
				
	
	
		
			483 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
	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.
 | 
						|
 | 
						|
	cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
/** @file MemTrie.cpp
 | 
						|
 * @author Gav Wood <i@gavwood.com>
 | 
						|
 * @date 2014
 | 
						|
 */
 | 
						|
 | 
						|
#include "MemTrie.h"
 | 
						|
 | 
						|
#include <libdevcrypto/TrieCommon.h>
 | 
						|
#include <libdevcrypto/SHA3.h>
 | 
						|
#include <libethcore/Common.h>
 | 
						|
using namespace std;
 | 
						|
using namespace dev;
 | 
						|
using namespace dev::eth;
 | 
						|
 | 
						|
namespace dev
 | 
						|
{
 | 
						|
 | 
						|
#define ENABLE_DEBUG_PRINT 0
 | 
						|
 | 
						|
/*/
 | 
						|
#define APPEND_CHILD appendData
 | 
						|
/*/
 | 
						|
#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::dec << 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 dev::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 << ": " << std::dec;
 | 
						|
				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 << toHex(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 << toHex(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 << dev::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)
 | 
						|
{
 | 
						|
	unsigned 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
 | 
						|
	{
 | 
						|
		unsigned 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() : sha3(dev::rlp(bytesConstRef()));
 | 
						|
}
 | 
						|
 | 
						|
bytes MemTrie::rlp() const
 | 
						|
{
 | 
						|
	return m_root ? m_root->rlp() : dev::rlp(bytesConstRef());
 | 
						|
}
 | 
						|
 | 
						|
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 = asNibbles(_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 = asNibbles(_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 = asNibbles(_key);
 | 
						|
		m_root = m_root->remove(&h);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
 |