Skip to content

Commit f200c0d

Browse files
authored
test: Load transaction chainId as uint64 (#1570)
from_json_tx_common parsed the transaction "chainId" with from_json<uint8_t>, throwing `from_json<uint8_t>: value > 0xFF` for any chain ID above 255. This crashed `evmone t8n` on common networks such as Sepolia (11155111) and Arbitrum One (42161): the transaction is parsed before t8n overrides chain_id from its args, so the parse failed first and no output was produced. Parse it as uint64_t, matching the Transaction::chain_id field type. Fixes #1569.
1 parent 1ea7547 commit f200c0d

3 files changed

Lines changed: 60 additions & 1 deletion

File tree

test/unittests/statetest_loader_tx_test.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,27 @@ TEST(statetest_loader, tx_create_legacy)
7474
EXPECT_EQ(tx.v, 1);
7575
}
7676

77+
TEST(statetest_loader, tx_max_chain_id)
78+
{
79+
// The maximum representable `chainId` (uint64 max = 0xffffffffffffffff) must be
80+
// loaded without overflow or truncation.
81+
constexpr std::string_view input = R"({
82+
"input": "b0b1",
83+
"gas": "0x9091",
84+
"chainId": "0xffffffffffffffff",
85+
"value": "0xe0e1",
86+
"sender": "a0a1",
87+
"gasPrice": "0x7071",
88+
"nonce": "0",
89+
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
90+
"s": "0x2222222222222222222222222222222222222222222222222222222222222222",
91+
"v": "1"
92+
})";
93+
94+
const auto tx = test::from_json<state::Transaction>(json::json::parse(input));
95+
EXPECT_EQ(tx.chain_id, std::numeric_limits<uint64_t>::max());
96+
}
97+
7798
TEST(statetest_loader, tx_eip1559)
7899
{
79100
constexpr std::string_view input = R"({

test/unittests/tooling_t8n_test.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,41 @@ TEST(tooling_t8n, mismatched_tx_hash_throws)
210210

211211
EXPECT_THROW(tooling::t8n(vm, args), std::logic_error);
212212
}
213+
214+
TEST(tooling_t8n, max_chain_id)
215+
{
216+
evmc::VM vm{evmc_create_evmone()};
217+
218+
// The maximum `chainId` (uint64 max = 0xffffffffffffffff) must be parsed and
219+
// executed without overflow; regression test for `chainId` being loaded as
220+
// `uint8_t`, which threw `from_json<uint8_t>: value > 0xFF`.
221+
static constexpr auto TX_MAX_CHAIN_ID = R"([{
222+
"to": null,
223+
"input": "0x60015ff3",
224+
"gas": "0x186a0",
225+
"nonce": "0x0",
226+
"value": "0x0",
227+
"gasPrice": "0x32",
228+
"chainId": "0xffffffffffffffff",
229+
"sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
230+
"v": "0x1b",
231+
"r": "0x468a915f087692bb9be503831a3dfef2cf9c8dee26deb40ff2ec99e8d22665ae",
232+
"s": "0x5cedae0810c3851ecd1004bfdbfe6ddc7753c2d665993bb01ce75af7857b13dc"
233+
}])";
234+
235+
std::istringstream env{ENV_JSON};
236+
std::istringstream alloc{ALLOC_JSON};
237+
std::istringstream txs{TX_MAX_CHAIN_ID};
238+
std::ostringstream out_result;
239+
240+
tooling::T8NArgs args;
241+
args.rev = EVMC_SHANGHAI;
242+
args.chain_id = std::numeric_limits<uint64_t>::max();
243+
args.alloc = &alloc;
244+
args.env = &env;
245+
args.txs = &txs;
246+
args.out_result = &out_result;
247+
248+
EXPECT_NO_THROW(tooling::t8n(vm, args));
249+
EXPECT_THAT(out_result.str(), HasSubstr("\"transactionHash\""));
250+
}

test/utils/statetest_loader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ static void from_json_tx_common(const json::json& j, state::Transaction& o)
375375
o.nonce = from_json<uint64_t>(j.at("nonce"));
376376

377377
if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end())
378-
o.chain_id = from_json<uint8_t>(*chain_id_it);
378+
o.chain_id = from_json<uint64_t>(*chain_id_it);
379379
else
380380
o.chain_id = 1;
381381

0 commit comments

Comments
 (0)