TLA Line data Source code
1 : // evmone: Fast Ethereum Virtual Machine implementation
2 : // Copyright 2022 The evmone Authors.
3 : // SPDX-License-Identifier: Apache-2.0
4 :
5 : #include "../utils/stdx/utility.hpp"
6 : #include "../utils/utils.hpp"
7 : #include "statetest.hpp"
8 : #include <evmone/eof.hpp>
9 : #include <nlohmann/json.hpp>
10 :
11 : namespace evmone::test
12 : {
13 : namespace json = nlohmann;
14 : using evmc::from_hex;
15 :
16 : template <>
17 CBC 55 : uint8_t from_json<uint8_t>(const json::json& j)
18 : {
19 55 : const auto ret = std::stoul(j.get<std::string>(), nullptr, 16);
20 55 : if (ret > std::numeric_limits<uint8_t>::max())
21 UBC 0 : throw std::out_of_range("from_json<uint8_t>: value > 0xFF");
22 :
23 CBC 55 : return static_cast<uint8_t>(ret);
24 : }
25 :
26 : template <typename T>
27 848 : static std::optional<T> integer_from_json(const json::json& j)
28 : {
29 848 : if (j.is_number_integer())
30 UBC 0 : return j.get<T>();
31 :
32 CBC 848 : if (!j.is_string())
33 UBC 0 : return {};
34 :
35 CBC 848 : const auto s = j.get<std::string>();
36 :
37 : // Always load integers as unsigned and cast to the required type.
38 : // This will work for cases where a test case uses uint64 timestamps while we use int64.
39 : // TODO: Change timestamp type to uint64.
40 848 : size_t num_processed = 0;
41 848 : const auto v = static_cast<T>(std::stoull(s, &num_processed, 0));
42 848 : if (num_processed == 0 || num_processed != s.size())
43 UBC 0 : return {};
44 CBC 848 : return v;
45 848 : }
46 :
47 : template <>
48 318 : int64_t from_json<int64_t>(const json::json& j)
49 : {
50 318 : const auto v = integer_from_json<int64_t>(j);
51 318 : if (!v.has_value())
52 UBC 0 : throw std::invalid_argument("from_json<int64_t>: must be integer or string of integer");
53 CBC 318 : return *v;
54 : }
55 :
56 : template <>
57 530 : uint64_t from_json<uint64_t>(const json::json& j)
58 : {
59 530 : const auto v = integer_from_json<uint64_t>(j);
60 530 : if (!v.has_value())
61 UBC 0 : throw std::invalid_argument("from_json<uint64_t>: must be integer or string of integer");
62 CBC 530 : return *v;
63 : }
64 :
65 : template <>
66 212 : bytes from_json<bytes>(const json::json& j)
67 : {
68 212 : return from_hex(j.get<std::string>()).value();
69 : }
70 :
71 : template <>
72 318 : address from_json<address>(const json::json& j)
73 : {
74 318 : return evmc::from_hex<address>(j.get<std::string>()).value();
75 : }
76 :
77 : template <>
78 672 : hash256 from_json<hash256>(const json::json& j)
79 : {
80 : // Special case to handle "0". Required by exec-spec-tests.
81 : // TODO: Get rid of it.
82 672 : if (j.is_string() && (j == "0" || j == "0x0"))
83 UBC 0 : return 0x00_bytes32;
84 : else
85 CBC 672 : return evmc::from_hex<hash256>(j.get<std::string>()).value();
86 : }
87 :
88 : template <>
89 372 : intx::uint256 from_json<intx::uint256>(const json::json& j)
90 : {
91 372 : const auto s = j.get<std::string>();
92 372 : if (s.starts_with("0x:bigint "))
93 UBC 0 : return std::numeric_limits<intx::uint256>::max(); // Fake it
94 CBC 372 : return intx::from_string<intx::uint256>(s);
95 372 : }
96 :
97 : template <>
98 1 : state::AccessList from_json<state::AccessList>(const json::json& j)
99 : {
100 1 : state::AccessList o;
101 1 : for (const auto& a : j)
102 : {
103 UBC 0 : std::vector<bytes32> storage_access_list;
104 0 : for (const auto& storage_key : a.at("storageKeys"))
105 0 : storage_access_list.emplace_back(from_json<bytes32>(storage_key));
106 0 : o.emplace_back(from_json<address>(a.at("address")), std::move(storage_access_list));
107 0 : }
108 CBC 1 : return o;
109 UBC 0 : }
110 :
111 : // Based on calculateEIP1559BaseFee from ethereum/retesteth
112 CBC 53 : inline uint64_t calculate_current_base_fee_eip1559(
113 : uint64_t parent_gas_used, uint64_t parent_gas_limit, uint64_t parent_base_fee)
114 : {
115 : // TODO: Make sure that 64-bit precision is good enough.
116 : static constexpr auto BASE_FEE_MAX_CHANGE_DENOMINATOR = 8;
117 : static constexpr auto ELASTICITY_MULTIPLIER = 2;
118 :
119 53 : uint64_t base_fee = 0;
120 :
121 53 : const auto parent_gas_target = parent_gas_limit / ELASTICITY_MULTIPLIER;
122 :
123 53 : if (parent_gas_used == parent_gas_target)
124 UBC 0 : base_fee = parent_base_fee;
125 CBC 53 : else if (parent_gas_used > parent_gas_target)
126 : {
127 UBC 0 : const auto gas_used_delta = parent_gas_used - parent_gas_target;
128 0 : const auto formula =
129 0 : parent_base_fee * gas_used_delta / parent_gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR;
130 0 : const auto base_fee_per_gas_delta = formula > 1 ? formula : 1;
131 0 : base_fee = parent_base_fee + base_fee_per_gas_delta;
132 : }
133 : else
134 : {
135 CBC 53 : const auto gas_used_delta = parent_gas_target - parent_gas_used;
136 :
137 : const auto base_fee_per_gas_delta_u128 =
138 : intx::uint128(parent_base_fee, 0) * intx::uint128(gas_used_delta, 0) /
139 53 : parent_gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR;
140 :
141 53 : const auto base_fee_per_gas_delta = base_fee_per_gas_delta_u128[0];
142 53 : if (parent_base_fee > base_fee_per_gas_delta)
143 53 : base_fee = parent_base_fee - base_fee_per_gas_delta;
144 : else
145 UBC 0 : base_fee = 0;
146 : }
147 CBC 53 : return base_fee;
148 : }
149 :
150 : template <>
151 UBC 0 : state::Withdrawal from_json<state::Withdrawal>(const json::json& j)
152 : {
153 0 : return {from_json<uint64_t>(j.at("index")), from_json<uint64_t>(j.at("validatorIndex")),
154 0 : from_json<evmc::address>(j.at("address")), from_json<uint64_t>(j.at("amount"))};
155 : }
156 :
157 : template <>
158 CBC 53 : state::BlockInfo from_json<state::BlockInfo>(const json::json& j)
159 : {
160 53 : evmc::bytes32 prev_randao;
161 53 : int64_t current_difficulty = 0;
162 53 : int64_t parent_difficulty = 0;
163 53 : const auto prev_randao_it = j.find("currentRandom");
164 53 : const auto current_difficulty_it = j.find("currentDifficulty");
165 53 : const auto parent_difficulty_it = j.find("parentDifficulty");
166 :
167 53 : if (current_difficulty_it != j.end())
168 53 : current_difficulty = from_json<int64_t>(*current_difficulty_it);
169 53 : if (parent_difficulty_it != j.end())
170 UBC 0 : parent_difficulty = from_json<int64_t>(*parent_difficulty_it);
171 :
172 : // When it's not defined init it with difficulty value.
173 CBC 53 : if (prev_randao_it != j.end())
174 53 : prev_randao = from_json<bytes32>(*prev_randao_it);
175 UBC 0 : else if (current_difficulty_it != j.end())
176 0 : prev_randao = from_json<bytes32>(*current_difficulty_it);
177 0 : else if (parent_difficulty_it != j.end())
178 0 : prev_randao = from_json<bytes32>(*parent_difficulty_it);
179 :
180 CBC 53 : hash256 parent_uncle_hash;
181 53 : const auto parent_uncle_hash_it = j.find("parentUncleHash");
182 53 : if (parent_uncle_hash_it != j.end())
183 UBC 0 : parent_uncle_hash = from_json<hash256>(*parent_uncle_hash_it);
184 :
185 CBC 53 : uint64_t base_fee = 0;
186 53 : if (j.contains("currentBaseFee"))
187 UBC 0 : base_fee = from_json<uint64_t>(j.at("currentBaseFee"));
188 CBC 53 : else if (j.contains("parentBaseFee"))
189 : {
190 53 : base_fee = calculate_current_base_fee_eip1559(from_json<uint64_t>(j.at("parentGasUsed")),
191 53 : from_json<uint64_t>(j.at("parentGasLimit")),
192 53 : from_json<uint64_t>(j.at("parentBaseFee")));
193 : }
194 :
195 53 : std::vector<state::Withdrawal> withdrawals;
196 53 : if (const auto withdrawals_it = j.find("withdrawals"); withdrawals_it != j.end())
197 : {
198 53 : for (const auto& withdrawal : *withdrawals_it)
199 UBC 0 : withdrawals.push_back(from_json<state::Withdrawal>(withdrawal));
200 : }
201 :
202 CBC 53 : std::unordered_map<int64_t, hash256> block_hashes;
203 53 : if (const auto block_hashes_it = j.find("blockHashes"); block_hashes_it != j.end())
204 : {
205 106 : for (const auto& [j_num, j_hash] : block_hashes_it->items())
206 106 : block_hashes[from_json<int64_t>(j_num)] = from_json<hash256>(j_hash);
207 : }
208 :
209 53 : std::vector<state::Ommer> ommers;
210 53 : if (const auto ommers_it = j.find("ommers"); ommers_it != j.end())
211 : {
212 UBC 0 : for (const auto& ommer : *ommers_it)
213 : {
214 0 : ommers.push_back(
215 0 : {from_json<evmc::address>(ommer.at("address")), ommer.at("delta").get<uint32_t>()});
216 : }
217 : }
218 :
219 CBC 53 : int64_t parent_timestamp = 0;
220 53 : auto parent_timestamp_it = j.find("parentTimestamp");
221 53 : if (parent_timestamp_it != j.end())
222 UBC 0 : parent_timestamp = from_json<int64_t>(*parent_timestamp_it);
223 :
224 CBC 53 : uint64_t excess_blob_gas = 0;
225 53 : if (const auto it = j.find("parentExcessBlobGas"); it != j.end())
226 : {
227 53 : const auto parent_excess_blob_gas = from_json<uint64_t>(*it);
228 53 : const auto parent_blob_gas_used = from_json<uint64_t>(j.at("parentBlobGasUsed"));
229 : static constexpr uint64_t TARGET_BLOB_GAS_PER_BLOCK = 0x60000;
230 53 : excess_blob_gas =
231 53 : std::max(parent_excess_blob_gas + parent_blob_gas_used, TARGET_BLOB_GAS_PER_BLOCK) -
232 : TARGET_BLOB_GAS_PER_BLOCK;
233 : }
234 UBC 0 : else if (const auto it2 = j.find("currentExcessBlobGas"); it2 != j.end())
235 : {
236 0 : excess_blob_gas = from_json<uint64_t>(*it2);
237 : }
238 :
239 : return state::BlockInfo{
240 CBC 53 : .number = from_json<int64_t>(j.at("currentNumber")),
241 53 : .timestamp = from_json<int64_t>(j.at("currentTimestamp")),
242 : .parent_timestamp = parent_timestamp,
243 53 : .gas_limit = from_json<int64_t>(j.at("currentGasLimit")),
244 53 : .coinbase = from_json<evmc::address>(j.at("currentCoinbase")),
245 : .difficulty = current_difficulty,
246 : .parent_difficulty = parent_difficulty,
247 : .parent_ommers_hash = parent_uncle_hash,
248 : .prev_randao = prev_randao,
249 : .parent_beacon_block_root = {},
250 : .base_fee = base_fee,
251 : .excess_blob_gas = excess_blob_gas,
252 53 : .blob_base_fee = state::compute_blob_gas_price(excess_blob_gas),
253 53 : .ommers = std::move(ommers),
254 53 : .withdrawals = std::move(withdrawals),
255 53 : .known_block_hashes = std::move(block_hashes),
256 318 : };
257 53 : }
258 :
259 : template <>
260 53 : state::State from_json<state::State>(const json::json& j)
261 : {
262 53 : state::State o;
263 212 : for (const auto& [j_addr, j_acc] : j.items())
264 : {
265 318 : auto& acc = o.insert(from_json<address>(j_addr),
266 159 : {.nonce = from_json<uint64_t>(j_acc.at("nonce")),
267 159 : .balance = from_json<intx::uint256>(j_acc.at("balance")),
268 159 : .code = from_json<bytes>(j_acc.at("code"))});
269 :
270 159 : if (const auto storage_it = j_acc.find("storage"); storage_it != j_acc.end())
271 : {
272 442 : for (const auto& [j_key, j_value] : storage_it->items())
273 : {
274 283 : if (const auto value = from_json<bytes32>(j_value); !is_zero(value))
275 283 : acc.storage.insert(
276 566 : {from_json<bytes32>(j_key), {.current = value, .original = value}});
277 159 : }
278 : }
279 53 : }
280 53 : return o;
281 UBC 0 : }
282 :
283 : /// Load common parts of Transaction or TestMultiTransaction.
284 CBC 53 : static void from_json_tx_common(const json::json& j, state::Transaction& o)
285 : {
286 53 : o.sender = from_json<evmc::address>(j.at("sender"));
287 53 : o.nonce = from_json<uint64_t>(j.at("nonce"));
288 :
289 53 : if (const auto to_it = j.find("to"); to_it != j.end() && !to_it->get<std::string>().empty())
290 53 : o.to = from_json<evmc::address>(*to_it);
291 :
292 53 : if (const auto gas_price_it = j.find("gasPrice"); gas_price_it != j.end())
293 : {
294 GBC 52 : o.type = state::Transaction::Type::legacy;
295 52 : o.max_gas_price = from_json<intx::uint256>(*gas_price_it);
296 52 : o.max_priority_gas_price = o.max_gas_price;
297 52 : if (j.contains("maxFeePerGas") || j.contains("maxPriorityFeePerGas"))
298 : {
299 UBC 0 : throw std::invalid_argument(
300 0 : "invalid transaction: contains both legacy and EIP-1559 fees");
301 : }
302 : }
303 : else
304 : {
305 CBC 1 : o.type = state::Transaction::Type::eip1559;
306 1 : o.max_gas_price = from_json<intx::uint256>(j.at("maxFeePerGas"));
307 1 : o.max_priority_gas_price = from_json<intx::uint256>(j.at("maxPriorityFeePerGas"));
308 : }
309 :
310 53 : if (const auto it = j.find("maxFeePerBlobGas"); it != j.end())
311 UBC 0 : o.max_blob_gas_price = from_json<intx::uint256>(*it);
312 :
313 CBC 53 : if (const auto it = j.find("blobVersionedHashes"); it != j.end())
314 : {
315 UBC 0 : o.type = state::Transaction::Type::blob;
316 0 : for (const auto& hash : *it)
317 0 : o.blob_hashes.push_back(from_json<bytes32>(hash));
318 : }
319 CBC 53 : }
320 :
321 : template <>
322 53 : state::Transaction from_json<state::Transaction>(const json::json& j)
323 : {
324 53 : state::Transaction o;
325 53 : from_json_tx_common(j, o);
326 53 : if (const auto chain_id_it = j.find("chainId"); chain_id_it != j.end())
327 1 : o.chain_id = from_json<uint8_t>(*chain_id_it);
328 :
329 53 : if (const auto it = j.find("data"); it != j.end())
330 UBC 0 : o.data = from_json<bytes>(*it);
331 : else
332 CBC 53 : o.data = from_json<bytes>(j.at("input"));
333 :
334 53 : if (const auto it = j.find("gasLimit"); it != j.end())
335 UBC 0 : o.gas_limit = from_json<int64_t>(*it);
336 : else
337 CBC 53 : o.gas_limit = from_json<int64_t>(j.at("gas"));
338 :
339 53 : o.value = from_json<intx::uint256>(j.at("value"));
340 :
341 53 : if (const auto ac_it = j.find("accessList"); ac_it != j.end())
342 : {
343 1 : o.access_list = from_json<state::AccessList>(*ac_it);
344 1 : if (o.type == state::Transaction::Type::legacy) // Upgrade tx type if tx has access list
345 UBC 0 : o.type = state::Transaction::Type::access_list;
346 : }
347 :
348 CBC 53 : if (const auto type_it = j.find("type"); type_it != j.end())
349 : {
350 1 : const auto infered_type = stdx::to_underlying(o.type);
351 1 : const auto type = from_json<uint8_t>(*type_it);
352 1 : if (type != infered_type)
353 UBC 0 : throw std::invalid_argument("wrong transaction type: " + std::to_string(type) +
354 0 : ", expected: " + std::to_string(infered_type));
355 : }
356 :
357 CBC 53 : o.nonce = from_json<uint64_t>(j.at("nonce"));
358 53 : o.r = from_json<intx::uint256>(j.at("r"));
359 53 : o.s = from_json<intx::uint256>(j.at("s"));
360 53 : o.v = from_json<uint8_t>(j.at("v"));
361 :
362 53 : return o;
363 UBC 0 : }
364 :
365 0 : static void from_json(const json::json& j, TestMultiTransaction& o)
366 : {
367 0 : from_json_tx_common(j, o);
368 :
369 0 : for (const auto& j_data : j.at("data"))
370 0 : o.inputs.emplace_back(from_json<bytes>(j_data));
371 :
372 0 : if (const auto ac_it = j.find("accessLists"); ac_it != j.end())
373 : {
374 0 : for (const auto& j_access_list : *ac_it)
375 0 : o.access_lists.emplace_back(from_json<state::AccessList>(j_access_list));
376 0 : if (o.type == state::Transaction::Type::legacy) // Upgrade tx type if tx has access lists
377 0 : o.type = state::Transaction::Type::access_list;
378 : }
379 :
380 0 : for (const auto& j_gas_limit : j.at("gasLimit"))
381 0 : o.gas_limits.emplace_back(from_json<int64_t>(j_gas_limit));
382 :
383 0 : for (const auto& j_value : j.at("value"))
384 0 : o.values.emplace_back(from_json<intx::uint256>(j_value));
385 0 : }
386 :
387 0 : static void from_json(const json::json& j, TestMultiTransaction::Indexes& o)
388 : {
389 0 : o.input = j.at("data").get<size_t>();
390 0 : o.gas_limit = j.at("gas").get<size_t>();
391 0 : o.value = j.at("value").get<size_t>();
392 0 : }
393 :
394 0 : static void from_json(const json::json& j, StateTransitionTest::Case::Expectation& o)
395 : {
396 0 : o.indexes = j.at("indexes").get<TestMultiTransaction::Indexes>();
397 0 : o.state_hash = from_json<hash256>(j.at("hash"));
398 0 : o.logs_hash = from_json<hash256>(j.at("logs"));
399 0 : o.exception = j.contains("expectException");
400 0 : }
401 :
402 0 : static void from_json(const json::json& j, StateTransitionTest& o)
403 : {
404 0 : if (!j.is_object() || j.empty())
405 0 : throw std::invalid_argument{"JSON test must be an object with single key of the test name"};
406 :
407 0 : const auto& j_t = *j.begin(); // Content is in a dict with the test name.
408 :
409 0 : o.pre_state = from_json<state::State>(j_t.at("pre"));
410 :
411 0 : o.multi_tx = j_t.at("transaction").get<TestMultiTransaction>();
412 :
413 0 : o.block = from_json<state::BlockInfo>(j_t.at("env"));
414 :
415 0 : if (const auto info_it = j_t.find("_info"); info_it != j_t.end())
416 : {
417 0 : if (const auto labels_it = info_it->find("labels"); labels_it != info_it->end())
418 : {
419 0 : for (const auto& [j_id, j_label] : labels_it->items())
420 0 : o.input_labels.emplace(from_json<uint64_t>(j_id), j_label);
421 : }
422 : }
423 :
424 0 : for (const auto& [rev_name, expectations] : j_t.at("post").items())
425 : {
426 : // TODO(c++20): Use emplace_back with aggregate initialization.
427 0 : o.cases.push_back({to_rev(rev_name),
428 : expectations.get<std::vector<StateTransitionTest::Case::Expectation>>()});
429 0 : }
430 0 : }
431 :
432 0 : StateTransitionTest load_state_test(std::istream& input)
433 : {
434 0 : return json::json::parse(input).get<StateTransitionTest>();
435 : }
436 :
437 CBC 53 : void validate_state(const state::State& state, evmc_revision rev)
438 : {
439 212 : for (const auto& [addr, acc] : state.get_accounts())
440 : {
441 : // TODO: Check for empty accounts after Paris.
442 : // https://github.com/ethereum/tests/issues/1331
443 159 : if (is_eof_container(acc.code))
444 : {
445 UBC 0 : if (rev >= EVMC_PRAGUE)
446 : {
447 0 : if (const auto result = validate_eof(rev, acc.code);
448 : result != EOFValidationError::success)
449 : {
450 0 : throw std::invalid_argument(
451 0 : "EOF container at " + hex0x(addr) +
452 0 : " is invalid: " + std::string(get_error_message(result)));
453 : }
454 : }
455 : else
456 : {
457 0 : throw std::invalid_argument("unexpected code with EOF prefix at " + hex0x(addr));
458 : }
459 : }
460 :
461 CBC 442 : for (const auto& [key, value] : acc.storage)
462 : {
463 283 : if (is_zero(value.original))
464 : {
465 UBC 0 : throw std::invalid_argument{"account " + hex0x(addr) +
466 0 : " contains invalid zero-value storage entry " +
467 0 : hex0x(key)};
468 : }
469 : }
470 : }
471 CBC 53 : }
472 : } // namespace evmone::test
|