TLA Line data Source code
1 : // evmone: Fast Ethereum Virtual Machine implementation
2 : // Copyright 2023 The evmone Authors.
3 : // SPDX-License-Identifier: Apache-2.0
4 :
5 : #include "../state/errors.hpp"
6 : #include "../state/ethash_difficulty.hpp"
7 : #include "../state/mpt_hash.hpp"
8 : #include "../state/rlp.hpp"
9 : #include "../statetest/statetest.hpp"
10 : #include "../utils/utils.hpp"
11 : #include <evmone/evmone.h>
12 : #include <evmone/version.h>
13 : #include <nlohmann/json.hpp>
14 : #include <filesystem>
15 : #include <fstream>
16 : #include <iostream>
17 : #include <string_view>
18 :
19 : namespace fs = std::filesystem;
20 : namespace json = nlohmann;
21 : using namespace evmone;
22 : using namespace evmone::test;
23 : using namespace std::literals;
24 :
25 CBC 54 : int main(int argc, const char* argv[])
26 : {
27 54 : evmc_revision rev = {};
28 54 : fs::path alloc_file;
29 54 : fs::path env_file;
30 54 : fs::path txs_file;
31 54 : fs::path output_dir;
32 54 : fs::path output_result_file;
33 54 : fs::path output_alloc_file;
34 54 : fs::path output_body_file;
35 54 : std::optional<uint64_t> block_reward;
36 54 : uint64_t chain_id = 0;
37 54 : bool trace = false;
38 :
39 : try
40 : {
41 691 : for (int i = 0; i < argc; ++i)
42 : {
43 638 : const std::string_view arg{argv[i]};
44 :
45 638 : if (arg == "-v")
46 : {
47 1 : std::cout << "evmone-t8n " EVMONE_VERSION "\n";
48 1 : return 0;
49 : }
50 637 : if (arg == "--state.fork" && ++i < argc)
51 53 : rev = to_rev(argv[i]);
52 584 : else if (arg == "--input.alloc" && ++i < argc)
53 53 : alloc_file = argv[i];
54 531 : else if (arg == "--input.env" && ++i < argc)
55 53 : env_file = argv[i];
56 478 : else if (arg == "--input.txs" && ++i < argc)
57 53 : txs_file = argv[i];
58 425 : else if (arg == "--output.basedir" && ++i < argc)
59 53 : output_dir = argv[i];
60 372 : else if (arg == "--output.result" && ++i < argc)
61 53 : output_result_file = argv[i];
62 319 : else if (arg == "--output.alloc" && ++i < argc)
63 53 : output_alloc_file = argv[i];
64 266 : else if (arg == "--state.reward" && ++i < argc && argv[i] != "-1"sv)
65 53 : block_reward = intx::from_string<uint64_t>(argv[i]);
66 213 : else if (arg == "--state.chainid" && ++i < argc)
67 53 : chain_id = intx::from_string<uint64_t>(argv[i]);
68 160 : else if (arg == "--output.body" && ++i < argc)
69 UBC 0 : output_body_file = argv[i];
70 CBC 160 : else if (arg == "--trace")
71 UBC 0 : trace = true;
72 : }
73 :
74 CBC 53 : state::BlockInfo block;
75 53 : state::State state;
76 :
77 53 : if (!alloc_file.empty())
78 : {
79 106 : const auto j = json::json::parse(std::ifstream{alloc_file}, nullptr, false);
80 53 : state = test::from_json<state::State>(j);
81 53 : }
82 53 : if (!env_file.empty())
83 : {
84 106 : const auto j = json::json::parse(std::ifstream{env_file});
85 53 : block = test::from_json<state::BlockInfo>(j);
86 53 : }
87 :
88 53 : json::json j_result;
89 :
90 : // Difficulty was received from upstream. No need to calc
91 : // TODO: Check if it's needed by the blockchain test. If not remove if statement true branch
92 53 : if (block.difficulty != 0)
93 UBC 0 : j_result["currentDifficulty"] = hex0x(block.difficulty);
94 : else
95 : {
96 CBC 53 : const auto current_difficulty = state::calculate_difficulty(block.parent_difficulty,
97 53 : block.parent_ommers_hash != EmptyListHash, block.parent_timestamp, block.timestamp,
98 : block.number, rev);
99 :
100 53 : j_result["currentDifficulty"] = hex0x(current_difficulty);
101 53 : block.difficulty = current_difficulty;
102 :
103 53 : if (rev < EVMC_PARIS) // Override prev_randao with difficulty pre-Merge
104 UBC 0 : block.prev_randao = intx::be::store<bytes32>(intx::uint256{current_difficulty});
105 : }
106 :
107 CBC 53 : j_result["currentBaseFee"] = hex0x(block.base_fee);
108 :
109 53 : int64_t cumulative_gas_used = 0;
110 53 : int64_t blob_gas_left = state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK;
111 53 : std::vector<state::Transaction> transactions;
112 53 : std::vector<state::TransactionReceipt> receipts;
113 53 : int64_t block_gas_left = block.gas_limit;
114 :
115 53 : validate_state(state, rev);
116 :
117 : // Parse and execute transactions
118 53 : if (!txs_file.empty())
119 : {
120 106 : const auto j_txs = json::json::parse(std::ifstream{txs_file});
121 :
122 53 : evmc::VM vm{evmc_create_evmone()};
123 :
124 53 : if (trace)
125 UBC 0 : vm.set_option("trace", "1");
126 :
127 CBC 53 : std::vector<state::Log> txs_logs;
128 :
129 53 : if (j_txs.is_array())
130 : {
131 53 : j_result["receipts"] = json::json::array();
132 53 : j_result["rejected"] = json::json::array();
133 :
134 106 : for (size_t i = 0; i < j_txs.size(); ++i)
135 : {
136 53 : auto tx = test::from_json<state::Transaction>(j_txs[i]);
137 53 : tx.chain_id = chain_id;
138 :
139 53 : const auto computed_tx_hash = keccak256(rlp::encode(tx));
140 53 : const auto computed_tx_hash_str = hex0x(computed_tx_hash);
141 :
142 53 : if (j_txs[i].contains("hash"))
143 : {
144 : const auto loaded_tx_hash_opt =
145 53 : evmc::from_hex<bytes32>(j_txs[i]["hash"].get<std::string>());
146 :
147 53 : if (loaded_tx_hash_opt != computed_tx_hash)
148 UBC 0 : throw std::logic_error("transaction hash mismatched: computed " +
149 0 : computed_tx_hash_str + ", expected " +
150 0 : hex0x(loaded_tx_hash_opt.value()));
151 : }
152 :
153 CBC 53 : std::ofstream trace_file_output;
154 53 : const auto orig_clog_buf = std::clog.rdbuf();
155 53 : if (trace)
156 : {
157 : const auto output_filename =
158 : output_dir /
159 UBC 0 : ("trace-" + std::to_string(i) + "-" + computed_tx_hash_str + ".jsonl");
160 :
161 : // `trace` flag enables trace logging to std::clog.
162 : // Redirect std::clog to the output file.
163 0 : trace_file_output.open(output_filename);
164 0 : std::clog.rdbuf(trace_file_output.rdbuf());
165 0 : }
166 :
167 : auto res =
168 CBC 53 : state::transition(state, block, tx, rev, vm, block_gas_left, blob_gas_left);
169 :
170 53 : if (holds_alternative<std::error_code>(res))
171 : {
172 UBC 0 : const auto ec = std::get<std::error_code>(res);
173 0 : json::json j_rejected_tx;
174 0 : j_rejected_tx["hash"] = computed_tx_hash_str;
175 0 : j_rejected_tx["index"] = i;
176 0 : j_rejected_tx["error"] = ec.message();
177 0 : j_result["rejected"].push_back(j_rejected_tx);
178 0 : }
179 : else
180 : {
181 CBC 53 : auto& receipt = get<state::TransactionReceipt>(res);
182 :
183 53 : const auto& tx_logs = receipt.logs;
184 :
185 53 : txs_logs.insert(txs_logs.end(), tx_logs.begin(), tx_logs.end());
186 53 : auto& j_receipt = j_result["receipts"][j_result["receipts"].size()];
187 :
188 53 : j_receipt["transactionHash"] = computed_tx_hash_str;
189 53 : j_receipt["gasUsed"] = hex0x(static_cast<uint64_t>(receipt.gas_used));
190 53 : cumulative_gas_used += receipt.gas_used;
191 53 : receipt.cumulative_gas_used = cumulative_gas_used;
192 53 : if (rev < EVMC_BYZANTIUM)
193 UBC 0 : receipt.post_state = state::mpt_hash(state.get_accounts());
194 CBC 53 : j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used);
195 :
196 53 : j_receipt["blockHash"] = hex0x(bytes32{});
197 53 : j_receipt["contractAddress"] = hex0x(address{});
198 53 : j_receipt["logsBloom"] = hex0x(receipt.logs_bloom_filter);
199 53 : j_receipt["logs"] = json::json::array(); // FIXME: Add to_json<state:Log>
200 53 : j_receipt["root"] = "";
201 53 : j_receipt["status"] = "0x1";
202 53 : j_receipt["transactionIndex"] = hex0x(i);
203 53 : blob_gas_left -= tx.blob_gas_used();
204 53 : transactions.emplace_back(std::move(tx));
205 53 : block_gas_left -= receipt.gas_used;
206 53 : receipts.emplace_back(std::move(receipt));
207 : }
208 :
209 : // Restore original std::clog buffer (otherwise std::clog crashes at exit).
210 53 : if (trace)
211 UBC 0 : std::clog.rdbuf(orig_clog_buf);
212 CBC 53 : }
213 : }
214 :
215 53 : state::finalize(
216 : state, rev, block.coinbase, block_reward, block.ommers, block.withdrawals);
217 :
218 53 : j_result["logsHash"] = hex0x(logs_hash(txs_logs));
219 53 : j_result["stateRoot"] = hex0x(state::mpt_hash(state.get_accounts()));
220 53 : }
221 :
222 53 : j_result["logsBloom"] = hex0x(compute_bloom_filter(receipts));
223 53 : j_result["receiptsRoot"] = hex0x(state::mpt_hash(receipts));
224 53 : if (rev >= EVMC_SHANGHAI)
225 53 : j_result["withdrawalsRoot"] = hex0x(state::mpt_hash(block.withdrawals));
226 :
227 53 : j_result["txRoot"] = hex0x(state::mpt_hash(transactions));
228 53 : j_result["gasUsed"] = hex0x(cumulative_gas_used);
229 53 : if (rev >= EVMC_CANCUN)
230 : {
231 53 : j_result["blobGasUsed"] =
232 106 : hex0x(state::BlockInfo::MAX_BLOB_GAS_PER_BLOCK - blob_gas_left);
233 53 : j_result["currentExcessBlobGas"] = hex0x(block.excess_blob_gas);
234 : }
235 :
236 53 : std::ofstream{output_dir / output_result_file} << std::setw(2) << j_result;
237 :
238 : // Print out current state to outAlloc file
239 106 : std::ofstream{output_dir / output_alloc_file} << std::setw(2)
240 53 : << to_json(state.get_accounts());
241 :
242 53 : if (!output_body_file.empty())
243 UBC 0 : std::ofstream{output_dir / output_body_file} << hex0x(rlp::encode(transactions));
244 CBC 53 : }
245 UBC 0 : catch (const std::exception& e)
246 : {
247 0 : std::cerr << e.what() << '\n';
248 0 : return 1;
249 0 : }
250 :
251 CBC 53 : return 0;
252 54 : }
|