diff --git a/test/unittests/statetest_loader_tx_test.cpp b/test/unittests/statetest_loader_tx_test.cpp index efabfd3846..0dc739fdf8 100644 --- a/test/unittests/statetest_loader_tx_test.cpp +++ b/test/unittests/statetest_loader_tx_test.cpp @@ -74,6 +74,27 @@ TEST(statetest_loader, tx_create_legacy) EXPECT_EQ(tx.v, 1); } +TEST(statetest_loader, tx_max_chain_id) +{ + // The maximum representable `chainId` (uint64 max = 0xffffffffffffffff) must be + // loaded without overflow or truncation. + constexpr std::string_view input = R"({ + "input": "b0b1", + "gas": "0x9091", + "chainId": "0xffffffffffffffff", + "value": "0xe0e1", + "sender": "a0a1", + "gasPrice": "0x7071", + "nonce": "0", + "r": "0x1111111111111111111111111111111111111111111111111111111111111111", + "s": "0x2222222222222222222222222222222222222222222222222222222222222222", + "v": "1" + })"; + + const auto tx = test::from_json(json::json::parse(input)); + EXPECT_EQ(tx.chain_id, std::numeric_limits::max()); +} + TEST(statetest_loader, tx_eip1559) { constexpr std::string_view input = R"({ diff --git a/test/unittests/tooling_t8n_test.cpp b/test/unittests/tooling_t8n_test.cpp index 129f068e57..b45453716f 100644 --- a/test/unittests/tooling_t8n_test.cpp +++ b/test/unittests/tooling_t8n_test.cpp @@ -210,3 +210,41 @@ TEST(tooling_t8n, mismatched_tx_hash_throws) EXPECT_THROW(tooling::t8n(vm, args), std::logic_error); } + +TEST(tooling_t8n, max_chain_id) +{ + evmc::VM vm{evmc_create_evmone()}; + + // The maximum `chainId` (uint64 max = 0xffffffffffffffff) must be parsed and + // executed without overflow; regression test for `chainId` being loaded as + // `uint8_t`, which threw `from_json: value > 0xFF`. + static constexpr auto TX_MAX_CHAIN_ID = R"([{ + "to": null, + "input": "0x60015ff3", + "gas": "0x186a0", + "nonce": "0x0", + "value": "0x0", + "gasPrice": "0x32", + "chainId": "0xffffffffffffffff", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "v": "0x1b", + "r": "0x468a915f087692bb9be503831a3dfef2cf9c8dee26deb40ff2ec99e8d22665ae", + "s": "0x5cedae0810c3851ecd1004bfdbfe6ddc7753c2d665993bb01ce75af7857b13dc" + }])"; + + std::istringstream env{ENV_JSON}; + std::istringstream alloc{ALLOC_JSON}; + std::istringstream txs{TX_MAX_CHAIN_ID}; + std::ostringstream out_result; + + tooling::T8NArgs args; + args.rev = EVMC_SHANGHAI; + args.chain_id = std::numeric_limits::max(); + args.alloc = &alloc; + args.env = &env; + args.txs = &txs; + args.out_result = &out_result; + + EXPECT_NO_THROW(tooling::t8n(vm, args)); + EXPECT_THAT(out_result.str(), HasSubstr("\"transactionHash\"")); +} diff --git a/test/utils/statetest_loader.cpp b/test/utils/statetest_loader.cpp index b503f8d3ea..6ed2302696 100644 --- a/test/utils/statetest_loader.cpp +++ b/test/utils/statetest_loader.cpp @@ -375,7 +375,7 @@ static void from_json_tx_common(const json::json& j, state::Transaction& o) o.nonce = from_json(j.at("nonce")); if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end()) - o.chain_id = from_json(*chain_id_it); + o.chain_id = from_json(*chain_id_it); else o.chain_id = 1;