OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
host.cpp
Go to the documentation of this file.
1// Copyright (c) 2022 MIT Digital Currency Initiative,
2// Federal Reserve Bank of Boston
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include "host.hpp"
7
8#include "address.hpp"
9#include "crypto/sha256.h"
10#include "format.hpp"
11#include "hash.hpp"
12#include "math.hpp"
13#include "rlp.hpp"
14#include "serialization.hpp"
15#include "util.hpp"
16
17#include <cassert>
18#include <evmc/hex.hpp>
19#include <evmone/evmone.h>
20#include <future>
21
23 evm_host::evm_host(std::shared_ptr<logging::log> log,
24 interface::try_lock_callback_type try_lock_callback,
25 evmc_tx_context tx_context,
26 evm_tx tx,
27 bool is_readonly_run,
29 : m_log(std::move(log)),
30 m_try_lock_callback(std::move(try_lock_callback)),
31 m_tx_context(tx_context),
32 m_tx(std::move(tx)),
33 m_is_readonly_run(is_readonly_run),
34 m_ticket_number(ticket_number) {
35 m_receipt.m_tx = m_tx;
36 m_receipt.m_ticket_number = m_ticket_number;
37 }
38
39 auto evm_host::get_account(const evmc::address& addr, bool write) const
40 -> std::optional<evm_account> {
41 m_log->trace(this,
42 "EVM request account:",
43 to_hex(addr),
44 "- Write:",
45 write);
46
47 if(is_precompile(addr)) {
48 // Precompile contract, return empty account
49 m_accessed_addresses.insert(addr);
50 return evm_account{};
51 }
52
53 auto it = m_accounts.find(addr);
54 if(it != m_accounts.end() && (it->second.second || !write)) {
55 return it->second.first;
56 }
57
58 m_log->trace(
59 this,
60 "EVM request account not in cache or wrong lock, getting [",
61 to_hex(addr),
62 "]");
63
64 auto addr_key = make_buffer(addr);
65 auto maybe_v = get_key(addr_key, write);
66 if(!maybe_v.has_value()) {
67 return std::nullopt;
68 }
69
70 m_accessed_addresses.insert(addr);
71 auto& v = maybe_v.value();
72 if(v.size() == 0) {
73 m_accounts[addr] = {std::nullopt, write};
74 return std::nullopt;
75 }
76 auto maybe_acc = from_buffer<evm_account>(v);
77 assert(maybe_acc.has_value());
78 auto& acc = maybe_acc.value();
79 m_accounts[addr] = {acc, write};
80 return acc;
81 }
82
83 auto evm_host::account_exists(const evmc::address& addr) const noexcept
84 -> bool {
85 const auto* log_str = "evm_account_exists";
86 m_log->trace(log_str, to_hex(addr));
87 auto maybe_acc = get_account(addr, false);
88 if(!maybe_acc.has_value()) {
89 return false;
90 }
91 auto& acc = maybe_acc.value();
92 return !acc.m_destruct;
93 }
94
95 auto evm_host::get_storage(const evmc::address& addr,
96 const evmc::bytes32& key) const noexcept
97 -> evmc::bytes32 {
98 const auto* log_str = "evm_get_storage";
99 m_log->trace(log_str, to_hex(addr), to_hex(key));
100 auto maybe_storage = get_account_storage(addr, key, false);
101 return maybe_storage.value_or(evmc::bytes32{});
102 }
103
104 auto evm_host::set_storage(const evmc::address& addr,
105 const evmc::bytes32& key,
106 const evmc::bytes32& value) noexcept
107 -> evmc_storage_status {
108 const auto* log_str = "evm_set_storage";
109 m_log->trace(log_str, to_hex(addr), to_hex(key), to_hex(value));
110
111 auto ret_val = std::optional<evmc_storage_status>();
112 auto maybe_acc = get_account(addr, false);
113 if(!maybe_acc.has_value()) {
114 if(!m_is_readonly_run) {
115 maybe_acc = get_account(addr, true);
116 assert(!maybe_acc.has_value());
117 }
118 maybe_acc = evm_account();
119 ret_val = EVMC_STORAGE_ADDED;
120 maybe_acc.value().m_modified.insert(key);
121 m_accounts[addr] = {maybe_acc.value(), !m_is_readonly_run};
122 }
123 auto& acc = maybe_acc.value();
124
125 auto maybe_storage
126 = get_account_storage(addr, key, !m_is_readonly_run);
127 auto prev_value = maybe_storage.value_or(evmc::bytes32{});
128
129 auto modified = acc.m_modified.find(key) != acc.m_modified.end();
130 if(!ret_val.has_value()) {
131 if(prev_value == value || modified) {
132 // NOTE: both unchanged value and modifying previously
133 // added/modified value cases belong in the same group
134 // related to minimal gas cost of only accessing warm storage.
135 // See evmc.h for more details.
136 ret_val = EVMC_STORAGE_ASSIGNED;
137 } else if(evmc::is_zero(value) && !modified) {
138 ret_val = EVMC_STORAGE_DELETED;
139 } else {
140 ret_val = EVMC_STORAGE_MODIFIED;
141 acc.m_modified.insert(key);
142 }
143 }
144 m_account_storage[addr][key] = {value, !m_is_readonly_run};
145 m_accounts[addr].first = acc;
146 assert(ret_val.has_value());
147 return ret_val.value();
148 }
149
150 auto evm_host::get_balance(const evmc::address& addr) const noexcept
151 -> evmc::uint256be {
152 const auto* log_str = "evm_get_balance";
153 m_log->trace(log_str, to_hex(addr));
154 auto maybe_acc = get_account(addr, false);
155 if(!maybe_acc.has_value()) {
156 return {};
157 }
158 auto& acc = maybe_acc.value();
159 return acc.m_balance;
160 }
161
162 auto evm_host::get_code_size(const evmc::address& addr) const noexcept
163 -> size_t {
164 const auto* log_str = "evm_get_code_size";
165 m_log->trace(log_str, to_hex(addr));
166 if(is_precompile(addr)) {
167 // Precompiles have no code, but this should be
168 // non-zero for the call to work
169 return 1;
170 }
171 auto maybe_code = get_account_code(addr, false);
172 return maybe_code.value_or(evm_account_code{}).size();
173 }
174
175 auto evm_host::get_code_hash(const evmc::address& addr) const noexcept
176 -> evmc::bytes32 {
177 const auto* log_str = "evm_get_code_hash";
178 m_log->trace(log_str, to_hex(addr));
179
180 auto maybe_code = get_account_code(addr, false);
181 if(!maybe_code.has_value()) {
182 return {};
183 }
184 auto& code = maybe_code.value();
185 auto sha = CSHA256();
186 sha.Write(code.data(), code.size());
187 auto ret = evmc::bytes32();
188 sha.Finalize(&ret.bytes[0]);
189 return ret;
190 }
191
192 auto evm_host::copy_code(const evmc::address& addr,
193 size_t code_offset,
194 uint8_t* buffer_data,
195 size_t buffer_size) const noexcept -> size_t {
196 const auto* log_str = "evm_copy_code";
197 m_log->trace(log_str, to_hex(addr), code_offset);
198
199 auto maybe_code = get_account_code(addr, false);
200 if(!maybe_code.has_value()) {
201 return 0;
202 }
203
204 const auto& code = maybe_code.value();
205
206 if(code_offset >= code.size()) {
207 return 0;
208 }
209
210 const auto n = std::min(buffer_size, code.size() - code_offset);
211 if(n > 0) {
212 std::copy_n(&code[code_offset], n, buffer_data);
213 }
214 return n;
215 }
216
217 auto evm_host::selfdestruct(const evmc::address& addr,
218 const evmc::address& beneficiary) noexcept
219 -> bool {
220 m_log->trace("EVM selfdestruct:", to_hex(addr), to_hex(beneficiary));
221 // TODO: delete storage keys and code
222 transfer(addr, beneficiary, evmc::uint256be{});
223 return true;
224 }
225
226 auto evm_host::create(const evmc_message& msg) noexcept -> evmc::Result {
227 auto maybe_sender_acc = get_account(msg.sender, false);
228 if(!maybe_sender_acc.has_value()) {
229 m_log->warn("EVM CREATE: sender account not found");
230 return evmc::Result(
231 evmc::make_result(evmc_status_code::EVMC_REVERT,
232 0,
233 0,
234 nullptr,
235 0));
236 }
237 auto& sender_acc = maybe_sender_acc.value();
238
239 auto new_addr = evmc::address();
240 if(msg.kind == EVMC_CREATE) {
241 new_addr = contract_address(msg.sender, sender_acc.m_nonce);
242 } else {
243 auto bytecode_hash
244 = cbdc::keccak_data(msg.input_data, msg.input_size);
245 new_addr = contract_address2(msg.sender,
246 msg.create2_salt,
247 bytecode_hash);
248 }
249
250 // Transfer endowment to deployed contract account
251 if(!evmc::is_zero(msg.value)) {
252 transfer(msg.sender, new_addr, msg.value);
253 }
254
255 if(msg.depth == 0) {
256 m_receipt.m_create_address = new_addr;
257 }
258
259 auto call_msg = evmc_message();
260 call_msg.depth = msg.depth;
261 call_msg.sender = msg.sender;
262 call_msg.value = msg.value;
263 call_msg.recipient = new_addr;
264 call_msg.kind = EVMC_CALL;
265 // TODO: do we need to deduct some gas for contract creation
266 // here?
267 call_msg.gas = msg.gas;
268
269 auto res = execute(call_msg, msg.input_data, msg.input_size);
270
271 if(res.status_code == EVMC_SUCCESS) {
272 // The VM does not populate res.create_address, and it must
273 // set to the address of the new contract.
274 // If left as 0x0, deployment of contracts by another contract
275 // (i.e. depth>0) is likely to fail (e.g.
276 // UniswapV3Factory.createPool())
277 res.create_address = new_addr;
278
279 auto maybe_acc = get_account(new_addr, !m_is_readonly_run);
280 if(!maybe_acc.has_value()) {
281 maybe_acc = evm_account();
282 }
283 auto& acc = maybe_acc.value();
284 m_accounts[new_addr] = {acc, !m_is_readonly_run};
285
286 auto maybe_code = get_account_code(new_addr, !m_is_readonly_run);
287 if(!maybe_code.has_value()) {
288 maybe_code = evm_account_code();
289 }
290 auto& code = maybe_code.value();
291 code.resize(res.output_size);
292 std::memcpy(code.data(), res.output_data, res.output_size);
293 m_account_code[new_addr] = {code, !m_is_readonly_run};
294 }
295
296 if(msg.depth == 0) {
297 m_receipt.m_output_data.resize(res.output_size);
298 std::memcpy(m_receipt.m_output_data.data(),
299 res.output_data,
300 res.output_size);
301 m_receipt.m_success
302 = res.status_code == evmc_status_code::EVMC_SUCCESS;
303 }
304
305 return res;
306 }
307
308 auto evm_host::call(const evmc_message& msg) noexcept -> evmc::Result {
309 if(msg.kind == EVMC_CREATE2 || msg.kind == EVMC_CREATE) {
310 return create(msg);
311 }
312
313 // Transfer message value from sender account to recipient
314 bool is_native_value_transfer{false};
315 if(!evmc::is_zero(msg.value) && msg.kind == EVMC_CALL) {
316 is_native_value_transfer = true;
317 // TODO: do DELETEGATECALL and CALLCODE transfer value as
318 // well?
319 transfer(msg.sender, msg.recipient, msg.value);
320 }
321
322 auto code_addr
323 = msg.kind == EVMC_DELEGATECALL || msg.kind == EVMC_CALLCODE
324 ? msg.code_address
325 : msg.recipient;
326
327 const auto code_size = get_code_size(code_addr);
328 if(code_size == 0) {
329 // TODO: deduct simple send fixed gas amount
330 const auto gas_refund = 0;
331 auto res = evmc::make_result(evmc_status_code::EVMC_SUCCESS,
332 msg.gas,
333 gas_refund,
334 nullptr,
335 0);
336
337 // TODO: Is it possible to have a case where this is not a native
338 // value transfer (i.e. is else-block needed here)?
339 if(is_native_value_transfer) {
340 m_receipt.m_success = true;
341 }
342
343 return evmc::Result(res);
344 }
345
346 auto code_buf = std::vector<uint8_t>(code_size);
347 [[maybe_unused]] auto n
348 = copy_code(code_addr, 0, code_buf.data(), code_buf.size());
349 assert(n == code_size);
350
351 auto inp = cbdc::buffer();
352 inp.append(msg.input_data, msg.input_size);
353 m_log->trace("EVM call:",
354 to_hex(code_addr),
355 msg.kind,
356 msg.flags,
357 msg.depth,
358 inp.to_hex());
359
360 auto res = execute(msg, code_buf.data(), code_buf.size());
361
362 if(msg.depth == 0) {
363 // TODO: refactor branch into call epilog method
364 m_receipt.m_output_data.resize(res.output_size);
365 std::memcpy(m_receipt.m_output_data.data(),
366 res.output_data,
367 res.output_size);
368 m_receipt.m_success
369 = res.status_code == evmc_status_code::EVMC_SUCCESS;
370 }
371
372 return res;
373 }
374
375 auto evm_host::get_tx_context() const noexcept -> evmc_tx_context {
376 return m_tx_context;
377 }
378
379 auto evm_host::get_block_hash(int64_t /* number */) const noexcept
380 -> evmc::bytes32 {
381 // TODO: there are no blocks for this host. Ensure it's okay to
382 // always return 0.
383 return {};
384 }
385
387 const evmc::address& addr,
388 const uint8_t* data,
389 size_t data_size,
390 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays)
391 const evmc::bytes32 topics[],
392 size_t topics_count) noexcept {
393 auto l = evm_log();
394 l.m_addr = addr;
395 l.m_data.resize(data_size);
396 std::memcpy(l.m_data.data(), data, data_size);
397 for(size_t i = 0; i < topics_count; i++) {
398 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
399 l.m_topics.push_back(topics[i]);
400 }
401 m_receipt.m_logs.push_back(l);
402 }
403
404 auto evm_host::access_account(const evmc::address& addr) noexcept
405 -> evmc_access_status {
406 m_log->trace("EVM access_account:", to_hex(addr));
407 if(m_accessed_addresses.find(addr) != m_accessed_addresses.end()) {
408 return EVMC_ACCESS_WARM;
409 }
410 m_accessed_addresses.insert(addr);
411 return EVMC_ACCESS_COLD;
412 }
413
414 auto evm_host::access_storage(const evmc::address& addr,
415 const evmc::bytes32& key) noexcept
416 -> evmc_access_status {
417 m_log->trace("EVM access_storage:", to_hex(addr), to_hex(key));
418 auto elem = std::make_pair(addr, key);
419 if(m_accessed_storage_keys.find(elem)
420 != m_accessed_storage_keys.end()) {
421 return EVMC_ACCESS_WARM;
422 }
423 m_accessed_storage_keys.insert(elem);
424 return EVMC_ACCESS_COLD;
425 }
426
428 std::optional<interface::ticket_number_type> tn) const
429 -> cbdc::buffer {
430 if(!tn) {
431 tn = m_ticket_number;
432 }
433 auto tn_buf = cbdc::make_buffer(tn.value());
434 CSHA256 sha;
435 hash_t tn_hash;
436
437 sha.Write(tn_buf.c_ptr(), tn_buf.size());
438 sha.Finalize(tn_hash.data());
439
440 return make_buffer(tn_hash);
441 }
442
444 evmc::address addr,
445 std::optional<interface::ticket_number_type> tn) const
446 -> cbdc::buffer {
447 if(!tn) {
448 tn = m_ticket_number;
449 }
450 auto tn_buf = cbdc::make_buffer(tn.value());
451 CSHA256 sha;
452 hash_t log_index_hash;
453 sha.Write(addr.bytes, sizeof(addr.bytes));
454 sha.Write(tn_buf.c_ptr(), tn_buf.size());
455 sha.Finalize(log_index_hash.data());
456
457 return make_buffer(log_index_hash);
458 }
459
460 auto evm_host::get_log_index_keys() const -> std::vector<cbdc::buffer> {
461 auto logs = get_sorted_logs();
462 auto keys = std::vector<cbdc::buffer>();
463 for(auto& log : logs) {
464 keys.push_back(log_index_key(log.first));
465 }
466 return keys;
467 }
468
469 auto evm_host::get_sorted_logs() const
470 -> std::unordered_map<evmc::address, std::vector<evm_log>> {
471 auto ret = std::unordered_map<evmc::address, std::vector<evm_log>>();
472 for(const auto& log : m_receipt.m_logs) {
473 if(ret.find(log.m_addr) == ret.end()) {
474 ret.insert({log.m_addr, {}});
475 }
476 ret[log.m_addr].push_back(log);
477 }
478 return ret;
479 }
480
482 -> runtime_locking_shard::state_update_type {
484 for(auto& [addr, acc_data] : m_accounts) {
485 auto& [acc, write] = acc_data;
486 if(!acc.has_value() || !write) {
487 continue;
488 }
489 auto key = make_buffer(addr);
490 auto val = cbdc::buffer();
491 if(!acc->m_destruct) {
492 val = make_buffer(*acc);
493 }
494 ret[key] = val;
495 }
496
497 for(auto& [addr, acc_code] : m_account_code) {
498 auto& [code, write] = acc_code;
499 if(!code.has_value() || !write) {
500 continue;
501 }
502 auto key = make_buffer(code_key{addr});
503 auto val = make_buffer(*code);
504 ret[key] = val;
505 }
506
507 for(auto& [addr, acc_storage] : m_account_storage) {
508 for(auto& [k, elem] : acc_storage) {
509 auto& [value, write] = elem;
510 if(!value.has_value() || !write) {
511 continue;
512 }
513 auto key = make_buffer(storage_key{addr, k});
514 auto val = make_buffer(*value);
515 ret[key] = val;
516 }
517 }
518
519 const auto tx = std::make_shared<evm_tx>(m_tx);
520 auto txid = tx_id(*tx);
521 auto tid = make_buffer(txid);
522 auto r = make_buffer(m_receipt);
523 ret[tid] = r;
524 ret[ticket_number_key()] = tid;
525
526 auto ordered_logs = get_sorted_logs();
527 for(auto& addr_log : ordered_logs) {
528 auto log_idx = evm_log_index();
529 log_idx.m_ticket_number = m_ticket_number;
530 log_idx.m_txid = txid;
531 log_idx.m_logs = addr_log.second;
532 ret[log_index_key(addr_log.first)] = make_buffer(log_idx);
533 }
534 return ret;
535 }
536
537 auto evm_host::should_retry() const -> bool {
538 return m_retry;
539 }
540
541 void evm_host::insert_account(const evmc::address& addr,
542 const evm_account& acc) {
543 m_accounts.insert({addr, {acc, !m_is_readonly_run}});
544 m_accessed_addresses.insert(addr);
545 m_init_state = m_accounts;
546 }
547
548 void evm_host::transfer(const evmc::address& from,
549 const evmc::address& to,
550 const evmc::uint256be& value) {
551 const auto* log_str = "evm_transfer";
552 m_log->trace(log_str, to_hex(from), to_hex(to));
553 auto maybe_acc = get_account(from, !m_is_readonly_run);
554 assert(maybe_acc.has_value());
555 auto& acc = maybe_acc.value();
556 auto val = value;
557 if(evmc::is_zero(value)) {
558 // Special case: destruct the from account if we're
559 // transfering the entire account balance
560 val = acc.m_balance;
561 acc.m_destruct = true;
562 }
563 acc.m_balance = acc.m_balance - val;
564 m_accounts[from] = {acc, !m_is_readonly_run};
565
566 auto maybe_to_acc = get_account(to, !m_is_readonly_run);
567 if(!maybe_to_acc.has_value()) {
568 // Create the to account if it doesn't exist
569 maybe_to_acc = evm_account();
570 }
571 auto& to_acc = maybe_to_acc.value();
572 to_acc.m_balance = to_acc.m_balance + val;
573 m_accounts[to] = {to_acc, !m_is_readonly_run};
574 }
575
576 void evm_host::finalize(int64_t gas_left, int64_t gas_used) {
577 if(!m_is_readonly_run) {
578 auto maybe_acc = get_account(m_tx_context.tx_origin, true);
579 assert(maybe_acc.has_value());
580 auto& acc = maybe_acc.value();
581 auto gas_refund = evmc::uint256be(static_cast<uint64_t>(gas_left))
582 * m_tx_context.tx_gas_price;
583 acc.m_balance = acc.m_balance + gas_refund;
584 m_accounts[m_tx_context.tx_origin] = {acc, true};
585 }
586 m_receipt.m_gas_used
587 = evmc::uint256be(static_cast<uint64_t>(gas_used));
588 m_receipt.m_timestamp = static_cast<uint64_t>(
589 std::chrono::duration_cast<std::chrono::seconds>(
590 std::chrono::system_clock::now().time_since_epoch())
591 .count());
592 }
593
595 m_accounts = m_init_state;
596 }
597
598 auto evm_host::is_precompile(const evmc::address& addr) -> bool {
599 auto addr_copy = addr;
600 constexpr auto precompile_suffix_sz = sizeof(uint16_t);
601 std::memset(
602 &addr_copy.bytes[sizeof(addr_copy.bytes) - precompile_suffix_sz],
603 0,
604 precompile_suffix_sz);
605 return evmc::is_zero(addr_copy)
606 && addr.bytes[sizeof(addr_copy.bytes) - 1] != 0;
607 }
608
609 auto evm_host::get_account_storage(const evmc::address& addr,
610 const evmc::bytes32& key,
611 bool write) const
612 -> std::optional<evmc::bytes32> {
613 m_log->trace("EVM request account storage:",
614 to_hex(addr),
615 to_hex(key));
616
617 if(is_precompile(addr)) {
618 // Precompile contract, return empty account
619 m_accessed_addresses.insert(addr);
620 return std::nullopt;
621 }
622
623 auto it = m_account_storage.find(addr);
624 if(it != m_account_storage.end()) {
625 auto& m = it->second;
626 auto itt = m.find(key);
627 if(itt != m.end() && (itt->second.second || !write)) {
628 return itt->second.first;
629 }
630 }
631
632 auto elem_key = make_buffer(storage_key{addr, key});
633 auto maybe_v = get_key(elem_key, write);
634 if(!maybe_v.has_value()) {
635 return std::nullopt;
636 }
637
638 m_accessed_addresses.insert(addr);
639 auto& v = maybe_v.value();
640 if(v.size() == 0) {
641 m_account_storage[addr][key] = {std::nullopt, write};
642 return std::nullopt;
643 }
644 auto maybe_data = from_buffer<evmc::bytes32>(v);
645 assert(maybe_data.has_value());
646 auto& data = maybe_data.value();
647 m_account_storage[addr][key] = {data, write};
648 return data;
649 }
650
651 auto evm_host::get_account_code(const evmc::address& addr,
652 bool write) const
653 -> std::optional<evm_account_code> {
654 m_log->trace("EVM request account code:", to_hex(addr));
655
656 if(is_precompile(addr)) {
657 // Precompile contract, return empty account
658 m_accessed_addresses.insert(addr);
659 return std::nullopt;
660 }
661
662 auto it = m_account_code.find(addr);
663 if(it != m_account_code.end() && (it->second.second || !write)) {
664 return it->second.first;
665 }
666
667 auto elem_key = make_buffer(code_key{addr});
668 auto maybe_v = get_key(elem_key, write);
669 if(!maybe_v.has_value()) {
670 return std::nullopt;
671 }
672
673 m_accessed_addresses.insert(addr);
674 auto& v = maybe_v.value();
675 if(v.size() == 0) {
676 m_account_code[addr] = {std::nullopt, write};
677 return std::nullopt;
678 }
679 auto maybe_code = from_buffer<evm_account_code>(v);
680 assert(maybe_code.has_value());
681 auto& code = maybe_code.value();
682 m_account_code[addr] = {code, write};
683 return code;
684 }
685
686 auto evm_host::get_key(const cbdc::buffer& key, bool write) const
687 -> std::optional<broker::value_type> {
688 const auto* log_str = "get_key";
689 auto res_prom
690 = std::promise<broker::interface::try_lock_return_type>();
691 auto res_fut = res_prom.get_future();
692
693 m_log->trace(m_ticket_number, log_str, key.to_hex(), "write =", write);
694
695 auto ret = m_try_lock_callback(
696 key,
697 write ? broker::lock_type::write : broker::lock_type::read,
699 res_prom.set_value(res);
700 });
701
702 if(!ret) {
703 m_log->trace(m_ticket_number,
704 "failed to make try_lock request, retrying");
705 m_retry = true;
706 return std::nullopt;
707 }
708
709 for(size_t i = 0;; i++) {
710 auto status = res_fut.wait_for(std::chrono::seconds(1));
711 if(status == std::future_status::ready) {
712 break;
713 }
714 m_log->trace(m_ticket_number,
715 "still waits for",
716 key.to_hex(),
717 write,
718 i);
719 }
720
721 auto res = res_fut.get();
722
723 m_log->trace(m_ticket_number, "got key", key.to_hex());
724
725 return std::visit(
727 [&](broker::value_type& val) -> std::optional<cbdc::buffer> {
728 return val;
729 },
730 [&](broker::interface::error_code& /* err */)
731 -> std::optional<cbdc::buffer> {
732 m_retry = true;
733 return std::nullopt;
734 },
735 [&](runtime_locking_shard::shard_error& /* err */)
736 -> std::optional<cbdc::buffer> {
737 m_retry = true;
738 return std::nullopt;
739 },
740 },
741 res);
742 }
743
744 auto evm_host::execute(const evmc_message& msg,
745 const uint8_t* code,
746 size_t code_size) -> evmc::Result {
747 // Make VM instance if we didn't already
748 if(!m_vm) {
749 m_vm = std::make_unique<evmc::VM>(evmc_create_evmone());
750 if(!(*m_vm) || !m_vm->is_abi_compatible()) {
751 m_log->error("Unable to load EVM implementation");
752 const auto gas_refund = 0;
753 auto res = evmc::make_result(evmc_status_code::EVMC_FAILURE,
754 msg.gas,
755 gas_refund,
756 nullptr,
757 0);
758 return evmc::Result(res);
759 }
760 }
761
762 auto res = m_vm->execute(*this,
763 EVMC_LATEST_STABLE_REVISION,
764 msg,
765 code,
766 code_size);
767
768 return res;
769 }
770}
Buffer to store and retrieve byte data.
Definition buffer.hpp:15
auto get_log_index_keys() const -> std::vector< cbdc::buffer >
Return the keys of the log indexes - these are sha256(addr, ticket) and will get value 1 - to indicat...
Definition host.cpp:460
auto should_retry() const -> bool
Returns whether the transaction needs to be retried due to a transient error.
Definition host.cpp:537
auto get_balance(const evmc::address &addr) const noexcept -> evmc::uint256be override final
Definition host.cpp:150
auto get_code_hash(const evmc::address &addr) const noexcept -> evmc::bytes32 override final
Definition host.cpp:175
auto get_tx_context() const noexcept -> evmc_tx_context override final
Definition host.cpp:375
void finalize(int64_t gas_left, int64_t gas_used)
Finalizes the state updates resulting from the transaction.
Definition host.cpp:576
void insert_account(const evmc::address &addr, const evm_account &acc)
Inserts an account into the host.
Definition host.cpp:541
void revert()
Set the state updates to revert the transaction changes due to a contract error.
Definition host.cpp:594
auto ticket_number_key(std::optional< interface::ticket_number_type > tn=std::nullopt) const -> cbdc::buffer
Return the key for the host's ticket number, which is the hash of the ticket number's serialized repr...
Definition host.cpp:427
auto get_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept -> evmc::bytes32 override final
Definition host.cpp:95
auto account_exists(const evmc::address &addr) const noexcept -> bool override
Definition host.cpp:83
evm_host(std::shared_ptr< logging::log > log, interface::try_lock_callback_type try_lock_callback, evmc_tx_context tx_context, evm_tx tx, bool is_readonly_run, interface::ticket_number_type ticket_number)
Constructs a new host instance.
Definition host.cpp:23
auto copy_code(const evmc::address &addr, size_t code_offset, uint8_t *buffer_data, size_t buffer_size) const noexcept -> size_t override final
Definition host.cpp:192
auto access_storage(const evmc::address &addr, const evmc::bytes32 &key) noexcept -> evmc_access_status override final
Definition host.cpp:414
auto call(const evmc_message &msg) noexcept -> evmc::Result override final
Definition host.cpp:308
auto get_block_hash(int64_t number) const noexcept -> evmc::bytes32 override final
Definition host.cpp:379
void emit_log(const evmc::address &addr, const uint8_t *data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final
Definition host.cpp:386
auto log_index_key(evmc::address addr, std::optional< interface::ticket_number_type > tn=std::nullopt) const -> cbdc::buffer
Return the key for the indicator of the existence of logs for a particular address at a particular ti...
Definition host.cpp:443
auto set_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept -> evmc_storage_status override final
Definition host.cpp:104
auto selfdestruct(const evmc::address &addr, const evmc::address &beneficiary) noexcept -> bool override final
Definition host.cpp:217
auto get_code_size(const evmc::address &addr) const noexcept -> size_t override final
Definition host.cpp:162
auto access_account(const evmc::address &addr) noexcept -> evmc_access_status override final
Definition host.cpp:404
auto get_state_updates() const -> runtime_locking_shard::state_update_type
Return the changes to the state resulting from transaction execution.
Definition host.cpp:481
std::function< bool(broker::key_type, broker::lock_type, broker::interface::try_lock_callback_type)> try_lock_callback_type
Callback function type for acquiring locks during function execution.
parsec::ticket_machine::ticket_number_type ticket_number_type
Type alias for a ticket number.
std::variant< value_type, error_code, runtime_locking_shard::shard_error > try_lock_return_type
Return type from a try lock operation.
error_code
Error codes returned by broker operations.
std::vector< uint8_t > evm_account_code
Type alias for EVM account code.
auto to_hex(const evmc::address &addr) -> std::string
auto contract_address2(const evmc::address &sender, const evmc::bytes32 &salt, const cbdc::hash_t &bytecode_hash) -> evmc::address
Calculates a contract address for the CREATE2 call keccak256(0xFF | sender | salt | keccak256(bytecod...
Definition address.cpp:33
auto contract_address(const evmc::address &sender, const evmc::uint256be &nonce) -> evmc::address
Calculates a contract address for the CREATE call keccak256(rlp([sender,nonce]))
Definition address.cpp:20
auto tx_id(const cbdc::parsec::agent::runner::evm_tx &tx, uint64_t chain_id) -> cbdc::hash_t
Calculate ethereum-compatible txid.
runtime_locking_shard::value_type value_type
Shard value type.
std:: unordered_map< key_type, value_type, hashing::const_sip_hash< key_type > > state_update_type
Type for state updates to a shard. A map of keys and their new values.
@ write
Write lock. Only one ticket can hold this lock at a time.
@ transfer
Base token transfer.
overloaded(Ts...) -> overloaded< Ts... >
auto from_buffer(nuraft::buffer &buf) -> std::optional< T >
Deserialize object of given type from a nuraft::buffer.
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
@ buffer
A singular RLP value (byte array)
auto keccak_data(const void *data, size_t len) -> hash_t
Calculates the Keccak256 hash of the specified data.
auto make_buffer(const T &obj) -> std::enable_if_t< std::is_same_v< B, nuraft::ptr< nuraft::buffer > >, nuraft::ptr< nuraft::buffer > >
Serialize object into nuraft::buffer using a cbdc::nuraft_serializer.
uint64_t m_timestamp
Timestamp of the transaction - needed to provide a timestamp in pretend blocks.
cbdc::parsec::agent::runner::interface::ticket_number_type m_ticket_number
Ticket number that ran this TX - needed to map to pretend blocks.
std::vector< evm_log > m_logs
List of logs emitted during transaction.