8#include "crypto/sha256.h"
27 std::shared_ptr<secp256k1_context> secp,
28 std::shared_ptr<thread_pool> t_pool,
35 std::move(result_callback),
36 std::move(try_lock_callback),
42 for(
auto& t : m_evm_threads) {
49 void evm_runner::do_run() {
50 if(m_function.size() != 1) {
51 m_log->error(
"EVM runner expects 1 byte in m_function, got ",
57 static constexpr uint8_t invalid_function = 255;
58 uint8_t f = invalid_function;
59 std::memcpy(&f, m_function.data(),
sizeof(uint8_t));
62 m_log->error(
"Unknown EVM runner function ", f);
70 success = run_execute_real_transaction();
73 success = run_get_account();
76 success = run_execute_dryrun_transaction();
79 success = run_get_account_code();
82 success = run_get_transaction();
85 success = run_get_transaction_receipt();
88 success = run_get_block_number();
91 success = run_get_block();
94 success = run_get_logs();
97 success = run_get_account();
110 auto evm_runner::run_get_account() ->
bool {
111 return m_try_lock_callback(
113 broker::lock_type::read,
115 if(!std::holds_alternative<broker::value_type>(res)) {
116 m_log->error(
"Failed to read account from shards");
117 m_result_callback(error_code::function_load);
120 auto v = std::get<broker::value_type>(res);
123 m_result_callback(ret);
127 auto evm_runner::run_get_block_number() ->
bool {
128 return m_try_lock_callback(
130 broker::lock_type::read,
135 m_result_callback(ret);
140 -> evm_pretend_block {
141 evm_pretend_block blk;
142 blk.m_ticket_number = tn;
143 blk.m_transactions = {};
147 auto evm_runner::run_get_block() ->
bool {
157 auto tn_key = m_host->ticket_number_key(tn);
159 auto success = m_try_lock_callback(
161 broker::lock_type::read,
163 if(!std::holds_alternative<broker::value_type>(res)) {
165 auto blk = make_pretend_block(tn);
167 m_result_callback(ret);
171 auto v = std::get<broker::value_type>(res);
175 auto blk = make_pretend_block(tn);
177 m_result_callback(ret);
181 lock_tx_receipt(v, tn);
188 const ticket_number_type& ticket_number) {
189 auto cb = [
this, ticket_number](
191 if(!std::holds_alternative<broker::value_type>(res)) {
192 m_log->error(
"Ticket number had TXID, but TX not found");
197 auto v = std::get<broker::value_type>(res);
199 if(!maybe_tx_receipt) {
200 m_log->error(
"Ticket number had TXID, but TX "
201 "receipt could not be deserialized");
207 auto blk = make_pretend_block(ticket_number);
208 blk.m_transactions.push_back(maybe_tx_receipt.value());
210 m_result_callback(ret);
214 if(!m_try_lock_callback(value, broker::lock_type::read, cb)) {
215 m_log->error(
"Could not send request for TX data");
220 auto evm_runner::run_get_logs() ->
bool {
221 m_log->info(m_ticket_number,
"run_get_logs started");
227 auto qry = maybe_qry.value();
230 auto keys = std::vector<cbdc::buffer>();
231 for(
auto blk = qry.m_from_block; blk <= qry.m_to_block; blk++) {
232 for(
auto& it : qry.m_addresses) {
233 keys.push_back(m_host->log_index_key(it, blk));
237 m_log->info(m_ticket_number,
242 auto log_indexes_mut = std::make_shared<std::mutex>();
243 auto log_indexes = std::make_shared<std::vector<evm_log_index>>();
244 auto acquired = std::make_shared<std::atomic<size_t>>();
245 for(
auto& key : keys) {
246 auto success = m_try_lock_callback(
248 broker::lock_type::read,
253 key_count = keys.size(),
255 handle_get_logs_try_lock_response(qry,
263 m_log->error(
"Unable to lock logs index key");
264 m_result_callback(error_code::internal_error);
272 void evm_runner::handle_get_logs_try_lock_response(
273 const evm_log_query& qry,
274 const std::shared_ptr<std::vector<evm_log_index>>& log_indexes,
275 const std::shared_ptr<std::atomic<size_t>>& acquired,
276 const std::shared_ptr<std::mutex>& log_indexes_mut,
279 if(!std::holds_alternative<broker::value_type>(res)) {
280 m_log->error(
"Unable to read log key");
285 m_log->info(m_ticket_number,
"got value from shard");
287 auto v = std::get<broker::value_type>(res);
291 std::unique_lock<std::mutex> lck(*log_indexes_mut);
292 log_indexes->push_back(maybe_logs.value());
294 if(++(*acquired) == key_count) {
295 handle_complete_get_logs(qry, log_indexes_mut, log_indexes);
299 void evm_runner::handle_complete_get_logs(
300 const evm_log_query& qry,
301 const std::shared_ptr<std::mutex>& log_indexes_mut,
302 const std::shared_ptr<std::vector<evm_log_index>>& log_indexes) {
303 m_log->info(m_ticket_number,
304 "completed all queries, filtering",
309 std::unique_lock<std::mutex> lck(*log_indexes_mut);
312 auto final_logs = std::vector<evm_log_index>();
313 for(
auto& log_idx : *log_indexes) {
315 for(
auto& log : log_idx.m_logs) {
316 for(
auto& have_topic : log.m_topics) {
317 for(
const auto& want_topic : qry.m_topics) {
318 if(have_topic == want_topic) {
329 final_logs.push_back(log_idx);
334 m_log->info(m_ticket_number,
337 "filtered log indexes");
341 m_result_callback(ret);
344 auto evm_runner::run_get_transaction_receipt() ->
bool {
345 auto success = m_try_lock_callback(
347 broker::lock_type::read,
349 if(!std::holds_alternative<broker::value_type>(res)) {
351 "Failed to read transaction receipt from shards");
352 m_result_callback(error_code::function_load);
355 auto v = std::get<broker::value_type>(res);
358 m_result_callback(ret);
364 auto evm_runner::run_get_transaction() ->
bool {
365 auto success = m_try_lock_callback(
367 broker::lock_type::read,
369 if(!std::holds_alternative<broker::value_type>(res)) {
371 "Failed to read transaction receipt from shards");
372 m_result_callback(error_code::function_load);
375 auto v = std::get<broker::value_type>(res);
378 m_log->trace(
"Read transaction receipt: ", v.to_hex());
381 if(!maybe_receipt.has_value()) {
382 m_log->error(
"Failed to deserialize transaction receipt");
383 m_result_callback(error_code::function_load);
386 ret[m_param] =
make_buffer(maybe_receipt.value().m_tx);
387 m_result_callback(ret);
393 auto evm_runner::run_get_account_code() ->
bool {
394 auto addr = evmc::address();
395 std::memcpy(addr.bytes, m_param.data(), m_param.size());
397 auto success = m_try_lock_callback(
399 broker::lock_type::read,
401 if(!std::holds_alternative<broker::value_type>(res)) {
402 m_log->error(
"Failed to read account from shards");
403 m_result_callback(error_code::function_load);
406 auto v = std::get<broker::value_type>(res);
409 m_result_callback(ret);
415 auto evm_runner::run_execute_real_transaction() ->
bool {
417 if(!maybe_tx.has_value()) {
418 m_log->error(
"Unable to deserialize transaction");
419 m_result_callback(error_code::function_load);
422 m_tx = std::move(maybe_tx.value());
425 if(!maybe_from.has_value()) {
426 m_log->error(
"Transaction signature is invalid");
427 m_result_callback(error_code::exec_error);
430 auto from = maybe_from.value();
431 return run_execute_transaction(from,
false);
434 auto evm_runner::run_execute_dryrun_transaction() ->
bool {
436 if(!maybe_tx.has_value()) {
437 m_log->error(
"Unable to deserialize transaction");
438 m_result_callback(error_code::function_load);
441 auto& dryrun_tx = maybe_tx.value();
442 m_tx = std::move(dryrun_tx.m_tx);
444 return run_execute_transaction(dryrun_tx.m_from,
true);
447 auto evm_runner::check_base_gas(
const evm_tx& evmtx,
bool is_readonly_run)
448 -> std::pair<evmc::uint256be, bool> {
449 constexpr auto base_gas = evmc::uint256be(21000);
450 constexpr auto creation_gas = evmc::uint256be(32000);
452 auto min_gas = base_gas;
453 if(!evmtx.m_to.has_value()) {
454 min_gas = min_gas + creation_gas;
457 return std::make_pair(
459 !(evmtx.m_gas_limit < min_gas && !is_readonly_run));
462 auto evm_runner::make_message(
const evmc::address& from,
464 bool is_readonly_run)
465 -> std::pair<evmc_message, bool> {
466 auto msg = evmc_message();
468 auto [min_gas, enough_gas] = check_base_gas(evmtx, is_readonly_run);
470 return std::make_pair(msg,
false);
476 msg.input_data = evmtx.m_input.data();
477 msg.input_size = evmtx.m_input.size();
481 if(!evmtx.m_to.has_value()) {
483 msg.kind = EVMC_CREATE;
486 msg.kind = EVMC_CALL;
487 msg.recipient = evmtx.m_to.value();
491 msg.value = evmtx.m_value;
492 if(is_readonly_run) {
493 msg.gas = std::numeric_limits<int64_t>::max();
496 =
static_cast<int64_t
>(
to_uint64(evmtx.m_gas_limit - min_gas));
498 return std::make_pair(msg,
true);
501 auto evm_runner::make_tx_context(
const evmc::address& from,
503 bool is_readonly_run) -> evmc_tx_context {
504 auto tx_ctx = evmc_tx_context();
506 tx_ctx.block_number = 1;
507 auto now = std::chrono::system_clock::now();
509 = std::chrono::time_point_cast<std::chrono::seconds>(now);
510 tx_ctx.block_timestamp = timestamp.time_since_epoch().count();
511 if(!is_readonly_run) {
512 tx_ctx.tx_origin = from;
513 tx_ctx.tx_gas_price = evmtx.m_gas_price;
514 tx_ctx.block_gas_limit
515 =
static_cast<int64_t
>(
to_uint64(evmtx.m_gas_limit));
517 tx_ctx.block_gas_limit = std::numeric_limits<int64_t>::max();
522 auto evm_runner::run_execute_transaction(
const evmc::address& from,
523 bool is_readonly_run) ->
bool {
524 auto tx_ctx = make_tx_context(from, m_tx, is_readonly_run);
526 m_host = std::make_unique<evm_host>(m_log,
533 auto [msg, enough_gas] = make_message(from, m_tx, is_readonly_run);
535 m_log->trace(
"TX does not have enough base gas");
536 m_result_callback(error_code::exec_error);
541 if(!is_readonly_run) {
542 m_log->trace(m_ticket_number,
543 "reading from account [",
547 auto r = m_try_lock_callback(
549 broker::lock_type::write,
551 m_log->trace(m_ticket_number,
"read from account");
552 handle_lock_from_account(res);
556 "Failed to send try_lock request for from account");
557 m_result_callback(error_code::internal_error);
567 void evm_runner::exec() {
568 m_log->trace(
this,
"Started evm_runner exec");
569 auto result = m_host->call(m_msg);
570 if(result.status_code < 0) {
571 m_log->error(
"Internal error running EVM contract",
572 evmc::to_string(result.status_code));
574 }
else if(m_host->should_retry()) {
575 m_log->trace(
"Contract was wounded");
578 if(result.status_code == EVMC_REVERT) {
579 m_log->trace(
"Contract reverted");
584 out_buf.append(result.output_data, result.output_size);
585 m_log->trace(
"EVM output data:", out_buf.to_hex());
587 m_log->trace(
"Result status: ", result.status_code);
588 auto fn = [
this, gas_left = result.gas_left]() {
589 auto gas_used = m_msg.gas - gas_left;
590 m_host->finalize(gas_left, gas_used);
591 auto state_updates = m_host->get_state_updates();
592 m_result_callback(state_updates);
598 void evm_runner::lock_index_keys(
const std::function<
void()>& callback) {
599 auto keys = m_host->get_log_index_keys();
604 auto acquired = std::make_shared<std::atomic<size_t>>();
605 for(
auto& key : keys) {
606 auto success = m_try_lock_callback(
608 broker::lock_type::write,
609 [acquired, callback, key_count = keys.size()](
611 if(++(*acquired) == key_count) {
616 m_log->error(
"Unable to lock logs index key");
623 void evm_runner::handle_lock_from_account(
625 if(!std::holds_alternative<broker::value_type>(res)) {
626 m_log->debug(
"Failed to read account from shards");
627 m_result_callback(error_code::wounded);
630 auto v = std::get<broker::value_type>(res);
631 auto from_acc = evm_account();
636 if(maybe_from_acc.has_value()) {
637 from_acc = maybe_from_acc.value();
641 auto exp_nonce = from_acc.m_nonce + evmc::uint256be(1);
642 if(exp_nonce != m_tx.m_nonce) {
643 m_log->error(m_ticket_number,
644 "TX has incorrect nonce for from account",
648 m_result_callback(error_code::exec_error);
653 auto total_gas_cost = m_tx.m_gas_limit * m_tx.m_gas_price;
654 auto required_funds = m_tx.m_value + total_gas_cost;
656 if(from_acc.m_balance < required_funds) {
657 m_log->error(
"From account has insufficient funds to cover gas "
659 to_hex(from_acc.m_balance),
662 m_result_callback(error_code::exec_error);
667 from_acc.m_balance = from_acc.m_balance - total_gas_cost;
669 from_acc.m_nonce = from_acc.m_nonce + evmc::uint256be(1);
670 m_host->insert_account(m_msg.sender, from_acc);
674 m_log->trace(m_ticket_number,
"locking TXID", txid_key.to_hex());
677 auto maybe_sent = m_try_lock_callback(
679 broker::lock_type::write,
681 if(!std::holds_alternative<broker::value_type>(r)) {
682 m_log->debug(
"Failed to lock key for TX receipt");
683 m_result_callback(error_code::wounded);
686 m_log->trace(m_ticket_number,
"locked TXID key");
687 lock_ticket_number_key();
690 m_log->error(
"Failed to send try_lock request for TX receipt");
691 m_result_callback(error_code::internal_error);
696 void evm_runner::lock_ticket_number_key() {
697 auto maybe_sent = m_try_lock_callback(
698 m_host->ticket_number_key(),
699 broker::lock_type::write,
701 if(!std::holds_alternative<broker::value_type>(r)) {
702 m_log->debug(
"Failed to lock key for ticket_number");
703 m_result_callback(error_code::wounded);
706 m_log->trace(m_ticket_number,
"locked ticket_number key");
711 "Failed to send try_lock request for ticket_number key");
712 m_result_callback(error_code::internal_error);
716 void evm_runner::schedule_exec() {
723 void evm_runner::schedule(
const std::function<
void()>& fn) {
728 m_evm_threads.emplace_back(fn);
731 void evm_runner::schedule_run() {
738 auto evm_runner::run() ->
bool {
Buffer to store and retrieve byte data.
~evm_runner() override
Blocks until the transaction has completed and all processing threads have ended.
Interface for a contract runner.
std::function< void(run_return_type)> run_callback_type
Callback type for function 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.
@ wounded
Ticket wounded during execution.
@ internal_error
Internal Runner error.
@ function_load
Error loading function bytecode.
std::variant< value_type, error_code, runtime_locking_shard::shard_error > try_lock_return_type
Return type from a try lock operation.
evm_runner_function
Commands accepted by the EVM contract runner.
@ get_transaction_receipt
Return the receipt for a transaction.
@ get_block_number
Return just the ticket number to simulate getting the latest block.
@ read_account_code
Read the contract code of an account.
@ get_transaction
Return a previously completed transaction.
@ get_logs
Query the logs for a particular address, block range and topic filter.
@ get_block
Return a pretend block that is based on the ticket number, and the transaction (potentially) correspo...
@ execute_transaction
Execute a normal transaction.
@ read_account_storage
Read a specific key of an account's storage.
@ read_account
Read the metadata of an account.
@ dryrun_transaction
Execute a transaction without applying any changes.
auto to_hex(const evmc::address &addr) -> std::string
auto check_signature(const cbdc::parsec::agent::runner::evm_tx &tx, const std::shared_ptr< secp256k1_context > &ctx, uint64_t chain_id) -> std::optional< evmc::address >
Checks the signature of an EVM transaction.
auto to_uint64(const evmc::uint256be &v) -> uint64_t
Converts an uint256be to a uint64_t, ignoring higher order bits.
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.
auto from_buffer(nuraft::buffer &buf) -> std::optional< T >
Deserialize object of given type from a nuraft::buffer.
@ buffer
A singular RLP value (byte array)
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.
Configuration parameters for a phase two system.