зеркало из 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>
|
template <typename... Ts>
|
||||||
ByteString encode(Ts&&... ts);
|
ByteString encode(Ts&&... ts);
|
||||||
|
|
||||||
|
namespace encode_details
|
||||||
|
{
|
||||||
void prefix_multiple_length(size_t total_length, ByteString& bs);
|
void prefix_multiple_length(size_t total_length, ByteString& bs);
|
||||||
|
|
||||||
inline ByteString to_byte_string(const 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.insert(
|
||||||
total_length_as_bytes.begin(), 0xf7 + length_of_total_length);
|
total_length_as_bytes.begin(), 0xf7 + length_of_total_length);
|
||||||
bs.insert(
|
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,
|
// 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);
|
auto length_as_bytes = to_byte_string(length);
|
||||||
const uint8_t length_of_length = length_as_bytes.size();
|
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.insert(
|
||||||
bytes.begin(), length_as_bytes.begin(), length_as_bytes.end());
|
bytes.begin(), length_as_bytes.begin(), length_as_bytes.end());
|
||||||
return bytes;
|
return bytes;
|
||||||
|
@ -223,7 +228,9 @@ namespace eevm
|
||||||
auto encode_multiple(const std::tuple<Ts...>& tup)
|
auto encode_multiple(const std::tuple<Ts...>& tup)
|
||||||
{
|
{
|
||||||
return std::apply(
|
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
|
// 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)
|
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
|
// Get total length by summing size of each term in tuple
|
||||||
const auto total_length = std::apply(
|
const auto total_length = std::apply(
|
||||||
|
@ -252,7 +260,7 @@ namespace eevm
|
||||||
},
|
},
|
||||||
nested_terms);
|
nested_terms);
|
||||||
|
|
||||||
prefix_multiple_length(total_length, flattened);
|
encode_details::prefix_multiple_length(total_length, flattened);
|
||||||
return flattened;
|
return flattened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +268,13 @@ namespace eevm
|
||||||
// Decoding
|
// Decoding
|
||||||
//
|
//
|
||||||
|
|
||||||
|
class decode_error : public std::logic_error
|
||||||
|
{
|
||||||
|
using logic_error::logic_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace decode_details
|
||||||
|
{
|
||||||
enum class Arity
|
enum class Arity
|
||||||
{
|
{
|
||||||
Single,
|
Single,
|
||||||
|
@ -273,14 +288,9 @@ namespace eevm
|
||||||
size_t length;
|
size_t length;
|
||||||
};
|
};
|
||||||
|
|
||||||
class decode_error : public std::logic_error
|
|
||||||
{
|
|
||||||
using logic_error::logic_error;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Forward declaration to allow recursive calls.
|
// Forward declaration to allow recursive calls.
|
||||||
template <typename... Ts>
|
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(
|
inline std::pair<Arity, size_t> decode_length(
|
||||||
const uint8_t*& data, size_t& size);
|
const uint8_t*& data, size_t& size);
|
||||||
|
@ -295,9 +305,9 @@ namespace eevm
|
||||||
{
|
{
|
||||||
if (size > 8)
|
if (size > 8)
|
||||||
{
|
{
|
||||||
throw decode_error(
|
throw decode_error(fmt::format(
|
||||||
"Trying to decode number: " + std::to_string(size) +
|
"Trying to decode number: {} is too many bytes for uint64_t",
|
||||||
" is too many bytes for uint64_t");
|
size));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t result = 0;
|
size_t result = 0;
|
||||||
|
@ -349,10 +359,10 @@ namespace eevm
|
||||||
{
|
{
|
||||||
if (size != N)
|
if (size != N)
|
||||||
{
|
{
|
||||||
throw decode_error(
|
throw decode_error(fmt::format(
|
||||||
"Trying to decode " + std::to_string(N) +
|
"Trying to decode {} byte array, but given {} bytes to decode",
|
||||||
" byte array, but given " + std::to_string(size) +
|
N,
|
||||||
" bytes to decode");
|
size));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<uint8_t, N> result;
|
std::array<uint8_t, N> result;
|
||||||
|
@ -375,7 +385,7 @@ namespace eevm
|
||||||
std::array<T, N> result;
|
std::array<T, N> result;
|
||||||
for (auto i = 0u; i < N; ++i)
|
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;
|
size = 0u;
|
||||||
|
@ -394,7 +404,8 @@ namespace eevm
|
||||||
std::vector<T> result;
|
std::vector<T> result;
|
||||||
while (contained_length > 0)
|
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;
|
size = 0u;
|
||||||
|
@ -472,10 +483,11 @@ namespace eevm
|
||||||
|
|
||||||
if (size < length_of_length)
|
if (size < length_of_length)
|
||||||
{
|
{
|
||||||
throw decode_error(
|
throw decode_error(fmt::format(
|
||||||
"Length of next element should be encoded in " +
|
"Length of next element should be encoded in {} bytes, but only "
|
||||||
std::to_string(length_of_length) + " bytes, but only " +
|
"{} remain",
|
||||||
std::to_string(size) + " remain");
|
length_of_length,
|
||||||
|
size));
|
||||||
}
|
}
|
||||||
|
|
||||||
size -= length_of_length;
|
size -= length_of_length;
|
||||||
|
@ -497,10 +509,11 @@ namespace eevm
|
||||||
|
|
||||||
if (size < length_of_length)
|
if (size < length_of_length)
|
||||||
{
|
{
|
||||||
throw decode_error(
|
throw decode_error(fmt::format(
|
||||||
"Length of next list should be encoded in " +
|
"Length of next list should be encoded in {} bytes, but only {} "
|
||||||
std::to_string(length_of_length) + " bytes, but only " +
|
"remain",
|
||||||
std::to_string(size) + " remain");
|
length_of_length,
|
||||||
|
size));
|
||||||
}
|
}
|
||||||
|
|
||||||
size -= length_of_length;
|
size -= length_of_length;
|
||||||
|
@ -530,15 +543,15 @@ namespace eevm
|
||||||
inline std::tuple<T, Ts...> decode_tuple(
|
inline std::tuple<T, Ts...> decode_tuple(
|
||||||
const uint8_t*& data, size_t& size, std::tuple<T, Ts...>)
|
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(
|
return std::tuple_cat(
|
||||||
first, decode_tuple<Ts...>(data, size, std::tuple<Ts...>{}));
|
first, decode_tuple<Ts...>(data, size, std::tuple<Ts...>{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type helper for decoding single item, forwarding to either decode_tuple
|
// Type helper for decoding single item, forwarding to either decode_tuple
|
||||||
// (to unwrap the types contained in a tuple) or directly to the main
|
// (to unwrap the types contained in a tuple) or directly to the top-level
|
||||||
// decode function
|
// decode_impl function
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto decode_item(const uint8_t*& data, size_t& size)
|
auto decode_item(const uint8_t*& data, size_t& size)
|
||||||
{
|
{
|
||||||
|
@ -548,13 +561,12 @@ namespace eevm
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return decode<T>(data, size);
|
return decode_impl<T>(data, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type helper for decoding single item, forwarding to either decode_tuple
|
// Type helper for decoding multiple items, decoding the first and then
|
||||||
// (to unwrap the types contained in a tuple) or directly to the main
|
// potentially recursing to concatenate the decoded tail
|
||||||
// decode function
|
|
||||||
template <typename T, typename... Ts>
|
template <typename T, typename... Ts>
|
||||||
std::tuple<T, Ts...> decode_multiple(const uint8_t*& data, size_t& size)
|
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
|
// Main decode_impl function. Reads initial length, then either converts a
|
||||||
// item from remaining bytes or forwards to decode_multiple
|
// single item from remaining bytes or forwards to decode_multiple
|
||||||
template <typename... Ts>
|
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);
|
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)
|
if (arity != Arity::Single)
|
||||||
{
|
{
|
||||||
|
@ -585,6 +598,7 @@ namespace eevm
|
||||||
}
|
}
|
||||||
|
|
||||||
size -= contained_length;
|
size -= contained_length;
|
||||||
|
|
||||||
return std::make_tuple(from_bytes<Ts...>{}(data, contained_length));
|
return std::make_tuple(from_bytes<Ts...>{}(data, contained_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,9 +612,9 @@ namespace eevm
|
||||||
{
|
{
|
||||||
if (contained_length != 0)
|
if (contained_length != 0)
|
||||||
{
|
{
|
||||||
throw decode_error(
|
throw decode_error(fmt::format(
|
||||||
"Expected empty list, but data contains " +
|
"Expected empty list, but data contains {} remaining bytes",
|
||||||
std::to_string(contained_length) + " remaining bytes");
|
contained_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple();
|
return std::make_tuple();
|
||||||
|
@ -608,11 +622,29 @@ namespace eevm
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
size -= contained_length;
|
size -= contained_length;
|
||||||
|
|
||||||
return decode_multiple<Ts...>(data, 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
|
// decode function
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
std::tuple<Ts...> decode(const ByteString& bytes)
|
std::tuple<Ts...> decode(const ByteString& bytes)
|
||||||
|
|
|
@ -20,11 +20,15 @@ const auto large_input_decoded = std::make_tuple(
|
||||||
66000u),
|
66000u),
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||||
"tempor incididunt ut labore et dolore magna aliqua"s);
|
"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 "
|
"\xf8\xa5\xda\x8bHello world\x8dSaluton "
|
||||||
"Mondo\xcd\xc8\xc1\x01\xc2\x02\x03\xc2\xc1\x04\x83\x01\x01\xd0\xb8zLorem "
|
"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 "
|
"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"))
|
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{0x1}) == 0x1);
|
||||||
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x7f}) == 0x7f);
|
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x7f}) == 0x7f);
|
||||||
CHECK(rlp::decode_single<size_t>(rlp::ByteString{0x81, 0x80}) == 0x80);
|
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<>(rlp::ByteString{0xc0}) == std::make_tuple());
|
||||||
CHECK(rlp::decode<std::string>(rlp::ByteString{0x80}) == 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::decode<std::tuple<std::tuple<size_t>>>(
|
||||||
rlp::ByteString{0xc2, 0xc1, 0x80}) ==
|
rlp::ByteString{0xc2, 0xc1, 0x80}) ==
|
||||||
std::make_tuple(std::make_tuple(std::make_tuple(0x0))));
|
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(
|
CHECK(
|
||||||
rlp::decode_single<
|
rlp::decode_single<
|
||||||
|
|
Загрузка…
Ссылка в новой задаче