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 "state.hpp"
6 : #include "../utils/stdx/utility.hpp"
7 : #include "errors.hpp"
8 : #include "host.hpp"
9 : #include "rlp.hpp"
10 : #include <evmone/evmone.h>
11 : #include <evmone/execution_state.hpp>
12 :
13 : namespace evmone::state
14 : {
15 : namespace
16 : {
17 UBC 0 : inline constexpr int64_t num_words(size_t size_in_bytes) noexcept
18 : {
19 0 : return static_cast<int64_t>((size_in_bytes + 31) / 32);
20 : }
21 :
22 CBC 53 : int64_t compute_tx_data_cost(evmc_revision rev, bytes_view data) noexcept
23 : {
24 53 : constexpr int64_t zero_byte_cost = 4;
25 53 : const int64_t nonzero_byte_cost = rev >= EVMC_ISTANBUL ? 16 : 68;
26 53 : int64_t cost = 0;
27 1589 : for (const auto b : data)
28 1536 : cost += (b == 0) ? zero_byte_cost : nonzero_byte_cost;
29 53 : return cost;
30 : }
31 :
32 53 : int64_t compute_access_list_cost(const AccessList& access_list) noexcept
33 : {
34 : static constexpr auto storage_key_cost = 1900;
35 : static constexpr auto address_cost = 2400;
36 :
37 53 : int64_t cost = 0;
38 53 : for (const auto& a : access_list)
39 UBC 0 : cost += address_cost + static_cast<int64_t>(a.second.size()) * storage_key_cost;
40 CBC 53 : return cost;
41 : }
42 :
43 53 : int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noexcept
44 : {
45 : static constexpr auto call_tx_cost = 21000;
46 : static constexpr auto create_tx_cost = 53000;
47 : static constexpr auto initcode_word_cost = 2;
48 53 : const auto is_create = !tx.to.has_value();
49 : const auto initcode_cost =
50 53 : is_create && rev >= EVMC_SHANGHAI ? initcode_word_cost * num_words(tx.data.size()) : 0;
51 53 : const auto tx_cost = is_create && rev >= EVMC_HOMESTEAD ? create_tx_cost : call_tx_cost;
52 53 : return tx_cost + compute_tx_data_cost(rev, tx.data) + compute_access_list_cost(tx.access_list) +
53 53 : initcode_cost;
54 : }
55 :
56 53 : evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept
57 : {
58 53 : const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{};
59 : return {
60 53 : tx.to.has_value() ? EVMC_CALL : EVMC_CREATE,
61 : 0,
62 : 0,
63 : execution_gas_limit,
64 : recipient,
65 : tx.sender,
66 53 : tx.data.data(),
67 53 : tx.data.size(),
68 106 : intx::be::store<evmc::uint256be>(tx.value),
69 : {},
70 : recipient,
71 106 : };
72 : }
73 : } // namespace
74 :
75 212 : Account& State::insert(const address& addr, Account account)
76 : {
77 212 : const auto r = m_accounts.insert({addr, std::move(account)});
78 212 : assert(r.second);
79 212 : return r.first->second;
80 : }
81 :
82 1543 : Account* State::find(const address& addr) noexcept
83 : {
84 1543 : const auto it = m_accounts.find(addr);
85 1543 : if (it != m_accounts.end())
86 1456 : return &it->second;
87 87 : return nullptr;
88 : }
89 :
90 1008 : Account& State::get(const address& addr) noexcept
91 : {
92 1008 : auto acc = find(addr);
93 1008 : assert(acc != nullptr);
94 1008 : return *acc;
95 : }
96 :
97 343 : Account& State::get_or_insert(const address& addr, Account account)
98 : {
99 343 : if (const auto acc = find(addr); acc != nullptr)
100 290 : return *acc;
101 53 : return insert(addr, std::move(account));
102 : }
103 :
104 185 : Account& State::touch(const address& addr)
105 : {
106 185 : auto& acc = get_or_insert(addr, {.erase_if_empty = true});
107 185 : if (!acc.erase_if_empty && acc.is_empty())
108 : {
109 UBC 0 : acc.erase_if_empty = true;
110 0 : m_journal.emplace_back(JournalTouched{addr});
111 : }
112 CBC 185 : return acc;
113 : }
114 :
115 UBC 0 : void State::journal_balance_change(const address& addr, const intx::uint256& prev_balance)
116 : {
117 0 : m_journal.emplace_back(JournalBalanceChange{{addr}, prev_balance});
118 0 : }
119 :
120 CBC 288 : void State::journal_storage_change(
121 : const address& addr, const bytes32& key, const StorageValue& value)
122 : {
123 288 : m_journal.emplace_back(JournalStorageChange{{addr}, key, value.current, value.access_status});
124 288 : }
125 :
126 153 : void State::journal_transient_storage_change(
127 : const address& addr, const bytes32& key, const bytes32& value)
128 : {
129 153 : m_journal.emplace_back(JournalTransientStorageChange{{addr}, key, value});
130 153 : }
131 :
132 UBC 0 : void State::journal_bump_nonce(const address& addr)
133 : {
134 0 : m_journal.emplace_back(JournalNonceBump{addr});
135 0 : }
136 :
137 0 : void State::journal_create(const address& addr, bool existed)
138 : {
139 0 : m_journal.emplace_back(JournalCreate{{addr}, existed});
140 0 : }
141 :
142 0 : void State::journal_destruct(const address& addr)
143 : {
144 0 : m_journal.emplace_back(JournalDestruct{addr});
145 0 : }
146 :
147 CBC 134 : void State::journal_access_account(const address& addr)
148 : {
149 134 : m_journal.emplace_back(JournalAccessAccount{addr});
150 134 : }
151 :
152 34 : void State::rollback(size_t checkpoint)
153 : {
154 82 : while (m_journal.size() != checkpoint)
155 : {
156 48 : std::visit(
157 48 : [this](const auto& e) {
158 : using T = std::decay_t<decltype(e)>;
159 : if constexpr (std::is_same_v<T, JournalNonceBump>)
160 : {
161 UBC 0 : get(e.addr).nonce -= 1;
162 : }
163 : else if constexpr (std::is_same_v<T, JournalTouched>)
164 : {
165 0 : get(e.addr).erase_if_empty = false;
166 : }
167 : else if constexpr (std::is_same_v<T, JournalDestruct>)
168 : {
169 0 : get(e.addr).destructed = false;
170 : }
171 : else if constexpr (std::is_same_v<T, JournalAccessAccount>)
172 : {
173 0 : get(e.addr).access_status = EVMC_ACCESS_COLD;
174 : }
175 : else if constexpr (std::is_same_v<T, JournalCreate>)
176 : {
177 0 : if (e.existed)
178 : {
179 : // This account is not always "touched". TODO: Why?
180 0 : auto& a = get(e.addr);
181 0 : a.nonce = 0;
182 0 : a.code.clear();
183 : }
184 : else
185 : {
186 : // TODO: Before Spurious Dragon we don't clear empty accounts ("erasable")
187 : // so we need to delete them here explicitly.
188 : // This should be changed by tuning "erasable" flag
189 : // and clear in all revisions.
190 0 : m_accounts.erase(e.addr);
191 : }
192 : }
193 : else if constexpr (std::is_same_v<T, JournalStorageChange>)
194 : {
195 0 : auto& s = get(e.addr).storage.find(e.key)->second;
196 0 : s.current = e.prev_value;
197 0 : s.access_status = e.prev_access_status;
198 : }
199 : else if constexpr (std::is_same_v<T, JournalTransientStorageChange>)
200 : {
201 CBC 48 : auto& s = get(e.addr).transient_storage.find(e.key)->second;
202 48 : s = e.prev_value;
203 : }
204 : else if constexpr (std::is_same_v<T, JournalBalanceChange>)
205 : {
206 UBC 0 : get(e.addr).balance = e.prev_balance;
207 : }
208 : else
209 : {
210 : // TODO(C++23): Change condition to `false` once CWG2518 is in.
211 : static_assert(std::is_void_v<T>, "unhandled journal entry type");
212 : }
213 CBC 48 : },
214 48 : m_journal.back());
215 48 : m_journal.pop_back();
216 : }
217 34 : }
218 :
219 53 : intx::uint256 compute_blob_gas_price(uint64_t excess_blob_gas) noexcept
220 : {
221 : /// A helper function approximating `factor * e ** (numerator / denominator)`.
222 : /// https://eips.ethereum.org/EIPS/eip-4844#helpers
223 53 : static constexpr auto fake_exponential = [](uint64_t factor, uint64_t numerator,
224 : uint64_t denominator) noexcept {
225 53 : intx::uint256 i = 1;
226 53 : intx::uint256 output = 0;
227 53 : intx::uint256 numerator_accum = factor * denominator;
228 106 : while (numerator_accum > 0)
229 : {
230 53 : output += numerator_accum;
231 53 : numerator_accum = (numerator_accum * numerator) / (denominator * i);
232 53 : i += 1;
233 : }
234 53 : return output / denominator;
235 : };
236 :
237 : static constexpr auto MIN_BLOB_GASPRICE = 1;
238 : static constexpr auto BLOB_GASPRICE_UPDATE_FRACTION = 3338477;
239 53 : return fake_exponential(MIN_BLOB_GASPRICE, excess_blob_gas, BLOB_GASPRICE_UPDATE_FRACTION);
240 : }
241 :
242 : /// Validates transaction and computes its execution gas limit (the amount of gas provided to EVM).
243 : /// @return Execution gas limit or transaction validation error.
244 53 : std::variant<int64_t, std::error_code> validate_transaction(const Account& sender_acc,
245 : const BlockInfo& block, const Transaction& tx, evmc_revision rev, int64_t block_gas_left,
246 : int64_t blob_gas_left) noexcept
247 : {
248 53 : switch (tx.type)
249 : {
250 UBC 0 : case Transaction::Type::blob:
251 0 : if (rev < EVMC_CANCUN)
252 0 : return make_error_code(TX_TYPE_NOT_SUPPORTED);
253 0 : if (!tx.to.has_value())
254 0 : return make_error_code(CREATE_BLOB_TX);
255 0 : if (tx.blob_hashes.empty())
256 0 : return make_error_code(EMPTY_BLOB_HASHES_LIST);
257 :
258 0 : if (tx.max_blob_gas_price < block.blob_base_fee)
259 0 : return make_error_code(FEE_CAP_LESS_THEN_BLOCKS);
260 :
261 0 : if (std::ranges::any_of(tx.blob_hashes, [](const auto& h) { return h.bytes[0] != 0x01; }))
262 0 : return make_error_code(INVALID_BLOB_HASH_VERSION);
263 0 : if (std::cmp_greater(tx.blob_gas_used(), blob_gas_left))
264 0 : return make_error_code(BLOB_GAS_LIMIT_EXCEEDED);
265 : [[fallthrough]];
266 :
267 : case Transaction::Type::eip1559:
268 CBC 1 : if (rev < EVMC_LONDON)
269 UBC 0 : return make_error_code(TX_TYPE_NOT_SUPPORTED);
270 :
271 CBC 1 : if (tx.max_priority_gas_price > tx.max_gas_price)
272 UBC 0 : return make_error_code(TIP_GT_FEE_CAP); // Priority gas price is too high.
273 : [[fallthrough]];
274 :
275 : case Transaction::Type::access_list:
276 CBC 1 : if (rev < EVMC_BERLIN)
277 UBC 0 : return make_error_code(TX_TYPE_NOT_SUPPORTED);
278 : [[fallthrough]];
279 :
280 : case Transaction::Type::legacy:;
281 : }
282 :
283 CBC 53 : assert(tx.max_priority_gas_price <= tx.max_gas_price);
284 :
285 53 : if (tx.gas_limit > block_gas_left)
286 UBC 0 : return make_error_code(GAS_LIMIT_REACHED);
287 :
288 CBC 53 : if (tx.max_gas_price < block.base_fee)
289 UBC 0 : return make_error_code(FEE_CAP_LESS_THEN_BLOCKS);
290 :
291 CBC 53 : if (!sender_acc.code.empty())
292 UBC 0 : return make_error_code(SENDER_NOT_EOA); // Origin must not be a contract (EIP-3607).
293 :
294 CBC 53 : if (sender_acc.nonce == Account::NonceMax) // Nonce value limit (EIP-2681).
295 UBC 0 : return make_error_code(NONCE_HAS_MAX_VALUE);
296 :
297 CBC 53 : if (sender_acc.nonce < tx.nonce)
298 UBC 0 : return make_error_code(NONCE_TOO_HIGH);
299 :
300 CBC 53 : if (sender_acc.nonce > tx.nonce)
301 UBC 0 : return make_error_code(NONCE_TOO_LOW);
302 :
303 : // initcode size is limited by EIP-3860.
304 CBC 53 : if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > max_initcode_size)
305 UBC 0 : return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED);
306 :
307 : // Compute and check if sender has enough balance for the theoretical maximum transaction cost.
308 : // Note this is different from tx_max_cost computed with effective gas price later.
309 : // The computation cannot overflow if done with 512-bit precision.
310 CBC 53 : auto max_total_fee = umul(intx::uint256{tx.gas_limit}, tx.max_gas_price);
311 53 : max_total_fee += tx.value;
312 :
313 53 : if (tx.type == Transaction::Type::blob)
314 : {
315 UBC 0 : const auto total_blob_gas = tx.blob_gas_used();
316 : // FIXME: Can overflow uint256.
317 0 : max_total_fee += total_blob_gas * tx.max_blob_gas_price;
318 : }
319 CBC 53 : if (sender_acc.balance < max_total_fee)
320 UBC 0 : return make_error_code(INSUFFICIENT_FUNDS);
321 :
322 CBC 53 : const auto execution_gas_limit = tx.gas_limit - compute_tx_intrinsic_cost(rev, tx);
323 53 : if (execution_gas_limit < 0)
324 UBC 0 : return make_error_code(INTRINSIC_GAS_TOO_LOW);
325 :
326 CBC 53 : return execution_gas_limit;
327 : }
328 :
329 : namespace
330 : {
331 : /// Deletes "touched" (marked as erasable) empty accounts in the state.
332 106 : void delete_empty_accounts(State& state)
333 : {
334 106 : std::erase_if(state.get_accounts(), [](const std::pair<const address, Account>& p) noexcept {
335 424 : const auto& acc = p.second;
336 424 : return acc.erase_if_empty && acc.is_empty();
337 : });
338 106 : }
339 : } // namespace
340 :
341 UBC 0 : void system_call(State& state, const BlockInfo& block, evmc_revision rev, evmc::VM& vm)
342 : {
343 : static constexpr auto SystemAddress = 0xfffffffffffffffffffffffffffffffffffffffe_address;
344 : static constexpr auto BeaconRootsAddress = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02_address;
345 :
346 0 : if (rev >= EVMC_CANCUN)
347 : {
348 0 : if (const auto acc = state.find(BeaconRootsAddress); acc != nullptr)
349 : {
350 0 : const evmc_message msg{
351 : .kind = EVMC_CALL,
352 : .gas = 30'000'000,
353 : .recipient = BeaconRootsAddress,
354 : .sender = SystemAddress,
355 0 : .input_data = block.parent_beacon_block_root.bytes,
356 : .input_size = sizeof(block.parent_beacon_block_root),
357 0 : };
358 :
359 0 : const Transaction empty_tx{};
360 0 : Host host{rev, vm, state, block, empty_tx};
361 0 : const auto& code = acc->code;
362 0 : [[maybe_unused]] const auto res = vm.execute(host, rev, msg, code.data(), code.size());
363 0 : assert(res.status_code == EVMC_SUCCESS);
364 0 : assert(acc->access_status == EVMC_ACCESS_COLD);
365 :
366 : // Reset storage status.
367 0 : for (auto& [_, val] : acc->storage)
368 : {
369 0 : val.access_status = EVMC_ACCESS_COLD;
370 0 : val.original = val.current;
371 : }
372 0 : }
373 : }
374 0 : }
375 :
376 CBC 53 : void finalize(State& state, evmc_revision rev, const address& coinbase,
377 : std::optional<uint64_t> block_reward, std::span<const Ommer> ommers,
378 : std::span<const Withdrawal> withdrawals)
379 : {
380 : // TODO: The block reward can be represented as a withdrawal.
381 53 : if (block_reward.has_value())
382 : {
383 53 : const auto reward = *block_reward;
384 53 : assert(reward % 32 == 0); // Assume block reward is divisible by 32.
385 53 : const auto reward_by_32 = reward / 32;
386 53 : const auto reward_by_8 = reward / 8;
387 :
388 53 : state.touch(coinbase).balance += reward + reward_by_32 * ommers.size();
389 53 : for (const auto& ommer : ommers)
390 : {
391 UBC 0 : assert(ommer.delta > 0 && ommer.delta < 8);
392 0 : state.touch(ommer.beneficiary).balance += reward_by_8 * (8 - ommer.delta);
393 : }
394 : }
395 :
396 CBC 53 : for (const auto& withdrawal : withdrawals)
397 UBC 0 : state.touch(withdrawal.recipient).balance += withdrawal.get_amount();
398 :
399 : // Delete potentially empty block reward recipients.
400 CBC 53 : if (rev >= EVMC_SPURIOUS_DRAGON)
401 53 : delete_empty_accounts(state);
402 53 : }
403 :
404 53 : std::variant<TransactionReceipt, std::error_code> transition(State& state, const BlockInfo& block,
405 : const Transaction& tx, evmc_revision rev, evmc::VM& vm, int64_t block_gas_left,
406 : int64_t blob_gas_left)
407 : {
408 53 : auto* sender_ptr = state.find(tx.sender);
409 :
410 : // Validate transaction. The validation needs the sender account, so in case
411 : // it doesn't exist provide an empty one. The account isn't created in the state
412 : // to prevent the state modification in case the transaction is invalid.
413 : const auto validation_result =
414 53 : validate_transaction((sender_ptr != nullptr) ? *sender_ptr : Account{}, block, tx, rev,
415 : block_gas_left, blob_gas_left);
416 :
417 53 : if (holds_alternative<std::error_code>(validation_result))
418 UBC 0 : return get<std::error_code>(validation_result);
419 :
420 : // Once the transaction is valid, create new sender account.
421 : // The account won't be empty because its nonce will be bumped.
422 CBC 53 : auto& sender_acc = (sender_ptr != nullptr) ? *sender_ptr : state.insert(tx.sender);
423 :
424 53 : const auto execution_gas_limit = get<int64_t>(validation_result);
425 :
426 53 : const auto base_fee = (rev >= EVMC_LONDON) ? block.base_fee : 0;
427 53 : assert(tx.max_gas_price >= base_fee); // Checked at the front.
428 53 : assert(tx.max_gas_price >= tx.max_priority_gas_price); // Checked at the front.
429 : const auto priority_gas_price =
430 53 : std::min(tx.max_priority_gas_price, tx.max_gas_price - base_fee);
431 53 : const auto effective_gas_price = base_fee + priority_gas_price;
432 :
433 53 : assert(effective_gas_price <= tx.max_gas_price);
434 53 : const auto tx_max_cost = tx.gas_limit * effective_gas_price;
435 :
436 53 : sender_acc.balance -= tx_max_cost; // Modify sender balance after all checks.
437 :
438 53 : if (tx.type == Transaction::Type::blob)
439 : {
440 UBC 0 : const auto blob_fee = tx.blob_gas_used() * block.blob_base_fee;
441 0 : assert(sender_acc.balance >= blob_fee); // Checked at the front.
442 0 : sender_acc.balance -= blob_fee;
443 : }
444 :
445 CBC 53 : Host host{rev, vm, state, block, tx};
446 :
447 53 : sender_acc.access_status = EVMC_ACCESS_WARM; // Tx sender is always warm.
448 53 : if (tx.to.has_value())
449 53 : host.access_account(*tx.to);
450 53 : for (const auto& [a, storage_keys] : tx.access_list)
451 : {
452 UBC 0 : host.access_account(a); // TODO: Return account ref.
453 0 : auto& storage = state.get(a).storage;
454 0 : for (const auto& key : storage_keys)
455 0 : storage[key].access_status = EVMC_ACCESS_WARM;
456 : }
457 : // EIP-3651: Warm COINBASE.
458 : // This may create an empty coinbase account. The account cannot be created unconditionally
459 : // because this breaks old revisions.
460 CBC 53 : if (rev >= EVMC_SHANGHAI)
461 53 : host.access_account(block.coinbase);
462 :
463 53 : const auto result = host.call(build_message(tx, execution_gas_limit));
464 :
465 53 : auto gas_used = tx.gas_limit - result.gas_left;
466 :
467 53 : const auto max_refund_quotient = rev >= EVMC_LONDON ? 5 : 2;
468 53 : const auto refund_limit = gas_used / max_refund_quotient;
469 53 : const auto refund = std::min(result.gas_refund, refund_limit);
470 53 : gas_used -= refund;
471 53 : assert(gas_used > 0);
472 :
473 53 : sender_acc.balance += tx_max_cost - gas_used * effective_gas_price;
474 53 : state.touch(block.coinbase).balance += gas_used * priority_gas_price;
475 :
476 : // Apply destructs.
477 53 : std::erase_if(state.get_accounts(),
478 212 : [](const std::pair<const address, Account>& p) noexcept { return p.second.destructed; });
479 :
480 : // Cumulative gas used is unknown in this scope.
481 53 : TransactionReceipt receipt{tx.type, result.status_code, gas_used, {}, host.take_logs(), {}, {}};
482 :
483 : // Cannot put it into constructor call because logs are std::moved from host instance.
484 53 : receipt.logs_bloom_filter = compute_bloom_filter(receipt.logs);
485 :
486 : // Delete empty accounts after every transaction. This is strictly required until Byzantium
487 : // where intermediate state root hashes are part of the transaction receipt.
488 : // TODO: Consider limiting this only to Spurious Dragon.
489 53 : if (rev >= EVMC_SPURIOUS_DRAGON)
490 53 : delete_empty_accounts(state);
491 :
492 : // Post-transaction clean-up.
493 : // - Set accounts and their storage access status to cold.
494 : // - Clear the "just created" account flag.
495 265 : for (auto& [addr, acc] : state.get_accounts())
496 : {
497 212 : acc.transient_storage.clear();
498 212 : acc.access_status = EVMC_ACCESS_COLD;
499 212 : acc.just_created = false;
500 499 : for (auto& [key, val] : acc.storage)
501 : {
502 287 : val.access_status = EVMC_ACCESS_COLD;
503 287 : val.original = val.current;
504 : }
505 : }
506 :
507 53 : return receipt;
508 53 : }
509 :
510 UBC 0 : [[nodiscard]] bytes rlp_encode(const Log& log)
511 : {
512 0 : return rlp::encode_tuple(log.addr, log.topics, log.data);
513 : }
514 :
515 CBC 106 : [[nodiscard]] bytes rlp_encode(const Transaction& tx)
516 : {
517 106 : assert(tx.type <= Transaction::Type::blob);
518 :
519 : // TODO: Refactor this function. For all type of transactions most of the code is similar.
520 106 : if (tx.type == Transaction::Type::legacy)
521 : {
522 : // rlp [nonce, gas_price, gas_limit, to, value, data, v, r, s];
523 UBC 0 : return rlp::encode_tuple(tx.nonce, tx.max_gas_price, static_cast<uint64_t>(tx.gas_limit),
524 GBC 208 : tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data, tx.v, tx.r, tx.s);
525 : }
526 CBC 2 : else if (tx.type == Transaction::Type::access_list)
527 : {
528 UBC 0 : if (tx.v > 1)
529 0 : throw std::invalid_argument("`v` value for eip2930 transaction must be 0 or 1");
530 : // tx_type +
531 : // rlp [nonce, gas_price, gas_limit, to, value, data, access_list, v, r, s];
532 0 : return bytes{0x01} + // Transaction type (eip2930 type == 1)
533 0 : rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_gas_price,
534 0 : static_cast<uint64_t>(tx.gas_limit),
535 0 : tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data,
536 0 : tx.access_list, static_cast<bool>(tx.v), tx.r, tx.s);
537 : }
538 CBC 2 : else if (tx.type == Transaction::Type::eip1559)
539 : {
540 2 : if (tx.v > 1)
541 UBC 0 : throw std::invalid_argument("`v` value for eip1559 transaction must be 0 or 1");
542 : // tx_type +
543 : // rlp [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value,
544 : // data, access_list, sig_parity, r, s];
545 CBC 4 : return bytes{0x02} + // Transaction type (eip1559 type == 2)
546 2 : rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_priority_gas_price, tx.max_gas_price,
547 2 : static_cast<uint64_t>(tx.gas_limit),
548 2 : tx.to.has_value() ? tx.to.value() : bytes_view(), tx.value, tx.data,
549 6 : tx.access_list, static_cast<bool>(tx.v), tx.r, tx.s);
550 : }
551 : else // Transaction::Type::blob
552 : {
553 UBC 0 : if (tx.v > 1)
554 0 : throw std::invalid_argument("`v` value for blob transaction must be 0 or 1");
555 0 : if (!tx.to.has_value()) // Blob tx has to have `to` address
556 0 : throw std::invalid_argument("`to` value for blob transaction must not be null");
557 : // tx_type +
558 : // rlp [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value,
559 : // data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, sig_parity, r, s];
560 0 : return bytes{stdx::to_underlying(Transaction::Type::blob)} +
561 0 : rlp::encode_tuple(tx.chain_id, tx.nonce, tx.max_priority_gas_price, tx.max_gas_price,
562 0 : static_cast<uint64_t>(tx.gas_limit), tx.to.value(), tx.value, tx.data,
563 0 : tx.access_list, tx.max_blob_gas_price, tx.blob_hashes, static_cast<bool>(tx.v),
564 0 : tx.r, tx.s);
565 : }
566 : }
567 :
568 CBC 53 : [[nodiscard]] bytes rlp_encode(const TransactionReceipt& receipt)
569 : {
570 53 : if (receipt.post_state.has_value())
571 : {
572 UBC 0 : assert(receipt.type == Transaction::Type::legacy);
573 :
574 : return rlp::encode_tuple(receipt.post_state.value(),
575 0 : static_cast<uint64_t>(receipt.cumulative_gas_used),
576 0 : bytes_view(receipt.logs_bloom_filter), receipt.logs);
577 : }
578 : else
579 : {
580 CBC 53 : const auto prefix = receipt.type == Transaction::Type::legacy ?
581 : bytes{} :
582 54 : bytes{stdx::to_underlying(receipt.type)};
583 :
584 53 : return prefix + rlp::encode_tuple(receipt.status == EVMC_SUCCESS,
585 53 : static_cast<uint64_t>(receipt.cumulative_gas_used),
586 106 : bytes_view(receipt.logs_bloom_filter), receipt.logs);
587 53 : }
588 : }
589 :
590 UBC 0 : [[nodiscard]] bytes rlp_encode(const Withdrawal& withdrawal)
591 : {
592 0 : return rlp::encode_tuple(withdrawal.index, withdrawal.validator_index, withdrawal.recipient,
593 0 : withdrawal.amount_in_gwei);
594 : }
595 :
596 : } // namespace evmone::state
|