зеркало из https://github.com/microsoft/eEVM.git
Strict RLP decoding (#68)
This commit is contained in:
Родитель
12951f37d3
Коммит
73b090fe6b
|
@ -39,6 +39,8 @@ namespace eevm
|
|||
template <typename... Ts>
|
||||
ByteString encode(Ts&&... ts);
|
||||
|
||||
namespace encode_details
|
||||
{
|
||||
void prefix_multiple_length(size_t total_length, ByteString& bs);
|
||||
|
||||
inline ByteString to_byte_string(const ByteString& bs)
|
||||
|
@ -163,7 +165,9 @@ namespace eevm
|
|||
total_length_as_bytes.insert(
|
||||
total_length_as_bytes.begin(), 0xf7 + length_of_total_length);
|
||||
bs.insert(
|
||||
bs.begin(), total_length_as_bytes.begin(), total_length_as_bytes.end());
|
||||
bs.begin(),
|
||||
total_length_as_bytes.begin(),
|
||||
total_length_as_bytes.end());
|
||||
}
|
||||
|
||||
// RLP-encode a single, non-tuple argument. Convert it to a ByteString,
|
||||
|
@ -200,7 +204,8 @@ namespace eevm
|
|||
auto length_as_bytes = to_byte_string(length);
|
||||
const uint8_t length_of_length = length_as_bytes.size();
|
||||
|
||||
length_as_bytes.insert(length_as_bytes.begin(), 0xb7 + length_of_length);
|
||||
length_as_bytes.insert(
|
||||
length_as_bytes.begin(), 0xb7 + length_of_length);
|
||||
bytes.insert(
|
||||
bytes.begin(), length_as_bytes.begin(), length_as_bytes.end());
|
||||
return bytes;
|
||||
|
@ -223,7 +228,9 @@ namespace eevm
|
|||
auto encode_multiple(const std::tuple<Ts...>& tup)
|
||||
{
|
||||
return std::apply(
|
||||
[](auto&&... entry) { return std::make_tuple(encode(entry)...); }, tup);
|
||||
[](auto&&... entry) { return std::make_tuple(encode(entry)...); },
|
||||
tup);
|
||||
}
|
||||
}
|
||||
|
||||
// Main encode function. Either forwards to encode_single, or calls
|
||||
|
@ -234,10 +241,11 @@ namespace eevm
|
|||
{
|
||||
if constexpr (sizeof...(Ts) == 1 && !is_tuple<std::decay_t<Ts>...>::value)
|
||||
{
|
||||
return encode_single(std::forward<Ts>(ts)...);
|
||||
return encode_details::encode_single(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
const auto nested_terms = encode_multiple(std::forward<Ts>(ts)...);
|
||||
const auto nested_terms =
|
||||
encode_details::encode_multiple(std::forward<Ts>(ts)...);
|
||||
|
||||
// Get total length by summing size of each term in tuple
|
||||
const auto total_length = std::apply(
|
||||
|
@ -252,7 +260,7 @@ namespace eevm
|
|||
},
|
||||
nested_terms);
|
||||
|
||||
prefix_multiple_length(total_length, flattened);
|
||||
encode_details::prefix_multiple_length(total_length, flattened);
|
||||
return flattened;
|
||||
}
|
||||
|
||||
|
@ -260,6 +268,13 @@ namespace eevm
|
|||
// Decoding
|
||||
//
|
||||
|
||||
class decode_error : public std::logic_error
|
||||
{
|
||||
using logic_error::logic_error;
|
||||
};
|
||||
|
||||
namespace decode_details
|
||||
{
|
||||
enum class Arity
|
||||
{
|
||||
Single,
|
||||
|
@ -273,14 +288,9 @@ namespace eevm
|
|||
size_t length;
|
||||
};
|
||||
|
||||
class decode_error : public std::logic_error
|
||||
{
|
||||
using logic_error::logic_error;
|
||||
};
|
||||
|
||||
// Forward declaration to allow recursive calls.
|
||||
template <typename... Ts>
|
||||
std::tuple<Ts...> decode(const uint8_t*&, size_t&);
|
||||
std::tuple<Ts...> decode_impl(const uint8_t*&, size_t&);
|
||||
|
||||
inline std::pair<Arity, size_t> decode_length(
|
||||
const uint8_t*& data, size_t& size);
|
||||
|
@ -295,9 +305,9 @@ namespace eevm
|
|||
{
|
||||
if (size > 8)
|
||||
{
|
||||
throw decode_error(
|
||||
"Trying to decode number: " + std::to_string(size) +
|
||||
" is too many bytes for uint64_t");
|
||||
throw decode_error(fmt::format(
|
||||
"Trying to decode number: {} is too many bytes for uint64_t",
|
||||
size));
|
||||
}
|
||||
|
||||
size_t result = 0;
|
||||
|
@ -349,10 +359,10 @@ namespace eevm
|
|||
{
|
||||
if (size != N)
|
||||
{
|
||||
throw decode_error(
|
||||
"Trying to decode " + std::to_string(N) +
|
||||
" byte array, but given " + std::to_string(size) +
|
||||
" bytes to decode");
|
||||
throw decode_error(fmt::format(
|
||||
"Trying to decode {} byte array, but given {} bytes to decode",
|
||||
N,
|
||||
size));
|
||||
}
|
||||
|
||||
std::array<uint8_t, N> result;
|
||||
|
@ -375,7 +385,7 @@ namespace eevm
|
|||
std::array<T, N> result;
|
||||
for (auto i = 0u; i < N; ++i)
|
||||
{
|
||||
result[i] = std::get<0>(decode<T>(data, size));
|
||||
result[i] = std::get<0>(decode_impl<T>(data, size));
|
||||
}
|
||||
|
||||
size = 0u;
|
||||
|
@ -394,7 +404,8 @@ namespace eevm
|
|||
std::vector<T> result;
|
||||
while (contained_length > 0)
|
||||
{
|
||||
result.push_back(std::get<0>(decode<T>(data, contained_length)));
|
||||
result.push_back(
|
||||
std::get<0>(decode_impl<T>(data, contained_length)));
|
||||
}
|
||||
|
||||
size = 0u;
|
||||
|
@ -472,10 +483,11 @@ namespace eevm
|
|||
|
||||
if (size < length_of_length)
|
||||
{
|
||||
throw decode_error(
|
||||
"Length of next element should be encoded in " +
|
||||
std::to_string(length_of_length) + " bytes, but only " +
|
||||
std::to_string(size) + " remain");
|
||||
throw decode_error(fmt::format(
|
||||
"Length of next element should be encoded in {} bytes, but only "
|
||||
"{} remain",
|
||||
length_of_length,
|
||||
size));
|
||||
}
|
||||
|
||||
size -= length_of_length;
|
||||
|
@ -497,10 +509,11 @@ namespace eevm
|
|||
|
||||
if (size < length_of_length)
|
||||
{
|
||||
throw decode_error(
|
||||
"Length of next list should be encoded in " +
|
||||
std::to_string(length_of_length) + " bytes, but only " +
|
||||
std::to_string(size) + " remain");
|
||||
throw decode_error(fmt::format(
|
||||
"Length of next list should be encoded in {} bytes, but only {} "
|
||||
"remain",
|
||||
length_of_length,
|
||||
size));
|
||||
}
|
||||
|
||||
size -= length_of_length;
|
||||
|
@ -530,15 +543,15 @@ namespace eevm
|
|||
inline std::tuple<T, Ts...> decode_tuple(
|
||||
const uint8_t*& data, size_t& size, std::tuple<T, Ts...>)
|
||||
{
|
||||
const auto first = decode<T>(data, size);
|
||||
const auto first = decode_impl<T>(data, size);
|
||||
|
||||
return std::tuple_cat(
|
||||
first, decode_tuple<Ts...>(data, size, std::tuple<Ts...>{}));
|
||||
}
|
||||
|
||||
// Type helper for decoding single item, forwarding to either decode_tuple
|
||||
// (to unwrap the types contained in a tuple) or directly to the main
|
||||
// decode function
|
||||
// (to unwrap the types contained in a tuple) or directly to the top-level
|
||||
// decode_impl function
|
||||
template <typename T>
|
||||
auto decode_item(const uint8_t*& data, size_t& size)
|
||||
{
|
||||
|
@ -548,13 +561,12 @@ namespace eevm
|
|||
}
|
||||
else
|
||||
{
|
||||
return decode<T>(data, size);
|
||||
return decode_impl<T>(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Type helper for decoding single item, forwarding to either decode_tuple
|
||||
// (to unwrap the types contained in a tuple) or directly to the main
|
||||
// decode function
|
||||
// Type helper for decoding multiple items, decoding the first and then
|
||||
// potentially recursing to concatenate the decoded tail
|
||||
template <typename T, typename... Ts>
|
||||
std::tuple<T, Ts...> decode_multiple(const uint8_t*& data, size_t& size)
|
||||
{
|
||||
|
@ -570,14 +582,15 @@ namespace eevm
|
|||
}
|
||||
}
|
||||
|
||||
// Main decode function. Reads initial length, then either converts a single
|
||||
// item from remaining bytes or forwards to decode_multiple
|
||||
// Main decode_impl function. Reads initial length, then either converts a
|
||||
// single item from remaining bytes or forwards to decode_multiple
|
||||
template <typename... Ts>
|
||||
std::tuple<Ts...> decode(const uint8_t*& data, size_t& size)
|
||||
std::tuple<Ts...> decode_impl(const uint8_t*& data, size_t& size)
|
||||
{
|
||||
auto [arity, contained_length] = decode_length(data, size);
|
||||
|
||||
if constexpr (sizeof...(Ts) == 1 && !is_tuple<std::decay_t<Ts>...>::value)
|
||||
if constexpr (
|
||||
sizeof...(Ts) == 1 && !is_tuple<std::decay_t<Ts>...>::value)
|
||||
{
|
||||
if (arity != Arity::Single)
|
||||
{
|
||||
|
@ -585,6 +598,7 @@ namespace eevm
|
|||
}
|
||||
|
||||
size -= contained_length;
|
||||
|
||||
return std::make_tuple(from_bytes<Ts...>{}(data, contained_length));
|
||||
}
|
||||
|
||||
|
@ -598,9 +612,9 @@ namespace eevm
|
|||
{
|
||||
if (contained_length != 0)
|
||||
{
|
||||
throw decode_error(
|
||||
"Expected empty list, but data contains " +
|
||||
std::to_string(contained_length) + " remaining bytes");
|
||||
throw decode_error(fmt::format(
|
||||
"Expected empty list, but data contains {} remaining bytes",
|
||||
contained_length));
|
||||
}
|
||||
|
||||
return std::make_tuple();
|
||||
|
@ -608,11 +622,29 @@ namespace eevm
|
|||
else
|
||||
{
|
||||
size -= contained_length;
|
||||
|
||||
return decode_multiple<Ts...>(data, contained_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper. Takes ByteString and forwards to contained data+size to main
|
||||
// Core helper. Forwards to decode_impl, ensures entire input has been
|
||||
// consumed
|
||||
template <typename... Ts>
|
||||
std::tuple<Ts...> decode(const uint8_t*& data, size_t& size)
|
||||
{
|
||||
auto res = decode_details::decode_impl<Ts...>(data, size);
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
throw decode_error(fmt::format(
|
||||
"Expected to decode entire input, but {} bytes remain", size));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Helper. Takes ByteString and forwards contained data+size to main
|
||||
// decode function
|
||||
template <typename... Ts>
|
||||
std::tuple<Ts...> decode(const ByteString& bytes)
|
||||
|
|
|
@ -20,11 +20,15 @@ const auto large_input_decoded = std::make_tuple(
|
|||
66000u),
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua"s);
|
||||
const auto large_input_encoded = rlp::to_byte_string(
|
||||
|
||||
const std::string large_input_encoded_s =
|
||||
"\xf8\xa5\xda\x8bHello world\x8dSaluton "
|
||||
"Mondo\xcd\xc8\xc1\x01\xc2\x02\x03\xc2\xc1\x04\x83\x01\x01\xd0\xb8zLorem "
|
||||
"ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua");
|
||||
"tempor incididunt ut labore et dolore magna aliqua";
|
||||
|
||||
const auto large_input_encoded = std::vector<uint8_t>(
|
||||
large_input_encoded_s.begin(), large_input_encoded_s.end());
|
||||
|
||||
TEST_CASE("encode" * doctest::test_suite("rlp"))
|
||||
{
|
||||
|
@ -83,6 +87,7 @@ TEST_CASE("decode" * doctest::test_suite("rlp"))
|
|||
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x1}) == 0x1);
|
||||
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x7f}) == 0x7f);
|
||||
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x81, 0x80}) == 0x80);
|
||||
CHECK_THROWS(rlp::decode_single<size_t>(rlp::ByteString{0x81, 0x80, 0x00}));
|
||||
|
||||
CHECK(rlp::decode<>(rlp::ByteString{0xc0}) == std::make_tuple());
|
||||
CHECK(rlp::decode<std::string>(rlp::ByteString{0x80}) == std::make_tuple(""));
|
||||
|
@ -123,6 +128,8 @@ TEST_CASE("decode" * doctest::test_suite("rlp"))
|
|||
rlp::decode<std::tuple<std::tuple<size_t>>>(
|
||||
rlp::ByteString{0xc2, 0xc1, 0x80}) ==
|
||||
std::make_tuple(std::make_tuple(std::make_tuple(0x0))));
|
||||
CHECK_THROWS(rlp::decode<std::tuple<std::tuple<size_t>>>(
|
||||
rlp::ByteString{0xc2, 0xc1, 0x80, 0x00}));
|
||||
|
||||
CHECK(
|
||||
rlp::decode_single<
|
||||
|
|
Загрузка…
Ссылка в новой задаче