OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
agent/runners/evm/impl.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 "impl.hpp"
7
8#include "crypto/sha256.h"
9#include "format.hpp"
10#include "host.hpp"
11#include "math.hpp"
12#include "serialization.hpp"
13#include "signature.hpp"
14#include "util.hpp"
16
17#include <future>
18
20 evm_runner::evm_runner(std::shared_ptr<logging::log> logger,
21 const cbdc::parsec::config& cfg,
23 parameter_type param,
24 bool is_readonly_run,
25 run_callback_type result_callback,
26 try_lock_callback_type try_lock_callback,
27 std::shared_ptr<secp256k1_context> secp,
28 std::shared_ptr<thread_pool> t_pool,
29 ticket_number_type ticket_number)
30 : interface(std::move(logger),
31 cfg,
32 std::move(function),
33 std::move(param),
34 is_readonly_run,
35 std::move(result_callback),
36 std::move(try_lock_callback),
37 std::move(secp),
38 std::move(t_pool),
39 ticket_number) {}
40
42 for(auto& t : m_evm_threads) {
43 if(t.joinable()) {
44 t.join();
45 }
46 }
47 }
48
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 ",
52 m_function.size());
53 m_result_callback(error_code::function_load);
54 return;
55 }
56
57 static constexpr uint8_t invalid_function = 255;
58 uint8_t f = invalid_function;
59 std::memcpy(&f, m_function.data(), sizeof(uint8_t));
60 if(f
61 > static_cast<uint8_t>(evm_runner_function::read_account_storage)) {
62 m_log->error("Unknown EVM runner function ", f);
63 m_result_callback(error_code::function_load);
64 return;
65 }
66
67 bool success{false};
68 switch(evm_runner_function(f)) {
70 success = run_execute_real_transaction();
71 break;
73 success = run_get_account();
74 break;
76 success = run_execute_dryrun_transaction();
77 break;
79 success = run_get_account_code();
80 break;
82 success = run_get_transaction();
83 break;
85 success = run_get_transaction_receipt();
86 break;
88 success = run_get_block_number();
89 break;
91 success = run_get_block();
92 break;
94 success = run_get_logs();
95 break;
97 success = run_get_account(); // m_param contains the right key
98 // already
99 break;
100 default:
101 m_result_callback(error_code::function_load);
102 break;
103 }
104
105 if(!success) {
106 m_result_callback(error_code::internal_error);
107 }
108 }
109
110 auto evm_runner::run_get_account() -> bool {
111 return m_try_lock_callback(
112 m_param,
113 broker::lock_type::read,
114 [this](const broker::interface::try_lock_return_type& res) {
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);
118 return;
119 }
120 auto v = std::get<broker::value_type>(res);
122 ret[m_param] = v;
123 m_result_callback(ret);
124 });
125 }
126
127 auto evm_runner::run_get_block_number() -> bool {
128 return m_try_lock_callback(
129 m_param,
130 broker::lock_type::read,
133 ret[m_param]
134 = cbdc::make_buffer(evmc::uint256be(m_ticket_number));
135 m_result_callback(ret);
136 });
137 }
138
139 auto evm_runner::make_pretend_block(interface::ticket_number_type tn)
140 -> evm_pretend_block {
141 evm_pretend_block blk;
142 blk.m_ticket_number = tn;
143 blk.m_transactions = {};
144 return blk;
145 }
146
147 auto evm_runner::run_get_block() -> bool {
148 // m_param contains the raw serialized block number - need to decrypt
149 // that first and then hash it to get to the key the
150 // block(ticket)number to txid mapping is stored under in the shard.
151
152 auto maybe_tn = cbdc::from_buffer<evmc::uint256be>(m_param);
153 if(!maybe_tn) {
154 return false;
155 }
156 auto tn = to_uint64(maybe_tn.value());
157 auto tn_key = m_host->ticket_number_key(tn);
158
159 auto success = m_try_lock_callback(
160 tn_key,
161 broker::lock_type::read,
162 [this, tn](const broker::interface::try_lock_return_type& res) {
163 if(!std::holds_alternative<broker::value_type>(res)) {
165 auto blk = make_pretend_block(tn);
166 ret[m_param] = make_buffer(blk);
167 m_result_callback(ret);
168 return;
169 }
170
171 auto v = std::get<broker::value_type>(res);
172 auto maybe_txid = from_buffer<cbdc::hash_t>(v);
173 if(!maybe_txid) {
175 auto blk = make_pretend_block(tn);
176 ret[m_param] = make_buffer(blk);
177 m_result_callback(ret);
178 return;
179 }
180
181 lock_tx_receipt(v, tn);
182 });
183
184 return success;
185 }
186
187 void evm_runner::lock_tx_receipt(const broker::value_type& value,
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");
193 m_result_callback(error_code::function_load);
194 return;
195 }
196
197 auto v = std::get<broker::value_type>(res);
198 auto maybe_tx_receipt = from_buffer<evm_tx_receipt>(v);
199 if(!maybe_tx_receipt) {
200 m_log->error("Ticket number had TXID, but TX "
201 "receipt could not be deserialized");
202 m_result_callback(error_code::function_load);
203 return;
204 }
205
207 auto blk = make_pretend_block(ticket_number);
208 blk.m_transactions.push_back(maybe_tx_receipt.value());
209 ret[m_param] = make_buffer(blk);
210 m_result_callback(ret);
211 return;
212 };
213
214 if(!m_try_lock_callback(value, broker::lock_type::read, cb)) {
215 m_log->error("Could not send request for TX data");
216 m_result_callback(error_code::function_load);
217 }
218 }
219
220 auto evm_runner::run_get_logs() -> bool {
221 m_log->info(m_ticket_number, "run_get_logs started");
222 // m_param contains the serialized evm_log_query
223 auto maybe_qry = cbdc::from_buffer<evm_log_query>(m_param);
224 if(!maybe_qry) {
225 return false;
226 }
227 auto qry = maybe_qry.value();
228
229 // First, determine the keys to query for log existence
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));
234 }
235 }
236
237 m_log->info(m_ticket_number,
238 "getting",
239 keys.size(),
240 "keys from shards");
241
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(
247 key,
248 broker::lock_type::read,
249 [this,
250 acquired,
251 log_indexes,
252 log_indexes_mut,
253 key_count = keys.size(),
255 handle_get_logs_try_lock_response(qry,
256 log_indexes,
257 acquired,
258 log_indexes_mut,
259 key_count,
260 res);
261 });
262 if(!success) {
263 m_log->error("Unable to lock logs index key");
264 m_result_callback(error_code::internal_error);
265 return false;
266 }
267 }
268
269 return true;
270 }
271
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,
277 size_t key_count,
279 if(!std::holds_alternative<broker::value_type>(res)) {
280 m_log->error("Unable to read log key");
281 m_result_callback(error_code::function_load);
282 return;
283 }
284
285 m_log->info(m_ticket_number, "got value from shard");
286
287 auto v = std::get<broker::value_type>(res);
288 auto maybe_logs = cbdc::from_buffer<evm_log_index>(v);
289 if(maybe_logs) {
290 // Found potentially relevant logs, add
291 std::unique_lock<std::mutex> lck(*log_indexes_mut);
292 log_indexes->push_back(maybe_logs.value());
293 }
294 if(++(*acquired) == key_count) {
295 handle_complete_get_logs(qry, log_indexes_mut, log_indexes);
296 }
297 }
298
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",
305 log_indexes->size(),
306 "logs");
307
308 // Scanned them all - finish
309 std::unique_lock<std::mutex> lck(*log_indexes_mut);
310
311 // Filter the final logs by topics
312 auto final_logs = std::vector<evm_log_index>();
313 for(auto& log_idx : *log_indexes) {
314 auto match = false;
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) {
319 match = true;
320 break;
321 }
322 }
323 if(match) {
324 break;
325 }
326 }
327 }
328 if(match) {
329 final_logs.push_back(log_idx);
330 }
331 }
332 lck.unlock();
333
334 m_log->info(m_ticket_number,
335 "returning",
336 final_logs.size(),
337 "filtered log indexes");
338
340 ret[m_param] = make_buffer(final_logs);
341 m_result_callback(ret);
342 }
343
344 auto evm_runner::run_get_transaction_receipt() -> bool {
345 auto success = m_try_lock_callback(
346 m_param,
347 broker::lock_type::read,
348 [this](const broker::interface::try_lock_return_type& res) {
349 if(!std::holds_alternative<broker::value_type>(res)) {
350 m_log->error(
351 "Failed to read transaction receipt from shards");
352 m_result_callback(error_code::function_load);
353 return;
354 }
355 auto v = std::get<broker::value_type>(res);
357 ret[m_param] = v;
358 m_result_callback(ret);
359 });
360
361 return success;
362 }
363
364 auto evm_runner::run_get_transaction() -> bool {
365 auto success = m_try_lock_callback(
366 m_param,
367 broker::lock_type::read,
368 [this](const broker::interface::try_lock_return_type& res) {
369 if(!std::holds_alternative<broker::value_type>(res)) {
370 m_log->error(
371 "Failed to read transaction receipt from shards");
372 m_result_callback(error_code::function_load);
373 return;
374 }
375 auto v = std::get<broker::value_type>(res);
377
378 m_log->trace("Read transaction receipt: ", v.to_hex());
379
380 auto maybe_receipt = cbdc::from_buffer<evm_tx_receipt>(v);
381 if(!maybe_receipt.has_value()) {
382 m_log->error("Failed to deserialize transaction receipt");
383 m_result_callback(error_code::function_load);
384 return;
385 }
386 ret[m_param] = make_buffer(maybe_receipt.value().m_tx);
387 m_result_callback(ret);
388 });
389
390 return success;
391 }
392
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());
396 auto key = make_buffer(code_key{addr});
397 auto success = m_try_lock_callback(
398 key,
399 broker::lock_type::read,
400 [this](const broker::interface::try_lock_return_type& res) {
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);
404 return;
405 }
406 auto v = std::get<broker::value_type>(res);
408 ret[m_param] = v;
409 m_result_callback(ret);
410 });
411
412 return success;
413 }
414
415 auto evm_runner::run_execute_real_transaction() -> bool {
416 auto maybe_tx = cbdc::from_buffer<evm_tx>(m_param);
417 if(!maybe_tx.has_value()) {
418 m_log->error("Unable to deserialize transaction");
419 m_result_callback(error_code::function_load);
420 return true;
421 }
422 m_tx = std::move(maybe_tx.value());
423
424 auto maybe_from = check_signature(m_tx, m_secp);
425 if(!maybe_from.has_value()) {
426 m_log->error("Transaction signature is invalid");
427 m_result_callback(error_code::exec_error);
428 return true;
429 }
430 auto from = maybe_from.value();
431 return run_execute_transaction(from, false);
432 }
433
434 auto evm_runner::run_execute_dryrun_transaction() -> bool {
435 auto maybe_tx = cbdc::from_buffer<evm_dryrun_tx>(m_param);
436 if(!maybe_tx.has_value()) {
437 m_log->error("Unable to deserialize transaction");
438 m_result_callback(error_code::function_load);
439 return true;
440 }
441 auto& dryrun_tx = maybe_tx.value();
442 m_tx = std::move(dryrun_tx.m_tx);
443
444 return run_execute_transaction(dryrun_tx.m_from, true);
445 }
446
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);
451
452 auto min_gas = base_gas;
453 if(!evmtx.m_to.has_value()) {
454 min_gas = min_gas + creation_gas;
455 }
456
457 return std::make_pair(
458 min_gas,
459 !(evmtx.m_gas_limit < min_gas && !is_readonly_run));
460 }
461
462 auto evm_runner::make_message(const evmc::address& from,
463 const evm_tx& evmtx,
464 bool is_readonly_run)
465 -> std::pair<evmc_message, bool> {
466 auto msg = evmc_message();
467
468 auto [min_gas, enough_gas] = check_base_gas(evmtx, is_readonly_run);
469 if(!enough_gas) {
470 return std::make_pair(msg, false);
471 }
472
473 // Note that input_data is a const reference to the input buffer. The
474 // buffer itself must remain in scope while msg is being used. Wrap tx
475 // in a shared_ptr and provide it to the thread using msg.
476 msg.input_data = evmtx.m_input.data();
477 msg.input_size = evmtx.m_input.size();
478 msg.depth = 0;
479
480 // Determine transaction type
481 if(!evmtx.m_to.has_value()) {
482 // Create contract transaction
483 msg.kind = EVMC_CREATE;
484 } else {
485 // Send transaction
486 msg.kind = EVMC_CALL;
487 msg.recipient = evmtx.m_to.value();
488 }
489
490 msg.sender = from;
491 msg.value = evmtx.m_value;
492 if(is_readonly_run) {
493 msg.gas = std::numeric_limits<int64_t>::max();
494 } else {
495 msg.gas
496 = static_cast<int64_t>(to_uint64(evmtx.m_gas_limit - min_gas));
497 }
498 return std::make_pair(msg, true);
499 }
500
501 auto evm_runner::make_tx_context(const evmc::address& from,
502 const evm_tx& evmtx,
503 bool is_readonly_run) -> evmc_tx_context {
504 auto tx_ctx = evmc_tx_context();
505 // TODO: consider setting block height to the TX ticket number
506 tx_ctx.block_number = 1;
507 auto now = std::chrono::system_clock::now();
508 auto timestamp
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));
516 } else {
517 tx_ctx.block_gas_limit = std::numeric_limits<int64_t>::max();
518 }
519 return tx_ctx;
520 }
521
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);
525
526 m_host = std::make_unique<evm_host>(m_log,
527 m_try_lock_callback,
528 tx_ctx,
529 m_tx,
530 is_readonly_run,
531 m_ticket_number);
532
533 auto [msg, enough_gas] = make_message(from, m_tx, is_readonly_run);
534 if(!enough_gas) {
535 m_log->trace("TX does not have enough base gas");
536 m_result_callback(error_code::exec_error);
537 return true;
538 }
539 m_msg = msg;
540
541 if(!is_readonly_run) {
542 m_log->trace(m_ticket_number,
543 "reading from account [",
544 to_hex(from),
545 "]");
546 auto addr_key = cbdc::make_buffer(from);
547 auto r = m_try_lock_callback(
548 addr_key,
549 broker::lock_type::write,
550 [this](const broker::interface::try_lock_return_type& res) {
551 m_log->trace(m_ticket_number, "read from account");
552 handle_lock_from_account(res);
553 });
554 if(!r) {
555 m_log->error(
556 "Failed to send try_lock request for from account");
557 m_result_callback(error_code::internal_error);
558 return false;
559 }
560 } else {
561 schedule_exec();
562 }
563
564 return true;
565 }
566
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));
573 m_result_callback(error_code::internal_error);
574 } else if(m_host->should_retry()) {
575 m_log->trace("Contract was wounded");
576 m_result_callback(error_code::wounded);
577 } else {
578 if(result.status_code == EVMC_REVERT) {
579 m_log->trace("Contract reverted");
580 m_host->revert();
581 }
582
583 auto out_buf = cbdc::buffer();
584 out_buf.append(result.output_data, result.output_size);
585 m_log->trace("EVM output data:", out_buf.to_hex());
586
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);
593 };
594 lock_index_keys(fn);
595 }
596 }
597
598 void evm_runner::lock_index_keys(const std::function<void()>& callback) {
599 auto keys = m_host->get_log_index_keys();
600 if(keys.empty()) {
601 callback();
602 return;
603 }
604 auto acquired = std::make_shared<std::atomic<size_t>>();
605 for(auto& key : keys) {
606 auto success = m_try_lock_callback(
607 key,
608 broker::lock_type::write,
609 [acquired, callback, key_count = keys.size()](
611 if(++(*acquired) == key_count) {
612 callback();
613 }
614 });
615 if(!success) {
616 m_log->error("Unable to lock logs index key");
617 m_result_callback(error_code::internal_error);
618 return;
619 }
620 }
621 }
622
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);
628 return;
629 }
630 auto v = std::get<broker::value_type>(res);
631 auto from_acc = evm_account();
632
633 // TODO: Start at zero?
634 if(v.size() > 0) {
635 auto maybe_from_acc = cbdc::from_buffer<evm_account>(v);
636 if(maybe_from_acc.has_value()) {
637 from_acc = maybe_from_acc.value();
638 }
639 }
640
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",
645 to_hex(m_tx.m_nonce),
646 "vs",
647 to_hex(exp_nonce));
648 m_result_callback(error_code::exec_error);
649 return;
650 }
651
652 // TODO: Priority fees for V2 transactions
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;
655
656 if(from_acc.m_balance < required_funds) {
657 m_log->error("From account has insufficient funds to cover gas "
658 "and tx value",
659 to_hex(from_acc.m_balance),
660 "vs",
661 to_hex(required_funds));
662 m_result_callback(error_code::exec_error);
663 return;
664 }
665
666 // Deduct gas
667 from_acc.m_balance = from_acc.m_balance - total_gas_cost;
668 // Increment nonce
669 from_acc.m_nonce = from_acc.m_nonce + evmc::uint256be(1);
670 m_host->insert_account(m_msg.sender, from_acc);
671
672 const auto txid_key = make_buffer(tx_id(m_tx));
673
674 m_log->trace(m_ticket_number, "locking TXID", txid_key.to_hex());
675
676 // Lock TXID key to store receipt later
677 auto maybe_sent = m_try_lock_callback(
678 txid_key,
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);
684 return;
685 }
686 m_log->trace(m_ticket_number, "locked TXID key");
687 lock_ticket_number_key();
688 });
689 if(!maybe_sent) {
690 m_log->error("Failed to send try_lock request for TX receipt");
691 m_result_callback(error_code::internal_error);
692 return;
693 }
694 }
695
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);
704 return;
705 }
706 m_log->trace(m_ticket_number, "locked ticket_number key");
707 schedule_exec();
708 });
709 if(!maybe_sent) {
710 m_log->error(
711 "Failed to send try_lock request for ticket_number key");
712 m_result_callback(error_code::internal_error);
713 }
714 }
715
716 void evm_runner::schedule_exec() {
717 auto fn = [this]() {
718 exec();
719 };
720 schedule(fn);
721 }
722
723 void evm_runner::schedule(const std::function<void()>& fn) {
724 if(m_threads) {
725 m_threads->push(fn);
726 return;
727 }
728 m_evm_threads.emplace_back(fn);
729 }
730
731 void evm_runner::schedule_run() {
732 auto fn = [this]() {
733 do_run();
734 };
735 schedule(fn);
736 }
737
738 auto evm_runner::run() -> bool {
739 schedule_run();
740 return true;
741 }
742}
Buffer to store and retrieve byte data.
Definition buffer.hpp:15
~evm_runner() override
Blocks until the transaction has completed and all processing threads have ended.
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.
@ 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.