Ethereum  PoC-8
The C++ Implementation of Ethereum
RLP.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "RLP.h"
19 using namespace std;
20 using namespace dev;
21 
24 
25 namespace {
26 
27 errinfo_comment constructRLPSizeErrorInfo(size_t _actualSize, size_t _dataSize)
28 {
29  std::stringstream s;
30  s << "Actual size: " << _actualSize << ", data size: " << _dataSize;
31  return errinfo_comment(s.str());
32 }
33 
34 }
35 
36 RLP::RLP(bytesConstRef _d, Strictness _s):
37  m_data(_d)
38 {
39  if ((_s & FailIfTooBig) && actualSize() < _d.size())
40  {
41  if (_s & ThrowOnFail)
42  BOOST_THROW_EXCEPTION(OversizeRLP() << constructRLPSizeErrorInfo(actualSize(), _d.size()));
43  else
44  m_data.reset();
45  }
46  if ((_s & FailIfTooSmall) && actualSize() > _d.size())
47  {
48  if (_s & ThrowOnFail)
49  BOOST_THROW_EXCEPTION(UndersizeRLP() << constructRLPSizeErrorInfo(actualSize(), _d.size()));
50  else
51  m_data.reset();
52  }
53 }
54 
56 {
57  if (m_remaining)
58  {
59  m_currentItem.retarget(m_currentItem.next().data(), m_remaining);
60  m_currentItem = m_currentItem.cropped(0, sizeAsEncoded(m_currentItem));
61  m_remaining -= std::min<size_t>(m_remaining, m_currentItem.size());
62  }
63  else
64  m_currentItem.retarget(m_currentItem.next().data(), 0);
65  return *this;
66 }
67 
68 RLP::iterator::iterator(RLP const& _parent, bool _begin)
69 {
70  if (_begin && _parent.isList())
71  {
72  auto pl = _parent.payload();
73  m_currentItem = pl.cropped(0, sizeAsEncoded(pl));
74  m_remaining = pl.size() - m_currentItem.size();
75  }
76  else
77  {
78  m_currentItem = _parent.data().cropped(_parent.data().size());
79  m_remaining = 0;
80  }
81 }
82 
83 RLP RLP::operator[](size_t _i) const
84 {
85  if (_i < m_lastIndex)
86  {
87  m_lastEnd = sizeAsEncoded(payload());
88  m_lastItem = payload().cropped(0, m_lastEnd);
89  m_lastIndex = 0;
90  }
91  for (; m_lastIndex < _i && m_lastItem.size(); ++m_lastIndex)
92  {
93  m_lastItem = payload().cropped(m_lastEnd);
94  m_lastItem = m_lastItem.cropped(0, sizeAsEncoded(m_lastItem));
95  m_lastEnd += m_lastItem.size();
96  }
97  return RLP(m_lastItem, ThrowOnFail | FailIfTooSmall);
98 }
99 
100 size_t RLP::actualSize() const
101 {
102  if (isNull())
103  return 0;
104  if (isSingleByte())
105  return 1;
106  if (isData() || isList())
107  return payloadOffset() + length();
108  return 0;
109 }
110 
111 void RLP::requireGood() const
112 {
113  if (isNull())
114  BOOST_THROW_EXCEPTION(BadRLP());
115  byte n = m_data[0];
116  if (n != c_rlpDataImmLenStart + 1)
117  return;
118  if (m_data.size() < 2)
119  BOOST_THROW_EXCEPTION(BadRLP());
120  if (m_data[1] < c_rlpDataImmLenStart)
121  BOOST_THROW_EXCEPTION(BadRLP());
122 }
123 
124 bool RLP::isInt() const
125 {
126  if (isNull())
127  return false;
128  requireGood();
129  byte n = m_data[0];
130  if (n < c_rlpDataImmLenStart)
131  return !!n;
132  else if (n == c_rlpDataImmLenStart)
133  return true;
134  else if (n <= c_rlpDataIndLenZero)
135  {
136  if (m_data.size() <= 1)
137  BOOST_THROW_EXCEPTION(BadRLP());
138  return m_data[1] != 0;
139  }
140  else if (n < c_rlpListStart)
141  {
142  if (m_data.size() <= size_t(1 + n - c_rlpDataIndLenZero))
143  BOOST_THROW_EXCEPTION(BadRLP());
144  return m_data[1 + n - c_rlpDataIndLenZero] != 0;
145  }
146  else
147  return false;
148  return false;
149 }
150 
151 size_t RLP::length() const
152 {
153  if (isNull())
154  return 0;
155  requireGood();
156  size_t ret = 0;
157  byte const n = m_data[0];
158  if (n < c_rlpDataImmLenStart)
159  return 1;
160  else if (n <= c_rlpDataIndLenZero)
161  return n - c_rlpDataImmLenStart;
162  else if (n < c_rlpListStart)
163  {
164  if (m_data.size() <= size_t(n - c_rlpDataIndLenZero))
165  BOOST_THROW_EXCEPTION(BadRLP());
166  if (m_data.size() > 1)
167  if (m_data[1] == 0)
168  BOOST_THROW_EXCEPTION(BadRLP());
169  unsigned lengthSize = n - c_rlpDataIndLenZero;
170  if (lengthSize > sizeof(ret))
171  // We did not check, but would most probably not fit in our memory.
172  BOOST_THROW_EXCEPTION(UndersizeRLP());
173  // No leading zeroes.
174  if (!m_data[1])
175  BOOST_THROW_EXCEPTION(BadRLP());
176  for (unsigned i = 0; i < lengthSize; ++i)
177  ret = (ret << 8) | m_data[i + 1];
178  // Must be greater than the limit.
179  if (ret < c_rlpListStart - c_rlpDataImmLenStart - c_rlpMaxLengthBytes)
180  BOOST_THROW_EXCEPTION(BadRLP());
181  }
182  else if (n <= c_rlpListIndLenZero)
183  return n - c_rlpListStart;
184  else
185  {
186  unsigned lengthSize = n - c_rlpListIndLenZero;
187  if (m_data.size() <= lengthSize)
188  BOOST_THROW_EXCEPTION(BadRLP());
189  if (m_data.size() > 1)
190  if (m_data[1] == 0)
191  BOOST_THROW_EXCEPTION(BadRLP());
192  if (lengthSize > sizeof(ret))
193  // We did not check, but would most probably not fit in our memory.
194  BOOST_THROW_EXCEPTION(UndersizeRLP());
195  if (!m_data[1])
196  BOOST_THROW_EXCEPTION(BadRLP());
197  for (unsigned i = 0; i < lengthSize; ++i)
198  ret = (ret << 8) | m_data[i + 1];
199  if (ret < 0x100 - c_rlpListStart - c_rlpMaxLengthBytes)
200  BOOST_THROW_EXCEPTION(BadRLP());
201  }
202  // We have to be able to add payloadOffset to length without overflow.
203  // This rejects roughly 4GB-sized RLPs on some platforms.
204  if (ret >= std::numeric_limits<size_t>::max() - 0x100)
205  BOOST_THROW_EXCEPTION(UndersizeRLP());
206  return ret;
207 }
208 
209 size_t RLP::items() const
210 {
211  if (isList())
212  {
213  bytesConstRef d = payload();
214  size_t i = 0;
215  for (; d.size(); ++i)
216  d = d.cropped(sizeAsEncoded(d));
217  return i;
218  }
219  return 0;
220 }
221 
223 {
224  m_out.insert(m_out.end(), _s.begin(), _s.end());
225  noteAppended(_itemCount);
226  return *this;
227 }
228 
229 void RLPStream::noteAppended(size_t _itemCount)
230 {
231  if (!_itemCount)
232  return;
233 // cdebug << "noteAppended(" << _itemCount << ")";
234  while (m_listStack.size())
235  {
236  if (m_listStack.back().first < _itemCount)
237  BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("itemCount too large") << RequirementError((bigint)m_listStack.back().first, (bigint)_itemCount));
238  m_listStack.back().first -= _itemCount;
239  if (m_listStack.back().first)
240  break;
241  else
242  {
243  auto p = m_listStack.back().second;
244  m_listStack.pop_back();
245  size_t s = m_out.size() - p; // list size
246  auto brs = bytesRequired(s);
247  unsigned encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs);
248 // cdebug << "s: " << s << ", p: " << p << ", m_out.size(): " << m_out.size() << ", encodeSize: " << encodeSize << " (br: " << brs << ")";
249  auto os = m_out.size();
250  m_out.resize(os + encodeSize);
251  memmove(m_out.data() + p + encodeSize, m_out.data() + p, os - p);
252  if (s < c_rlpListImmLenCount)
253  m_out[p] = (byte)(c_rlpListStart + s);
254  else if (c_rlpListIndLenZero + brs <= 0xff)
255  {
256  m_out[p] = (byte)(c_rlpListIndLenZero + brs);
257  byte* b = &(m_out[p + brs]);
258  for (; s; s >>= 8)
259  *(b--) = (byte)s;
260  }
261  else
262  BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("itemCount too large for RLP"));
263  }
264  _itemCount = 1; // for all following iterations, we've effectively appended a single item only since we completed a list.
265  }
266 }
267 
269 {
270 // cdebug << "appendList(" << _items << ")";
271  if (_items)
272  m_listStack.push_back(std::make_pair(_items, m_out.size()));
273  else
274  appendList(bytes());
275  return *this;
276 }
277 
279 {
280  if (_rlp.size() < c_rlpListImmLenCount)
281  m_out.push_back((byte)(_rlp.size() + c_rlpListStart));
282  else
283  pushCount(_rlp.size(), c_rlpListIndLenZero);
284  appendRaw(_rlp, 1);
285  return *this;
286 }
287 
289 {
290  size_t s = _s.size();
291  byte const* d = _s.data();
292  if (_compact)
293  for (size_t i = 0; i < _s.size() && !*d; ++i, --s, ++d) {}
294 
295  if (s == 1 && *d < c_rlpDataImmLenStart)
296  m_out.push_back(*d);
297  else
298  {
299  if (s < c_rlpDataImmLenCount)
300  m_out.push_back((byte)(s + c_rlpDataImmLenStart));
301  else
302  pushCount(s, c_rlpDataIndLenZero);
303  appendRaw(bytesConstRef(d, s), 0);
304  }
305  noteAppended();
306  return *this;
307 }
308 
310 {
311  if (!_i)
312  m_out.push_back(c_rlpDataImmLenStart);
313  else if (_i < c_rlpDataImmLenStart)
314  m_out.push_back((byte)_i);
315  else
316  {
317  unsigned br = bytesRequired(_i);
318  if (br < c_rlpDataImmLenCount)
319  m_out.push_back((byte)(br + c_rlpDataImmLenStart));
320  else
321  {
322  auto brbr = bytesRequired(br);
323  if (c_rlpDataIndLenZero + brbr > 0xff)
324  BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Number too large for RLP"));
325  m_out.push_back((byte)(c_rlpDataIndLenZero + brbr));
326  pushInt(br, brbr);
327  }
328  pushInt(_i, br);
329  }
330  noteAppended();
331  return *this;
332 }
333 
334 void RLPStream::pushCount(size_t _count, byte _base)
335 {
336  auto br = bytesRequired(_count);
337  if (int(br) + _base > 0xff)
338  BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Count too large for RLP"));
339  m_out.push_back((byte)(br + _base)); // max 8 bytes.
340  pushInt(_count, br);
341 }
342 
343 static void streamOut(std::ostream& _out, dev::RLP const& _d, unsigned _depth = 0)
344 {
345  if (_depth > 64)
346  _out << "<max-depth-reached>";
347  else if (_d.isNull())
348  _out << "null";
349  else if (_d.isInt())
350  _out << std::showbase << std::hex << std::nouppercase << _d.toInt<bigint>(RLP::LaissezFaire) << dec;
351  else if (_d.isData())
352  _out << escaped(_d.toString(), false);
353  else if (_d.isList())
354  {
355  _out << "[";
356  int j = 0;
357  for (auto i: _d)
358  {
359  _out << (j++ ? ", " : " ");
360  streamOut(_out, i, _depth + 1);
361  }
362  _out << " ]";
363  }
364 }
365 
366 std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d)
367 {
368  streamOut(_out, _d);
369  return _out;
370 }
dev::vector_ref::begin
_T * begin()
Definition: vector_ref.h:91
dev::vector_ref< byte const >
byte
uint8_t byte
Definition: Common.h:57
dev::RLP::iterator::operator++
iterator & operator++()
Definition: RLP.cpp:55
dev::RLP::Strictness
int Strictness
Definition: RLP.h:62
dev::RLP::isData
bool isData() const
String value.
Definition: RLP.h:92
dev::RLPException
Base class for all RLP exceptions.
Definition: Exceptions.h:50
dev::RLP::actualSize
size_t actualSize() const
Definition: RLP.cpp:100
dev::vector_ref::next
vector_ref< _T > next() const
Definition: vector_ref.h:56
dev::RLP::isList
bool isList() const
List value.
Definition: RLP.h:95
dev::rlp
bytes rlp(_T _t)
Export a single item in RLP format, returning a byte array.
Definition: RLP.h:453
dev::vector_ref::end
_T * end()
Definition: vector_ref.h:92
dev::RLP::isNull
bool isNull() const
No value.
Definition: RLP.h:86
dev::operator<<
std::ostream & operator<<(std::ostream &_out, bytes const &_e)
Definition: CommonIO.h:77
dev::bytesRequired
unsigned bytesRequired(T _i)
Determine bytes required to encode the given integer value.
Definition: CommonData.h:213
dev::vector_ref::cropped
vector_ref< _T > cropped(size_t _begin, size_t _count) const
Definition: vector_ref.h:60
dev::RLP::isInt
bool isInt() const
Integer value. Must not have a leading zero.
Definition: RLP.cpp:124
dev::bytes
std::vector< byte > bytes
Definition: Common.h:72
dev::RLP::RLP
RLP()
Construct a null node.
Definition: RLP.h:65
dev::vector_ref::data
_T * data() const
Definition: vector_ref.h:49
dev::bytesConstRef
vector_ref< byte const > bytesConstRef
Definition: Common.h:74
dev::RLP::toString
std::string toString(int _flags=LaissezFaire) const
Converts to string.
Definition: RLP.h:181
dev::RLP::LaissezFaire
@ LaissezFaire
Definition: RLP.h:59
dev::vector_ref::size
size_t size() const
Definition: vector_ref.h:53
dev::RLP::data
bytesConstRef data() const
The bare data of the RLP.
Definition: RLP.h:80
dev::RLP::payload
bytesConstRef payload() const
Definition: RLP.h:308
dev::RLPStream
Class for writing to an RLP bytestream.
Definition: RLP.h:370
dev::RLPStream::append
RLPStream & append(unsigned _s)
Append given datum to the byte stream.
Definition: RLP.h:381
dev::RLPNull
bytes RLPNull
The empty string in RLP format.
Definition: RLP.cpp:22
dev::escaped
std::string escaped(std::string const &_s, bool _all=true)
Definition: CommonData.cpp:52
dev::vector_ref::retarget
void retarget(_T *_d, size_t _s)
Definition: vector_ref.h:63
dev::bigint
boost::multiprecision::number< boost::multiprecision::cpp_int_backend<> > bigint
Definition: Common.h:118
dev::RLP::FailIfTooBig
@ FailIfTooBig
Definition: RLP.h:55
std
Definition: FixedHash.h:393
dev::RLP::operator[]
RLP operator[](size_t _i) const
Definition: RLP.cpp:83
dev::RLP::toInt
_T toInt(int _flags=Strict) const
Converts to int of type given; if isData(), decodes as big-endian bytestream.
Definition: RLP.h:257
dev::RLP::iterator
Iterator class for iterating through items of RLP list.
Definition: RLP.h:131
dev
Definition: Address.cpp:21
dev::RequirementError
boost::tuple< errinfo_required, errinfo_got > RequirementError
Definition: Exceptions.h:87
dev::rlpList
bytes rlpList()
Export a list of items in RLP format, returning a byte array.
Definition: RLP.h:456
dev::vector_ref::reset
void reset()
Definition: vector_ref.h:102
dev::RLP::ThrowOnFail
@ ThrowOnFail
Definition: RLP.h:54
dev::RLP::FailIfTooSmall
@ FailIfTooSmall
Definition: RLP.h:56
dev::RLPStream::appendRaw
RLPStream & appendRaw(bytesConstRef _rlp, size_t _itemCount=1)
Appends raw (pre-serialised) RLP data. Use with caution.
Definition: RLP.cpp:222
dev::RLP
Definition: RLP.h:48
dev::RLPEmptyList
bytes RLPEmptyList
The empty list in RLP format.
Definition: RLP.cpp:23
RLP.h
dev::RLPStream::appendList
RLPStream & appendList(size_t _items)
Appends a list.
Definition: RLP.cpp:268
dev::errinfo_comment
boost::error_info< struct tag_comment, std::string > errinfo_comment
Definition: Assertions.h:69