diff --git a/libethereum/MemTrie.cpp b/libethereum/MemTrie.cpp index 4e80113ae..343de3466 100644 --- a/libethereum/MemTrie.cpp +++ b/libethereum/MemTrie.cpp @@ -434,12 +434,12 @@ MemTrie::~MemTrie() h256 MemTrie::hash256() const { - return m_root ? m_root->hash256() : eth::sha3(RLPNull); + return m_root ? m_root->hash256() : h256(); } bytes MemTrie::rlp() const { - return m_root ? m_root->rlp() : RLPNull; + return m_root ? m_root->rlp() : bytes(); } void MemTrie::debugPrint() diff --git a/libethereum/RLP.cpp b/libethereum/RLP.cpp index d84e6d66f..b78d3230b 100644 --- a/libethereum/RLP.cpp +++ b/libethereum/RLP.cpp @@ -45,14 +45,6 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin) { auto pl = _parent.payload(); m_lastItem = pl.cropped(0, RLP(pl).actualSize()); - - uint t = 0; - for (uint i = 0; i < _parent.itemCount(); ++i) - t += _parent[i].actualSize(); - if (pl.size() != t) - cout << _parent.itemCount() << " " << asHex(pl); - assert(pl.size() == t); - m_remaining = pl.size() - m_lastItem.size(); } else @@ -64,15 +56,13 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin) RLP RLP::operator[](uint _i) const { - if (!isList() || itemCount() <= _i) - return RLP(); if (_i < m_lastIndex) { m_lastEnd = RLP(payload()).actualSize(); m_lastItem = payload().cropped(0, m_lastEnd); m_lastIndex = 0; } - for (; m_lastIndex < _i; ++m_lastIndex) + for (; m_lastIndex < _i && m_lastItem.size(); ++m_lastIndex) { m_lastItem = payload().cropped(m_lastEnd); m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem).actualSize()); @@ -86,9 +76,8 @@ RLPs RLP::toList() const RLPs ret; if (!isList()) return ret; - uint64_t c = items(); - for (uint64_t i = 0; i < c; ++i) - ret.push_back(operator[](i)); + for (auto const& i: *this) + ret.push_back(i); return ret; } @@ -98,15 +87,8 @@ eth::uint RLP::actualSize() const return 0; if (isSingleByte()) return 1; - if (isData()) - return payload().data() - m_data.data() + items(); - if (isList()) - { - bytesConstRef d = payload(); - uint64_t c = items(); - for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).actualSize())) {} - return d.data() - m_data.data(); - } + if (isData() || isList()) + return payload().data() - m_data.data() + length(); return 0; } @@ -115,7 +97,9 @@ bool RLP::isInt() const byte n = m_data[0]; if (n < c_rlpDataImmLenStart) return !!n; - else if (n <= c_rlpDataImmLenStart + c_rlpDataImmLenCount) + else if (n == c_rlpDataImmLenStart) + return true; + else if (n <= c_rlpDataIndLenZero) return m_data[1]; else if (n < c_rlpListStart) return m_data[1 + n - c_rlpDataIndLenZero]; @@ -124,18 +108,18 @@ bool RLP::isInt() const return false; } -eth::uint RLP::items() const +eth::uint RLP::length() const { uint ret = 0; byte n = m_data[0]; if (n < c_rlpDataImmLenStart) return 1; - else if (n <= c_rlpDataImmLenStart + c_rlpDataImmLenCount) + else if (n <= c_rlpDataIndLenZero) return n - c_rlpDataImmLenStart; else if (n < c_rlpListStart) for (int i = 0; i < n - c_rlpDataIndLenZero; ++i) ret = (ret << 8) | m_data[i + 1]; - else if (n <= c_rlpListStart + c_rlpDataImmLenCount) + else if (n <= c_rlpListIndLenZero) return n - c_rlpListStart; else for (int i = 0; i < n - c_rlpListIndLenZero; ++i) @@ -143,20 +127,81 @@ eth::uint RLP::items() const return ret; } -RLPStream& RLPStream::appendRaw(bytesConstRef _s) +eth::uint RLP::items() const +{ + if (isList()) + { + bytesConstRef d = payload().cropped(0, length()); + eth::uint i = 0; + for (; d.size(); ++i) + d = d.cropped(RLP(d).actualSize()); + return i; + } + return 0; +} + +RLPStream& RLPStream::appendRaw(bytesConstRef _s, uint _itemCount) { uint os = m_out.size(); m_out.resize(os + _s.size()); memcpy(m_out.data() + os, _s.data(), _s.size()); + noteAppended(_itemCount); + return *this; +} + +void RLPStream::noteAppended(uint _itemCount) +{ + if (!_itemCount) + return; +// cdebug << "noteAppended(" << _itemCount << ")"; + while (m_listStack.size()) + { + assert(m_listStack.back().first >= _itemCount); + m_listStack.back().first -= _itemCount; + if (m_listStack.back().first) + break; + else + { + auto p = m_listStack.back().second; + m_listStack.pop_back(); + uint s = m_out.size() - p; // list size + auto brs = bytesRequired(s); + uint encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs); +// cdebug << "s: " << s << ", p: " << p << ", m_out.size(): " << m_out.size() << ", encodeSize: " << encodeSize << " (br: " << brs << ")"; + auto os = m_out.size(); + m_out.resize(os + encodeSize); + memmove(m_out.data() + p + encodeSize, m_out.data() + p, os - p); + if (s < c_rlpListImmLenCount) + m_out[p] = c_rlpListStart + s; + else + { + m_out[p] = c_rlpListIndLenZero + brs; + byte* b = &(m_out[p + brs]); + for (; s; s >>= 8) + *(b--) = (byte)s; + } + } + _itemCount = 1; // for all following iterations, we've effectively appended a single item only since we completed a list. + } +} + +RLPStream& RLPStream::appendList(unsigned _items) +{ +// cdebug << "appendList(" << _items << ")"; + if (_items) + m_listStack.push_back(std::make_pair(_items, m_out.size())); + else + appendList(bytes()); return *this; } -RLPStream& RLPStream::appendList(uint _count) +RLPStream& RLPStream::appendList(bytesConstRef _rlp) { - if (_count < c_rlpListImmLenCount) - m_out.push_back((byte)(_count + c_rlpListStart)); + if (_rlp.size() < c_rlpListImmLenCount) + m_out.push_back((byte)(_rlp.size() + c_rlpListStart)); else - pushCount(_count, c_rlpListIndLenZero); + pushCount(_rlp.size(), c_rlpListIndLenZero); + appendRaw(_rlp, 1); return *this; } @@ -175,8 +220,9 @@ RLPStream& RLPStream::append(bytesConstRef _s, bool _compact) m_out.push_back((byte)(s + c_rlpDataImmLenStart)); else pushCount(s, c_rlpDataIndLenZero); - appendRaw(bytesConstRef(d, s)); + appendRaw(bytesConstRef(d, s), 0); } + noteAppended(); return *this; } @@ -199,6 +245,7 @@ RLPStream& RLPStream::append(bigint _i) } pushInt(_i, br); } + noteAppended(); return *this; } diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 1ce6fcf9d..dd6ebfaf9 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -103,8 +103,8 @@ public: uint itemCountStrict() const { if (!isList()) throw BadCast(); return items(); } /// @returns the number of bytes in the data, or zero if it isn't data. - uint size() const { return isData() ? items() : 0; } - uint sizeStrict() const { if (!isData()) throw BadCast(); return items(); } + uint size() const { return isData() ? length() : 0; } + uint sizeStrict() const { if (!isData()) throw BadCast(); return length(); } /// Equality operators; does best-effort conversion and checks for equality. bool operator==(char const* _s) const { return isData() && toString() == _s; } @@ -166,13 +166,13 @@ public: template explicit operator FixedHash<_N>() const { return toHash>(); } /// Converts to bytearray. @returns the empty byte array if not a string. - bytes toBytes() const { if (!isData()) return bytes(); return bytes(payload().data(), payload().data() + items()); } + bytes toBytes() const { if (!isData()) return bytes(); return bytes(payload().data(), payload().data() + length()); } /// Converts to bytearray. @returns the empty byte array if not a string. - bytesConstRef toBytesConstRef() const { if (!isData()) return bytesConstRef(); return payload().cropped(0, items()); } + bytesConstRef toBytesConstRef() const { if (!isData()) return bytesConstRef(); return payload().cropped(0, length()); } /// Converts to string. @returns the empty string if not a string. - std::string toString() const { if (!isData()) return std::string(); return payload().cropped(0, items()).toString(); } + std::string toString() const { if (!isData()) return std::string(); return payload().cropped(0, length()).toString(); } /// Converts to string. @throws BadCast if not a string. - std::string toStringStrict() const { if (!isData()) throw BadCast(); return payload().cropped(0, items()).toString(); } + std::string toStringStrict() const { if (!isData()) throw BadCast(); return payload().cropped(0, length()).toString(); } template std::vector toVector() const { std::vector ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; } template std::array toArray() const { std::array ret; if (itemCount() != N) throw BadCast(); if (isList()) for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; } @@ -210,7 +210,7 @@ public: template _N toHash(int _flags = Strict) const { - if (!isData() || (items() > _N::size && (_flags & FailIfTooBig))) + if (!isData() || (length() > _N::size && (_flags & FailIfTooBig))) if (_flags & ThrowOnFail) throw BadCast(); else @@ -218,7 +218,7 @@ public: else{} _N ret; - size_t s = std::min((size_t)_N::size, (size_t)items()); + size_t s = std::min((size_t)_N::size, (size_t)length()); memcpy(ret.data() + _N::size - s, payload().data(), s); return ret; } @@ -227,11 +227,11 @@ public: RLPs toList() const; /// @returns the data payload. Valid for all types. - bytesConstRef payload() const { return isSingleByte() ? m_data.cropped(0, 1) : m_data.cropped(1 + lengthSize(), items()); } + bytesConstRef payload() const { return isSingleByte() ? m_data.cropped(0, 1) : m_data.cropped(1 + lengthSize()); } private: /// Single-byte data payload. - bool isSingleByte() const { assert(!isNull()); return m_data[0] < c_rlpDataImmLenStart; } + bool isSingleByte() const { return !isNull() && m_data[0] < c_rlpDataImmLenStart; } /// @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. @@ -240,7 +240,10 @@ private: /// @returns the bytes used to encode the length of the data. Valid for all types. uint lengthSize() const { if (isData() && m_data[0] > c_rlpDataIndLenZero) return m_data[0] - c_rlpDataIndLenZero; if (isList() && m_data[0] > c_rlpListIndLenZero) return m_data[0] - c_rlpListIndLenZero; return 0; } - /// @returns the number of data items (bytes in the case of strings & ints, items in the case of lists). Valid for all types. + /// @returns the size in bytes of the payload, as given by the RLP as opposed to as inferred from m_data. + uint length() const; + + /// @returns the number of data items. uint items() const; /// Our byte data. @@ -264,6 +267,8 @@ public: /// Initializes the RLPStream as a list of @a _listItems items. explicit RLPStream(uint _listItems) { appendList(_listItems); } + ~RLPStream() {} + /// Append given datum to the byte stream. RLPStream& append(uint _s) { return append(bigint(_s)); } RLPStream& append(u160 _s) { return append(bigint(_s)); } @@ -276,33 +281,38 @@ public: RLPStream& append(h160 _s, bool _compact = false) { return append(_s.ref(), _compact); } RLPStream& append(h256 _s, bool _compact = false) { return append(_s.ref(), _compact); } - /// Appends an arbitrary RLP fragment. - RLPStream& append(RLP const& _rlp) { return appendRaw(_rlp.data()); } + /// Appends an arbitrary RLP fragment - this *must* be a single item. + RLPStream& append(RLP const& _rlp, uint _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); } /// Appends a sequence of data to the stream as a list. template RLPStream& append(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } /// Appends a list. - RLPStream& appendList(uint _count); + RLPStream& appendList(unsigned _items); + RLPStream& appendList(bytesConstRef _rlp); + RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); } + RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); } /// Appends raw (pre-serialised) RLP data. Use with caution. - RLPStream& appendRaw(bytesConstRef _rlp); - RLPStream& appendRaw(bytes const& _rlp) { return appendRaw(&_rlp); } + RLPStream& appendRaw(bytesConstRef _rlp, uint _itemCount = 1); + RLPStream& appendRaw(bytes const& _rlp, uint _itemCount = 1) { return appendRaw(&_rlp, _itemCount); } /// Shift operators for appending data items. template RLPStream& operator<<(T _data) { return append(_data); } /// Clear the output stream so far. - void clear() { m_out.clear(); } + void clear() { m_out.clear(); m_listStack.clear(); } /// Read the byte stream. - bytes const& out() const { return m_out; } + bytes const& out() const { assert(m_listStack.empty()); return m_out; } /// Swap the contents of the output stream out for some other byte array. - void swapOut(bytes& _dest) { swap(m_out, _dest); } + void swapOut(bytes& _dest) { assert(m_listStack.empty()); swap(m_out, _dest); } private: + void noteAppended(uint _itemCount = 1); + /// 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 _offset); @@ -326,6 +336,8 @@ private: /// Our output byte stream. bytes m_out; + + std::vector> m_listStack; }; template void rlpListAux(RLPStream& _out, _T _t) { _out << _t; } diff --git a/libethereum/TrieDB.h b/libethereum/TrieDB.h index 3d327074b..06ec0e4b6 100644 --- a/libethereum/TrieDB.h +++ b/libethereum/TrieDB.h @@ -182,7 +182,14 @@ public: m_trail.pop_back(); continue; } - assert(rlp.isList() && (rlp.itemCount() == 2 || rlp.itemCount() == 17)); + if (!(rlp.isList() && (rlp.itemCount() == 2 || rlp.itemCount() == 17))) + { + cdebug << b.rlp.size() << asHex(b.rlp); + cdebug << rlp; + auto c = rlp.itemCount(); + cdebug << c; + assert(rlp.isList() && (rlp.itemCount() == 2 || rlp.itemCount() == 17)); + } if (rlp.itemCount() == 2) { // Just turn it into a valid Branch diff --git a/libethereum/TrieHash.cpp b/libethereum/TrieHash.cpp index 153e84e17..460465e01 100644 --- a/libethereum/TrieHash.cpp +++ b/libethereum/TrieHash.cpp @@ -159,7 +159,7 @@ h256 hash256(StringMap const& _s) { // build patricia tree. if (_s.empty()) - return sha3(RLPNull); + return h256(); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) hexMap[toHex(i->first)] = i->second; @@ -172,7 +172,7 @@ bytes rlp256(StringMap const& _s) { // build patricia tree. if (_s.empty()) - return RLPNull; + return bytes(); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) hexMap[toHex(i->first)] = i->second; @@ -185,7 +185,7 @@ h256 hash256(u256Map const& _s) { // build patricia tree. if (_s.empty()) - return sha3(RLPNull); + return h256(); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) hexMap[toHex(toBigEndianString(i->first))] = asString(rlp(i->second)); diff --git a/test/main.cpp b/test/main.cpp index cf0ce9280..e80d5f150 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -35,19 +35,19 @@ using namespace eth; int main(int argc, char** argv) { - RLPStream s; +/* RLPStream s; BlockInfo::genesis().fillStream(s, false); std::cout << RLP(s.out()) << std::endl; std::cout << asHex(s.out()) << std::endl; - std::cout << sha3(s.out()) << std::endl; + std::cout << sha3(s.out()) << std::endl;*/ -// hexPrefixTest(); -// rlpTest(); + hexPrefixTest(); + rlpTest(); trieTest(); // daggerTest(); // cryptoTest(); // stateTest(); - peerTest(argc, argv); +// peerTest(argc, argv); return 0; } diff --git a/test/rlp.cpp b/test/rlp.cpp index 931d5fe46..0f6dada5c 100644 --- a/test/rlp.cpp +++ b/test/rlp.cpp @@ -35,12 +35,12 @@ int rlpTest() assert(asString(rlp("dog")) == "\x83""dog"); // 2-item list - string twoItemListString = "\xc2\x0f\x83""dog"; + string twoItemListString = "\xc5\x0f\x83""dog"; RLP twoItemList(twoItemListString); assert(twoItemList.itemCount() == 2); assert(twoItemList[0] == 15); assert(twoItemList[1] == "dog"); - assert(asString(rlpList(15, "dog")) == "\xc2\x0f\x83""dog"); + assert(asString(rlpList(15, "dog")) == "\xc5\x0f\x83""dog"); // null assert(RLP("\x80") == ""); @@ -56,7 +56,7 @@ int rlpTest() // 32-byte (256-bit) int assert(RLP("\xa0\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f") == bigint("0x100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")); - assert(asString(rlp(bigint("0x100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"))) == "\x37\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); + assert(asString(rlp(bigint("0x100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"))) == "\xa0\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); // 56-character string. assert(RLP("\xb8\x38""Lorem ipsum dolor sit amet, consectetur adipisicing elit") == "Lorem ipsum dolor sit amet, consectetur adipisicing elit");