Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
transaction_context.cpp
Go to the documentation of this file.
10
11#pragma push_macro("N")
12#undef N
13#include <boost/accumulators/accumulators.hpp>
14#include <boost/accumulators/statistics/stats.hpp>
15#include <boost/accumulators/statistics/min.hpp>
16#include <boost/accumulators/statistics/max.hpp>
17#include <boost/accumulators/statistics/weighted_mean.hpp>
18#include <boost/accumulators/statistics/weighted_variance.hpp>
19#pragma pop_macro("N")
20
21#include <chrono>
22
23namespace sysio { namespace chain {
24
26 : expired(timer.expired), _timer(timer) {
27 expired = 0;
28 }
29
33
35 _timer.stop();
36 }
37
38 void transaction_checktime_timer::set_expiration_callback(void(*func)(void*), void* user) {
39 _timer.set_expiration_callback(func, user);
40 }
41
46
48 const packed_transaction& t,
51 bool read_only)
52 :control(c)
53 ,packed_trx(t)
54 ,undo_session()
55 ,trace(std::make_shared<transaction_trace>())
56 ,start(s)
57 ,transaction_timer(std::move(tmr))
58 ,is_read_only(read_only)
59 ,net_usage(trace->net_usage)
60 ,pseudo_start(s)
61 {
62 if (!c.skip_db_sessions()) {
63 undo_session.emplace(c.mutable_db().start_undo_session(true));
64 }
65 trace->id = packed_trx.id();
66 trace->block_num = c.head_block_num() + 1;
67 trace->block_time = c.pending_block_time();
68 trace->producer_block_id = c.pending_producer_block_id();
69
70 if(auto dm_logger = control.get_deep_mind_logger())
71 {
72 dm_logger->on_start_transaction();
73 }
74 }
75
77 {
78 if(auto dm_logger = control.get_deep_mind_logger())
79 {
80 dm_logger->on_end_transaction();
81 }
82 }
83
84 void transaction_context::disallow_transaction_extensions( const char* error_msg )const {
86 SYS_THROW( subjective_block_production_exception, error_msg );
87 } else {
88 SYS_THROW( disallowed_transaction_extensions_bad_block_exception, error_msg );
89 }
90 }
91
92 void transaction_context::init(uint64_t initial_net_usage)
93 {
94 SYS_ASSERT( !is_initialized, transaction_exception, "cannot initialize twice" );
95
96 // set maximum to a semi-valid deadline to allow for pause math and conversion to dates for logging
98
101
102 net_limit = rl.get_block_net_limit();
103
104 objective_duration_limit = fc::microseconds( rl.get_block_cpu_limit() );
105 _deadline = start + objective_duration_limit;
106
107 // Possibly lower net_limit to the maximum net usage a transaction is allowed to be billed
108 // if( cfg.max_transaction_net_usage <= net_limit ) {
109 // net_limit = cfg.max_transaction_net_usage;
110 // net_limit_due_to_block = false;
111 // }
112 if( cfg.max_transaction_net_usage > 0 && cfg.max_transaction_net_usage <= net_limit ) {
113 net_limit = cfg.max_transaction_net_usage;
114 net_limit_due_to_block = false;
115 }
116
117 // Possibly lower objective_duration_limit to the maximum cpu usage a transaction is allowed to be billed
118 // if( cfg.max_transaction_cpu_usage <= objective_duration_limit.count() ) {
119 // objective_duration_limit = fc::microseconds(cfg.max_transaction_cpu_usage);
120 // billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
121 // _deadline = start + objective_duration_limit;
122 // }
123 if( cfg.max_transaction_cpu_usage > 0 && cfg.max_transaction_cpu_usage <= objective_duration_limit.count() ) {
124 objective_duration_limit = fc::microseconds(cfg.max_transaction_cpu_usage);
125 billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
126 _deadline = start + objective_duration_limit;
127 }
128
129 // ** ADDED NEW ** If the configuration values are zero, set net_limit and objective_duration_limit to default maximum values to prevent them from being zero
130 if( net_limit == 0 ) {
131 net_limit = cfg.max_block_net_usage;
132 }
133 if( objective_duration_limit.count() == 0 ) {
134 objective_duration_limit = fc::microseconds(cfg.max_block_cpu_usage);
135 }
136
137 eager_net_limit = net_limit;
138
139 const transaction& trx = packed_trx.get_transaction();
140 // Possibly lower net_limit to optional limit set in the transaction header
141 uint64_t trx_specified_net_usage_limit = static_cast<uint64_t>(trx.max_net_usage_words.value) * 8;
142 if( trx_specified_net_usage_limit > 0 && trx_specified_net_usage_limit <= net_limit ) {
143 net_limit = trx_specified_net_usage_limit;
144 net_limit_due_to_block = false;
145 }
146
147 // Possibly lower objective_duration_limit to optional limit set in transaction header
148 if( trx.max_cpu_usage_ms > 0 ) {
149 auto trx_specified_cpu_usage_limit = fc::milliseconds(trx.max_cpu_usage_ms);
150 if( trx_specified_cpu_usage_limit <= objective_duration_limit ) {
151 objective_duration_limit = trx_specified_cpu_usage_limit;
152 billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
153 _deadline = start + objective_duration_limit;
154 }
155 }
156
157 initial_objective_duration_limit = objective_duration_limit;
158
159 // if( explicit_billed_cpu_time )
160 // validate_cpu_usage_to_bill( billed_cpu_time_us, std::numeric_limits<int64_t>::max(), false ); // Fail early if the amount to be billed is too high
161
162 // Record accounts to be billed for network and CPU usage
163 // if( control.is_builtin_activated(builtin_protocol_feature_t::only_bill_first_authorizer) ) {
164 // bill_to_accounts.insert( trx.first_authorizer() );
165 // } else {
166 // for( const auto& act : trx.actions ) {
167 // for( const auto& auth : act.authorization ) {
168 // bill_to_accounts.insert( auth.actor );
169 // }
170 // }
171 // }
172 // validate_ram_usage.reserve( bill_to_accounts.size() );
173
174 // // Update usage values of accounts to reflect new time
175 // rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot );
176
177 // // Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed
178 // int64_t account_net_limit = 0;
179 // int64_t account_cpu_limit = 0;
180 // bool greylisted_net = false, greylisted_cpu = false;
181 // std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay();
182 // net_limit_due_to_greylist |= greylisted_net;
183 // cpu_limit_due_to_greylist |= greylisted_cpu;
184
185 // eager_net_limit = net_limit;
186
187 // // Possibly lower eager_net_limit to what the billed accounts can pay plus some (objective) leeway
188 // auto new_eager_net_limit = std::min( eager_net_limit, static_cast<uint64_t>(account_net_limit + cfg.net_usage_leeway) );
189 // if( new_eager_net_limit < eager_net_limit ) {
190 // eager_net_limit = new_eager_net_limit;
191 // net_limit_due_to_block = false;
192 // }
193
194 // // Possibly limit deadline if the duration accounts can be billed for (+ a subjective leeway) does not exceed current delta
195 // if( (fc::microseconds(account_cpu_limit) + leeway) <= (_deadline - start) ) {
196 // _deadline = start + fc::microseconds(account_cpu_limit) + leeway;
197 // billing_timer_exception_code = leeway_deadline_exception::code_value;
198 // }
199
200 // Possibly limit deadline to subjective max_transaction_time
201 if( max_transaction_time_subjective != fc::microseconds::maximum() && (start + max_transaction_time_subjective) <= _deadline ) {
202 _deadline = start + max_transaction_time_subjective;
203 billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
204 }
205
206 // Possibly limit deadline to caller provided wall clock block deadline
207 if( block_deadline < _deadline ) {
208 _deadline = block_deadline;
209 billing_timer_exception_code = deadline_exception::code_value;
210 }
211
212 // Explicit billed_cpu_time_us should be used, block_deadline will be maximum unless in test code
214 _deadline = block_deadline;
215 deadline_exception_code = deadline_exception::code_value;
216 } else {
217 deadline_exception_code = billing_timer_exception_code;
218 }
219
220 // if( !explicit_billed_cpu_time ) {
221 // // Fail early if amount of the previous speculative execution is within 10% of remaining account cpu available
222 // int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us + leeway.count(); // Add leeway to allow powerup
223 // if( validate_account_cpu_limit > 0 )
224 // validate_account_cpu_limit -= SYS_PERCENT( validate_account_cpu_limit, 10 * config::percent_1 );
225 // if( validate_account_cpu_limit < 0 ) validate_account_cpu_limit = 0;
226 // validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit );
227 // }
228
229 eager_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes) so check_net_usage can be efficient
230
231 if( initial_net_usage > 0 )
232 add_net_usage( initial_net_usage ); // Fail early if current net usage is already greater than the calculated limit
233
236 } else {
237 transaction_timer.start( _deadline );
238 checktime(); // Fail early if deadline has already been exceeded
239 }
240
241 is_initialized = true;
242 }
243
245 {
247 if( trx.transaction_extensions.size() > 0 ) {
248 disallow_transaction_extensions( "no transaction extensions supported yet for implicit transactions" );
249 }
250
252 init( initial_net_usage);
253 }
254
255 void transaction_context::init_for_input_trx( uint64_t packed_trx_unprunable_size,
256 uint64_t packed_trx_prunable_size )
257 {
259 if( trx.transaction_extensions.size() > 0 ) {
260 disallow_transaction_extensions( "no transaction extensions supported yet for input transactions" );
261 }
262
263 const auto& cfg = control.get_global_properties().configuration;
264
265 uint64_t discounted_size_for_pruned_data = packed_trx_prunable_size;
266 if( cfg.context_free_discount_net_usage_den > 0
267 && cfg.context_free_discount_net_usage_num < cfg.context_free_discount_net_usage_den )
268 {
269 discounted_size_for_pruned_data *= cfg.context_free_discount_net_usage_num;
270 discounted_size_for_pruned_data = ( discounted_size_for_pruned_data + cfg.context_free_discount_net_usage_den - 1)
271 / cfg.context_free_discount_net_usage_den; // rounds up
272 }
273
274 uint64_t initial_net_usage = static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
275 + packed_trx_unprunable_size + discounted_size_for_pruned_data;
276
277
278 if( trx.delay_sec.value > 0 ) {
279 // If delayed, also charge ahead of time for the additional net usage needed to retire the delayed transaction
280 // whether that be by successfully executing, soft failure, hard failure, or expiration.
281 initial_net_usage += static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
282 + static_cast<uint64_t>(config::transaction_id_net_usage);
283 }
284
286 is_input = true;
287 if (!control.skip_trx_checks()) {
291 }
292 init( initial_net_usage);
293 record_transaction( packed_trx.id(), trx.expiration );
294 }
295
297 {
299 if( (trx.expiration.sec_since_epoch() != 0) && (trx.transaction_extensions.size() > 0) ) {
300 disallow_transaction_extensions( "no transaction extensions supported yet for deferred transactions" );
301 }
302 // If (trx.expiration.sec_since_epoch() == 0) then it was created after NO_DUPLICATE_DEFERRED_ID activation,
303 // and so validation of its extensions was done either in:
304 // * apply_context::schedule_deferred_transaction for contract-generated transactions;
305 // * or transaction_context::init_for_input_trx for delayed input transactions.
306
307 published = p;
308 trace->scheduled = true;
309 apply_context_free = false;
310 init( 0 );
311 }
312
314 SYS_ASSERT( is_initialized, transaction_exception, "must first initialize" );
315
317 if( apply_context_free ) {
318 for( const auto& act : trx.context_free_actions ) {
319 schedule_action( act, act.account, true, 0, 0 );
320 }
321 }
322
323 if( delay == fc::microseconds() ) {
324 for( const auto& act : trx.actions ) {
325 schedule_action( act, act.account, false, 0, 0 );
326 }
327 }
328
329 auto& action_traces = trace->action_traces;
330 uint32_t num_original_actions_to_execute = action_traces.size();
331 for( uint32_t i = 1; i <= num_original_actions_to_execute; ++i ) {
332 execute_action( i, 0 );
333 }
334
335 if( delay != fc::microseconds() ) {
336 schedule_transaction();
337 }
338 }
339
341 SYS_ASSERT( is_initialized, transaction_exception, "must first initialize" );
342
343 if( is_input ) {
346 for( const auto& act : trx.actions ) {
347 for( const auto& auth : act.authorization ) {
348 am.update_permission_usage( am.get_permission(auth) );
349 }
350 }
351 }
352
353 // auto& rl = control.get_mutable_resource_limits_manager();
354 // for( auto a : validate_ram_usage ) {
355 // rl.verify_account_ram_usage( a );
356 // }
357
358 // // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed
359 // int64_t account_net_limit = 0;
360 // int64_t account_cpu_limit = 0;
361 // bool greylisted_net = false, greylisted_cpu = false;
362 // std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay();
363 // net_limit_due_to_greylist |= greylisted_net;
364 // cpu_limit_due_to_greylist |= greylisted_cpu;
365
366 // // Possibly lower net_limit to what the billed accounts can pay
367 // if( static_cast<uint64_t>(account_net_limit) <= net_limit ) {
368 // // NOTE: net_limit may possibly not be objective anymore due to net greylisting, but it should still be no greater than the truly objective net_limit
369 // net_limit = static_cast<uint64_t>(account_net_limit);
370 // net_limit_due_to_block = false;
371 // }
372
373 // // Possibly lower objective_duration_limit to what the billed accounts can pay
374 // if( account_cpu_limit <= objective_duration_limit.count() ) {
375 // // NOTE: objective_duration_limit may possibly not be objective anymore due to cpu greylisting, but it should still be no greater than the truly objective objective_duration_limit
376 // objective_duration_limit = fc::microseconds(account_cpu_limit);
377 // billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
378 // }
379
380 net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes)
381
382 eager_net_limit = net_limit;
384
385 auto now = fc::time_point::now();
386 trace->elapsed = now - start;
387
389
390 // validate_cpu_usage_to_bill( billed_cpu_time_us, account_cpu_limit, true );
391 // auto& rl = control.get_mutable_resource_limits_manager();
392 // rl.add_transaction_usage( bill_to_accounts, static_cast<uint64_t>(billed_cpu_time_us), net_usage,
393 // block_timestamp_type(control.pending_block_time()).slot ); // Should never fail
394 }
395
397 if (undo_session) undo_session->squash();
398 }
399
401 if (undo_session) undo_session->undo();
402 }
403
405 if (!control.skip_trx_checks()) {
406 if( BOOST_UNLIKELY(net_usage > eager_net_limit) ) {
407 if ( net_limit_due_to_block ) {
408 SYS_THROW( block_net_usage_exceeded,
409 "not enough space left in block: ${net_usage} > ${net_limit}",
410 ("net_usage", net_usage)("net_limit", eager_net_limit) );
411 } else if (net_limit_due_to_greylist) {
412 SYS_THROW( greylist_net_usage_exceeded,
413 "greylisted transaction net usage is too high: ${net_usage} > ${net_limit}",
414 ("net_usage", net_usage)("net_limit", eager_net_limit) );
415 } else {
416 SYS_THROW( tx_net_usage_exceeded,
417 "transaction net usage is too high: ${net_usage} > ${net_limit}",
418 ("net_usage", net_usage)("net_limit", eager_net_limit) );
419 }
420 }
421 }
422 }
423
425 if(BOOST_LIKELY(transaction_timer.expired == false))
426 return;
427
428 auto now = fc::time_point::now();
429 if( explicit_billed_cpu_time || deadline_exception_code == deadline_exception::code_value ) {
430 SYS_THROW( deadline_exception, "deadline exceeded ${billing_timer}us",
431 ("billing_timer", now - pseudo_start)("now", now)("deadline", _deadline)("start", start) );
432 } else if( deadline_exception_code == block_cpu_usage_exceeded::code_value ) {
433 SYS_THROW( block_cpu_usage_exceeded,
434 "not enough time left in block to complete executing transaction ${billing_timer}us",
435 ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) );
436 } else if( deadline_exception_code == tx_cpu_usage_exceeded::code_value ) {
437 if (cpu_limit_due_to_greylist) {
438 SYS_THROW( greylist_cpu_usage_exceeded,
439 "greylisted transaction was executing for too long ${billing_timer}us",
440 ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) );
441 } else {
442 SYS_THROW( tx_cpu_usage_exceeded,
443 "transaction was executing for too long ${billing_timer}us",
444 ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) );
445 }
446 } else if( deadline_exception_code == leeway_deadline_exception::code_value ) {
447 SYS_THROW( leeway_deadline_exception,
448 "the transaction was unable to complete by deadline, "
449 "but it is possible it could have succeeded if it were allowed to run to completion ${billing_timer}",
450 ("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start) );
451 }
452 SYS_ASSERT( false, transaction_exception, "unexpected deadline exception code ${code}", ("code", deadline_exception_code) );
453 }
454
456 if( explicit_billed_cpu_time || pseudo_start == fc::time_point() ) return; // either irrelevant or already paused
457
458 paused_time = fc::time_point::now();
459 billed_time = paused_time - pseudo_start;
460 pseudo_start = fc::time_point();
462 }
463
465 if( explicit_billed_cpu_time || pseudo_start != fc::time_point() ) return; // either irrelevant or already running
466
467 auto now = fc::time_point::now();
468 auto paused = now - paused_time;
469
470 pseudo_start = now - billed_time;
471 _deadline += paused;
472
473 // do not allow to go past block wall clock deadline
474 if( block_deadline < _deadline ) {
475 deadline_exception_code = deadline_exception::code_value;
476 _deadline = block_deadline;
477 }
478
479 transaction_timer.start(_deadline);
480 }
481
482 void transaction_context::validate_cpu_usage_to_bill( int64_t billed_us, int64_t account_cpu_limit, bool check_minimum )const {
483 if (!control.skip_trx_checks()) {
484 if( check_minimum ) {
485 const auto& cfg = control.get_global_properties().configuration;
486 SYS_ASSERT( billed_us >= cfg.min_transaction_cpu_usage, transaction_exception,
487 "cannot bill CPU time less than the minimum of ${min_billable} us",
488 ("min_billable", cfg.min_transaction_cpu_usage)("billed_cpu_time_us", billed_us)
489 );
490 }
491
492 validate_account_cpu_usage( billed_us, account_cpu_limit );
493 }
494 }
495
496 void transaction_context::validate_account_cpu_usage( int64_t billed_us, int64_t account_cpu_limit )const {
497 if( (billed_us > 0) && !control.skip_trx_checks() ) {
498 const bool cpu_limited_by_account = (account_cpu_limit <= objective_duration_limit.count());
499
500 if( !cpu_limited_by_account && (billing_timer_exception_code == block_cpu_usage_exceeded::code_value) ) {
501 SYS_ASSERT( billed_us <= objective_duration_limit.count(),
502 block_cpu_usage_exceeded,
503 "billed CPU time (${billed} us) is greater than the billable CPU time left in the block (${billable} us)",
504 ("billed", billed_us)( "billable", objective_duration_limit.count() )
505 );
506 } else {
507 if( cpu_limit_due_to_greylist && cpu_limited_by_account ) {
508 SYS_ASSERT( billed_us <= account_cpu_limit,
509 greylist_cpu_usage_exceeded,
510 "billed CPU time (${billed} us) is greater than the maximum greylisted billable CPU time for the transaction (${billable} us)",
511 ("billed", billed_us)( "billable", account_cpu_limit )
512 );
513 } else {
514 // exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
515 const int64_t cpu_limit = (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
516 SYS_ASSERT( billed_us <= cpu_limit,
517 tx_cpu_usage_exceeded,
518 "billed CPU time (${billed} us) is greater than the maximum billable CPU time for the transaction (${billable} us)",
519 ("billed", billed_us)( "billable", cpu_limit )
520 );
521 }
522 }
523 }
524 }
525
526 void transaction_context::validate_account_cpu_usage_estimate( int64_t prev_billed_us, int64_t account_cpu_limit )const {
527 // prev_billed_us can be 0, but so can account_cpu_limit
528 if( (prev_billed_us >= 0) && !control.skip_trx_checks() ) {
529 const bool cpu_limited_by_account = (account_cpu_limit <= objective_duration_limit.count());
530
531 if( !cpu_limited_by_account && (billing_timer_exception_code == block_cpu_usage_exceeded::code_value) ) {
532 SYS_ASSERT( prev_billed_us < objective_duration_limit.count(),
533 block_cpu_usage_exceeded,
534 "estimated CPU time (${billed} us) is not less than the billable CPU time left in the block (${billable} us)",
535 ("billed", prev_billed_us)( "billable", objective_duration_limit.count() )
536 );
537 } else {
538 if( cpu_limit_due_to_greylist && cpu_limited_by_account ) {
539 SYS_ASSERT( prev_billed_us < account_cpu_limit,
540 greylist_cpu_usage_exceeded,
541 "estimated CPU time (${billed} us) is not less than the maximum greylisted billable CPU time for the transaction (${billable} us)",
542 ("billed", prev_billed_us)( "billable", account_cpu_limit )
543 );
544 } else {
545 // exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
546 const int64_t cpu_limit = (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
547 SYS_ASSERT( prev_billed_us < cpu_limit,
548 tx_cpu_usage_exceeded,
549 "estimated CPU time (${billed} us) is not less than the maximum billable CPU time for the transaction (${billable} us)",
550 ("billed", prev_billed_us)( "billable", cpu_limit )
551 );
552 }
553 }
554 }
555 }
556
557 void transaction_context::add_ram_usage( account_name account, int64_t ram_delta ) {
559 rl.add_pending_ram_usage( account, ram_delta );
560 if( ram_delta > 0 ) {
561 validate_ram_usage.insert( account );
562 }
563 }
564
566 if( explicit_billed_cpu_time ) return static_cast<uint32_t>(billed_cpu_time_us);
567
568 const auto& cfg = control.get_global_properties().configuration;
569 billed_cpu_time_us = std::max( (now - pseudo_start).count(), static_cast<int64_t>(cfg.min_transaction_cpu_usage) );
570
571 return static_cast<uint32_t>(billed_cpu_time_us);
572 }
573
574 std::tuple<int64_t, int64_t, bool, bool> transaction_context::max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits ) const{
575 // Assumes rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot ) was already called prior
576
577 // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed
579 const static int64_t large_number_no_overflow = std::numeric_limits<int64_t>::max()/2;
580 int64_t account_net_limit = large_number_no_overflow;
581 int64_t account_cpu_limit = large_number_no_overflow;
582 bool greylisted_net = false;
583 bool greylisted_cpu = false;
584
585 uint32_t specified_greylist_limit = control.get_greylist_limit();
586 for( const auto& a : bill_to_accounts ) {
587 uint32_t greylist_limit = config::maximum_elastic_resource_multiplier;
588 if( !force_elastic_limits && control.is_producing_block() ) {
590 greylist_limit = 1;
591 } else {
592 greylist_limit = specified_greylist_limit;
593 }
594 }
595 auto [net_limit, net_was_greylisted] = rl.get_account_net_limit(a, greylist_limit);
596 if( net_limit >= 0 ) {
597 account_net_limit = std::min( account_net_limit, net_limit );
598 greylisted_net |= net_was_greylisted;
599 }
600 auto [cpu_limit, cpu_was_greylisted] = rl.get_account_cpu_limit(a, greylist_limit);
601 if( cpu_limit >= 0 ) {
602 account_cpu_limit = std::min( account_cpu_limit, cpu_limit );
603 greylisted_cpu |= cpu_was_greylisted;
604 }
605 }
606
607 SYS_ASSERT( (!force_elastic_limits && control.is_producing_block()) || (!greylisted_cpu && !greylisted_net),
608 transaction_exception, "greylisted when not producing block" );
609
610 return std::make_tuple(account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu);
611 }
612
613 action_trace& transaction_context::get_action_trace( uint32_t action_ordinal ) {
614 SYS_ASSERT( 0 < action_ordinal && action_ordinal <= trace->action_traces.size() ,
616 "action_ordinal ${ordinal} is outside allowed range [1,${max}]",
617 ("ordinal", action_ordinal)("max", trace->action_traces.size())
618 );
619 return trace->action_traces[action_ordinal-1];
620 }
621
622 const action_trace& transaction_context::get_action_trace( uint32_t action_ordinal )const {
623 SYS_ASSERT( 0 < action_ordinal && action_ordinal <= trace->action_traces.size() ,
625 "action_ordinal ${ordinal} is outside allowed range [1,${max}]",
626 ("ordinal", action_ordinal)("max", trace->action_traces.size())
627 );
628 return trace->action_traces[action_ordinal-1];
629 }
630
631 uint32_t transaction_context::schedule_action( const action& act, account_name receiver, bool context_free,
632 uint32_t creator_action_ordinal,
633 uint32_t closest_unnotified_ancestor_action_ordinal )
634 {
635 uint32_t new_action_ordinal = trace->action_traces.size() + 1;
636
637 trace->action_traces.emplace_back( *trace, act, receiver, context_free,
638 new_action_ordinal, creator_action_ordinal,
639 closest_unnotified_ancestor_action_ordinal );
640
641 return new_action_ordinal;
642 }
643
644 uint32_t transaction_context::schedule_action( action&& act, account_name receiver, bool context_free,
645 uint32_t creator_action_ordinal,
646 uint32_t closest_unnotified_ancestor_action_ordinal )
647 {
648 uint32_t new_action_ordinal = trace->action_traces.size() + 1;
649
650 trace->action_traces.emplace_back( *trace, std::move(act), receiver, context_free,
651 new_action_ordinal, creator_action_ordinal,
652 closest_unnotified_ancestor_action_ordinal );
653
654 return new_action_ordinal;
655 }
656
657 uint32_t transaction_context::schedule_action( uint32_t action_ordinal, account_name receiver, bool context_free,
658 uint32_t creator_action_ordinal,
659 uint32_t closest_unnotified_ancestor_action_ordinal )
660 {
661 uint32_t new_action_ordinal = trace->action_traces.size() + 1;
662
663 trace->action_traces.reserve( new_action_ordinal );
664
665 const action& provided_action = get_action_trace( action_ordinal ).act;
666
667 // The reserve above is required so that the emplace_back below does not invalidate the provided_action reference.
668
669 trace->action_traces.emplace_back( *trace, provided_action, receiver, context_free,
670 new_action_ordinal, creator_action_ordinal,
671 closest_unnotified_ancestor_action_ordinal );
672
673 return new_action_ordinal;
674 }
675
676 void transaction_context::execute_action( uint32_t action_ordinal, uint32_t recurse_depth ) {
677 apply_context acontext( control, *this, action_ordinal, recurse_depth );
678
679 if (recurse_depth == 0) {
680 if (auto dm_logger = control.get_deep_mind_logger()) {
681 dm_logger->on_input_action();
682 }
683 }
684
685 acontext.exec();
686 }
687
688
689 void transaction_context::schedule_transaction() {
690 // Charge ahead of time for the additional net usage needed to retire the delayed transaction
691 // whether that be by successfully executing, soft failure, hard failure, or expiration.
692 const transaction& trx = packed_trx.get_transaction();
693 if( trx.delay_sec.value == 0 ) { // Do not double bill. Only charge if we have not already charged for the delay.
694 const auto& cfg = control.get_global_properties().configuration;
695 add_net_usage( static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
696 + static_cast<uint64_t>(config::transaction_id_net_usage) ); // Will exit early if net usage cannot be payed.
697 }
698
699 auto first_auth = trx.first_authorizer();
700
701 uint32_t trx_size = 0;
702 const auto& cgto = control.mutable_db().create<generated_transaction_object>( [&]( auto& gto ) {
703 gto.trx_id = packed_trx.id();
704 gto.payer = first_auth;
705 gto.sender = account_name();
706 gto.sender_id = transaction_id_to_sender_id( gto.trx_id );
707 gto.published = control.pending_block_time();
708 gto.delay_until = gto.published + delay;
710 trx_size = gto.set( trx );
711
712 if (auto dm_logger = control.get_deep_mind_logger()) {
713 std::string event_id = RAM_EVENT_ID("${id}", ("id", gto.id));
714
715 dm_logger->on_create_deferred(deep_mind_handler::operation_qualifier::push, gto, packed_trx);
716 dm_logger->on_ram_trace(std::move(event_id), "deferred_trx", "push", "deferred_trx_pushed");
717 }
718 });
719
721 add_ram_usage( cgto.payer, ram_delta );
722 trace->account_ram_delta = account_delta( cgto.payer, ram_delta );
723 }
724
725 void transaction_context::record_transaction( const transaction_id_type& id, fc::time_point_sec expire ) {
726 try {
727 control.mutable_db().create<transaction_object>([&](transaction_object& transaction) {
728 transaction.trx_id = id;
729 transaction.expiration = expire;
730 });
731 } catch( const boost::interprocess::bad_alloc& ) {
732 throw;
733 } catch ( ... ) {
734 SYS_ASSERT( false, tx_duplicate,
735 "duplicate transaction ${id}", ("id", id ) );
736 }
737 }
738
739 void transaction_context::validate_referenced_accounts( const transaction& trx, bool enforce_actor_whitelist_blacklist )const {
740 const auto& db = control.db();
741 const auto& auth_manager = control.get_authorization_manager();
742
743 for( const auto& a : trx.context_free_actions ) {
744 auto* code = db.find<account_object, by_name>(a.account);
745 SYS_ASSERT( code != nullptr, transaction_exception,
746 "action's code account '${account}' does not exist", ("account", a.account) );
747 SYS_ASSERT( a.authorization.size() == 0, transaction_exception,
748 "context-free actions cannot have authorizations" );
749 }
750
751 flat_set<account_name> actors;
752
753 bool one_auth = false;
754 for( const auto& a : trx.actions ) {
755 auto* code = db.find<account_object, by_name>(a.account);
756 SYS_ASSERT( code != nullptr, transaction_exception,
757 "action's code account '${account}' does not exist", ("account", a.account) );
758 for( const auto& auth : a.authorization ) {
759 one_auth = true;
760 auto* actor = db.find<account_object, by_name>(auth.actor);
762 "action's authorizing actor '${account}' does not exist", ("account", auth.actor) );
763 SYS_ASSERT( auth_manager.find_permission(auth) != nullptr, transaction_exception,
764 "action's authorizations include a non-existent permission: ${permission}",
765 ("permission", auth) );
766 if( enforce_actor_whitelist_blacklist )
767 actors.insert( auth.actor );
768 }
769 }
770 SYS_ASSERT( one_auth || is_read_only, tx_no_auths, "transaction must have at least one authorization" );
771
772 if( enforce_actor_whitelist_blacklist ) {
773 control.check_actor_list( actors );
774 }
775 }
776
777
778} }
const mie::Vuint & p
Definition bn.cpp:27
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
const ObjectType & create(Constructor &&con)
static constexpr microseconds maximum()
Definition time.hpp:14
constexpr uint32_t sec_since_epoch() const
Definition time.hpp:88
static time_point now()
Definition time.cpp:14
static constexpr time_point maximum()
Definition time.hpp:46
const global_property_object & get_global_properties() const
const chainbase::database & db() const
bool is_producing_block() const
uint32_t head_block_num() const
void validate_expiration(const transaction &t) const
authorization_manager & get_mutable_authorization_manager()
deep_mind_handler * get_deep_mind_logger() const
std::optional< block_id_type > pending_producer_block_id() const
const authorization_manager & get_authorization_manager() const
void validate_tapos(const transaction &t) const
uint32_t get_greylist_limit() const
bool is_resource_greylisted(const account_name &name) const
void check_actor_list(const flat_set< account_name > &actors) const
time_point pending_block_time() const
resource_limits_manager & get_mutable_resource_limits_manager()
void add_pending_ram_usage(const account_name account, int64_t ram_delta)
void init_for_deferred_trx(fc::time_point published)
const packed_transaction & packed_trx
uint32_t update_billed_cpu_time(fc::time_point now)
transaction_checktime_timer transaction_timer
flat_set< account_name > validate_ram_usage
void init_for_implicit_trx(uint64_t initial_net_usage=0)
transaction_context(controller &c, const packed_transaction &t, transaction_checktime_timer &&timer, fc::time_point start=fc::time_point::now(), bool read_only=false)
void validate_referenced_accounts(const transaction &trx, bool enforce_actor_whitelist_blacklist) const
record_transaction
std::optional< chainbase::database::session > undo_session
flat_set< account_name > bill_to_accounts
std::tuple< int64_t, int64_t, bool, bool > max_bandwidth_billed_accounts_can_pay(bool force_elastic_limits=false) const
void init_for_input_trx(uint64_t packed_trx_unprunable_size, uint64_t packed_trx_prunable_size)
uint64_t id
Definition code_cache.cpp:0
#define RAM_EVENT_ID(FORMAT,...)
Definition deep_mind.hpp:27
int * count
void init()
Definition lib_test.cpp:3
@ read_only
constexpr microseconds hours(int64_t h)
Definition time.hpp:35
constexpr microseconds milliseconds(int64_t s)
Definition time.hpp:33
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
Definition name.hpp:106
constexpr uint64_t billable_size_v
Definition config.hpp:147
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
checksum_type transaction_id_type
Definition types.hpp:236
uint128_t transaction_id_to_sender_id(const transaction_id_type &tid)
name account_name
Definition types.hpp:120
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 __int64 uint64_t
Definition stdint.h:136
uint32_t value
Definition varint.hpp:17
uint32_t deferred_trx_expiration_window
the number of seconds after the time a deferred transaction can first execute until it expires
const transaction_id_type & id() const
const transaction & get_transaction() const
void set_expiration_callback(void(*func)(void *), void *user)
void set_expiration_callback(void(*func)(void *), void *user)
fc::unsigned_int delay_sec
upper limit on the total CPU time billed for this transaction
time_point_sec expiration
the time at which a transaction expires
vector< action > actions
vector< action > context_free_actions
extensions_type transaction_extensions
account_name first_authorizer() const
transaction_checktime_timer & _timer
Definition sys-vm.cpp:42
char * s