9#include "crypto/sha256.h"
18#include <evmc/hex.hpp>
19#include <evmone/evmone.h>
25 evmc_tx_context tx_context,
29 : m_log(std::move(log)),
30 m_try_lock_callback(std::move(try_lock_callback)),
31 m_tx_context(tx_context),
33 m_is_readonly_run(is_readonly_run),
34 m_ticket_number(ticket_number) {
35 m_receipt.
m_tx = m_tx;
39 auto evm_host::get_account(
const evmc::address& addr,
bool write)
const
40 -> std::optional<evm_account> {
42 "EVM request account:",
47 if(is_precompile(addr)) {
49 m_accessed_addresses.insert(addr);
53 auto it = m_accounts.find(addr);
54 if(it != m_accounts.end() && (it->second.second || !write)) {
55 return it->second.first;
60 "EVM request account not in cache or wrong lock, getting [",
65 auto maybe_v = get_key(addr_key, write);
66 if(!maybe_v.has_value()) {
70 m_accessed_addresses.insert(addr);
71 auto& v = maybe_v.value();
73 m_accounts[addr] = {std::nullopt, write};
77 assert(maybe_acc.has_value());
78 auto& acc = maybe_acc.value();
79 m_accounts[addr] = {acc,
write};
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()) {
91 auto& acc = maybe_acc.value();
92 return !acc.m_destruct;
96 const evmc::bytes32& key)
const noexcept
98 const auto* log_str =
"evm_get_storage";
100 auto maybe_storage = get_account_storage(addr, key,
false);
101 return maybe_storage.value_or(evmc::bytes32{});
105 const evmc::bytes32& key,
106 const evmc::bytes32& value)
noexcept
107 -> evmc_storage_status {
108 const auto* log_str =
"evm_set_storage";
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());
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};
123 auto& acc = maybe_acc.value();
126 = get_account_storage(addr, key, !m_is_readonly_run);
127 auto prev_value = maybe_storage.value_or(evmc::bytes32{});
129 auto modified = acc.m_modified.find(key) != acc.m_modified.end();
130 if(!ret_val.has_value()) {
131 if(prev_value == value || modified) {
136 ret_val = EVMC_STORAGE_ASSIGNED;
137 }
else if(evmc::is_zero(value) && !modified) {
138 ret_val = EVMC_STORAGE_DELETED;
140 ret_val = EVMC_STORAGE_MODIFIED;
141 acc.m_modified.insert(key);
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();
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()) {
158 auto& acc = maybe_acc.value();
159 return acc.m_balance;
164 const auto* log_str =
"evm_get_code_size";
165 m_log->trace(log_str,
to_hex(addr));
166 if(is_precompile(addr)) {
171 auto maybe_code = get_account_code(addr,
false);
177 const auto* log_str =
"evm_get_code_hash";
178 m_log->trace(log_str,
to_hex(addr));
180 auto maybe_code = get_account_code(addr,
false);
181 if(!maybe_code.has_value()) {
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]);
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);
199 auto maybe_code = get_account_code(addr,
false);
200 if(!maybe_code.has_value()) {
204 const auto& code = maybe_code.value();
206 if(code_offset >= code.size()) {
210 const auto n = std::min(buffer_size, code.size() - code_offset);
212 std::copy_n(&code[code_offset], n, buffer_data);
218 const evmc::address& beneficiary)
noexcept
220 m_log->trace(
"EVM selfdestruct:",
to_hex(addr),
to_hex(beneficiary));
222 transfer(addr, beneficiary, evmc::uint256be{});
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");
231 evmc::make_result(evmc_status_code::EVMC_REVERT,
237 auto& sender_acc = maybe_sender_acc.value();
239 auto new_addr = evmc::address();
240 if(msg.kind == EVMC_CREATE) {
251 if(!evmc::is_zero(msg.value)) {
252 transfer(msg.sender, new_addr, msg.value);
256 m_receipt.m_create_address = new_addr;
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;
267 call_msg.gas = msg.gas;
269 auto res = execute(call_msg, msg.input_data, msg.input_size);
271 if(res.status_code == EVMC_SUCCESS) {
277 res.create_address = new_addr;
279 auto maybe_acc = get_account(new_addr, !m_is_readonly_run);
280 if(!maybe_acc.has_value()) {
281 maybe_acc = evm_account();
283 auto& acc = maybe_acc.value();
284 m_accounts[new_addr] = {acc, !m_is_readonly_run};
286 auto maybe_code = get_account_code(new_addr, !m_is_readonly_run);
287 if(!maybe_code.has_value()) {
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};
297 m_receipt.m_output_data.resize(res.output_size);
298 std::memcpy(m_receipt.m_output_data.data(),
302 = res.status_code == evmc_status_code::EVMC_SUCCESS;
309 if(msg.kind == EVMC_CREATE2 || msg.kind == EVMC_CREATE) {
314 bool is_native_value_transfer{
false};
315 if(!evmc::is_zero(msg.value) && msg.kind == EVMC_CALL) {
316 is_native_value_transfer =
true;
319 transfer(msg.sender, msg.recipient, msg.value);
323 = msg.kind == EVMC_DELEGATECALL || msg.kind == EVMC_CALLCODE
327 const auto code_size = get_code_size(code_addr);
330 const auto gas_refund = 0;
331 auto res = evmc::make_result(evmc_status_code::EVMC_SUCCESS,
339 if(is_native_value_transfer) {
340 m_receipt.m_success =
true;
343 return evmc::Result(res);
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);
352 inp.append(msg.input_data, msg.input_size);
353 m_log->trace(
"EVM call:",
360 auto res = execute(msg, code_buf.data(), code_buf.size());
364 m_receipt.m_output_data.resize(res.output_size);
365 std::memcpy(m_receipt.m_output_data.data(),
369 = res.status_code == evmc_status_code::EVMC_SUCCESS;
387 const evmc::address& addr,
391 const evmc::bytes32 topics[],
392 size_t topics_count)
noexcept {
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++) {
399 l.m_topics.push_back(topics[i]);
401 m_receipt.m_logs.push_back(l);
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;
410 m_accessed_addresses.insert(addr);
411 return EVMC_ACCESS_COLD;
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;
423 m_accessed_storage_keys.insert(elem);
424 return EVMC_ACCESS_COLD;
428 std::optional<interface::ticket_number_type> tn)
const
431 tn = m_ticket_number;
437 sha.Write(tn_buf.c_ptr(), tn_buf.size());
438 sha.Finalize(tn_hash.data());
445 std::optional<interface::ticket_number_type> tn)
const
448 tn = m_ticket_number;
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());
461 auto logs = get_sorted_logs();
462 auto keys = std::vector<cbdc::buffer>();
463 for(
auto& log : logs) {
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, {}});
476 ret[log.m_addr].push_back(log);
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) {
491 if(!acc->m_destruct) {
497 for(
auto& [addr, acc_code] : m_account_code) {
498 auto& [code, write] = acc_code;
499 if(!code.has_value() || !write) {
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) {
519 const auto tx = std::make_shared<evm_tx>(m_tx);
520 auto txid =
tx_id(*tx);
526 auto ordered_logs = get_sorted_logs();
527 for(
auto& addr_log : ordered_logs) {
529 log_idx.m_ticket_number = m_ticket_number;
530 log_idx.m_txid = txid;
531 log_idx.m_logs = addr_log.second;
543 m_accounts.insert({addr, {acc, !m_is_readonly_run}});
544 m_accessed_addresses.insert(addr);
545 m_init_state = m_accounts;
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";
553 auto maybe_acc = get_account(from, !m_is_readonly_run);
554 assert(maybe_acc.has_value());
555 auto& acc = maybe_acc.value();
557 if(evmc::is_zero(value)) {
561 acc.m_destruct =
true;
563 acc.m_balance = acc.m_balance - val;
564 m_accounts[from] = {acc, !m_is_readonly_run};
566 auto maybe_to_acc = get_account(to, !m_is_readonly_run);
567 if(!maybe_to_acc.has_value()) {
569 maybe_to_acc = evm_account();
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};
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};
587 = evmc::uint256be(
static_cast<uint64_t
>(gas_used));
589 std::chrono::duration_cast<std::chrono::seconds>(
590 std::chrono::system_clock::now().time_since_epoch())
595 m_accounts = m_init_state;
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);
602 &addr_copy.bytes[
sizeof(addr_copy.bytes) - precompile_suffix_sz],
604 precompile_suffix_sz);
605 return evmc::is_zero(addr_copy)
606 && addr.bytes[
sizeof(addr_copy.bytes) - 1] != 0;
609 auto evm_host::get_account_storage(
const evmc::address& addr,
610 const evmc::bytes32& key,
612 -> std::optional<evmc::bytes32> {
613 m_log->trace(
"EVM request account storage:",
617 if(is_precompile(addr)) {
619 m_accessed_addresses.insert(addr);
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;
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()) {
638 m_accessed_addresses.insert(addr);
639 auto& v = maybe_v.value();
641 m_account_storage[addr][key] = {std::nullopt,
write};
645 assert(maybe_data.has_value());
646 auto& data = maybe_data.value();
647 m_account_storage[addr][key] = {data,
write};
651 auto evm_host::get_account_code(
const evmc::address& addr,
653 -> std::optional<evm_account_code> {
654 m_log->trace(
"EVM request account code:",
to_hex(addr));
656 if(is_precompile(addr)) {
658 m_accessed_addresses.insert(addr);
662 auto it = m_account_code.find(addr);
663 if(it != m_account_code.end() && (it->second.second || !write)) {
664 return it->second.first;
668 auto maybe_v = get_key(elem_key, write);
669 if(!maybe_v.has_value()) {
673 m_accessed_addresses.insert(addr);
674 auto& v = maybe_v.value();
676 m_account_code[addr] = {std::nullopt,
write};
680 assert(maybe_code.has_value());
681 auto& code = maybe_code.value();
682 m_account_code[addr] = {code,
write};
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";
690 = std::promise<broker::interface::try_lock_return_type>();
691 auto res_fut = res_prom.get_future();
693 m_log->trace(m_ticket_number, log_str, key.to_hex(),
"write =", write);
695 auto ret = m_try_lock_callback(
697 write ? broker::lock_type::write : broker::lock_type::read,
699 res_prom.set_value(res);
703 m_log->trace(m_ticket_number,
704 "failed to make try_lock request, retrying");
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) {
714 m_log->trace(m_ticket_number,
721 auto res = res_fut.get();
723 m_log->trace(m_ticket_number,
"got key", key.to_hex());
731 -> std::optional<cbdc::buffer> {
735 [&](runtime_locking_shard::shard_error& )
736 -> std::optional<cbdc::buffer> {
744 auto evm_host::execute(
const evmc_message& msg,
746 size_t code_size) -> evmc::Result {
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,
758 return evmc::Result(res);
762 auto res = m_vm->execute(*
this,
763 EVMC_LATEST_STABLE_REVISION,
Buffer to store and retrieve byte data.
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...
auto should_retry() const -> bool
Returns whether the transaction needs to be retried due to a transient error.
auto get_balance(const evmc::address &addr) const noexcept -> evmc::uint256be override final
auto get_code_hash(const evmc::address &addr) const noexcept -> evmc::bytes32 override final
auto get_tx_context() const noexcept -> evmc_tx_context override final
void finalize(int64_t gas_left, int64_t gas_used)
Finalizes the state updates resulting from the transaction.
void insert_account(const evmc::address &addr, const evm_account &acc)
Inserts an account into the host.
void revert()
Set the state updates to revert the transaction changes due to a contract error.
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...
auto get_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept -> evmc::bytes32 override final
auto account_exists(const evmc::address &addr) const noexcept -> bool override
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.
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
auto access_storage(const evmc::address &addr, const evmc::bytes32 &key) noexcept -> evmc_access_status override final
auto call(const evmc_message &msg) noexcept -> evmc::Result override final
auto get_block_hash(int64_t number) const noexcept -> evmc::bytes32 override final
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
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...
auto set_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept -> evmc_storage_status override final
auto selfdestruct(const evmc::address &addr, const evmc::address &beneficiary) noexcept -> bool override final
auto get_code_size(const evmc::address &addr) const noexcept -> size_t override final
auto access_account(const evmc::address &addr) noexcept -> evmc_access_status override final
auto get_state_updates() const -> runtime_locking_shard::state_update_type
Return the changes to the state resulting from transaction execution.
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...
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]))
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.
evmc::uint256be m_gas_used
Gas used in transaction.
std::vector< evm_log > m_logs
List of logs emitted during transaction.
evm_tx m_tx
EVM transaction.
Type for account storage keys.