Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
sysio::chain::apply_context Class Reference

#include <apply_context.hpp>

Collaboration diagram for sysio::chain::apply_context:

Classes

class  generic_index
 

Public Member Functions

 apply_context (controller &con, transaction_context &trx_ctx, uint32_t action_ordinal, uint32_t depth=0)
 class generic_index
 
void exec_one ()
 Execution methods:
 
void exec ()
 
void execute_inline (action &&a)
 
void execute_context_free_inline (action &&a)
 
void schedule_deferred_transaction (const uint128_t &sender_id, account_name payer, transaction &&trx, bool replace_existing)
 
bool cancel_deferred_transaction (const uint128_t &sender_id, account_name sender)
 
bool cancel_deferred_transaction (const uint128_t &sender_id)
 
void require_authorization (const account_name &account)
 Authorization methods:
 
bool has_authorization (const account_name &account) const
 
void require_authorization (const account_name &account, const permission_name &permission)
 
bool is_account (const account_name &account) const
 exec()
 
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 require_recipient (account_name account)
 
bool has_recipient (account_name account) const
 
void console_append (std::string_view val)
 Console methods:
 
void update_db_usage (const account_name &payer, int64_t delta)
 Database methods:
 
int db_store_i64 (name scope, name table, const account_name &payer, uint64_t id, const char *buffer, size_t buffer_size)
 
void db_update_i64 (int iterator, account_name payer, const char *buffer, size_t buffer_size)
 
void db_remove_i64 (int iterator)
 
int db_get_i64 (int iterator, char *buffer, size_t buffer_size)
 
int db_next_i64 (int iterator, uint64_t &primary)
 
int db_previous_i64 (int iterator, uint64_t &primary)
 
int db_find_i64 (name code, name scope, name table, uint64_t id)
 
int db_lowerbound_i64 (name code, name scope, name table, uint64_t id)
 
int db_upperbound_i64 (name code, name scope, name table, uint64_t id)
 
int db_end_i64 (name code, name scope, name table)
 
int get_action (uint32_t type, uint32_t index, char *buffer, size_t buffer_size) const
 Misc methods:
 
int get_context_free_data (uint32_t index, char *buffer, size_t buffer_size) const
 
vector< account_nameget_active_producers () const
 
uint64_t next_global_sequence ()
 
uint64_t next_recv_sequence (const account_metadata_object &receiver_account)
 
uint64_t next_auth_sequence (account_name actor)
 
void add_ram_usage (account_name account, int64_t ram_delta)
 
void finalize_trace (action_trace &trace, const fc::time_point &start)
 
bool is_context_free () const
 
bool is_privileged () const
 
action_name get_receiver () const
 
const actionget_action () const
 
action_name get_sender () const
 

Public Attributes

controllercontrol
 Fields:
 
chainbase::databasedb
 database where state is stored
 
transaction_contexttrx_context
 transaction context in which the action is running
 
std::vector< char > action_return_value
 
generic_index< index64_objectidx64
 
generic_index< index128_objectidx128
 
generic_index< index256_object, uint128_t *, const uint128_t * > idx256
 
generic_index< index_double_objectidx_double
 
generic_index< index_long_double_objectidx_long_double
 

Protected Member Functions

uint32_t schedule_action (uint32_t ordinal_of_action_to_schedule, account_name receiver, bool context_free)
 
uint32_t schedule_action (action &&act_to_schedule, account_name receiver, bool context_free)
 

Detailed Description

Definition at line 18 of file apply_context.hpp.

Constructor & Destructor Documentation

◆ apply_context()

sysio::chain::apply_context::apply_context ( controller & con,
transaction_context & trx_ctx,
uint32_t action_ordinal,
uint32_t depth = 0 )

Constructor

Definition at line 34 of file apply_context.cpp.

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}
generic_index< index_long_double_object > idx_long_double
generic_index< index_double_object > idx_double
controller & control
Fields:
generic_index< index128_object > idx128
transaction_context & trx_context
transaction context in which the action is running
chainbase::database & db
database where state is stored
generic_index< index256_object, uint128_t *, const uint128_t * > idx256
generic_index< index64_object > idx64

Member Function Documentation

◆ add_ram_usage()

void sysio::chain::apply_context::add_ram_usage ( account_name account,
int64_t ram_delta )

Definition at line 1050 of file apply_context.cpp.

1050 {
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}
const mie::Vuint & p
Definition bn.cpp:27
Here is the caller graph for this function:

◆ cancel_deferred_transaction() [1/2]

bool sysio::chain::apply_context::cancel_deferred_transaction ( const uint128_t & sender_id)
inline

Definition at line 502 of file apply_context.hpp.

502{ return cancel_deferred_transaction(sender_id, receiver); }
bool cancel_deferred_transaction(const uint128_t &sender_id, account_name sender)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ cancel_deferred_transaction() [2/2]

bool sysio::chain::apply_context::cancel_deferred_transaction ( const uint128_t & sender_id,
account_name sender )

Definition at line 626 of file apply_context.cpp.

626 {
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}
const ObjectType * find(CompatibleKey &&key) const
generic_index< MultiIndexType > & get_mutable_index()
void add_ram_usage(account_name account, int64_t ram_delta)
deep_mind_handler * get_deep_mind_logger() const
#define RAM_EVENT_ID(FORMAT,...)
Definition deep_mind.hpp:27
constexpr uint64_t billable_size_v
Definition config.hpp:147
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
Here is the call graph for this function:

◆ console_append()

void sysio::chain::apply_context::console_append ( std::string_view val)
inline

Definition at line 547 of file apply_context.hpp.

547 {
548 _pending_console_output += val;
549 }

◆ db_end_i64()

int sysio::chain::apply_context::db_end_i64 ( name code,
name scope,
name table )

Definition at line 1019 of file apply_context.cpp.

1019 {
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}

◆ db_find_i64()

int sysio::chain::apply_context::db_find_i64 ( name code,
name scope,
name table,
uint64_t id )

Definition at line 973 of file apply_context.cpp.

973 {
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}
Here is the call graph for this function:

◆ db_get_i64()

int sysio::chain::apply_context::db_get_i64 ( int iterator,
char * buffer,
size_t buffer_size )

Definition at line 914 of file apply_context.cpp.

914 {
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}
char * s
memcpy((char *) pInfo->slotDescription, s, l)
Here is the call graph for this function:

◆ db_lowerbound_i64()

int sysio::chain::apply_context::db_lowerbound_i64 ( name code,
name scope,
name table,
uint64_t id )

Definition at line 987 of file apply_context.cpp.

987 {
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}
const generic_index< MultiIndexType > & get_index() const
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
Here is the call graph for this function:

◆ db_next_i64()

int sysio::chain::apply_context::db_next_i64 ( int iterator,
uint64_t & primary )

Definition at line 926 of file apply_context.cpp.

926 {
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}
Here is the call graph for this function:

◆ db_previous_i64()

int sysio::chain::apply_context::db_previous_i64 ( int iterator,
uint64_t & primary )

Definition at line 941 of file apply_context.cpp.

941 {
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}
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
Here is the call graph for this function:

◆ db_remove_i64()

void sysio::chain::apply_context::db_remove_i64 ( int iterator)

Definition at line 878 of file apply_context.cpp.

878 {
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
896 update_db_usage( obj.payer, -(obj.value.size() + config::billable_size_v<key_value_object>) );
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}
std::string name
void modify(const ObjectType &obj, Modifier &&m)
void remove(const ObjectType &obj)
void update_db_usage(const account_name &payer, int64_t delta)
Database methods:
Here is the call graph for this function:

◆ db_store_i64()

int sysio::chain::apply_context::db_store_i64 ( name scope,
name table,
const account_name & payer,
uint64_t id,
const char * buffer,
size_t buffer_size )

Definition at line 778 of file apply_context.cpp.

778 {
779 return db_store_i64( receiver, scope, table, payer, id, buffer, buffer_size);
780}
int db_store_i64(name scope, name table, const account_name &payer, uint64_t id, const char *buffer, size_t buffer_size)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ db_update_i64()

void sysio::chain::apply_context::db_update_i64 ( int iterator,
account_name payer,
const char * buffer,
size_t buffer_size )

Definition at line 822 of file apply_context.cpp.

822 {
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}
name account_name
Definition types.hpp:120
signed __int64 int64_t
Definition stdint.h:135
Here is the call graph for this function:

◆ db_upperbound_i64()

int sysio::chain::apply_context::db_upperbound_i64 ( name code,
name scope,
name table,
uint64_t id )

Definition at line 1003 of file apply_context.cpp.

1003 {
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}
Here is the call graph for this function:

◆ exec()

void sysio::chain::apply_context::exec ( )

Definition at line 200 of file apply_context.cpp.

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}
void exec_one()
Execution methods:
const global_property_object & get_global_properties() const
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
unsigned int uint32_t
Definition stdint.h:126
uint16_t max_inline_action_depth
recursion depth limit on sending inline actions
Here is the call graph for this function:

◆ exec_one()

void sysio::chain::apply_context::exec_one ( )

Definition at line 53 of file apply_context.cpp.

54{
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}
const mie::Vuint & r
Definition bn.cpp:28
const ObjectType & get(CompatibleKey &&key) const
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
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
std::vector< char > action_return_value
bool has_authorization(const account_name &account) const
void finalize_trace(action_trace &trace, const fc::time_point &start)
uint64_t next_auth_sequence(account_name actor)
uint64_t next_recv_sequence(const account_metadata_object &receiver_account)
void check_action_list(account_name code, action_name action) const
bool is_producing_block() const
const apply_handler * find_apply_handler(account_name contract, scope_name scope, action_name act) const
wasm_interface & get_wasm_interface()
bool is_builtin_activated(builtin_protocol_feature_t f) const
static std::optional< uint64_t > convert_exception_to_error_code(const fc::exception &e)
void check_contract_list(account_name code) const
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)
#define FC_RETHROW_EXCEPTIONS(LOG_LEVEL, FORMAT,...)
Catchs all exception's, std::exceptions, and ... and rethrows them after appending the provided log m...
checksum_type digest_type
Definition types.hpp:237
auto generate_action_digest(Hasher &&hash, const action &act, const vector< char > &action_output)
Definition action.hpp:99
vector< permission_level > authorization
Definition action.hpp:59
std::vector< char > return_value
Definition trace.hpp:47
std::optional< uint64_t > error_code
Definition trace.hpp:46
CK_ULONG datalen
Here is the call graph for this function:
Here is the caller graph for this function:

◆ execute_context_free_inline()

void sysio::chain::apply_context::execute_context_free_inline ( action && a)

Definition at line 400 of file apply_context.cpp.

400 {
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}
uint32_t schedule_action(uint32_t ordinal_of_action_to_schedule, account_name receiver, bool context_free)
const chainbase::database & db() const
uint32_t get_max_nonprivileged_inline_action_size() const
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
chain_config_v1 chain_config
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
uint32_t max_inline_action_size
maximum allowed size (in bytes) of an inline action
Here is the call graph for this function:

◆ execute_inline()

void sysio::chain::apply_context::execute_inline ( action && a)

This will execute an action after checking the authorization. Inline transactions are implicitly authorized by the current receiver (running code). This method has significant security considerations and several options have been considered:

  1. privileged accounts (those marked as such by block producers) can authorize any action
  2. all other actions are only authorized by 'receiver' which means the following: a. the user must set permissions on their account to allow the 'receiver' to act on their behalf

Discarded Implementation: at one point we allowed any account that authorized the current transaction to implicitly authorize an inline transaction. This approach would allow privilege escalation and make it unsafe for users to interact with certain contracts. We opted instead to have applications ask the user for permission to take certain actions rather than making it implicit. This way users can better understand the security risk.

Definition at line 312 of file apply_context.cpp.

312 {
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);
331 SYS_ASSERT( actor != nullptr, action_validate_exception,
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}
#define SYS_THROW(exc_type, FORMAT,...)
const log_messages & get_log() const
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
const authorization_manager & get_authorization_manager() const
void check_actor_list(const flat_set< account_name > &actors) const
time_point pending_block_time() const
#define FC_LOG_MESSAGE(LOG_LEVEL, FORMAT,...)
A helper method for generating log messages.
Here is the call graph for this function:

◆ finalize_trace()

void sysio::chain::apply_context::finalize_trace ( action_trace & trace,
const fc::time_point & start )

Definition at line 189 of file apply_context.cpp.

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}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_action() [1/2]

const action & sysio::chain::apply_context::get_action ( ) const
inline

Definition at line 594 of file apply_context.hpp.

594{ return *act; }

◆ get_action() [2/2]

int sysio::chain::apply_context::get_action ( uint32_t type,
uint32_t index,
char * buffer,
size_t buffer_size ) const

Definition at line 737 of file apply_context.cpp.

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}
const packed_transaction & packed_trx
static const Segment ds(Segment::ds)
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
size_t pack_size(const T &v)
Definition raw.hpp:671
const transaction & get_transaction() const
Here is the call graph for this function:

◆ get_active_producers()

vector< account_name > sysio::chain::apply_context::get_active_producers ( ) const

Definition at line 713 of file apply_context.cpp.

713 {
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}
const producer_authority_schedule & active_producers() const
Here is the call graph for this function:

◆ get_code_hash()

void sysio::chain::apply_context::get_code_hash ( account_name account,
uint64_t & code_sequence,
fc::sha256 & code_hash,
uint8_t & vm_type,
uint8_t & vm_version ) const

Definition at line 228 of file apply_context.cpp.

229 {
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}
wasm_interface::vm_type vm_type
Here is the call graph for this function:

◆ get_context_free_data()

int sysio::chain::apply_context::get_context_free_data ( uint32_t index,
char * buffer,
size_t buffer_size ) const

Definition at line 763 of file apply_context.cpp.

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}
const signed_transaction & get_signed_transaction() const
vector< bytes > context_free_data
for each context-free action, there is an entry here
Here is the call graph for this function:

◆ get_receiver()

action_name sysio::chain::apply_context::get_receiver ( ) const
inline

Definition at line 593 of file apply_context.hpp.

593{ return receiver; }

◆ get_sender()

action_name sysio::chain::apply_context::get_sender ( ) const

Definition at line 1059 of file apply_context.cpp.

1059 {
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}
sysio::chain::action_name action_name

◆ has_authorization()

bool sysio::chain::apply_context::has_authorization ( const account_name & account) const

Definition at line 257 of file apply_context.cpp.

257 {
258 for( const auto& auth : act->authorization )
259 if( auth.actor == account )
260 return true;
261 return false;
262}
Here is the caller graph for this function:

◆ has_recipient()

bool sysio::chain::apply_context::has_recipient ( account_name account) const

Return true if the current action has already been scheduled to be delivered to the specified account.

Definition at line 276 of file apply_context.cpp.

276 {
277 for( const auto& p : _notified )
278 if( p.first == code )
279 return true;
280 return false;
281}
Here is the caller graph for this function:

◆ is_account()

bool sysio::chain::apply_context::is_account ( const account_name & account) const
Returns
true if account exists, false if it does not

Definition at line 224 of file apply_context.cpp.

224 {
225 return nullptr != db.find<account_object,by_name>( account );
226}
Here is the call graph for this function:

◆ is_context_free()

bool sysio::chain::apply_context::is_context_free ( ) const
inline

Definition at line 591 of file apply_context.hpp.

591{ return context_free; }

◆ is_privileged()

bool sysio::chain::apply_context::is_privileged ( ) const
inline

Definition at line 592 of file apply_context.hpp.

592{ return privileged; }

◆ next_auth_sequence()

uint64_t sysio::chain::apply_context::next_auth_sequence ( account_name actor)

Definition at line 1042 of file apply_context.cpp.

1042 {
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}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ next_global_sequence()

uint64_t sysio::chain::apply_context::next_global_sequence ( )

Definition at line 1028 of file apply_context.cpp.

1028 {
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}
const dynamic_global_property_object & get_dynamic_global_properties() const
Here is the call graph for this function:
Here is the caller graph for this function:

◆ next_recv_sequence()

uint64_t sysio::chain::apply_context::next_recv_sequence ( const account_metadata_object & receiver_account)

Definition at line 1036 of file apply_context.cpp.

1036 {
1037 db.modify( receiver_account, [&]( auto& ra ) {
1038 ++ra.recv_sequence;
1039 });
1040 return receiver_account.recv_sequence;
1041}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ require_authorization() [1/2]

void sysio::chain::apply_context::require_authorization ( const account_name & account)

Require account to have approved of this message

Parameters
accountThe account whose approval is required

This method will check that account is listed in the message's declared authorizations, and marks the authorization as used. Note that all authorizations on a message must be used, or the message is invalid.

Exceptions
missing_auth_exceptionIf no sufficient permission was found

Definition at line 248 of file apply_context.cpp.

248 {
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}
Here is the caller graph for this function:

◆ require_authorization() [2/2]

void sysio::chain::apply_context::require_authorization ( const account_name & account,
const permission_name & permission )

Definition at line 264 of file apply_context.cpp.

265 {
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}

◆ require_recipient()

void sysio::chain::apply_context::require_recipient ( account_name account)

Requires that the current action be delivered to account

Definition at line 283 of file apply_context.cpp.

283 {
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}
bool has_recipient(account_name account) const
Here is the call graph for this function:

◆ schedule_action() [1/2]

uint32_t sysio::chain::apply_context::schedule_action ( action && act_to_schedule,
account_name receiver,
bool context_free )
protected

Definition at line 651 of file apply_context.cpp.

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}

◆ schedule_action() [2/2]

uint32_t sysio::chain::apply_context::schedule_action ( uint32_t ordinal_of_action_to_schedule,
account_name receiver,
bool context_free )
protected

Definition at line 641 of file apply_context.cpp.

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}
Here is the caller graph for this function:

◆ schedule_deferred_transaction()

void sysio::chain::apply_context::schedule_deferred_transaction ( const uint128_t & sender_id,
account_name payer,
transaction && trx,
bool replace_existing )

uses payer's storage

Definition at line 426 of file apply_context.cpp.

426 {
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,
460 fc::raw::pack( deferred_transaction_generation_context( trx_context.packed_trx.id(), sender_id, receiver ) )
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
552
553 SYS_ASSERT( replace_deferred_activated || !control.is_producing_block()
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;
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;
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
619 || (receiver == act->account) || (receiver == payer) || privileged,
620 subjective_block_production_exception,
621 "Cannot charge RAM to other accounts during notify."
622 );
624}
const ObjectType & create(Constructor &&con)
void require_authorization(const account_name &account)
Authorization methods:
void add_to_ram_correction(account_name account, uint64_t ram_bytes)
block_id_type head_block_id() const
bool is_ram_billing_in_notify_allowed() const
bool sender_avoids_whitelist_blacklist_enforcement(account_name sender) const
bool all_subjective_mitigations_disabled() const
void validate_referenced_accounts(const transaction &trx, bool enforce_actor_whitelist_blacklist) const
record_transaction
void delay(websocketpp::connection_hdl, long duration)
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
auto emplace_extension(extensions_type &exts, uint16_t eid, vector< char > &&data)
Definition types.hpp:264
checksum_type transaction_id_type
Definition types.hpp:236
@ self
the connection is to itself
Definition protocol.hpp:48
unsigned __int64 uint64_t
Definition stdint.h:136
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
Here is the call graph for this function:

◆ update_db_usage()

void sysio::chain::apply_context::update_db_usage ( const account_name & payer,
int64_t delta )

Definition at line 723 of file apply_context.cpp.

723 {
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}
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ action_return_value

std::vector<char> sysio::chain::apply_context::action_return_value

Definition at line 616 of file apply_context.hpp.

◆ control

controller& sysio::chain::apply_context::control

Definition at line 601 of file apply_context.hpp.

◆ db

chainbase::database& sysio::chain::apply_context::db

Definition at line 602 of file apply_context.hpp.

◆ idx128

generic_index<index128_object> sysio::chain::apply_context::idx128

Definition at line 618 of file apply_context.hpp.

◆ idx256

generic_index<index256_object, uint128_t*, const uint128_t*> sysio::chain::apply_context::idx256

Definition at line 619 of file apply_context.hpp.

◆ idx64

generic_index<index64_object> sysio::chain::apply_context::idx64

Definition at line 617 of file apply_context.hpp.

◆ idx_double

generic_index<index_double_object> sysio::chain::apply_context::idx_double

Definition at line 620 of file apply_context.hpp.

◆ idx_long_double

generic_index<index_long_double_object> sysio::chain::apply_context::idx_long_double

Definition at line 621 of file apply_context.hpp.

◆ trx_context

transaction_context& sysio::chain::apply_context::trx_context

Definition at line 603 of file apply_context.hpp.


The documentation for this class was generated from the following files: