Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
apply_context.cpp
Go to the documentation of this file.
1#include <algorithm>
14#include <boost/container/flat_set.hpp>
15
16using boost::container::flat_set;
17
18namespace sysio { namespace chain {
19
20static inline void print_debug(account_name receiver, const action_trace& ar) {
21 if (!ar.console.empty()) {
22 auto prefix = fc::format_string(
23 "\n[(${a},${n})->${r}]",
25 ("a", ar.act.account)
26 ("n", ar.act.name)
27 ("r", receiver));
28 dlog(prefix + ": CONSOLE OUTPUT BEGIN =====================\n"
29 + ar.console
30 + prefix + ": CONSOLE OUTPUT END =====================" );
31 }
32}
33
35:control(con)
36,db(con.mutable_db())
37,trx_context(trx_ctx)
38,recurse_depth(depth)
39,first_receiver_action_ordinal(action_ordinal)
40,action_ordinal(action_ordinal)
41,idx64(*this)
42,idx128(*this)
43,idx256(*this)
44,idx_double(*this)
45,idx_long_double(*this)
46{
47 action_trace& trace = trx_ctx.get_action_trace(action_ordinal);
48 act = &trace.act;
49 receiver = trace.receiver;
50 context_free = trace.context_free;
51}
52
54{
55 auto start = fc::time_point::now();
56
57 digest_type act_digest;
58
59 const account_metadata_object* receiver_account = nullptr;
60
61 auto handle_exception = [&](const auto& e)
62 {
63 action_trace& trace = trx_context.get_action_trace( action_ordinal );
65 trace.except = e;
66 finalize_trace( trace, start );
67 throw;
68 };
69
70 try {
71 try {
72 action_return_value.clear();
73 receiver_account = &db.get<account_metadata_object,by_name>( receiver );
74 privileged = receiver_account->is_privileged();
75 auto native = control.find_apply_handler( receiver, act->account, act->name );
76 if( native ) {
78 control.check_contract_list( receiver );
80 }
81 (*native)( *this );
82 }
83
84 if( ( receiver_account->code_hash != digest_type() ) &&
85 ( !( act->account == config::system_account_name
86 && act->name == "setcode"_n
87 && receiver == config::system_account_name )
89 )
90 ) {
92 control.check_contract_list( receiver );
94 }
95 try {
96 control.get_wasm_interface().apply( receiver_account->code_hash, receiver_account->vm_type, receiver_account->vm_version, *this );
97 } catch( const wasm_exit& ) {}
98 }
99
101 const size_t checktime_interval = 10;
102 size_t counter = 0;
103 bool not_in_notify_context = (receiver == act->account);
104 const auto end = _account_ram_deltas.end();
105 for( auto itr = _account_ram_deltas.begin(); itr != end; ++itr, ++counter ) {
106 if( counter == checktime_interval ) {
108 counter = 0;
109 }
110 if( itr->delta > 0 && itr->account != receiver ) {
111 SYS_ASSERT( not_in_notify_context, unauthorized_ram_usage_increase,
112 "unprivileged contract cannot increase RAM usage of another account within a notify context: ${account}",
113 ("account", itr->account)
114 );
115 SYS_ASSERT( has_authorization( itr->account ), unauthorized_ram_usage_increase,
116 "unprivileged contract cannot increase RAM usage of another account that has not authorized the action: ${account}",
117 ("account", itr->account)
118 );
119 }
120 }
121 }
122 } FC_RETHROW_EXCEPTIONS( warn, "pending console output: ${console}", ("console", _pending_console_output) )
123
125 act_digest = generate_action_digest(
126 [this](const char* data, uint32_t datalen) {
128 },
129 *act,
131 );
132 } else {
133 act_digest = digest_type::hash(*act);
134 }
135 } catch ( const std::bad_alloc& ) {
136 throw;
137 } catch ( const boost::interprocess::bad_alloc& ) {
138 throw;
139 } catch( const fc::exception& e ) {
140 handle_exception(e);
141 } catch ( const std::exception& e ) {
143 handle_exception(wrapper);
144 }
145
146 // Note: It should not be possible for receiver_account to be invalidated because:
147 // * a pointer to an object in a chainbase index is not invalidated if other objects in that index are modified, removed, or added;
148 // * a pointer to an object in a chainbase index is not invalidated if the fields of that object are modified;
149 // * and, the *receiver_account object itself cannot be removed because accounts cannot be deleted in SYSIO.
150
151 action_trace& trace = trx_context.get_action_trace( action_ordinal );
152 trace.return_value = std::move(action_return_value);
153 trace.receipt.emplace();
154
155 action_receipt& r = *trace.receipt;
156 r.receiver = receiver;
157 r.act_digest = act_digest;
158 r.global_sequence = next_global_sequence();
159 r.recv_sequence = next_recv_sequence( *receiver_account );
160
161 const account_metadata_object* first_receiver_account = nullptr;
162 if( act->account == receiver ) {
163 first_receiver_account = receiver_account;
164 } else {
165 first_receiver_account = &db.get<account_metadata_object, by_name>(act->account);
166 }
167
168 r.code_sequence = first_receiver_account->code_sequence; // could be modified by action execution above
169 r.abi_sequence = first_receiver_account->abi_sequence; // could be modified by action execution above
170
171 for( const auto& auth : act->authorization ) {
172 r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor );
173 }
174
175 trx_context.executed_action_receipt_digests.emplace_back( r.digest() );
176
177 finalize_trace( trace, start );
178
179 if ( control.contracts_console() ) {
180 print_debug(receiver, trace);
181 }
182
183 if (auto dm_logger = control.get_deep_mind_logger())
184 {
185 dm_logger->on_end_action();
186 }
187}
188
190{
191 trace.account_ram_deltas = std::move( _account_ram_deltas );
192 _account_ram_deltas.clear();
193
194 trace.console = std::move( _pending_console_output );
195 _pending_console_output.clear();
196
197 trace.elapsed = fc::time_point::now() - start;
198}
199
201{
202 _notified.emplace_back( receiver, action_ordinal );
203 exec_one();
204 for( uint32_t i = 1; i < _notified.size(); ++i ) {
205 std::tie( receiver, action_ordinal ) = _notified[i];
206 exec_one();
207 }
208
209 if( _cfa_inline_actions.size() > 0 || _inline_actions.size() > 0 ) {
211 transaction_exception, "max inline action depth per transaction reached" );
212 }
213
214 for( uint32_t ordinal : _cfa_inline_actions ) {
215 trx_context.execute_action( ordinal, recurse_depth + 1 );
216 }
217
218 for( uint32_t ordinal : _inline_actions ) {
219 trx_context.execute_action( ordinal, recurse_depth + 1 );
220 }
221
222}
223
224bool apply_context::is_account( const account_name& account )const {
225 return nullptr != db.find<account_object,by_name>( account );
226}
227
229 account_name account, uint64_t& code_sequence, fc::sha256& code_hash, uint8_t& vm_type, uint8_t& vm_version) const {
230
231 auto obj = db.find<account_metadata_object,by_name>(account);
232 if(!obj || obj->code_hash == fc::sha256{}) {
233 if(obj)
234 code_sequence = obj->code_sequence;
235 else
236 code_sequence = 0;
237 code_hash = {};
238 vm_type = 0;
239 vm_version = 0;
240 } else {
241 code_sequence = obj->code_sequence;
242 code_hash = obj->code_hash;
243 vm_type = obj->vm_type;
244 vm_version = obj->vm_version;
245 }
246}
247
249 for( uint32_t i=0; i < act->authorization.size(); i++ ) {
250 if( act->authorization[i].actor == account ) {
251 return;
252 }
253 }
254 SYS_ASSERT( false, missing_auth_exception, "missing authority of ${account}", ("account",account));
255}
256
258 for( const auto& auth : act->authorization )
259 if( auth.actor == account )
260 return true;
261 return false;
262}
263
265 const permission_name& permission) {
266 for( uint32_t i=0; i < act->authorization.size(); i++ )
267 if( act->authorization[i].actor == account ) {
268 if( act->authorization[i].permission == permission ) {
269 return;
270 }
271 }
272 SYS_ASSERT( false, missing_auth_exception, "missing authority of ${account}/${permission}",
273 ("account",account)("permission",permission) );
274}
275
277 for( const auto& p : _notified )
278 if( p.first == code )
279 return true;
280 return false;
281}
282
284 if( !has_recipient(recipient) ) {
285 _notified.emplace_back(
286 recipient,
287 schedule_action( action_ordinal, recipient, false )
288 );
289
290 if (auto dm_logger = control.get_deep_mind_logger()) {
291 dm_logger->on_require_recipient();
292 }
293 }
294}
295
296
313 auto* code = control.db().find<account_object, by_name>(a.account);
314 SYS_ASSERT( code != nullptr, action_validate_exception,
315 "inline action's code account ${account} does not exist", ("account", a.account) );
316
317 bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_producing_block();
318 flat_set<account_name> actors;
319
321 bool send_to_self = (a.account == receiver);
322 bool inherit_parent_authorizations = (!disallow_send_to_self_bypass && send_to_self && (receiver == act->account) && control.is_producing_block());
323
324 flat_set<permission_level> inherited_authorizations;
325 if( inherit_parent_authorizations ) {
326 inherited_authorizations.reserve( a.authorization.size() );
327 }
328
329 for( const auto& auth : a.authorization ) {
330 auto* actor = control.db().find<account_object, by_name>(auth.actor);
332 "inline action's authorizing actor ${account} does not exist", ("account", auth.actor) );
334 "inline action's authorizations include a non-existent permission: ${permission}",
335 ("permission", auth) );
336 if( enforce_actor_whitelist_blacklist )
337 actors.insert( auth.actor );
338
339 if( inherit_parent_authorizations && std::find(act->authorization.begin(), act->authorization.end(), auth) != act->authorization.end() ) {
340 inherited_authorizations.insert( auth );
341 }
342 }
343
344 if( enforce_actor_whitelist_blacklist ) {
345 control.check_actor_list( actors );
346 }
347
348 if( !privileged && control.is_producing_block() ) {
351 inline_action_too_big_nonprivileged,
352 "inline action too big for nonprivileged account ${account}", ("account", a.account));
353 }
354 // No need to check authorization if replaying irreversible blocks or contract is privileged
355 if( !control.skip_auth_check() && !privileged ) {
356 try {
359 {},
360 {{receiver, config::sysio_code_name}},
362 std::bind(&transaction_context::checktime, &this->trx_context),
363 false,
365 inherited_authorizations
366 );
367
368 //QUESTION: Is it smart to allow a deferred transaction that has been delayed for some time to get away
369 // with sending an inline action that requires a delay even though the decision to send that inline
370 // action was made at the moment the deferred transaction was executed with potentially no forewarning?
371 } catch( const fc::exception& e ) {
372 if( disallow_send_to_self_bypass || !send_to_self ) {
373 throw;
374 } else if( control.is_producing_block() ) {
375 subjective_block_production_exception new_exception(FC_LOG_MESSAGE( error, "Authorization failure with inline action sent to self"));
376 for (const auto& log: e.get_log()) {
377 new_exception.append_log(log);
378 }
379 throw new_exception;
380 }
381 } catch( ... ) {
382 if( disallow_send_to_self_bypass || !send_to_self ) {
383 throw;
384 } else if( control.is_producing_block() ) {
385 SYS_THROW(subjective_block_production_exception, "Unexpected exception occurred validating inline action sent to self");
386 }
387 }
388 }
389
390 auto inline_receiver = a.account;
391 _inline_actions.emplace_back(
392 schedule_action( std::move(a), inline_receiver, false )
393 );
394
395 if (auto dm_logger = control.get_deep_mind_logger()) {
396 dm_logger->on_send_inline();
397 }
398}
399
401 auto* code = control.db().find<account_object, by_name>(a.account);
402 SYS_ASSERT( code != nullptr, action_validate_exception,
403 "inline action's code account ${account} does not exist", ("account", a.account) );
404
405 SYS_ASSERT( a.authorization.size() == 0, action_validate_exception,
406 "context-free actions cannot have authorizations" );
407
408 if( !privileged && control.is_producing_block() ) {
411 inline_action_too_big_nonprivileged,
412 "inline action too big for nonprivileged account ${account}", ("account", a.account));
413 }
414
415 auto inline_receiver = a.account;
416 _cfa_inline_actions.emplace_back(
417 schedule_action( std::move(a), inline_receiver, true )
418 );
419
420 if (auto dm_logger = control.get_deep_mind_logger()) {
421 dm_logger->on_send_context_free_inline();
422 }
423}
424
425
426void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx, bool replace_existing ) {
427 SYS_ASSERT( trx.context_free_actions.size() == 0, cfa_inside_generated_tx, "context free actions are not currently allowed in generated transactions" );
428
429 bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_producing_block()
431 trx_context.validate_referenced_accounts( trx, enforce_actor_whitelist_blacklist );
432
434 auto exts = trx.validate_and_extract_extensions();
435 if( exts.size() > 0 ) {
436 auto itr = exts.lower_bound( deferred_transaction_generation_context::extension_id() );
437
438 SYS_ASSERT( exts.size() == 1 && itr != exts.end(), invalid_transaction_extension,
439 "only the deferred_transaction_generation_context extension is currently supported for deferred transactions"
440 );
441
442 const auto& context = std::get<deferred_transaction_generation_context>(itr->second);
443
444 SYS_ASSERT( context.sender == receiver, ill_formed_deferred_transaction_generation_context,
445 "deferred transaction generaction context contains mismatching sender",
446 ("expected", receiver)("actual", context.sender)
447 );
448 SYS_ASSERT( context.sender_id == sender_id, ill_formed_deferred_transaction_generation_context,
449 "deferred transaction generaction context contains mismatching sender_id",
450 ("expected", sender_id)("actual", context.sender_id)
451 );
452 SYS_ASSERT( context.sender_trx_id == trx_context.packed_trx.id(), ill_formed_deferred_transaction_generation_context,
453 "deferred transaction generaction context contains mismatching sender_trx_id",
454 ("expected", trx_context.packed_trx.id())("actual", context.sender_trx_id)
455 );
456 } else {
458 trx.transaction_extensions,
461 );
462 }
463 trx.expiration = time_point_sec();
464 trx.ref_block_num = 0;
465 trx.ref_block_prefix = 0;
466 } else {
467 trx.expiration = control.pending_block_time() + fc::microseconds(999'999); // Rounds up to nearest second (makes expiration check unnecessary)
468 trx.set_reference_block(control.head_block_id()); // No TaPoS check necessary
469 }
470
471 // Charge ahead of time for the additional net usage needed to retire the deferred transaction
472 // whether that be by successfully executing, soft failure, hard failure, or expiration.
473 const auto& cfg = control.get_global_properties().configuration;
474 trx_context.add_net_usage( static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
475 + static_cast<uint64_t>(config::transaction_id_net_usage) ); // Will exit early if net usage cannot be payed.
476
477 auto delay = fc::seconds(trx.delay_sec);
478
480
481 if( !control.skip_auth_check() && !privileged ) { // Do not need to check authorization if replayng irreversible block or if contract is privileged
482 if( payer != receiver ) {
483 if( ram_restrictions_activated ) {
485 "cannot bill RAM usage of deferred transactions to another account within notify context"
486 );
488 "cannot bill RAM usage of deferred transaction to another account that has not authorized the action: ${payer}",
489 ("payer", payer)
490 );
491 } else {
492 require_authorization(payer);
493 }
494 }
495
496 // Originally this code bypassed authorization checks if a contract was deferring only actions to itself.
497 // The idea was that the code could already do whatever the deferred transaction could do, so there was no point in checking authorizations.
498 // But this is not true. The original implementation didn't validate the authorizations on the actions which allowed for privilege escalation.
499 // It would make it possible to bill RAM to some unrelated account.
500 // Furthermore, even if the authorizations were forced to be a subset of the current action's authorizations, it would still violate the expectations
501 // of the signers of the original transaction, because the deferred transaction would allow billing more CPU and network bandwidth than the maximum limit
502 // specified on the original transaction.
503 // So, the deferred transaction must always go through the authorization checking if it is not sent by a privileged contract.
504 // However, the old logic must still be considered because it cannot objectively change until a consensus protocol upgrade.
505
507
508 auto is_sending_only_to_self = [&trx]( const account_name& self ) {
509 bool send_to_self = true;
510 for( const auto& act : trx.actions ) {
511 if( act.account != self ) {
512 send_to_self = false;
513 break;
514 }
515 }
516 return send_to_self;
517 };
518
519 try {
521 .check_authorization( trx.actions,
522 {},
523 {{receiver, config::sysio_code_name}},
524 delay,
526 false
527 );
528 } catch( const fc::exception& e ) {
529 if( disallow_send_to_self_bypass || !is_sending_only_to_self(receiver) ) {
530 throw;
531 } else if( control.is_producing_block() ) {
532 subjective_block_production_exception new_exception(FC_LOG_MESSAGE( error, "Authorization failure with sent deferred transaction consisting only of actions to self"));
533 for (const auto& log: e.get_log()) {
534 new_exception.append_log(log);
535 }
536 throw new_exception;
537 }
538 } catch( ... ) {
539 if( disallow_send_to_self_bypass || !is_sending_only_to_self(receiver) ) {
540 throw;
541 } else if( control.is_producing_block() ) {
542 SYS_THROW(subjective_block_production_exception, "Unexpected exception occurred validating sent deferred transaction consisting only of actions to self");
543 }
544 }
545 }
546
547 uint32_t trx_size = 0;
548 if ( auto ptr = db.find<generated_transaction_object,by_sender_id>(boost::make_tuple(receiver, sender_id)) ) {
549 SYS_ASSERT( replace_existing, deferred_tx_duplicate, "deferred transaction with the same sender_id and payer already exists" );
550
551 bool replace_deferred_activated = control.is_builtin_activated(builtin_protocol_feature_t::replace_deferred);
552
553 SYS_ASSERT( replace_deferred_activated || !control.is_producing_block()
554 || control.all_subjective_mitigations_disabled(),
555 subjective_block_production_exception,
556 "Replacing a deferred transaction is temporarily disabled." );
557
558 if (auto dm_logger = control.get_deep_mind_logger()) {
559 dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", ptr->id)), "deferred_trx", "cancel", "deferred_trx_cancel");
560 }
561
562 uint64_t orig_trx_ram_bytes = config::billable_size_v<generated_transaction_object> + ptr->packed_trx.size();
563 if( replace_deferred_activated ) {
564 add_ram_usage( ptr->payer, -static_cast<int64_t>( orig_trx_ram_bytes ) );
565 } else {
566 control.add_to_ram_correction( ptr->payer, orig_trx_ram_bytes );
567 }
568
569 transaction_id_type trx_id_for_new_obj;
570 if( replace_deferred_activated ) {
571 trx_id_for_new_obj = trx.id();
572 } else {
573 trx_id_for_new_obj = ptr->trx_id;
574 }
575
576 if (auto dm_logger = control.get_deep_mind_logger()) {
577 dm_logger->on_cancel_deferred(deep_mind_handler::operation_qualifier::modify, *ptr);
578 }
579
580 // Use remove and create rather than modify because mutating the trx_id field in a modifier is unsafe.
581 db.remove( *ptr );
582 db.create<generated_transaction_object>( [&]( auto& gtx ) {
583 gtx.trx_id = trx_id_for_new_obj;
584 gtx.sender = receiver;
585 gtx.sender_id = sender_id;
586 gtx.payer = payer;
587 gtx.published = control.pending_block_time();
588 gtx.delay_until = gtx.published + delay;
589 gtx.expiration = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
590
591 trx_size = gtx.set( trx );
592
593 if (auto dm_logger = control.get_deep_mind_logger()) {
594 dm_logger->on_send_deferred(deep_mind_handler::operation_qualifier::modify, gtx);
595 dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gtx.id)), "deferred_trx", "update", "deferred_trx_add");
596 }
597 } );
598 } else {
599 db.create<generated_transaction_object>( [&]( auto& gtx ) {
600 gtx.trx_id = trx.id();
601 gtx.sender = receiver;
602 gtx.sender_id = sender_id;
603 gtx.payer = payer;
604 gtx.published = control.pending_block_time();
605 gtx.delay_until = gtx.published + delay;
606 gtx.expiration = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
607
608 trx_size = gtx.set( trx );
609
610 if (auto dm_logger = control.get_deep_mind_logger()) {
611 dm_logger->on_send_deferred(deep_mind_handler::operation_qualifier::none, gtx);
612 dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gtx.id)), "deferred_trx", "add", "deferred_trx_add");
613 }
614 } );
615 }
616
617 SYS_ASSERT( ram_restrictions_activated
618 || control.is_ram_billing_in_notify_allowed()
619 || (receiver == act->account) || (receiver == payer) || privileged,
620 subjective_block_production_exception,
621 "Cannot charge RAM to other accounts during notify."
622 );
623 add_ram_usage( payer, (config::billable_size_v<generated_transaction_object> + trx_size) );
624}
625
627 auto& generated_transaction_idx = db.get_mutable_index<generated_transaction_multi_index>();
628 const auto* gto = db.find<generated_transaction_object,by_sender_id>(boost::make_tuple(sender, sender_id));
629 if ( gto ) {
630 if (auto dm_logger = control.get_deep_mind_logger()) {
631 dm_logger->on_cancel_deferred(deep_mind_handler::operation_qualifier::none, *gto);
632 dm_logger->on_ram_trace(RAM_EVENT_ID("${id}", ("id", gto->id)), "deferred_trx", "cancel", "deferred_trx_cancel");
633 }
634
635 add_ram_usage( gto->payer, -(config::billable_size_v<generated_transaction_object> + gto->packed_trx.size()) );
636 generated_transaction_idx.remove(*gto);
637 }
638 return gto;
639}
640
641uint32_t apply_context::schedule_action( uint32_t ordinal_of_action_to_schedule, account_name receiver, bool context_free )
642{
643 uint32_t scheduled_action_ordinal = trx_context.schedule_action( ordinal_of_action_to_schedule,
644 receiver, context_free,
645 action_ordinal, first_receiver_action_ordinal );
646
647 act = &trx_context.get_action_trace( action_ordinal ).act;
648 return scheduled_action_ordinal;
649}
650
651uint32_t apply_context::schedule_action( action&& act_to_schedule, account_name receiver, bool context_free )
652{
653 uint32_t scheduled_action_ordinal = trx_context.schedule_action( std::move(act_to_schedule),
654 receiver, context_free,
655 action_ordinal, first_receiver_action_ordinal );
656
657 act = &trx_context.get_action_trace( action_ordinal ).act;
658 return scheduled_action_ordinal;
659}
660
661const table_id_object* apply_context::find_table( name code, name scope, name table ) {
662 return db.find<table_id_object, by_code_scope_table>(boost::make_tuple(code, scope, table));
663}
664
665const table_id_object& apply_context::find_or_create_table( name code, name scope, name table, const account_name &payer ) {
666 const auto* existing_tid = db.find<table_id_object, by_code_scope_table>(boost::make_tuple(code, scope, table));
667 if (existing_tid != nullptr) {
668 return *existing_tid;
669 }
670
671 if (auto dm_logger = control.get_deep_mind_logger()) {
672 std::string event_id = RAM_EVENT_ID("${code}:${scope}:${table}",
673 ("code", code)
674 ("scope", scope)
675 ("table", table)
676 );
677 dm_logger->on_ram_trace(std::move(event_id), "table", "add", "create_table");
678 }
679
681
682 return db.create<table_id_object>([&](table_id_object &t_id){
683 t_id.code = code;
684 t_id.scope = scope;
685 t_id.table = table;
686 t_id.payer = payer;
687
688 if (auto dm_logger = control.get_deep_mind_logger()) {
689 dm_logger->on_create_table(t_id);
690 }
691 });
692}
693
694void apply_context::remove_table( const table_id_object& tid ) {
695 if (auto dm_logger = control.get_deep_mind_logger()) {
696 std::string event_id = RAM_EVENT_ID("${code}:${scope}:${table}",
697 ("code", tid.code)
698 ("scope", tid.scope)
699 ("table", tid.table)
700 );
701 dm_logger->on_ram_trace(std::move(event_id), "table", "remove", "remove_table");
702 }
703
705
706 if (auto dm_logger = control.get_deep_mind_logger()) {
707 dm_logger->on_remove_table(tid);
708 }
709
710 db.remove(tid);
711}
712
714 const auto& ap = control.active_producers();
715 vector<account_name> accounts; accounts.reserve( ap.producers.size() );
716
717 for(const auto& producer : ap.producers )
718 accounts.push_back(producer.producer_name);
719
720 return accounts;
721}
722
724 if( delta > 0 ) {
725 if( !(privileged || payer == account_name(receiver)
727 {
729 subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." );
730 require_authorization( payer );
731 }
732 }
733 add_ram_usage(payer, delta);
734}
735
736
737int apply_context::get_action( uint32_t type, uint32_t index, char* buffer, size_t buffer_size )const
738{
739 const auto& trx = trx_context.packed_trx.get_transaction();
740 const action* act_ptr = nullptr;
741
742 if( type == 0 ) {
743 if( index >= trx.context_free_actions.size() )
744 return -1;
745 act_ptr = &trx.context_free_actions[index];
746 }
747 else if( type == 1 ) {
748 if( index >= trx.actions.size() )
749 return -1;
750 act_ptr = &trx.actions[index];
751 }
752
753 SYS_ASSERT(act_ptr, action_not_found_exception, "action is not found" );
754
755 auto ps = fc::raw::pack_size( *act_ptr );
756 if( ps <= buffer_size ) {
757 fc::datastream<char*> ds(buffer, buffer_size);
758 fc::raw::pack( ds, *act_ptr );
759 }
760 return ps;
761}
762
763int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t buffer_size )const
764{
766
767 if( index >= trx.context_free_data.size() ) return -1;
768
769 auto s = trx.context_free_data[index].size();
770 if( buffer_size == 0 ) return s;
771
772 auto copy_size = std::min( buffer_size, s );
773 memcpy( buffer, trx.context_free_data[index].data(), copy_size );
774
775 return copy_size;
776}
777
778int apply_context::db_store_i64( name scope, name table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
779 return db_store_i64( receiver, scope, table, payer, id, buffer, buffer_size);
780}
781
782int apply_context::db_store_i64( name code, name scope, name table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
783// require_write_lock( scope );
784 const auto& tab = find_or_create_table( code, scope, table, payer );
785 auto tableid = tab.id;
786
787 SYS_ASSERT( payer != account_name(), invalid_table_payer, "must specify a valid account to pay for new record" );
788
789 const auto& obj = db.create<key_value_object>( [&]( auto& o ) {
790 o.t_id = tableid;
791 o.primary_key = id;
792 o.value.assign( buffer, buffer_size );
793 o.payer = payer;
794 });
795
796 db.modify( tab, [&]( auto& t ) {
797 ++t.count;
798 });
799
800 int64_t billable_size = (int64_t)(buffer_size + config::billable_size_v<key_value_object>);
801
802 if (auto dm_logger = control.get_deep_mind_logger()) {
803 std::string event_id = RAM_EVENT_ID("${table_code}:${scope}:${table_name}:${primkey}",
804 ("table_code", tab.code)
805 ("scope", tab.scope)
806 ("table_name", tab.table)
807 ("primkey", name(obj.primary_key))
808 );
809 dm_logger->on_ram_trace(std::move(event_id), "table_row", "add", "primary_index_add");
810 }
811
812 update_db_usage( payer, billable_size);
813
814 if (auto dm_logger = control.get_deep_mind_logger()) {
815 dm_logger->on_db_store_i64(tab, obj);
816 }
817
818 keyval_cache.cache_table( tab );
819 return keyval_cache.add( obj );
820}
821
822void apply_context::db_update_i64( int iterator, account_name payer, const char* buffer, size_t buffer_size ) {
823 const key_value_object& obj = keyval_cache.get( iterator );
824
825 const auto& table_obj = keyval_cache.get_table( obj.t_id );
826 SYS_ASSERT( table_obj.code == receiver, table_access_violation, "db access violation" );
827
828// require_write_lock( table_obj.scope );
829
831 int64_t old_size = (int64_t)(obj.value.size() + overhead);
832 int64_t new_size = (int64_t)(buffer_size + overhead);
833
834 if( payer == account_name() ) payer = obj.payer;
835
836 std::string event_id;
837 if (control.get_deep_mind_logger() != nullptr) {
838 event_id = RAM_EVENT_ID("${table_code}:${scope}:${table_name}:${primkey}",
839 ("table_code", table_obj.code)
840 ("scope", table_obj.scope)
841 ("table_name", table_obj.table)
842 ("primkey", name(obj.primary_key))
843 );
844 }
845
846 if( account_name(obj.payer) != payer ) {
847 // refund the existing payer
848 if (auto dm_logger = control.get_deep_mind_logger())
849 {
850 dm_logger->on_ram_trace(std::string(event_id), "table_row", "remove", "primary_index_update_remove_old_payer");
851 }
852 update_db_usage( obj.payer, -(old_size) );
853 // charge the new payer
854 if (auto dm_logger = control.get_deep_mind_logger())
855 {
856 dm_logger->on_ram_trace(std::move(event_id), "table_row", "add", "primary_index_update_add_new_payer");
857 }
858 update_db_usage( payer, (new_size));
859 } else if(old_size != new_size) {
860 // charge/refund the existing payer the difference
861 if (auto dm_logger = control.get_deep_mind_logger())
862 {
863 dm_logger->on_ram_trace(std::move(event_id) , "table_row", "update", "primary_index_update");
864 }
865 update_db_usage( obj.payer, new_size - old_size);
866 }
867
868 if (auto dm_logger = control.get_deep_mind_logger()) {
869 dm_logger->on_db_update_i64(table_obj, obj, payer, buffer, buffer_size);
870 }
871
872 db.modify( obj, [&]( auto& o ) {
873 o.value.assign( buffer, buffer_size );
874 o.payer = payer;
875 });
876}
877
878void apply_context::db_remove_i64( int iterator ) {
879 const key_value_object& obj = keyval_cache.get( iterator );
880
881 const auto& table_obj = keyval_cache.get_table( obj.t_id );
882 SYS_ASSERT( table_obj.code == receiver, table_access_violation, "db access violation" );
883
884// require_write_lock( table_obj.scope );
885
886 if (auto dm_logger = control.get_deep_mind_logger()) {
887 std::string event_id = RAM_EVENT_ID("${table_code}:${scope}:${table_name}:${primkey}",
888 ("table_code", table_obj.code)
889 ("scope", table_obj.scope)
890 ("table_name", table_obj.table)
891 ("primkey", name(obj.primary_key))
892 );
893 dm_logger->on_ram_trace(std::move(event_id), "table_row", "remove", "primary_index_remove");
894 }
895
897
898 if (auto dm_logger = control.get_deep_mind_logger()) {
899 dm_logger->on_db_remove_i64(table_obj, obj);
900 }
901
902 db.modify( table_obj, [&]( auto& t ) {
903 --t.count;
904 });
905 db.remove( obj );
906
907 if (table_obj.count == 0) {
908 remove_table(table_obj);
909 }
910
911 keyval_cache.remove( iterator );
912}
913
914int apply_context::db_get_i64( int iterator, char* buffer, size_t buffer_size ) {
915 const key_value_object& obj = keyval_cache.get( iterator );
916
917 auto s = obj.value.size();
918 if( buffer_size == 0 ) return s;
919
920 auto copy_size = std::min( buffer_size, s );
921 memcpy( buffer, obj.value.data(), copy_size );
922
923 return copy_size;
924}
925
926int apply_context::db_next_i64( int iterator, uint64_t& primary ) {
927 if( iterator < -1 ) return -1; // cannot increment past end iterator of table
928
929 const auto& obj = keyval_cache.get( iterator ); // Check for iterator != -1 happens in this call
930 const auto& idx = db.get_index<key_value_index, by_scope_primary>();
931
932 auto itr = idx.iterator_to( obj );
933 ++itr;
934
935 if( itr == idx.end() || itr->t_id != obj.t_id ) return keyval_cache.get_end_iterator_by_table_id(obj.t_id);
936
937 primary = itr->primary_key;
938 return keyval_cache.add( *itr );
939}
940
941int apply_context::db_previous_i64( int iterator, uint64_t& primary ) {
942 const auto& idx = db.get_index<key_value_index, by_scope_primary>();
943
944 if( iterator < -1 ) // is end iterator
945 {
946 auto tab = keyval_cache.find_table_by_end_iterator(iterator);
947 SYS_ASSERT( tab, invalid_table_iterator, "not a valid end iterator" );
948
949 auto itr = idx.upper_bound(tab->id);
950 if( idx.begin() == idx.end() || itr == idx.begin() ) return -1; // Empty table
951
952 --itr;
953
954 if( itr->t_id != tab->id ) return -1; // Empty table
955
956 primary = itr->primary_key;
957 return keyval_cache.add(*itr);
958 }
959
960 const auto& obj = keyval_cache.get(iterator); // Check for iterator != -1 happens in this call
961
962 auto itr = idx.iterator_to(obj);
963 if( itr == idx.begin() ) return -1; // cannot decrement past beginning iterator of table
964
965 --itr;
966
967 if( itr->t_id != obj.t_id ) return -1; // cannot decrement past beginning iterator of table
968
969 primary = itr->primary_key;
970 return keyval_cache.add(*itr);
971}
972
973int apply_context::db_find_i64( name code, name scope, name table, uint64_t id ) {
974 //require_read_lock( code, scope ); // redundant?
975
976 const auto* tab = find_table( code, scope, table );
977 if( !tab ) return -1;
978
979 auto table_end_itr = keyval_cache.cache_table( *tab );
980
981 const key_value_object* obj = db.find<key_value_object, by_scope_primary>( boost::make_tuple( tab->id, id ) );
982 if( !obj ) return table_end_itr;
983
984 return keyval_cache.add( *obj );
985}
986
988 //require_read_lock( code, scope ); // redundant?
989
990 const auto* tab = find_table( code, scope, table );
991 if( !tab ) return -1;
992
993 auto table_end_itr = keyval_cache.cache_table( *tab );
994
995 const auto& idx = db.get_index<key_value_index, by_scope_primary>();
996 auto itr = idx.lower_bound( boost::make_tuple( tab->id, id ) );
997 if( itr == idx.end() ) return table_end_itr;
998 if( itr->t_id != tab->id ) return table_end_itr;
999
1000 return keyval_cache.add( *itr );
1001}
1002
1004 //require_read_lock( code, scope ); // redundant?
1005
1006 const auto* tab = find_table( code, scope, table );
1007 if( !tab ) return -1;
1008
1009 auto table_end_itr = keyval_cache.cache_table( *tab );
1010
1011 const auto& idx = db.get_index<key_value_index, by_scope_primary>();
1012 auto itr = idx.upper_bound( boost::make_tuple( tab->id, id ) );
1013 if( itr == idx.end() ) return table_end_itr;
1014 if( itr->t_id != tab->id ) return table_end_itr;
1015
1016 return keyval_cache.add( *itr );
1017}
1018
1019int apply_context::db_end_i64( name code, name scope, name table ) {
1020 //require_read_lock( code, scope ); // redundant?
1021
1022 const auto* tab = find_table( code, scope, table );
1023 if( !tab ) return -1;
1024
1025 return keyval_cache.cache_table( *tab );
1026}
1027
1029 const auto& p = control.get_dynamic_global_properties();
1030 db.modify( p, [&]( auto& dgp ) {
1031 ++dgp.global_action_sequence;
1032 });
1033 return p.global_action_sequence;
1034}
1035
1037 db.modify( receiver_account, [&]( auto& ra ) {
1038 ++ra.recv_sequence;
1039 });
1040 return receiver_account.recv_sequence;
1041}
1043 const auto& amo = db.get<account_metadata_object,by_name>( actor );
1044 db.modify( amo, [&](auto& am ){
1045 ++am.auth_sequence;
1046 });
1047 return amo.auth_sequence;
1048}
1049
1051 trx_context.add_ram_usage( account, ram_delta );
1052
1053 auto p = _account_ram_deltas.emplace( account, ram_delta );
1054 if( !p.second ) {
1055 p.first->delta += ram_delta;
1056 }
1057}
1058
1060 const action_trace& trace = trx_context.get_action_trace( action_ordinal );
1061 if (trace.creator_action_ordinal > 0) {
1062 const action_trace& creator_trace = trx_context.get_action_trace( trace.creator_action_ordinal );
1063 return creator_trace.receiver;
1064 }
1065 return action_name();
1066}
1067
1068} }
const mie::Vuint & p
Definition bn.cpp:27
const mie::Vuint & r
Definition bn.cpp:28
std::string name
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
const generic_index< MultiIndexType > & get_index() const
void modify(const ObjectType &obj, Modifier &&m)
const ObjectType * find(CompatibleKey &&key) const
void remove(const ObjectType &obj)
const ObjectType & create(Constructor &&con)
generic_index< MultiIndexType > & get_mutable_index()
const ObjectType & get(CompatibleKey &&key) const
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
const log_messages & get_log() const
An order-preserving dictionary of variants.
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
static std_exception_wrapper from_current_exception(const std::exception &e)
static time_point now()
Definition time.cpp:14
void require_recipient(account_name account)
uint32_t schedule_action(uint32_t ordinal_of_action_to_schedule, account_name receiver, bool context_free)
void add_ram_usage(account_name account, int64_t ram_delta)
std::vector< char > action_return_value
vector< account_name > get_active_producers() const
bool cancel_deferred_transaction(const uint128_t &sender_id, account_name sender)
int db_lowerbound_i64(name code, name scope, name table, uint64_t id)
const action & get_action() const
void exec_one()
Execution methods:
void update_db_usage(const account_name &payer, int64_t delta)
Database methods:
int db_find_i64(name code, name scope, name table, uint64_t id)
int get_context_free_data(uint32_t index, char *buffer, size_t buffer_size) const
int db_store_i64(name scope, name table, const account_name &payer, uint64_t id, const char *buffer, size_t buffer_size)
int db_get_i64(int iterator, char *buffer, size_t buffer_size)
controller & control
Fields:
int db_upperbound_i64(name code, name scope, name table, uint64_t id)
int db_next_i64(int iterator, uint64_t &primary)
void require_authorization(const account_name &account)
Authorization methods:
apply_context(controller &con, transaction_context &trx_ctx, uint32_t action_ordinal, uint32_t depth=0)
class generic_index
bool has_authorization(const account_name &account) const
void finalize_trace(action_trace &trace, const fc::time_point &start)
bool is_account(const account_name &account) const
exec()
transaction_context & trx_context
transaction context in which the action is running
uint64_t next_auth_sequence(account_name actor)
void db_update_i64(int iterator, account_name payer, const char *buffer, size_t buffer_size)
action_name get_sender() const
int db_previous_i64(int iterator, uint64_t &primary)
void db_remove_i64(int iterator)
void schedule_deferred_transaction(const uint128_t &sender_id, account_name payer, transaction &&trx, bool replace_existing)
chainbase::database & db
database where state is stored
uint64_t next_recv_sequence(const account_metadata_object &receiver_account)
int db_end_i64(name code, name scope, name table)
bool has_recipient(account_name account) const
void get_code_hash(account_name account, uint64_t &code_sequence, fc::sha256 &code_hash, uint8_t &vm_type, uint8_t &vm_version) const
void execute_context_free_inline(action &&a)
void check_authorization(const vector< action > &actions, const flat_set< public_key_type > &provided_keys, const flat_set< permission_level > &provided_permissions=flat_set< permission_level >(), fc::microseconds provided_delay=fc::microseconds(0), const std::function< void()> &checktime=std::function< void()>(), bool allow_unused_keys=false, bool check_but_dont_fail=false, const flat_set< permission_level > &satisfied_authorizations=flat_set< permission_level >()) const
Check authorizations of a vector of actions with provided keys, permission levels,...
const permission_object * find_permission(const permission_level &level) const
void check_action_list(account_name code, action_name action) const
const global_property_object & get_global_properties() const
const chainbase::database & db() const
bool is_producing_block() const
const dynamic_global_property_object & get_dynamic_global_properties() const
block_id_type head_block_id() const
const apply_handler * find_apply_handler(account_name contract, scope_name scope, action_name act) const
bool is_ram_billing_in_notify_allowed() const
deep_mind_handler * get_deep_mind_logger() const
uint32_t get_max_nonprivileged_inline_action_size() const
wasm_interface & get_wasm_interface()
const authorization_manager & get_authorization_manager() const
void check_actor_list(const flat_set< account_name > &actors) const
bool is_builtin_activated(builtin_protocol_feature_t f) const
bool sender_avoids_whitelist_blacklist_enforcement(account_name sender) const
time_point pending_block_time() const
const producer_authority_schedule & active_producers() const
static std::optional< uint64_t > convert_exception_to_error_code(const fc::exception &e)
void check_contract_list(account_name code) const
The table_id_object class tracks the mapping of (scope, code, table) to an opaque identifier.
const packed_transaction & packed_trx
void validate_referenced_accounts(const transaction &trx, bool enforce_actor_whitelist_blacklist) const
record_transaction
vector< digest_type > executed_action_receipt_digests
DigestType hash_with_checktime(const char *data, uint32_t datalen) const
void apply(const digest_type &code_hash, const uint8_t &vm_type, const uint8_t &vm_version, apply_context &context)
uint64_t id
Definition code_cache.cpp:0
#define RAM_EVENT_ID(FORMAT,...)
Definition deep_mind.hpp:27
#define FC_RETHROW_EXCEPTIONS(LOG_LEVEL, FORMAT,...)
Catchs all exception's, std::exceptions, and ... and rethrows them after appending the provided log m...
void delay(websocketpp::connection_hdl, long duration)
#define FC_LOG_MESSAGE(LOG_LEVEL, FORMAT,...)
A helper method for generating log messages.
#define dlog(FORMAT,...)
Definition logger.hpp:101
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
size_t pack_size(const T &v)
Definition raw.hpp:671
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
fc::string format_string(const fc::string &, const variant_object &, bool minimize=false)
Definition variant.cpp:773
constexpr uint64_t billable_size_v
Definition config.hpp:147
auto emplace_extension(extensions_type &exts, uint16_t eid, vector< char > &&data)
Definition types.hpp:264
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type Block can not be found Unlinkable block Block does not guarantee concurrent execution without conflicts Block exhausted allowed resources Block is from the future Block is not signed by expected producer Block includes an ill formed protocol feature activation extension Block includes an ill formed additional block signature extension transaction_exception
auto generate_action_digest(Hasher &&hash, const action &act, const vector< char > &action_output)
Definition action.hpp:99
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type Block can not be found Unlinkable block Block does not guarantee concurrent execution without conflicts Block exhausted allowed resources Block is from the future Block is not signed by expected producer Block includes an ill formed protocol feature activation extension Block includes an ill formed additional block signature extension Error decompressing transaction Transaction should have at least one required authority Expired Transaction Invalid Reference Block Duplicate deferred transaction The transaction can not be found Transaction is too big Invalid transaction extension Transaction includes disallowed Transaction exceeded transient resource limit action_validate_exception
sysio::chain::action_name action_name
unsigned __int128 uint128_t
Definition types.hpp:242
chainbase::shared_multi_index_container< generated_transaction_object, indexed_by< ordered_unique< tag< by_id >, BOOST_MULTI_INDEX_MEMBER(generated_transaction_object, generated_transaction_object::id_type, id)>, ordered_unique< tag< by_trx_id >, BOOST_MULTI_INDEX_MEMBER(generated_transaction_object, transaction_id_type, trx_id)>, ordered_unique< tag< by_expiration >, composite_key< generated_transaction_object, BOOST_MULTI_INDEX_MEMBER(generated_transaction_object, time_point, expiration), > >, ordered_unique< tag< by_delay >, composite_key< generated_transaction_object, BOOST_MULTI_INDEX_MEMBER(generated_transaction_object, time_point, delay_until), > >, ordered_unique< tag< by_sender_id >, composite_key< generated_transaction_object, BOOST_MULTI_INDEX_MEMBER(generated_transaction_object, account_name, sender), > > > > generated_transaction_multi_index
checksum_type transaction_id_type
Definition types.hpp:236
chainbase::shared_multi_index_container< key_value_object, indexed_by< ordered_unique< tag< by_id >, member< key_value_object, key_value_object::id_type, &key_value_object::id > >, ordered_unique< tag< by_scope_primary >, composite_key< key_value_object, member< key_value_object, table_id, &key_value_object::t_id >, member< key_value_object, uint64_t, &key_value_object::primary_key > >, composite_key_compare< std::less< table_id >, std::less< uint64_t > > > > > key_value_index
name account_name
Definition types.hpp:120
@ self
the connection is to itself
Definition protocol.hpp:48
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
vector< permission_level > authorization
Definition action.hpp:59
fc::microseconds elapsed
Definition trace.hpp:38
fc::unsigned_int creator_action_ordinal
Definition trace.hpp:32
std::optional< fc::exception > except
Definition trace.hpp:45
flat_set< account_delta > account_ram_deltas
Definition trace.hpp:44
std::vector< char > return_value
Definition trace.hpp:47
std::optional< action_receipt > receipt
Definition trace.hpp:34
std::optional< uint64_t > error_code
Definition trace.hpp:46
uint16_t max_inline_action_depth
recursion depth limit on sending inline actions
uint32_t max_inline_action_size
maximum allowed size (in bytes) of an inline action
v1 Producer-voted blockchain configuration parameters
Immutable except for fc::from_variant.
Definition name.hpp:43
const signed_transaction & get_signed_transaction() const
const transaction_id_type & id() const
const transaction & get_transaction() const
vector< bytes > context_free_data
for each context-free action, there is an entry here
CK_ULONG datalen
char * s
memcpy((char *) pInfo->slotDescription, s, l)