You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
12 KiB
344 lines
12 KiB
/*
|
|
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 RLP.h
|
|
* @author Gav Wood <i@gavwood.com>
|
|
* @date 2014
|
|
*
|
|
* RLP (de-)serialisation.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <exception>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include "vector_ref.h"
|
|
#include "Common.h"
|
|
|
|
namespace eth
|
|
{
|
|
|
|
class RLP;
|
|
typedef std::vector<RLP> RLPs;
|
|
|
|
template <class _T> struct intTraits { static const uint maxSize = sizeof(_T); };
|
|
template <> struct intTraits<u160> { static const uint maxSize = 20; };
|
|
template <> struct intTraits<u256> { static const uint maxSize = 32; };
|
|
template <> struct intTraits<bigint> { static const uint maxSize = ~(uint)0; };
|
|
|
|
/**
|
|
* @brief Class for interpreting Recursive Linear-Prefix Data.
|
|
* @by Gav Wood, 2013
|
|
*
|
|
* Class for reading byte arrays of data in RLP format.
|
|
*/
|
|
class RLP
|
|
{
|
|
public:
|
|
class BadCast: public std::exception {};
|
|
|
|
/// Construct a null node.
|
|
RLP() {}
|
|
|
|
/// Construct a node of value given in the bytes.
|
|
explicit RLP(bytesConstRef _d): m_data(_d) {}
|
|
|
|
/// Construct a node of value given in the bytes.
|
|
explicit RLP(bytes const& _d): m_data(const_cast<bytes*>(&_d)) {} // a bit horrible, but we know we won't be altering the data. TODO: allow vector<T> const* to be passed to vector_ref<T const>.
|
|
|
|
/// Construct a node to read RLP data in the bytes given.
|
|
RLP(byte const* _b, uint _s): m_data(bytesConstRef(_b, _s)) {}
|
|
|
|
/// Construct a node to read RLP data in the string.
|
|
explicit RLP(std::string const& _s): m_data(bytesConstRef((byte const*)_s.data(), _s.size())) {}
|
|
|
|
bytesConstRef data() const { return m_data; }
|
|
|
|
/// @returns true if the RLP is non-null.
|
|
explicit operator bool() const { return !isNull(); }
|
|
|
|
/// No value.
|
|
bool isNull() const { return m_data.size() == 0; }
|
|
|
|
/// Contains a zero-length string or zero-length list.
|
|
bool isEmpty() const { return !isNull() && (m_data[0] == 0x40 || m_data[0] == 0x80); }
|
|
|
|
/// String value.
|
|
bool isString() const { return !isNull() && m_data[0] >= 0x40 && m_data[0] < 0x80; }
|
|
|
|
/// List value.
|
|
bool isList() const { return !isNull() && m_data[0] >= 0x80 && m_data[0] < 0xc0; }
|
|
|
|
/// Integer value. Either isSlimInt(), isFatInt() or isBigInt().
|
|
bool isInt() const { return !isNull() && m_data[0] < 0x40; }
|
|
|
|
/// Fits into eth::uint type. Can use toSlimInt() to read (as well as toFatInt() or toBigInt() ).
|
|
bool isSlimInt() const { return !isNull() && m_data[0] < 0x20; }
|
|
|
|
/// Fits into eth::u256 or eth::bigint type. Use only toFatInt() or toBigInt() to read.
|
|
bool isFatInt() const { return !isNull() && m_data[0] >= 0x20 && m_data[0] < 0x38; }
|
|
|
|
/// Fits into eth::u256 type, though might fit into eth::uint type.
|
|
bool isFixedInt() const { return !isNull() && m_data[0] < 0x38; }
|
|
|
|
/// Fits only into eth::bigint type. Use only toBigInt() to read.
|
|
bool isBigInt() const { return !isNull() && m_data[0] >= 0x38 && m_data[0] < 0x40; }
|
|
|
|
/// @returns the number of items in the list, or zero if it isn't a list.
|
|
uint itemCount() const { return isList() ? items() : 0; }
|
|
uint itemCountStrict() const { if (!isList()) throw BadCast(); return items(); }
|
|
|
|
/// @returns the number of characters in the string, or zero if it isn't a string.
|
|
uint stringSize() const { return isString() ? items() : 0; }
|
|
|
|
/// Equality operators; does best-effort conversion and checks for equality.
|
|
bool operator==(char const* _s) const { return isString() && toString() == _s; }
|
|
bool operator!=(char const* _s) const { return isString() && toString() != _s; }
|
|
bool operator==(std::string const& _s) const { return isString() && toString() == _s; }
|
|
bool operator!=(std::string const& _s) const { return isString() && toString() != _s; }
|
|
bool operator==(uint const& _i) const { return (isInt() || isString()) && toSlimInt() == _i; }
|
|
bool operator!=(uint const& _i) const { return (isInt() || isString()) && toSlimInt() != _i; }
|
|
bool operator==(u256 const& _i) const { return (isInt() || isString()) && toFatInt() == _i; }
|
|
bool operator!=(u256 const& _i) const { return (isInt() || isString()) && toFatInt() != _i; }
|
|
bool operator==(bigint const& _i) const { return (isInt() || isString()) && toBigInt() == _i; }
|
|
bool operator!=(bigint const& _i) const { return (isInt() || isString()) && toBigInt() != _i; }
|
|
|
|
/// Subscript operator.
|
|
/// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise.
|
|
/// @note if used to access items in ascending order, this is efficient.
|
|
RLP operator[](uint _i) const;
|
|
|
|
typedef RLP element_type;
|
|
|
|
/// @brief Iterator class for iterating through items of RLP list.
|
|
class iterator
|
|
{
|
|
friend class RLP;
|
|
|
|
public:
|
|
typedef RLP value_type;
|
|
typedef RLP element_type;
|
|
|
|
iterator& operator++();
|
|
iterator operator++(int) { auto ret = *this; operator++(); return ret; }
|
|
RLP operator*() const { return RLP(m_lastItem); }
|
|
bool operator==(iterator const& _cmp) const { return m_lastItem == _cmp.m_lastItem; }
|
|
bool operator!=(iterator const& _cmp) const { return !operator==(_cmp); }
|
|
|
|
private:
|
|
iterator() {}
|
|
iterator(RLP const& _parent, bool _begin);
|
|
|
|
uint m_remaining = 0;
|
|
bytesConstRef m_lastItem;
|
|
};
|
|
|
|
/// @brief Iterator into beginning of sub-item list (valid only if we are a list).
|
|
iterator begin() const { return iterator(*this, true); }
|
|
|
|
/// @brief Iterator into end of sub-item list (valid only if we are a list).
|
|
iterator end() const { return iterator(*this, false); }
|
|
|
|
/// Best-effort conversion operators.
|
|
explicit operator std::string() const { return toString(); }
|
|
explicit operator RLPs() const { return toList(); }
|
|
explicit operator uint() const { return toSlimInt(); }
|
|
explicit operator u256() const { return toFatInt(); }
|
|
explicit operator bigint() const { return toBigInt(); }
|
|
|
|
/// Converts to string. @returns the empty string if not a string.
|
|
std::string toString() const { if (!isString()) return std::string(); return payload().cropped(0, items()).toString(); }
|
|
/// Converts to string. @throws BadCast if not a string.
|
|
std::string toStringStrict() const { if (!isString()) throw BadCast(); return payload().cropped(0, items()).toString(); }
|
|
|
|
/// Int conversion flags
|
|
enum
|
|
{
|
|
AllowString = 1,
|
|
AllowInt = 2,
|
|
ThrowOnFail = 4,
|
|
FailIfTooBig = 8,
|
|
Strict = AllowString | AllowInt | ThrowOnFail | FailIfTooBig,
|
|
StrictlyString = AllowString | ThrowOnFail | FailIfTooBig,
|
|
StrictlyInt = AllowInt | ThrowOnFail | FailIfTooBig,
|
|
LaisezFaire = AllowString | AllowInt
|
|
};
|
|
|
|
/// Converts to int of type given; if isString(), decodes as big-endian bytestream. @returns 0 if not an int or string.
|
|
template <class _T = uint> _T toInt(int _flags = Strict) const
|
|
{
|
|
if ((isString() && !(_flags & AllowString)) || (isInt() && !(_flags & AllowInt)) || isList() || isNull())
|
|
if (_flags & ThrowOnFail)
|
|
throw BadCast();
|
|
else
|
|
return 0;
|
|
else {}
|
|
|
|
if (isDirectValueInt())
|
|
return m_data[0];
|
|
|
|
auto s = isInt() ? intSize() - lengthSize() : isString() ? items() : 0;
|
|
if (s > intTraits<_T>::maxSize && (_flags & FailIfTooBig))
|
|
if (_flags & ThrowOnFail)
|
|
throw BadCast();
|
|
else
|
|
return 0;
|
|
else {}
|
|
|
|
_T ret = 0;
|
|
uint o = lengthSize() + 1;
|
|
for (uint i = 0; i < s; ++i)
|
|
ret = (ret << 8) | m_data[i + o];
|
|
return ret;
|
|
}
|
|
|
|
/// Converts to eth::uint. @see toInt()
|
|
uint toSlimInt(int _flags = Strict) const { return toInt<uint>(_flags); }
|
|
|
|
/// Converts to eth::u256. @see toInt()
|
|
u256 toFatInt(int _flags = Strict) const { return toInt<u256>(_flags); }
|
|
|
|
/// Converts to eth::bigint. @see toInt()
|
|
bigint toBigInt(int _flags = Strict) const { return toInt<bigint>(_flags); }
|
|
|
|
/// Converts to RLPs collection object. Useful if you need random access to sub items or will iterate over multiple times.
|
|
RLPs toList() const;
|
|
|
|
private:
|
|
/// Direct value integer.
|
|
bool isDirectValueInt() const { assert(!isNull()); return m_data[0] < 0x18; }
|
|
|
|
/// Indirect-value integer.
|
|
bool isIndirectValueInt() const { assert(!isNull()); return m_data[0] >= 0x18 && m_data[0] < 0x38; }
|
|
|
|
/// Indirect addressed integer.
|
|
bool isIndirectAddressedInt() const { assert(!isNull()); return m_data[0] < 0x40 && m_data[0] >= 0x38; }
|
|
|
|
/// Direct-length string.
|
|
bool isSmallString() const { assert(!isNull()); return m_data[0] >= 0x40 && m_data[0] < 0x78; }
|
|
|
|
/// Direct-length list.
|
|
bool isSmallList() const { assert(!isNull()); return m_data[0] >= 0x80 && m_data[0] < 0xb8; }
|
|
|
|
/// @returns the theoretical size of this item; if it's a list, will require a deep traversal which could take a while.
|
|
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.
|
|
uint actualSize() const;
|
|
|
|
/// @returns the total additional bytes used to encode the integer. Includes the data-size and potentially the length-size. Returns 0 if not isInt().
|
|
uint intSize() const { return (!isInt() || isDirectValueInt()) ? 0 : isIndirectAddressedInt() ? lengthSize() + items() : (m_data[0] - 0x17); }
|
|
|
|
/// @returns the bytes used to encode the length of the data. Valid for all types.
|
|
uint lengthSize() const { auto n = (m_data[0] & 0x3f); return n > 0x37 ? n - 0x37 : 0; }
|
|
|
|
/// @returns the number of data items (bytes in the case of strings & ints, items in the case of lists). Valid for all types.
|
|
uint items() const;
|
|
|
|
/// @returns the data payload. Valid for all types.
|
|
bytesConstRef payload() const { auto n = (m_data[0] & 0x3f); return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37))); }
|
|
|
|
/// Our byte data.
|
|
bytesConstRef m_data;
|
|
|
|
/// The list-indexing cache.
|
|
mutable uint m_lastIndex = (uint)-1;
|
|
mutable uint m_lastEnd = 0;
|
|
mutable bytesConstRef m_lastItem;
|
|
};
|
|
|
|
/**
|
|
* @brief Class for writing to an RLP bytestream.
|
|
*/
|
|
class RLPStream
|
|
{
|
|
public:
|
|
/// Initializes empty RLPStream.
|
|
RLPStream() {}
|
|
|
|
/// Initializes the RLPStream as a list of @a _listItems items.
|
|
explicit RLPStream(uint _listItems) { appendList(_listItems); }
|
|
|
|
/// Append given data to the byte stream.
|
|
RLPStream& append(uint _s);
|
|
RLPStream& append(u256 _s);
|
|
RLPStream& append(bigint _s);
|
|
RLPStream& append(std::string const& _s);
|
|
RLPStream& appendList(uint _count);
|
|
RLPStream& appendRaw(bytes const& _rlp);
|
|
|
|
/// Shift operators for appending data items.
|
|
RLPStream& operator<<(uint _i) { return append(_i); }
|
|
RLPStream& operator<<(u256 _i) { return append(_i); }
|
|
RLPStream& operator<<(bigint _i) { return append(_i); }
|
|
RLPStream& operator<<(char const* _s) { return append(std::string(_s)); }
|
|
RLPStream& operator<<(std::string const& _s) { return append(_s); }
|
|
template <class _T> RLPStream& operator<<(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
|
|
|
|
/// Read the byte stream.
|
|
bytes const& out() const { return m_out; }
|
|
|
|
private:
|
|
/// Push the node-type byte (using @a _base) along with the item count @a _count.
|
|
/// @arg _count is number of characters for strings, data-bytes for ints, or items for lists.
|
|
void pushCount(uint _count, byte _base);
|
|
|
|
/// Push an integer as a raw big-endian byte-stream.
|
|
template <class _T> void pushInt(_T _i, uint _br)
|
|
{
|
|
m_out.resize(m_out.size() + _br);
|
|
byte* b = &m_out.back();
|
|
for (; _i; _i >>= 8)
|
|
*(b--) = (byte)_i;
|
|
}
|
|
|
|
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
|
|
template <class _T> static uint bytesRequired(_T _i)
|
|
{
|
|
_i >>= 8;
|
|
uint i = 1;
|
|
for (; _i != 0; ++i, _i >>= 8) {}
|
|
return i;
|
|
}
|
|
|
|
/// Our output byte stream.
|
|
bytes m_out;
|
|
};
|
|
|
|
template <class _T> void rlpListAux(RLPStream& _out, _T _t) { _out << _t; }
|
|
template <class _T, class ... _Ts> void rlpListAux(RLPStream& _out, _T _t, _Ts ... _ts) { rlpListAux(_out << _t, _ts...); }
|
|
|
|
/// Export a single item in RLP format, returning a byte array.
|
|
template <class _T> bytes rlp(_T _t) { return (RLPStream() << _t).out(); }
|
|
|
|
/// Export a list of items in RLP format, returning a byte array.
|
|
inline bytes rlpList() { return RLPStream(0).out(); }
|
|
template <class ... _Ts> bytes rlpList(_Ts ... _ts)
|
|
{
|
|
RLPStream out(sizeof ...(_Ts));
|
|
rlpListAux(out, _ts...);
|
|
return out.out();
|
|
}
|
|
|
|
/// The empty string in RLP format.
|
|
extern bytes RLPNull;
|
|
|
|
/// The empty list in RLP format.
|
|
extern bytes RLPEmptyList;
|
|
|
|
}
|
|
|
|
/// Human readable version of RLP.
|
|
std::ostream& operator<<(std::ostream& _out, eth::RLP _d);
|
|
|