Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions test/state/rlp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ inline bytes encode_tuple(const Types&... elements)
return internal::wrap_list((encode(elements) + ...));
}

/// Encodes a pair of values as RPL list.
template <typename T1, typename T2>
inline bytes encode(const std::pair<T1, T2>& p)
{
return encode_tuple(p.first, p.second);
}

/// Encodes the container as RLP list.
///
/// @tparam InputIterator Type of the input iterator.
Expand Down
15 changes: 13 additions & 2 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,30 @@ std::variant<TransactionReceipt, std::error_code> transition(
return rlp::encode_tuple(tx.nonce, tx.max_gas_price, static_cast<uint64_t>(tx.gas_limit),
tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data, tx.v, tx.r, tx.s);
}
else if (tx.kind == Transaction::Kind::eip2930)
{
if (tx.v > 1)
throw std::invalid_argument("`v` value for eip2930 transaction must be 0 or 1");
// tx_type +
// rlp [nonce, gas_price, gas_limit, to, value, data, access_list, v, r, s];
return bytes{0x01} + // Transaction type (eip2930 type == 1)
rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_gas_price,
static_cast<uint64_t>(tx.gas_limit),
tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data,
tx.access_list, static_cast<bool>(tx.v), tx.r, tx.s);
}
else
{
if (tx.v > 1)
throw std::invalid_argument("`v` value for eip1559 transaction must be 0 or 1");
// TODO: Implement AccessList encoding
// tx_type +
// rlp [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value,
// data, access_list, sig_parity, r, s];
return bytes{0x02} + // Transaction type (eip1559 type == 2)
rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_priority_gas_price, tx.max_gas_price,
static_cast<uint64_t>(tx.gas_limit),
tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data,
std::vector<uint8_t>(), static_cast<bool>(tx.v), tx.r, tx.s);
tx.access_list, static_cast<bool>(tx.v), tx.r, tx.s);
}
}

Expand Down
7 changes: 4 additions & 3 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,11 @@ using AccessList = std::vector<std::pair<address, std::vector<bytes32>>>;

struct Transaction
{
enum class Kind
enum class Kind : uint8_t
{
legacy,
eip1559
legacy = 0,
eip2930 = 1, ///< Transaction with access list https://eips.ethereum.org/EIPS/eip-2930
eip1559 = 2 ///< EIP1559 transaction https://eips.ethereum.org/EIPS/eip-1559
};

Kind kind = Kind::legacy;
Expand Down
15 changes: 14 additions & 1 deletion test/statetest/statetest_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "../utils/stdx/utility.hpp"
#include "statetest.hpp"
#include <evmone/eof.hpp>
#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -249,7 +250,7 @@ static void from_json_tx_common(const json::json& j, state::Transaction& o)
if (j.contains("maxFeePerGas") || j.contains("maxPriorityFeePerGas"))
{
throw std::invalid_argument(
"Misformatted transaction -- contains both legacy and 1559 fees");
"invalid transaction: contains both legacy and EIP-1559 fees");
}
}
else
Expand All @@ -265,12 +266,24 @@ state::Transaction from_json<state::Transaction>(const json::json& j)
{
state::Transaction o;
from_json_tx_common(j, o);
if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this optional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In generated state tests it's not present but it looks that state test loader does not use this function. I make is mandatory and run test generation and state tests once again.

Copy link
Member Author

@rodiazet rodiazet Apr 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When generating state tests using retesteth txs.json does not contain chainId. Probably because it's passed via program param.

Running tests for config 'Ethereum GO on StateTool' 2
Test Case "stCallCodes": (1 of 67)
Finishing retesteth run
*** Total Tests Run: 0

Error: [json.exception.out_of_range.403] key 'chainId' not found
 (GeneralStateTests/stCallCodes/callcodecallcall_100, fork: Berlin, TrInfo: d: 0, g: 0, v: 0)
Error: ERROR OCCURED FILLING TESTS: [json.exception.out_of_range.403] key 'chainId' not found
 (GeneralStateTests/stCallCodes/callcodecallcall_100, fork: Berlin, TrInfo: d: 0, g: 0, v: 0)

--------
TestOutputHelper detected 2 errors during test execution!
/Users/rodia/projects/retesteth/retesteth/TestOutputHelper.cpp:226: error: in "GeneralStateTests/stCallCodes":

txs.json

[
    {
        "input" : "0x",
        "gas" : "0x2dc6c0",
        "gasPrice" : "0xa",
        "nonce" : "0x0",
        "to" : "0x1000000000000000000000000000000000000000",
        "value" : "0x0",
        "v" : "0x1b",
        "r" : "0x7763c200d265ba0050627c8c7b4c19bf118b3581a3de22dbcebdfcb6e8565790",
        "s" : "0x514908f638ddc4b1402230e34692f43bb92713ff2cad3627814cf6fb19ac2e43",
        "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
        "hash" : "0x62b5ffbdaec4813617bf2aca28840d88d48b2d3fce4ec95d0271f3d8c547852d",
        "sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"
    }
]% 

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with whatever. I was just curious.

o.chain_id = from_json<uint8_t>(*chain_id_it);
o.data = from_json<bytes>(j.at("input"));
o.gas_limit = from_json<int64_t>(j.at("gas"));
o.value = from_json<intx::uint256>(j.at("value"));

if (const auto ac_it = j.find("accessList"); ac_it != j.end())
{
o.access_list = from_json<state::AccessList>(*ac_it);
if (o.kind == state::Transaction::Kind::legacy) // Upgrade tx type if tx has "accessList"
o.kind = state::Transaction::Kind::eip2930;
}

if (const auto type_it = j.find("type"); type_it != j.end())
{
if (stdx::to_underlying(o.kind) != from_json<uint8_t>(*type_it))
throw std::invalid_argument("wrong transaction type");
}

o.nonce = from_json<uint64_t>(j.at("nonce"));
o.r = from_json<intx::uint256>(j.at("r"));
Expand Down
78 changes: 76 additions & 2 deletions test/unittests/state_rlp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <test/state/hash_utils.hpp>
#include <test/state/rlp.hpp>
#include <test/state/state.hpp>
Expand All @@ -12,6 +12,7 @@
using namespace evmone;
using namespace evmc::literals;
using namespace intx;
using namespace testing;

static constexpr auto emptyBytesHash =
0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32;
Expand Down Expand Up @@ -375,5 +376,78 @@ TEST(state_rlp, tx_to_rlp_eip1559_invalid_v_value)
tx.v = 2;
tx.chain_id = 1;

EXPECT_THROW(rlp::encode(tx), std::invalid_argument);
EXPECT_THAT([tx]() { rlp::encode(tx); },
ThrowsMessage<std::invalid_argument>("`v` value for eip1559 transaction must be 0 or 1"));
}

TEST(state_rlp, tx_to_rlp_eip2930_invalid_v_value)
{
state::Transaction tx{};
tx.kind = evmone::state::Transaction::Kind::eip2930;
tx.data = ""_hex;
tx.gas_limit = 1;
tx.max_gas_price = 1;
tx.max_priority_gas_price = 1;
tx.sender = 0x0000000000000000000000000000000000000000_address;
tx.to = 0x0000000000000000000000000000000000000000_address;
tx.value = 0;
tx.access_list = {};
tx.nonce = 47;
tx.r = 0x0000000000000000000000000000000000000000000000000000000000000000_u256;
tx.s = 0x0000000000000000000000000000000000000000000000000000000000000000_u256;
tx.v = 2;
tx.chain_id = 1;

EXPECT_THAT([tx]() { rlp::encode(tx); },
ThrowsMessage<std::invalid_argument>("`v` value for eip2930 transaction must be 0 or 1"));
}

TEST(state_rlp, tx_to_rlp_eip1559_with_non_empty_access_list)
{
state::Transaction tx{};
tx.kind = evmone::state::Transaction::Kind::eip1559;
tx.data = "00"_hex;
tx.gas_limit = 0x3d0900;
tx.max_gas_price = 0x7d0;
tx.max_priority_gas_price = 0xa;
tx.sender = 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b_address;
tx.to = 0xcccccccccccccccccccccccccccccccccccccccc_address;
tx.value = 0;
tx.access_list = {{0xcccccccccccccccccccccccccccccccccccccccc_address,
{0x0000000000000000000000000000000000000000000000000000000000000000_bytes32,
0x0000000000000000000000000000000000000000000000000000000000000001_bytes32}}};
tx.nonce = 1;
tx.r = 0xd671815898b8dd34321adbba4cb6a57baa7017323c26946f3719b00e70c755c2_u256;
tx.s = 0x3528b9efe3be57ea65a933d1e6bbf3b7d0c78830138883c1201e0c641fee6464_u256;
tx.v = 0;
tx.chain_id = 1;

EXPECT_EQ(keccak256(rlp::encode(tx)),
0xfb18421827800adcf465688e303cc9863045fdb96971473a114677916a3a08a4_bytes32);
}

TEST(state_rlp, tx_to_rlp_eip2930_with_non_empty_access_list)
{
// https://etherscan.io/tx/0xf076e75aa935552e20e5d9fd4d1dda4ff33399ff3d6ac22843ae646f82c385d4

state::Transaction tx{};
tx.kind = evmone::state::Transaction::Kind::eip2930;
tx.data =
"0x095ea7b3000000000000000000000000f17d23136b4fead139f54fb766c8795faae09660ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"_hex;
tx.gas_limit = 51253;
tx.max_gas_price = 15650965396;
tx.max_priority_gas_price = 15650965396;
tx.sender = 0xcb0b99284784d9e400b1020b01fc40ff193d3540_address;
tx.to = 0x9232a548dd9e81bac65500b5e0d918f8ba93675c_address;
tx.value = 0;
tx.access_list = {{0x9232a548dd9e81bac65500b5e0d918f8ba93675c_address,
{0x8e947fe742892ee6fffe7cfc013acac35d33a3892c58597344bed88b21eb1d2f_bytes32}}};
tx.nonce = 62;
tx.r = 0x2cfaa5ffa42172bfa9f83207a257c53ba3a106844ee58e9131466f655ecc11e9_u256;
tx.s = 0x419366dadd905a16cd433f2953f9ed976560822bb2611ac192b939f7b9c2a98c_u256;
tx.v = 1;
tx.chain_id = 1;

EXPECT_EQ(keccak256(rlp::encode(tx)),
0xf076e75aa935552e20e5d9fd4d1dda4ff33399ff3d6ac22843ae646f82c385d4_bytes32);
}
119 changes: 116 additions & 3 deletions test/unittests/statetest_loader_tx_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <intx/intx.hpp>
#include <test/statetest/statetest.hpp>

using namespace evmone;
using namespace intx;
using namespace testing;

// TODO: Add specific test of loading nonce, chainId, r, s, v

Expand All @@ -16,6 +17,7 @@ TEST(statetest_loader, tx_create_legacy)
constexpr std::string_view input = R"({
"input": "b0b1",
"gas": "0x9091",
"chainId": "0x5",
"value": "0xe0e1",
"sender": "a0a1",
"to": "",
Expand All @@ -30,6 +32,7 @@ TEST(statetest_loader, tx_create_legacy)
EXPECT_EQ(tx.kind, state::Transaction::Kind::legacy);
EXPECT_EQ(tx.data, (bytes{0xb0, 0xb1}));
EXPECT_EQ(tx.gas_limit, 0x9091);
EXPECT_EQ(tx.chain_id, 5);
EXPECT_EQ(tx.value, 0xe0e1);
EXPECT_EQ(tx.sender, 0xa0a1_address);
EXPECT_FALSE(tx.to.has_value());
Expand Down Expand Up @@ -63,6 +66,7 @@ TEST(statetest_loader, tx_eip1559)
EXPECT_EQ(tx.kind, state::Transaction::Kind::eip1559);
EXPECT_EQ(tx.data, (bytes{0xb0, 0xb1}));
EXPECT_EQ(tx.gas_limit, 0x9091);
EXPECT_EQ(tx.chain_id, 0);
EXPECT_EQ(tx.value, 0xe0e1);
EXPECT_EQ(tx.sender, 0xa0a1_address);
EXPECT_EQ(tx.to, 0xc0c1_address);
Expand Down Expand Up @@ -133,8 +137,117 @@ TEST(statetest_loader, tx_confusing)
"v": "1"
})";

EXPECT_THROW(
test::from_json<state::Transaction>(json::json::parse(input)), std::invalid_argument);
EXPECT_THAT([&] { test::from_json<state::Transaction>(json::json::parse(input)); },
ThrowsMessage<std::invalid_argument>(
"invalid transaction: contains both legacy and EIP-1559 fees"));
}

TEST(statetest_loader, tx_type_1)
{
constexpr std::string_view input = R"({
"input": "",
"gas": "0",
"type": "1",
"value": "0",
"sender": "",
"to": "",
"gasPrice": "0",
"accessList": [
{"address": "ac01", "storageKeys": []},
{"address": "ac02", "storageKeys": ["fe", "00"]}
],
"nonce": "0",
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222",
"v": "1"
})";

const auto tx = test::from_json<state::Transaction>(json::json::parse(input));
EXPECT_EQ(tx.kind, state::Transaction::Kind::eip2930);
EXPECT_TRUE(tx.data.empty());
EXPECT_EQ(tx.gas_limit, 0);
EXPECT_EQ(tx.value, 0);
EXPECT_EQ(tx.sender, 0x00_address);
EXPECT_FALSE(tx.to.has_value());
EXPECT_EQ(tx.max_gas_price, 0);
EXPECT_EQ(tx.max_priority_gas_price, 0);
ASSERT_EQ(tx.access_list.size(), 2);
EXPECT_EQ(tx.access_list[0].first, 0xac01_address);
EXPECT_EQ(tx.access_list[0].second.size(), 0);
EXPECT_EQ(tx.access_list[1].first, 0xac02_address);
EXPECT_EQ(tx.access_list[1].second, (std::vector{0xfe_bytes32, 0x00_bytes32}));
EXPECT_EQ(tx.nonce, 0);
EXPECT_EQ(tx.r, 0x1111111111111111111111111111111111111111111111111111111111111111_u256);
EXPECT_EQ(tx.s, 0x2222222222222222222222222222222222222222222222222222222222222222_u256);
EXPECT_EQ(tx.v, 1);
}

TEST(statetest_loader, invalid_tx_type)
{
{
constexpr std::string_view input = R"({
"input": "",
"gas": "0",
"type": "2",
"value": "0",
"sender": "",
"to": "",
"gasPrice": "0",
"accessList": [
{"address": "ac01", "storageKeys": []},
{"address": "ac02", "storageKeys": ["fe", "00"]}
],
"nonce": "0",
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222",
"v": "1"
})";

EXPECT_THAT([&] { test::from_json<state::Transaction>(json::json::parse(input)); },
ThrowsMessage<std::invalid_argument>("wrong transaction type"));
}
{
constexpr std::string_view input = R"({
"input": "",
"gas": "0",
"type": "1",
"value": "0",
"sender": "",
"to": "",
"gasPrice": "0",
"nonce": "0",
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222",
"v": "1"
})";

EXPECT_THAT([&] { test::from_json<state::Transaction>(json::json::parse(input)); },
ThrowsMessage<std::invalid_argument>("wrong transaction type"));
}

{
constexpr std::string_view input = R"({
"input": "",
"gas": "0",
"type": "1",
"value": "0",
"sender": "",
"to": "",
"maxFeePerGas": "0",
"maxPriorityFeePerGas": "0",
"accessList": [
{"address": "ac01", "storageKeys": []},
{"address": "ac02", "storageKeys": ["fe", "00"]}
],
"nonce": "0",
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222",
"v": "1"
})";

EXPECT_THAT([&] { test::from_json<state::Transaction>(json::json::parse(input)); },
ThrowsMessage<std::invalid_argument>("wrong transaction type"));
}
}

namespace evmone::test
Expand Down