Skip to content

Commit 0f0f383

Browse files
committed
state: Add RLP encoding implementation
1 parent 59f5533 commit 0f0f383

File tree

4 files changed

+243
-1
lines changed

4 files changed

+243
-1
lines changed

test/state/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ target_link_libraries(evmone-state INTERFACE ethash::keccak)
88
target_sources(
99
evmone-state INTERFACE
1010
hash_utils.hpp
11+
rlp.hpp
1112
)

test/state/rlp.hpp

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2021 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include <intx/intx.hpp>
8+
#include <cassert>
9+
#include <string>
10+
#include <string_view>
11+
#include <utility>
12+
#include <vector>
13+
14+
namespace evmone::rlp
15+
{
16+
using bytes = std::basic_string<uint8_t>;
17+
using bytes_view = std::basic_string_view<uint8_t>;
18+
19+
namespace internal
20+
{
21+
template <uint8_t ShortBase, uint8_t LongBase>
22+
inline bytes encode_length(size_t l)
23+
{
24+
static constexpr auto short_cutoff = 55;
25+
static_assert(ShortBase + short_cutoff <= 0xff);
26+
assert(l <= 0xffffff);
27+
28+
if (l <= short_cutoff)
29+
return {static_cast<uint8_t>(ShortBase + l)};
30+
else if (const auto l0 = static_cast<uint8_t>(l); l <= 0xff)
31+
return {LongBase + 1, l0};
32+
else if (const auto l1 = static_cast<uint8_t>(l >> 8); l <= 0xffff)
33+
return {LongBase + 2, l1, l0};
34+
else
35+
return {LongBase + 3, static_cast<uint8_t>(l >> 16), l1, l0};
36+
}
37+
38+
inline bytes wrap_list(const bytes& content)
39+
{
40+
return internal::encode_length<192, 247>(content.size()) + content;
41+
}
42+
43+
template <typename InputIterator>
44+
inline bytes encode_container(InputIterator begin, InputIterator end);
45+
} // namespace internal
46+
47+
inline bytes_view trim(bytes_view b) noexcept
48+
{
49+
b.remove_prefix(std::min(b.find_first_not_of(uint8_t{0x00}), b.size()));
50+
return b;
51+
}
52+
53+
template <typename T>
54+
inline decltype(rlp_encode(std::declval<T>())) encode(const T& v)
55+
{
56+
return rlp_encode(v);
57+
}
58+
59+
inline bytes encode(bytes_view data)
60+
{
61+
static constexpr uint8_t short_base = 128;
62+
if (data.size() == 1 && data[0] < short_base)
63+
return {data[0]};
64+
65+
return internal::encode_length<short_base, 183>(data.size()) += data;
66+
}
67+
68+
inline bytes encode(uint64_t x)
69+
{
70+
uint8_t b[sizeof(x)];
71+
intx::be::store(b, x);
72+
return encode(trim({b, sizeof(b)}));
73+
}
74+
75+
inline bytes encode(const intx::uint256& x)
76+
{
77+
uint8_t b[sizeof(x)];
78+
intx::be::store(b, x);
79+
return encode(trim({b, sizeof(b)}));
80+
}
81+
82+
template <typename T>
83+
inline bytes encode(const std::vector<T>& v)
84+
{
85+
return internal::encode_container(v.begin(), v.end());
86+
}
87+
88+
template <typename T, size_t N>
89+
inline bytes encode(const T (&v)[N])
90+
{
91+
return internal::encode_container(std::begin(v), std::end(v));
92+
}
93+
94+
/// Encodes the fixed-size collection of heterogeneous values as RLP list.
95+
template <typename... Types>
96+
inline bytes encode_tuple(const Types&... elements)
97+
{
98+
return internal::wrap_list((encode(elements) + ...));
99+
}
100+
101+
/// Encodes the container as RLP list.
102+
///
103+
/// @tparam InputIterator Type of the input iterator.
104+
/// @param begin Begin iterator.
105+
/// @param end End iterator.
106+
/// @return Bytes of the RLP list.
107+
template <typename InputIterator>
108+
inline bytes internal::encode_container(InputIterator begin, InputIterator end)
109+
{
110+
bytes content;
111+
for (auto it = begin; it != end; ++it)
112+
content += encode(*it);
113+
return wrap_list(content);
114+
}
115+
} // namespace evmone::rlp

test/unittests/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ add_executable(evmone-unittests
2727
evmone_test.cpp
2828
execution_state_test.cpp
2929
instructions_test.cpp
30+
state_rlp_test.cpp
3031
tracing_test.cpp
3132
utils_test.cpp
3233
)
33-
target_link_libraries(evmone-unittests PRIVATE evmone testutils evmc::instructions GTest::gtest GTest::gtest_main)
34+
target_link_libraries(evmone-unittests PRIVATE evmone evmone::state testutils evmc::instructions GTest::gtest GTest::gtest_main)
3435
target_include_directories(evmone-unittests PRIVATE ${evmone_private_include_dir})
3536

3637
gtest_discover_tests(evmone-unittests TEST_PREFIX ${PROJECT_NAME}/unittests/)

test/unittests/state_rlp_test.cpp

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2022 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include <gtest/gtest.h>
6+
#include <test/state/hash_utils.hpp>
7+
#include <test/state/rlp.hpp>
8+
#include <test/utils/utils.hpp>
9+
10+
using namespace evmone;
11+
using namespace evmc::literals;
12+
using namespace intx;
13+
14+
static constexpr auto emptyBytesHash =
15+
0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32;
16+
17+
static constexpr auto emptyMPTHash =
18+
0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421_bytes32;
19+
20+
TEST(state_rlp, empty_bytes_hash)
21+
{
22+
EXPECT_EQ(keccak256({}), emptyBytesHash);
23+
}
24+
25+
TEST(state_rlp, empty_mpt_hash)
26+
{
27+
const auto rlp_null = rlp::encode(0);
28+
EXPECT_EQ(rlp_null, bytes{0x80});
29+
EXPECT_EQ(keccak256(rlp_null), emptyMPTHash);
30+
}
31+
32+
TEST(state_rlp, encode_string_short)
33+
{
34+
EXPECT_EQ(rlp::encode(0x01), "01"_hex);
35+
EXPECT_EQ(rlp::encode(0x31), "31"_hex);
36+
EXPECT_EQ(rlp::encode(0x7f), "7f"_hex);
37+
}
38+
39+
TEST(state_rlp, encode_string_long)
40+
{
41+
const auto buffer = std::make_unique<uint8_t[]>(0xffffff);
42+
43+
const auto r1 = rlp::encode({buffer.get(), 0xaabb});
44+
EXPECT_EQ(r1.size(), 0xaabb + 3);
45+
EXPECT_EQ(hex({r1.data(), 10}), "b9aabb00000000000000");
46+
47+
const auto r2 = rlp::encode({buffer.get(), 0xffff});
48+
EXPECT_EQ(r2.size(), 0xffff + 3);
49+
EXPECT_EQ(hex({r2.data(), 10}), "b9ffff00000000000000");
50+
51+
const auto r3 = rlp::encode({buffer.get(), 0xaabbcc});
52+
EXPECT_EQ(r3.size(), 0xaabbcc + 4);
53+
EXPECT_EQ(hex({r3.data(), 10}), "baaabbcc000000000000");
54+
55+
const auto r4 = rlp::encode({buffer.get(), 0xffffff});
56+
EXPECT_EQ(r4.size(), 0xffffff + 4);
57+
EXPECT_EQ(hex({r4.data(), 10}), "baffffff000000000000");
58+
}
59+
60+
TEST(state_rlp, encode_c_array)
61+
{
62+
uint64_t a[]{1, 2, 3};
63+
EXPECT_EQ(hex(rlp::encode(a)), "c3010203");
64+
}
65+
66+
TEST(state_rlp, encode_vector)
67+
{
68+
const auto x = 0xe1e2e3e4e5e6e7d0d1d2d3d4d5d6d7c0c1c2c3c4c5c6c7b0b1b2b3b4b5b6b7_u256;
69+
EXPECT_EQ(
70+
rlp::encode(x), "9fe1e2e3e4e5e6e7d0d1d2d3d4d5d6d7c0c1c2c3c4c5c6c7b0b1b2b3b4b5b6b7"_hex);
71+
std::vector<uint256> v(0xffffff / 32, x);
72+
const auto r = rlp::encode(v);
73+
EXPECT_EQ(r.size(), v.size() * 32 + 4);
74+
}
75+
76+
TEST(state_rlp, encode_account_with_balance)
77+
{
78+
const auto expected =
79+
"f8 44"
80+
"80"
81+
"01"
82+
"a0 56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
83+
"a0 c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"_hex;
84+
85+
const auto r = rlp::encode_tuple(uint64_t{0}, 1_u256, emptyMPTHash, emptyBytesHash);
86+
EXPECT_EQ(r, expected);
87+
}
88+
89+
TEST(state_rlp, encode_storage_value)
90+
{
91+
const auto value = 0x00000000000000000000000000000000000000000000000000000000000001ff_bytes32;
92+
const auto xvalue = rlp::encode(rlp::trim(value));
93+
EXPECT_EQ(xvalue, "8201ff"_hex);
94+
}
95+
96+
TEST(state_rlp, encode_mpt_node)
97+
{
98+
const auto path = "2041"_hex;
99+
const auto value = "765f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f31"_hex;
100+
const auto node = rlp::encode_tuple(path, value);
101+
EXPECT_EQ(node, "e18220419d765f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f31"_hex);
102+
}
103+
104+
struct CustomStruct
105+
{
106+
uint64_t a;
107+
bytes b;
108+
};
109+
110+
inline bytes rlp_encode(const CustomStruct& t)
111+
{
112+
return rlp::encode_tuple(t.a, t.b);
113+
}
114+
115+
TEST(state_rlp, encode_custom_struct)
116+
{
117+
const CustomStruct t{1, {0x02, 0x03}};
118+
EXPECT_EQ(rlp::encode(t), "c4 01 820203"_hex);
119+
}
120+
121+
TEST(state_rlp, encode_custom_struct_list)
122+
{
123+
std::vector<CustomStruct> v{{1, {0x02, 0x03}}, {4, {0x05, 0x06}}};
124+
EXPECT_EQ(rlp::encode(v), "ca c401820203 c404820506"_hex);
125+
}

0 commit comments

Comments
 (0)