Skip to content

Commit a5dde60

Browse files
committed
state: Add RLP encoding implementation
1 parent 7450adf commit a5dde60

File tree

4 files changed

+223
-1
lines changed

4 files changed

+223
-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

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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<0xc0, 0xf7>(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 = 0x80;
62+
if (data.size() == 1 && data[0] < short_base)
63+
return {data[0]};
64+
65+
auto r = internal::encode_length<short_base, 0xb7>(data.size());
66+
r += data;
67+
return r;
68+
}
69+
70+
inline bytes encode(uint64_t x)
71+
{
72+
uint8_t b[sizeof(x)];
73+
intx::be::store(b, x);
74+
return encode(trim({b, sizeof(b)}));
75+
}
76+
77+
inline bytes encode(const intx::uint256& x)
78+
{
79+
uint8_t b[sizeof(x)];
80+
intx::be::store(b, x);
81+
return encode(trim({b, sizeof(b)}));
82+
}
83+
84+
template <typename T>
85+
inline bytes encode(const std::vector<T>& v)
86+
{
87+
return internal::encode_container(v.begin(), v.end());
88+
}
89+
90+
template <typename T, size_t N>
91+
inline bytes encode(const T (&v)[N])
92+
{
93+
return internal::encode_container(std::begin(v), std::end(v));
94+
}
95+
96+
/// Encodes the fixed-size collection of heterogeneous values as RLP list.
97+
template <typename... Types>
98+
inline bytes tuple(const Types&... elements)
99+
{
100+
return internal::wrap_list((encode(elements) + ...));
101+
}
102+
103+
/// Encodes the container as RLP list.
104+
///
105+
/// @tparam InputIterator Type of the input iterator.
106+
/// @param begin Begin iterator.
107+
/// @param end End iterator.
108+
/// @return Bytes of the RLP list.
109+
template <typename InputIterator>
110+
inline bytes internal::encode_container(InputIterator begin, InputIterator end)
111+
{
112+
bytes content;
113+
for (auto it = begin; it != end; ++it)
114+
content += encode(*it);
115+
return wrap_list(content);
116+
}
117+
} // 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

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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::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 bytes path{0x20, 0x41};
99+
const bytes value{'v', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
100+
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '1'};
101+
const auto node = rlp::tuple(path, value);
102+
EXPECT_EQ(node, "e18220419d765f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f31"_hex);
103+
}

0 commit comments

Comments
 (0)