diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 15e8bfb0802..f12bdabe70e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -44,7 +44,7 @@ add_library( eosio_chain # # contracts/chain_initializer.cpp - + trace.cpp transaction_metadata.cpp protocol_state_object.cpp protocol_feature_activation.cpp diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 37c78ff7c7b..46ea155aee8 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -29,41 +29,53 @@ static inline void print_debug(account_name receiver, const action_trace& ar) { } } -void apply_context::exec_one( action_trace& trace ) +apply_context::apply_context(controller& con, transaction_context& trx_ctx, uint32_t action_ordinal, uint32_t depth) +:control(con) +,db(con.mutable_db()) +,trx_context(trx_ctx) +,recurse_depth(depth) +,first_receiver_action_ordinal(action_ordinal) +,action_ordinal(action_ordinal) +,idx64(*this) +,idx128(*this) +,idx256(*this) +,idx_double(*this) +,idx_long_double(*this) +{ + action_trace& trace = trx_ctx.get_action_trace(action_ordinal); + act = &trace.act; + receiver = trace.receiver; + context_free = trace.context_free; +} + +void apply_context::exec_one() { auto start = fc::time_point::now(); action_receipt r; r.receiver = receiver; - r.act_digest = digest_type::hash(act); - - trace.trx_id = trx_context.id; - trace.block_num = control.head_block_num() + 1; - trace.block_time = control.pending_block_time(); - trace.producer_block_id = control.pending_producer_block_id(); - trace.act = act; - trace.context_free = context_free; + r.act_digest = digest_type::hash(*act); const auto& cfg = control.get_global_properties().configuration; try { try { const auto& a = control.get_account( receiver ); privileged = a.privileged; - auto native = control.find_apply_handler( receiver, act.account, act.name ); + auto native = control.find_apply_handler( receiver, act->account, act->name ); if( native ) { if( trx_context.enforce_whiteblacklist && control.is_producing_block() ) { control.check_contract_list( receiver ); - control.check_action_list( act.account, act.name ); + control.check_action_list( act->account, act->name ); } (*native)( *this ); } if( a.code.size() > 0 - && !(act.account == config::system_account_name && act.name == N( setcode ) && + && !(act->account == config::system_account_name && act->name == N( setcode ) && receiver == config::system_account_name) ) { if( trx_context.enforce_whiteblacklist && control.is_producing_block() ) { control.check_contract_list( receiver ); - control.check_action_list( act.account, act.name ); + control.check_action_list( act->account, act->name ); } try { control.get_wasm_interface().apply( a.code_version, a.code, *this ); @@ -71,7 +83,7 @@ void apply_context::exec_one( action_trace& trace ) } } FC_RETHROW_EXCEPTIONS( warn, "pending console output: ${console}", ("console", _pending_console_output) ) } catch( fc::exception& e ) { - trace.receipt = r; // fill with known data + action_trace& trace = trx_context.get_action_trace( action_ordinal ); trace.except = e; finalize_trace( trace, start ); throw; @@ -80,17 +92,18 @@ void apply_context::exec_one( action_trace& trace ) r.global_sequence = next_global_sequence(); r.recv_sequence = next_recv_sequence( receiver ); - const auto& account_sequence = db.get(act.account); + const auto& account_sequence = db.get(act->account); r.code_sequence = account_sequence.code_sequence; // could be modified by action execution above r.abi_sequence = account_sequence.abi_sequence; // could be modified by action execution above - for( const auto& auth : act.authorization ) { + for( const auto& auth : act->authorization ) { r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); } + action_trace& trace = trx_context.get_action_trace( action_ordinal ); trace.receipt = r; - trx_context.executed.emplace_back( move(r) ); + trx_context.executed.emplace_back( std::move(r) ); finalize_trace( trace, start ); @@ -110,14 +123,13 @@ void apply_context::finalize_trace( action_trace& trace, const fc::time_point& s trace.elapsed = fc::time_point::now() - start; } -void apply_context::exec( action_trace& trace ) +void apply_context::exec() { - _notified.push_back(receiver); - exec_one( trace ); + _notified.emplace_back( receiver, action_ordinal ); + exec_one(); for( uint32_t i = 1; i < _notified.size(); ++i ) { - receiver = _notified[i]; - trace.inline_traces.emplace_back( ); - exec_one( trace.inline_traces.back() ); + std::tie( receiver, action_ordinal ) = _notified[i]; + exec_one(); } if( _cfa_inline_actions.size() > 0 || _inline_actions.size() > 0 ) { @@ -125,14 +137,12 @@ void apply_context::exec( action_trace& trace ) transaction_exception, "max inline action depth per transaction reached" ); } - for( const auto& inline_action : _cfa_inline_actions ) { - trace.inline_traces.emplace_back(); - trx_context.dispatch_action( trace.inline_traces.back(), inline_action, inline_action.account, true, recurse_depth + 1 ); + for( uint32_t ordinal : _cfa_inline_actions ) { + trx_context.execute_action( ordinal, recurse_depth + 1 ); } - for( const auto& inline_action : _inline_actions ) { - trace.inline_traces.emplace_back(); - trx_context.dispatch_action( trace.inline_traces.back(), inline_action, inline_action.account, false, recurse_depth + 1 ); + for( uint32_t ordinal : _inline_actions ) { + trx_context.execute_action( ordinal, recurse_depth + 1 ); } } /// exec() @@ -142,9 +152,8 @@ bool apply_context::is_account( const account_name& account )const { } void apply_context::require_authorization( const account_name& account ) { - for( uint32_t i=0; i < act.authorization.size(); i++ ) { - if( act.authorization[i].actor == account ) { - used_authorizations[i] = true; + for( uint32_t i=0; i < act->authorization.size(); i++ ) { + if( act->authorization[i].actor == account ) { return; } } @@ -152,7 +161,7 @@ void apply_context::require_authorization( const account_name& account ) { } bool apply_context::has_authorization( const account_name& account )const { - for( const auto& auth : act.authorization ) + for( const auto& auth : act->authorization ) if( auth.actor == account ) return true; return false; @@ -160,10 +169,9 @@ bool apply_context::has_authorization( const account_name& account )const { void apply_context::require_authorization(const account_name& account, const permission_name& permission) { - for( uint32_t i=0; i < act.authorization.size(); i++ ) - if( act.authorization[i].actor == account ) { - if( act.authorization[i].permission == permission ) { - used_authorizations[i] = true; + for( uint32_t i=0; i < act->authorization.size(); i++ ) + if( act->authorization[i].actor == account ) { + if( act->authorization[i].permission == permission ) { return; } } @@ -172,15 +180,18 @@ void apply_context::require_authorization(const account_name& account, } bool apply_context::has_recipient( account_name code )const { - for( auto a : _notified ) - if( a == code ) + for( const auto& p : _notified ) + if( p.first == code ) return true; return false; } void apply_context::require_recipient( account_name recipient ) { if( !has_recipient(recipient) ) { - _notified.push_back(recipient); + _notified.emplace_back( + recipient, + schedule_action( action_ordinal, recipient, false ) + ); } } @@ -210,7 +221,7 @@ void apply_context::execute_inline( action&& a ) { bool disallow_send_to_self_bypass = false; // eventually set to whether the appropriate protocol feature has been activated bool send_to_self = (a.account == receiver); - bool inherit_parent_authorizations = (!disallow_send_to_self_bypass && send_to_self && (receiver == act.account) && control.is_producing_block()); + bool inherit_parent_authorizations = (!disallow_send_to_self_bypass && send_to_self && (receiver == act->account) && control.is_producing_block()); flat_set inherited_authorizations; if( inherit_parent_authorizations ) { @@ -227,7 +238,7 @@ void apply_context::execute_inline( action&& a ) { if( enforce_actor_whitelist_blacklist ) actors.insert( auth.actor ); - if( inherit_parent_authorizations && std::find(act.authorization.begin(), act.authorization.end(), auth) != act.authorization.end() ) { + if( inherit_parent_authorizations && std::find(act->authorization.begin(), act->authorization.end(), auth) != act->authorization.end() ) { inherited_authorizations.insert( auth ); } } @@ -271,7 +282,10 @@ void apply_context::execute_inline( action&& a ) { } } - _inline_actions.emplace_back( move(a) ); + auto inline_receiver = a.account; + _inline_actions.emplace_back( + schedule_action( std::move(a), inline_receiver, false ) + ); } void apply_context::execute_context_free_inline( action&& a ) { @@ -282,7 +296,11 @@ void apply_context::execute_context_free_inline( action&& a ) { EOS_ASSERT( a.authorization.size() == 0, action_validate_exception, "context-free actions cannot have authorizations" ); - _cfa_inline_actions.emplace_back( move(a) ); + + auto inline_receiver = a.account; + _cfa_inline_actions.emplace_back( + schedule_action( std::move(a), inline_receiver, true ) + ); } @@ -404,7 +422,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a } ); } - EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account) || (receiver == payer) || privileged, + EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act->account) || (receiver == payer) || privileged, subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." ); add_ram_usage( payer, (config::billable_size_v + trx_size) ); } @@ -419,6 +437,26 @@ bool apply_context::cancel_deferred_transaction( const uint128_t& sender_id, acc return gto; } +uint32_t apply_context::schedule_action( uint32_t ordinal_of_action_to_schedule, account_name receiver, bool context_free ) +{ + uint32_t scheduled_action_ordinal = trx_context.schedule_action( ordinal_of_action_to_schedule, + receiver, context_free, + action_ordinal, first_receiver_action_ordinal ); + + act = &trx_context.get_action_trace( action_ordinal ).act; + return scheduled_action_ordinal; +} + +uint32_t apply_context::schedule_action( action&& act_to_schedule, account_name receiver, bool context_free ) +{ + uint32_t scheduled_action_ordinal = trx_context.schedule_action( std::move(act_to_schedule), + receiver, context_free, + action_ordinal, first_receiver_action_ordinal ); + + act = &trx_context.get_action_trace( action_ordinal ).act; + return scheduled_action_ordinal; +} + const table_id_object* apply_context::find_table( name code, name scope, name table ) { return db.find(boost::make_tuple(code, scope, table)); } @@ -462,7 +500,7 @@ bytes apply_context::get_packed_transaction() { void apply_context::update_db_usage( const account_name& payer, int64_t delta ) { if( delta > 0 ) { if( !(privileged || payer == account_name(receiver)) ) { - EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account), + EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act->account), subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." ); require_authorization( payer ); } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2e732c869dc..14fffa061b5 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -966,8 +966,7 @@ struct controller_impl { try { trx_context.init_for_implicit_trx(); trx_context.published = gtrx.published; - trx_context.trace->action_traces.emplace_back(); - trx_context.dispatch_action( trx_context.trace->action_traces.back(), etrx.actions.back(), gtrx.sender ); + trx_context.execute_action( trx_context.schedule_action( etrx.actions.back(), gtrx.sender, false, 0, 0 ), 0 ); trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful auto restore = make_block_restore_point(); @@ -988,14 +987,13 @@ struct controller_impl { return trace; } - void remove_scheduled_transaction( const generated_transaction_object& gto ) { - resource_limits.add_pending_ram_usage( - gto.payer, - -(config::billable_size_v + gto.packed_trx.size()) - ); + int64_t remove_scheduled_transaction( const generated_transaction_object& gto ) { + int64_t ram_delta = -(config::billable_size_v + gto.packed_trx.size()); + resource_limits.add_pending_ram_usage( gto.payer, ram_delta ); // No need to verify_account_ram_usage since we are only reducing memory db.remove( gto ); + return ram_delta; } bool failure_is_subjective( const fc::exception& e ) const { @@ -1042,7 +1040,7 @@ struct controller_impl { // // IF the transaction FAILs in a subjective way, `undo_session` should expire without being squashed // resulting in the GTO being restored and available for a future block to retire. - remove_scheduled_transaction(gto); + int64_t trx_removal_ram_delta = remove_scheduled_transaction(gto); fc::datastream ds( gtrx.packed_trx.data(), gtrx.packed_trx.size() ); @@ -1064,6 +1062,7 @@ struct controller_impl { trace->producer_block_id = self.pending_producer_block_id(); trace->scheduled = true; trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::expired, billed_cpu_time_us, 0 ); // expire the transaction + trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); emit( self.accepted_transaction, trx ); emit( self.applied_transaction, trace ); undo_session.squash(); @@ -1103,6 +1102,8 @@ struct controller_impl { fc::move_append( pending->_block_stage.get()._actions, move(trx_context.executed) ); + trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); + emit( self.accepted_transaction, trx ); emit( self.applied_transaction, trace ); @@ -1133,6 +1134,7 @@ struct controller_impl { error_trace->failed_dtrx_trace = trace; trace = error_trace; if( !trace->except_ptr ) { + trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); emit( self.accepted_transaction, trx ); emit( self.applied_transaction, trace ); undo_session.squash(); @@ -1169,6 +1171,7 @@ struct controller_impl { block_timestamp_type(self.pending_block_time()).slot ); // Should never fail trace->receipt = push_receipt(gtrx.trx_id, transaction_receipt::hard_fail, cpu_time_to_bill_us, 0); + trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); emit( self.accepted_transaction, trx ); emit( self.applied_transaction, trace ); diff --git a/libraries/chain/eosio_contract.cpp b/libraries/chain/eosio_contract.cpp index cc303bb50cd..026efefb4a2 100644 --- a/libraries/chain/eosio_contract.cpp +++ b/libraries/chain/eosio_contract.cpp @@ -68,7 +68,7 @@ void validate_authority_precondition( const apply_context& context, const author * This method is called assuming precondition_system_newaccount succeeds a */ void apply_eosio_newaccount(apply_context& context) { - auto create = context.act.data_as(); + auto create = context.get_action().data_as(); try { context.require_authorization(create.creator); // context.require_write_lock( config::eosio_auth_scope ); @@ -129,7 +129,7 @@ void apply_eosio_setcode(apply_context& context) { const auto& cfg = context.control.get_global_properties().configuration; auto& db = context.db; - auto act = context.act.data_as(); + auto act = context.get_action().data_as(); context.require_authorization(act.account); EOS_ASSERT( act.vmtype == 0, invalid_contract_vm_type, "code should be 0" ); @@ -174,7 +174,7 @@ void apply_eosio_setcode(apply_context& context) { void apply_eosio_setabi(apply_context& context) { auto& db = context.db; - auto act = context.act.data_as(); + auto act = context.get_action().data_as(); context.require_authorization(act.account); @@ -205,7 +205,7 @@ void apply_eosio_setabi(apply_context& context) { void apply_eosio_updateauth(apply_context& context) { - auto update = context.act.data_as(); + auto update = context.get_action().data_as(); context.require_authorization(update.account); // only here to mark the single authority on this action as used auto& authorization = context.control.get_mutable_authorization_manager(); @@ -270,7 +270,7 @@ void apply_eosio_updateauth(apply_context& context) { void apply_eosio_deleteauth(apply_context& context) { // context.require_write_lock( config::eosio_auth_scope ); - auto remove = context.act.data_as(); + auto remove = context.get_action().data_as(); context.require_authorization(remove.account); // only here to mark the single authority on this action as used EOS_ASSERT(remove.permission != config::active_name, action_validate_exception, "Cannot delete active authority"); @@ -301,7 +301,7 @@ void apply_eosio_deleteauth(apply_context& context) { void apply_eosio_linkauth(apply_context& context) { // context.require_write_lock( config::eosio_auth_scope ); - auto requirement = context.act.data_as(); + auto requirement = context.get_action().data_as(); try { EOS_ASSERT(!requirement.requirement.empty(), action_validate_exception, "Required permission cannot be empty"); @@ -318,7 +318,7 @@ void apply_eosio_linkauth(apply_context& context) { const permission_object* permission = nullptr; if( context.control.is_builtin_activated( builtin_protocol_feature_t::only_link_to_existing_permission ) ) { permission = db.find( - boost::make_tuple( requirement.account, requirement.requirement ) + boost::make_tuple( requirement.account, requirement.requirement ) ); } else { permission = db.find(requirement.requirement); @@ -358,7 +358,7 @@ void apply_eosio_unlinkauth(apply_context& context) { // context.require_write_lock( config::eosio_auth_scope ); auto& db = context.db; - auto unlink = context.act.data_as(); + auto unlink = context.get_action().data_as(); context.require_authorization(unlink.account); // only here to mark the single authority on this action as used @@ -374,7 +374,7 @@ void apply_eosio_unlinkauth(apply_context& context) { } void apply_eosio_canceldelay(apply_context& context) { - auto cancel = context.act.data_as(); + auto cancel = context.get_action().data_as(); context.require_authorization(cancel.canceling_auth.actor); // only here to mark the single authority on this action as used const auto& trx_id = cancel.trx_id; diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 398f219ced8..1e87cd26ef1 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -248,7 +248,6 @@ namespace impl { std::is_same::value || std::is_same::value || std::is_same::value || - std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index 951422cb753..d1eac6495dd 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -452,34 +452,23 @@ class apply_context { /// Constructor public: - apply_context(controller& con, transaction_context& trx_ctx, const action& a, uint32_t depth=0) - :control(con) - ,db(con.mutable_db()) - ,trx_context(trx_ctx) - ,act(a) - ,receiver(act.account) - ,used_authorizations(act.authorization.size(), false) - ,recurse_depth(depth) - ,idx64(*this) - ,idx128(*this) - ,idx256(*this) - ,idx_double(*this) - ,idx_long_double(*this) - { - } - + apply_context(controller& con, transaction_context& trx_ctx, uint32_t action_ordinal, uint32_t depth=0); /// Execution methods: public: - void exec_one( action_trace& trace ); - void exec( action_trace& trace ); + void exec_one(); + 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 ) { return cancel_deferred_transaction(sender_id, receiver); } + protected: + 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 ); + /// Authorization methods: public: @@ -560,20 +549,29 @@ class apply_context { 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 { return context_free; } + bool is_privileged()const { return privileged; } + action_name get_receiver()const { return receiver; } + const action& get_action()const { return *act; } + /// Fields: public: controller& control; chainbase::database& db; ///< database where state is stored transaction_context& trx_context; ///< transaction context in which the action is running - const action& act; ///< message being applied + + private: + const action* act = nullptr; ///< action being applied + // act pointer may be invalidated on call to trx_context.schedule_action account_name receiver; ///< the code that is currently running - vector used_authorizations; ///< Parallel to act.authorization; tracks which permissions have been used while processing the message uint32_t recurse_depth; ///< how deep inline actions can recurse + uint32_t first_receiver_action_ordinal = 0; + uint32_t action_ordinal = 0; bool privileged = false; bool context_free = false; - bool used_context_free_api = false; + public: generic_index idx64; generic_index idx128; generic_index idx256; @@ -583,9 +581,9 @@ class apply_context { private: iterator_cache keyval_cache; - vector _notified; ///< keeps track of new accounts to be notifed of current message - vector _inline_actions; ///< queued inline messages - vector _cfa_inline_actions; ///< queued inline messages + vector< std::pair > _notified; ///< keeps track of new accounts to be notifed of current message + vector _inline_actions; ///< action_ordinals of queued inline actions + vector _cfa_inline_actions; ///< action_ordinals of queued inline context-free actions std::string _pending_console_output; flat_set _account_ram_deltas; ///< flat_set of account_delta so json is an array of objects diff --git a/libraries/chain/include/eosio/chain/trace.hpp b/libraries/chain/include/eosio/chain/trace.hpp index 58de120bdd8..5331bef1563 100644 --- a/libraries/chain/include/eosio/chain/trace.hpp +++ b/libraries/chain/include/eosio/chain/trace.hpp @@ -20,33 +20,33 @@ namespace eosio { namespace chain { friend bool operator<( const account_delta& lhs, const account_delta& rhs ) { return lhs.account < rhs.account; } }; - struct base_action_trace { - base_action_trace( const action_receipt& r ):receipt(r){} - base_action_trace(){} - - action_receipt receipt; - action act; - bool context_free = false; - fc::microseconds elapsed; - string console; + struct transaction_trace; + using transaction_trace_ptr = std::shared_ptr; - transaction_id_type trx_id; ///< the transaction that generated this action - uint32_t block_num = 0; - block_timestamp_type block_time; + struct action_trace { + action_trace( const transaction_trace& trace, const action& act, account_name receiver, bool context_free, + uint32_t action_ordinal, uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ); + action_trace( const transaction_trace& trace, action&& act, account_name receiver, bool context_free, + uint32_t action_ordinal, uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ); + action_trace(){} + + fc::unsigned_int action_ordinal; + fc::unsigned_int creator_action_ordinal; + fc::unsigned_int parent_action_ordinal; + fc::optional receipt; + action_name receiver; + action act; + bool context_free = false; + fc::microseconds elapsed; + string console; + transaction_id_type trx_id; ///< the transaction that generated this action + uint32_t block_num = 0; + block_timestamp_type block_time; fc::optional producer_block_id; flat_set account_ram_deltas; fc::optional except; }; - struct action_trace : public base_action_trace { - using base_action_trace::base_action_trace; - - vector inline_traces; - }; - - struct transaction_trace; - using transaction_trace_ptr = std::shared_ptr; - struct transaction_trace { transaction_id_type id; uint32_t block_num = 0; @@ -56,7 +56,8 @@ namespace eosio { namespace chain { fc::microseconds elapsed; uint64_t net_usage = 0; bool scheduled = false; - vector action_traces; ///< disposable + vector action_traces; + fc::optional account_ram_delta; transaction_trace_ptr failed_dtrx_trace; fc::optional except; @@ -68,13 +69,11 @@ namespace eosio { namespace chain { FC_REFLECT( eosio::chain::account_delta, (account)(delta) ) -FC_REFLECT( eosio::chain::base_action_trace, - (receipt)(act)(context_free)(elapsed)(console)(trx_id) - (block_num)(block_time)(producer_block_id)(account_ram_deltas)(except) ) - -FC_REFLECT_DERIVED( eosio::chain::action_trace, - (eosio::chain::base_action_trace), (inline_traces) ) +FC_REFLECT( eosio::chain::action_trace, + (action_ordinal)(creator_action_ordinal)(parent_action_ordinal)(receipt) + (receiver)(act)(context_free)(elapsed)(console)(trx_id)(block_num)(block_time) + (producer_block_id)(account_ram_deltas)(except) ) FC_REFLECT( eosio::chain::transaction_trace, (id)(block_num)(block_time)(producer_block_id) (receipt)(elapsed)(net_usage)(scheduled) - (action_traces)(failed_dtrx_trace)(except) ) + (action_traces)(account_ram_delta)(failed_dtrx_trace)(except) ) diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index b0327dafb18..bbfe53810f6 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -64,10 +64,23 @@ namespace eosio { namespace chain { void add_ram_usage( account_name account, int64_t ram_delta ); - void dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free = false, uint32_t recurse_depth = 0 ); - inline void dispatch_action( action_trace& trace, const action& a, bool context_free = false ) { - dispatch_action(trace, a, a.account, context_free); - }; + action_trace& get_action_trace( uint32_t action_ordinal ); + const action_trace& get_action_trace( uint32_t action_ordinal )const; + + /** invalidates any action_trace references returned by get_action_trace */ + uint32_t schedule_action( const action& act, account_name receiver, bool context_free, + uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ); + + /** invalidates any action_trace references returned by get_action_trace */ + uint32_t schedule_action( action&& act, account_name receiver, bool context_free, + uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ); + + /** invalidates any action_trace references returned by get_action_trace */ + uint32_t schedule_action( uint32_t action_ordinal, account_name receiver, bool context_free, + uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ); + + void execute_action( uint32_t action_ordinal, uint32_t recurse_depth ); + void schedule_transaction(); void record_transaction( const transaction_id_type& id, fc::time_point_sec expire ); diff --git a/libraries/chain/trace.cpp b/libraries/chain/trace.cpp new file mode 100644 index 00000000000..2379018518f --- /dev/null +++ b/libraries/chain/trace.cpp @@ -0,0 +1,41 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#include + +namespace eosio { namespace chain { + +action_trace::action_trace( + const transaction_trace& trace, const action& act, account_name receiver, bool context_free, + uint32_t action_ordinal, uint32_t creator_action_ordinal, uint32_t parent_action_ordinal +) +:action_ordinal( action_ordinal ) +,creator_action_ordinal( creator_action_ordinal ) +,parent_action_ordinal( parent_action_ordinal ) +,receiver( receiver ) +,act( act ) +,context_free( context_free ) +,trx_id( trace.id ) +,block_num( trace.block_num ) +,block_time( trace.block_time ) +,producer_block_id( trace.producer_block_id ) +{} + +action_trace::action_trace( + const transaction_trace& trace, action&& act, account_name receiver, bool context_free, + uint32_t action_ordinal, uint32_t creator_action_ordinal, uint32_t parent_action_ordinal +) +:action_ordinal( action_ordinal ) +,creator_action_ordinal( creator_action_ordinal ) +,parent_action_ordinal( parent_action_ordinal ) +,receiver( receiver ) +,act( std::move(act) ) +,context_free( context_free ) +,trx_id( trace.id ) +,block_num( trace.block_num ) +,block_time( trace.block_time ) +,producer_block_id( trace.producer_block_id ) +{} + +} } // eosio::chain diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 226e6863a16..0cfd87fef8d 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -333,17 +333,23 @@ namespace bacc = boost::accumulators; if( apply_context_free ) { for( const auto& act : trx.context_free_actions ) { - trace->action_traces.emplace_back(); - dispatch_action( trace->action_traces.back(), act, true ); + schedule_action( act, act.account, true, 0, 0 ); } } if( delay == fc::microseconds() ) { for( const auto& act : trx.actions ) { - trace->action_traces.emplace_back(); - dispatch_action( trace->action_traces.back(), act ); + schedule_action( act, act.account, false, 0, 0 ); } - } else { + } + + auto& action_traces = trace->action_traces; + uint32_t num_original_actions_to_execute = action_traces.size(); + for( uint32_t i = 1; i <= num_original_actions_to_execute; ++i ) { + execute_action( i, 0 ); + } + + if( delay != fc::microseconds() ) { schedule_transaction(); } } @@ -566,14 +572,69 @@ namespace bacc = boost::accumulators; return std::make_tuple(account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu); } - void transaction_context::dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) { - apply_context acontext( control, *this, a, recurse_depth ); - acontext.context_free = context_free; - acontext.receiver = receiver; + action_trace& transaction_context::get_action_trace( uint32_t action_ordinal ) { + EOS_ASSERT( 0 < action_ordinal && action_ordinal <= trace->action_traces.size() , + transaction_exception, + "action_ordinal ${ordinal} is outside allowed range [1,${max}]", + ("ordinal", action_ordinal)("max", trace->action_traces.size()) + ); + return trace->action_traces[action_ordinal-1]; + } + + const action_trace& transaction_context::get_action_trace( uint32_t action_ordinal )const { + EOS_ASSERT( 0 < action_ordinal && action_ordinal <= trace->action_traces.size() , + transaction_exception, + "action_ordinal ${ordinal} is outside allowed range [1,${max}]", + ("ordinal", action_ordinal)("max", trace->action_traces.size()) + ); + return trace->action_traces[action_ordinal-1]; + } + + uint32_t transaction_context::schedule_action( const action& act, account_name receiver, bool context_free, + uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ) + { + uint32_t new_action_ordinal = trace->action_traces.size() + 1; + + trace->action_traces.emplace_back( *trace, act, receiver, context_free, + new_action_ordinal, creator_action_ordinal, parent_action_ordinal ); - acontext.exec( trace ); + return new_action_ordinal; } + uint32_t transaction_context::schedule_action( action&& act, account_name receiver, bool context_free, + uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ) + { + uint32_t new_action_ordinal = trace->action_traces.size() + 1; + + trace->action_traces.emplace_back( *trace, std::move(act), receiver, context_free, + new_action_ordinal, creator_action_ordinal, parent_action_ordinal ); + + return new_action_ordinal; + } + + uint32_t transaction_context::schedule_action( uint32_t action_ordinal, account_name receiver, bool context_free, + uint32_t creator_action_ordinal, uint32_t parent_action_ordinal ) + { + uint32_t new_action_ordinal = trace->action_traces.size() + 1; + + trace->action_traces.reserve( new_action_ordinal ); + + const action& provided_action = get_action_trace( action_ordinal ).act; + + // The reserve above is required so that the emplace_back below does not invalidate the provided_action reference. + + trace->action_traces.emplace_back( *trace, provided_action, receiver, context_free, + new_action_ordinal, creator_action_ordinal, parent_action_ordinal ); + + return new_action_ordinal; + } + + void transaction_context::execute_action( uint32_t action_ordinal, uint32_t recurse_depth ) { + apply_context acontext( control, *this, action_ordinal, recurse_depth ); + acontext.exec(); + } + + void transaction_context::schedule_transaction() { // Charge ahead of time for the additional net usage needed to retire the delayed transaction // whether that be by successfully executing, soft failure, hard failure, or expiration. @@ -597,7 +658,9 @@ namespace bacc = boost::accumulators; trx_size = gto.set( trx ); }); - add_ram_usage( cgto.payer, (config::billable_size_v + trx_size) ); + int64_t ram_delta = (config::billable_size_v + trx_size); + add_ram_usage( cgto.payer, ram_delta ); + trace->account_ram_delta = account_delta( cgto.payer, ram_delta ); } void transaction_context::record_transaction( const transaction_id_type& id, fc::time_point_sec expire ) { diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 3414d9dc972..004a7326e83 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -77,9 +77,8 @@ class context_aware_api { context_aware_api(apply_context& ctx, bool context_free = false ) :context(ctx) { - if( context.context_free ) + if( context.is_context_free() ) EOS_ASSERT( context_free, unaccessible_api, "only context free api's can be used in this context" ); - context.used_context_free_api |= !context_free; } void checktime() { @@ -96,7 +95,7 @@ class context_free_api : public context_aware_api { context_free_api( apply_context& ctx ) :context_aware_api(ctx, true) { /* the context_free_data is not available during normal application because it is prunable */ - EOS_ASSERT( context.context_free, unaccessible_api, "this API may only be called from context_free apply" ); + EOS_ASSERT( context.is_context_free(), unaccessible_api, "this API may only be called from context_free apply" ); } int get_context_free_data( uint32_t index, array_ptr buffer, size_t buffer_size )const { @@ -109,7 +108,7 @@ class privileged_api : public context_aware_api { privileged_api( apply_context& ctx ) :context_aware_api(ctx) { - EOS_ASSERT( context.privileged, unaccessible_api, "${code} does not have permission to call this API", ("code",context.receiver) ); + EOS_ASSERT( context.is_privileged(), unaccessible_api, "${code} does not have permission to call this API", ("code",context.get_receiver()) ); } /** @@ -978,21 +977,21 @@ class action_api : public context_aware_api { :context_aware_api(ctx,true){} int read_action_data(array_ptr memory, size_t buffer_size) { - auto s = context.act.data.size(); + auto s = context.get_action().data.size(); if( buffer_size == 0 ) return s; auto copy_size = std::min( buffer_size, s ); - memcpy( memory, context.act.data.data(), copy_size ); + memcpy( memory, context.get_action().data.data(), copy_size ); return copy_size; } int action_data_size() { - return context.act.data.size(); + return context.get_action().data.size(); } name current_receiver() { - return context.receiver; + return context.get_receiver(); } }; diff --git a/libraries/chain/webassembly/wabt.cpp b/libraries/chain/webassembly/wabt.cpp index 2d45fa4ee01..a23919e0ec6 100644 --- a/libraries/chain/webassembly/wabt.cpp +++ b/libraries/chain/webassembly/wabt.cpp @@ -28,7 +28,7 @@ class wabt_instantiated_module : public wasm_instantiated_module_interface { continue; _initial_globals.emplace_back(_env->GetGlobal(i), _env->GetGlobal(i)->typed_value); } - + if(_env->GetMemoryCount()) _initial_memory_configuration = _env->GetMemory(0)->page_limits; } @@ -50,9 +50,9 @@ class wabt_instantiated_module : public wasm_instantiated_module_interface { memcpy(memory->data.data(), _initial_memory.data(), _initial_memory.size()); } - _params[0].set_i64(uint64_t(context.receiver)); - _params[1].set_i64(uint64_t(context.act.account)); - _params[2].set_i64(uint64_t(context.act.name)); + _params[0].set_i64(uint64_t(context.get_receiver())); + _params[1].set_i64(uint64_t(context.get_action().account)); + _params[2].set_i64(uint64_t(context.get_action().name)); ExecResult res = _executor.RunStartFunction(_instatiated_module); EOS_ASSERT( res.result == interp::Result::Ok, wasm_execution_error, "wabt start function failure (${s})", ("s", ResultToString(res.result)) ); @@ -92,7 +92,7 @@ std::unique_ptr wabt_runtime::instantiate_mo wabt::Result res = ReadBinaryInterp(env.get(), code_bytes, code_size, read_binary_options, &errors, &instantiated_module); EOS_ASSERT( Succeeded(res), wasm_execution_error, "Error building wabt interp: ${e}", ("e", wabt::FormatErrorsToString(errors, Location::Type::Binary)) ); - + return std::make_unique(std::move(env), initial_memory, instantiated_module); } diff --git a/libraries/chain/webassembly/wavm.cpp b/libraries/chain/webassembly/wavm.cpp index e614398c74e..a4e519a14e0 100644 --- a/libraries/chain/webassembly/wavm.cpp +++ b/libraries/chain/webassembly/wavm.cpp @@ -30,9 +30,9 @@ class wavm_instantiated_module : public wasm_instantiated_module_interface { {} void apply(apply_context& context) override { - vector args = {Value(uint64_t(context.receiver)), - Value(uint64_t(context.act.account)), - Value(uint64_t(context.act.name))}; + vector args = {Value(uint64_t(context.get_receiver())), + Value(uint64_t(context.get_action().account)), + Value(uint64_t(context.get_action().name))}; call("apply", args, context); } diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 154ebef7410..0bccbfe8034 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -126,7 +126,7 @@ namespace eosio { namespace testing { vector get_scheduled_transactions() const; transaction_trace_ptr push_transaction( packed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US ); - transaction_trace_ptr push_transaction( signed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US ); + transaction_trace_ptr push_transaction( signed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US, bool no_throw = false ); action_result push_action(action&& cert_act, uint64_t authorizer); // TODO/QUESTION: Is this needed? transaction_trace_ptr push_action( const account_name& code, diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 93dc4b5f61f..82f91b90576 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -454,7 +454,8 @@ namespace eosio { namespace testing { transaction_trace_ptr base_tester::push_transaction( signed_transaction& trx, fc::time_point deadline, - uint32_t billed_cpu_time_us + uint32_t billed_cpu_time_us, + bool no_throw ) { try { if( !control->is_building_block() ) @@ -466,6 +467,7 @@ namespace eosio { namespace testing { } auto r = control->push_transaction( std::make_shared(trx,c), deadline, billed_cpu_time_us ); + if (no_throw) return r; if( r->except_ptr ) std::rethrow_exception( r->except_ptr ); if( r->except) throw *r->except; return r; diff --git a/plugins/chain_api_plugin/chain_api_plugin.cpp b/plugins/chain_api_plugin/chain_api_plugin.cpp index 6433ce02ba0..3a591eedbb5 100644 --- a/plugins/chain_api_plugin/chain_api_plugin.cpp +++ b/plugins/chain_api_plugin/chain_api_plugin.cpp @@ -106,7 +106,8 @@ void chain_api_plugin::plugin_startup() { CHAIN_RO_CALL(get_transaction_id, 200), CHAIN_RW_CALL_ASYNC(push_block, chain_apis::read_write::push_block_results, 202), CHAIN_RW_CALL_ASYNC(push_transaction, chain_apis::read_write::push_transaction_results, 202), - CHAIN_RW_CALL_ASYNC(push_transactions, chain_apis::read_write::push_transactions_results, 202) + CHAIN_RW_CALL_ASYNC(push_transactions, chain_apis::read_write::push_transactions_results, 202), + CHAIN_RW_CALL_ASYNC(send_transaction, chain_apis::read_write::send_transaction_results, 202) }); } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index aca508db776..e367d0b85d2 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1881,7 +1881,6 @@ void read_write::push_block(read_write::push_block_params&& params, next_functio } void read_write::push_transaction(const read_write::push_transaction_params& params, next_function next) { - try { auto pretty_input = std::make_shared(); auto resolver = make_resolver(this, abi_serializer_max_time); @@ -1901,6 +1900,45 @@ void read_write::push_transaction(const read_write::push_transaction_params& par fc::variant output; try { output = db.to_variant_with_abi( *trx_trace_ptr, abi_serializer_max_time ); + + // Create map of (parent_action_ordinal, global_sequence) with action trace + std::map< std::pair, fc::mutable_variant_object > act_traces_map; + for( const auto& act_trace : output["action_traces"].get_array() ) { + if (act_trace["receipt"].is_null() && act_trace["except"].is_null()) continue; + auto parent_action_ordinal = act_trace["parent_action_ordinal"].as().value; + auto global_sequence = act_trace["receipt"].is_null() ? + std::numeric_limits::max() : + act_trace["receipt"]["global_sequence"].as(); + act_traces_map.emplace( std::make_pair( parent_action_ordinal, global_sequence ), + act_trace.get_object() ); + } + + std::function(uint32_t)> convert_act_trace_to_tree_struct = [&](uint32_t parent_action_ordinal) { + vector restructured_act_traces; + auto it = act_traces_map.lower_bound( std::make_pair(parent_action_ordinal, 0) ); + for( ; it != act_traces_map.end() && it->first.first == parent_action_ordinal; ++it ) { + auto& act_trace_mvo = it->second; + + auto action_ordinal = act_trace_mvo["action_ordinal"].as().value; + act_trace_mvo["inline_traces"] = convert_act_trace_to_tree_struct(action_ordinal); + if (act_trace_mvo["receipt"].is_null()) { + act_trace_mvo["receipt"] = fc::mutable_variant_object()("abi_sequence", 0) + ("act_digest", digest_type::hash(trx_trace_ptr->action_traces[action_ordinal-1].act)) + ("auth_sequence", flat_map()) + ("code_sequence", 0) + ("global_sequence", 0) + ("receiver", act_trace_mvo["receiver"]) + ("recv_sequence", 0); + } + restructured_act_traces.push_back( std::move(act_trace_mvo) ); + } + return restructured_act_traces; + }; + + fc::mutable_variant_object output_mvo(output); + output_mvo["action_traces"] = convert_act_trace_to_tree_struct(0); + + output = output_mvo; } catch( chain::abi_exception& ) { output = *trx_trace_ptr; } @@ -1910,8 +1948,6 @@ void read_write::push_transaction(const read_write::push_transaction_params& par } CATCH_AND_CALL(next); } }); - - } catch ( boost::interprocess::bad_alloc& ) { chain_plugin::handle_db_exhaustion(); } CATCH_AND_CALL(next); @@ -1946,7 +1982,43 @@ void read_write::push_transactions(const read_write::push_transactions_params& p result->reserve(params.size()); push_recurse(this, 0, params_copy, result, next); + } catch ( boost::interprocess::bad_alloc& ) { + chain_plugin::handle_db_exhaustion(); + } CATCH_AND_CALL(next); +} + +void read_write::send_transaction(const read_write::send_transaction_params& params, next_function next) { + + try { + auto pretty_input = std::make_shared(); + auto resolver = make_resolver(this, abi_serializer_max_time); + transaction_metadata_ptr ptrx; + try { + abi_serializer::from_variant(params, *pretty_input, resolver, abi_serializer_max_time); + ptrx = std::make_shared( pretty_input ); + } EOS_RETHROW_EXCEPTIONS(chain::packed_transaction_type_exception, "Invalid packed transaction") + + app().get_method()(ptrx, true, [this, next](const fc::static_variant& result) -> void{ + if (result.contains()) { + next(result.get()); + } else { + auto trx_trace_ptr = result.get(); + try { + fc::variant output; + try { + output = db.to_variant_with_abi( *trx_trace_ptr, abi_serializer_max_time ); + } catch( chain::abi_exception& ) { + output = *trx_trace_ptr; + } + + const chain::transaction_id_type& id = trx_trace_ptr->id; + next(read_write::send_transaction_results{id, output}); + } CATCH_AND_CALL(next); + } + }); + } catch ( boost::interprocess::bad_alloc& ) { + chain_plugin::handle_db_exhaustion(); } CATCH_AND_CALL(next); } diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index 733bc32825f..6c85c354481 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -600,6 +600,10 @@ class read_write { using push_transactions_results = vector; void push_transactions(const push_transactions_params& params, chain::plugin_interface::next_function next); + using send_transaction_params = push_transaction_params; + using send_transaction_results = push_transaction_results; + void send_transaction(const send_transaction_params& params, chain::plugin_interface::next_function next); + friend resolver_factory; }; diff --git a/plugins/history_plugin/history_plugin.cpp b/plugins/history_plugin/history_plugin.cpp index a44321ca0bc..6cdcffcb546 100644 --- a/plugins/history_plugin/history_plugin.cpp +++ b/plugins/history_plugin/history_plugin.cpp @@ -148,34 +148,34 @@ namespace eosio { if (bypass_filter) { pass_on = true; } - if (filter_on.find({ act.receipt.receiver, 0, 0 }) != filter_on.end()) { + if (filter_on.find({ act.receiver, 0, 0 }) != filter_on.end()) { pass_on = true; } - if (filter_on.find({ act.receipt.receiver, act.act.name, 0 }) != filter_on.end()) { + if (filter_on.find({ act.receiver, act.act.name, 0 }) != filter_on.end()) { pass_on = true; } for (const auto& a : act.act.authorization) { - if (filter_on.find({ act.receipt.receiver, 0, a.actor }) != filter_on.end()) { + if (filter_on.find({ act.receiver, 0, a.actor }) != filter_on.end()) { pass_on = true; } - if (filter_on.find({ act.receipt.receiver, act.act.name, a.actor }) != filter_on.end()) { + if (filter_on.find({ act.receiver, act.act.name, a.actor }) != filter_on.end()) { pass_on = true; } } if (!pass_on) { return false; } - if (filter_out.find({ act.receipt.receiver, 0, 0 }) != filter_out.end()) { + if (filter_out.find({ act.receiver, 0, 0 }) != filter_out.end()) { return false; } - if (filter_out.find({ act.receipt.receiver, act.act.name, 0 }) != filter_out.end()) { + if (filter_out.find({ act.receiver, act.act.name, 0 }) != filter_out.end()) { return false; } for (const auto& a : act.act.authorization) { - if (filter_out.find({ act.receipt.receiver, 0, a.actor }) != filter_out.end()) { + if (filter_out.find({ act.receiver, 0, a.actor }) != filter_out.end()) { return false; } - if (filter_out.find({ act.receipt.receiver, act.act.name, a.actor }) != filter_out.end()) { + if (filter_out.find({ act.receiver, act.act.name, a.actor }) != filter_out.end()) { return false; } } @@ -186,17 +186,17 @@ namespace eosio { set account_set( const action_trace& act ) { set result; - result.insert( act.receipt.receiver ); + result.insert( act.receiver ); for( const auto& a : act.act.authorization ) { if( bypass_filter || - filter_on.find({ act.receipt.receiver, 0, 0}) != filter_on.end() || - filter_on.find({ act.receipt.receiver, 0, a.actor}) != filter_on.end() || - filter_on.find({ act.receipt.receiver, act.act.name, 0}) != filter_on.end() || - filter_on.find({ act.receipt.receiver, act.act.name, a.actor }) != filter_on.end() ) { - if ((filter_out.find({ act.receipt.receiver, 0, 0 }) == filter_out.end()) && - (filter_out.find({ act.receipt.receiver, 0, a.actor }) == filter_out.end()) && - (filter_out.find({ act.receipt.receiver, act.act.name, 0 }) == filter_out.end()) && - (filter_out.find({ act.receipt.receiver, act.act.name, a.actor }) == filter_out.end())) { + filter_on.find({ act.receiver, 0, 0}) != filter_on.end() || + filter_on.find({ act.receiver, 0, a.actor}) != filter_on.end() || + filter_on.find({ act.receiver, act.act.name, 0}) != filter_on.end() || + filter_on.find({ act.receiver, act.act.name, a.actor }) != filter_on.end() ) { + if ((filter_out.find({ act.receiver, 0, 0 }) == filter_out.end()) && + (filter_out.find({ act.receiver, 0, a.actor }) == filter_out.end()) && + (filter_out.find({ act.receiver, act.act.name, 0 }) == filter_out.end()) && + (filter_out.find({ act.receiver, act.act.name, a.actor }) == filter_out.end())) { result.insert( a.actor ); } } @@ -204,7 +204,7 @@ namespace eosio { return result; } - void record_account_action( account_name n, const base_action_trace& act ) { + void record_account_action( account_name n, const action_trace& act ) { auto& chain = chain_plug->chain(); chainbase::database& db = const_cast( chain.db() ); // Override read-only access to state DB (highly unrecommended practice!) @@ -216,13 +216,11 @@ namespace eosio { if( itr->account == n ) asn = itr->account_sequence_num + 1; - //idump((n)(act.receipt.global_sequence)(asn)); const auto& a = db.create( [&]( auto& aho ) { aho.account = n; - aho.action_sequence_num = act.receipt.global_sequence; + aho.action_sequence_num = act.receipt->global_sequence; aho.account_sequence_num = asn; }); - //idump((a.account)(a.action_sequence_num)(a.action_sequence_num)); } void on_system_action( const action_trace& at ) { @@ -263,7 +261,7 @@ namespace eosio { aho.packed_action_trace.resize(ps); datastream ds( aho.packed_action_trace.data(), ps ); fc::raw::pack( ds, at ); - aho.action_sequence_num = at.receipt.global_sequence; + aho.action_sequence_num = at.receipt->global_sequence; aho.block_num = chain.head_block_num() + 1; aho.block_time = chain.pending_block_time(); aho.trx_id = at.trx_id; @@ -274,11 +272,8 @@ namespace eosio { record_account_action( a, at ); } } - if( at.receipt.receiver == chain::config::system_account_name ) + if( at.receiver == chain::config::system_account_name ) on_system_action( at ); - for( const auto& iline : at.inline_traces ) { - on_action_trace( iline ); - } } void on_applied_transaction( const transaction_trace_ptr& trace ) { @@ -286,6 +281,7 @@ namespace eosio { trace->receipt->status != transaction_receipt_header::soft_fail) ) return; for( const auto& atrace : trace->action_traces ) { + if( !atrace.receipt ) continue; on_action_trace( atrace ); } } diff --git a/plugins/mongo_db_plugin/mongo_db_plugin.cpp b/plugins/mongo_db_plugin/mongo_db_plugin.cpp index 8131b6a2bb2..3b3b39f4f84 100644 --- a/plugins/mongo_db_plugin/mongo_db_plugin.cpp +++ b/plugins/mongo_db_plugin/mongo_db_plugin.cpp @@ -751,7 +751,7 @@ void mongo_db_plugin_impl::_process_accepted_transaction( const chain::transacti const signed_transaction& trx = t->packed_trx->get_signed_transaction(); if( !filter_include( trx ) ) return; - + auto trans_doc = bsoncxx::builder::basic::document{}; auto now = std::chrono::duration_cast( @@ -830,22 +830,20 @@ mongo_db_plugin_impl::add_action_trace( mongocxx::bulk_write& bulk_action_traces using namespace bsoncxx::types; using bsoncxx::builder::basic::kvp; - if( executed && atrace.receipt.receiver == chain::config::system_account_name ) { + if( executed && atrace.receiver == chain::config::system_account_name ) { update_account( atrace.act ); } bool added = false; const bool in_filter = (store_action_traces || store_transaction_traces) && start_block_reached && - filter_include( atrace.receipt.receiver, atrace.act.name, atrace.act.authorization ); + filter_include( atrace.receiver, atrace.act.name, atrace.act.authorization ); write_ttrace |= in_filter; if( start_block_reached && store_action_traces && in_filter ) { auto action_traces_doc = bsoncxx::builder::basic::document{}; - const chain::base_action_trace& base = atrace; // without inline action traces - // improve data distributivity when using mongodb sharding action_traces_doc.append( kvp( "_id", make_custom_oid() ) ); - auto v = to_variant_with_abi( base ); + auto v = to_variant_with_abi( atrace ); string json = fc::json::to_string( v ); try { const auto& value = bsoncxx::from_json( json ); @@ -871,10 +869,6 @@ mongo_db_plugin_impl::add_action_trace( mongocxx::bulk_write& bulk_action_traces added = true; } - for( const auto& iline_atrace : atrace.inline_traces ) { - added |= add_action_trace( bulk_action_traces, iline_atrace, t, executed, now, write_ttrace ); - } - return added; } diff --git a/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp b/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp index 37c817dd9cc..3251abef7fb 100644 --- a/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp +++ b/plugins/state_history_plugin/include/eosio/state_history_plugin/state_history_serialization.hpp @@ -480,7 +480,14 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { fc::raw::pack(ds, fc::unsigned_int(0)); - fc::raw::pack(ds, make_history_serial_wrapper(obj.db, as_type(obj.obj.receipt))); + fc::raw::pack(ds, as_type(obj.obj.action_ordinal)); + fc::raw::pack(ds, as_type(obj.obj.creator_action_ordinal)); + fc::raw::pack(ds, as_type(obj.obj.parent_action_ordinal)); + fc::raw::pack(ds, bool(obj.obj.receipt)); + if (obj.obj.receipt) { + fc::raw::pack(ds, make_history_serial_wrapper(obj.db, as_type(*obj.obj.receipt))); + } + fc::raw::pack(ds, as_type(obj.obj.receiver.value)); fc::raw::pack(ds, make_history_serial_wrapper(obj.db, as_type(obj.obj.act))); fc::raw::pack(ds, as_type(obj.obj.context_free)); fc::raw::pack(ds, as_type(obj.obj.elapsed.count())); @@ -492,7 +499,6 @@ datastream& operator<<(datastream& ds, const history_serial_wrapperto_string(); fc::raw::pack(ds, as_type>(e)); - history_serialize_container(ds, obj.db, as_type>(obj.obj.inline_traces)); return ds; } @@ -519,6 +525,11 @@ datastream& operator<<(datastream& fc::raw::pack(ds, as_type(obj.obj.scheduled)); history_serialize_container(ds, obj.db, as_type>(obj.obj.action_traces)); + fc::raw::pack(ds, bool(obj.obj.account_ram_delta)); + if (obj.obj.account_ram_delta) { + fc::raw::pack(ds, make_history_serial_wrapper(obj.db, as_type(*obj.obj.account_ram_delta))); + } + fc::optional e; if (obj.obj.except) e = obj.obj.except->to_string(); diff --git a/plugins/state_history_plugin/state_history_plugin_abi.cpp b/plugins/state_history_plugin/state_history_plugin_abi.cpp index bdedcc81cd9..b35882a7b83 100644 --- a/plugins/state_history_plugin/state_history_plugin_abi.cpp +++ b/plugins/state_history_plugin/state_history_plugin_abi.cpp @@ -93,14 +93,17 @@ extern const char* const state_history_plugin_abi = R"({ }, { "name": "action_trace_v0", "fields": [ - { "name": "receipt", "type": "action_receipt" }, + { "name": "action_ordinal", "type": "varuint32" }, + { "name": "creator_action_ordinal", "type": "varuint32" }, + { "name": "parent_action_ordinal", "type": "varuint32" }, + { "name": "receipt", "type": "action_receipt?" }, + { "name": "receiver", "type": "name" }, { "name": "act", "type": "action" }, { "name": "context_free", "type": "bool" }, { "name": "elapsed", "type": "int64" }, { "name": "console", "type": "string" }, { "name": "account_ram_deltas", "type": "account_delta[]" }, - { "name": "except", "type": "string?" }, - { "name": "inline_traces", "type": "action_trace[]" } + { "name": "except", "type": "string?" } ] }, { @@ -113,6 +116,7 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "net_usage", "type": "uint64" }, { "name": "scheduled", "type": "bool" }, { "name": "action_traces", "type": "action_trace[]" }, + { "name": "account_ram_delta", "type": "account_delta?" }, { "name": "except", "type": "string?" }, { "name": "failed_dtrx_trace", "type": "transaction_trace?" } ] diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index caa24ae5ccf..00c99d17261 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -443,9 +443,11 @@ bytes json_or_file_to_bin( const account_name& account, const action_name& actio void print_action_tree( const fc::variant& action ) { print_action( action ); - const auto& inline_traces = action["inline_traces"].get_array(); - for( const auto& t : inline_traces ) { - print_action_tree( t ); + if( action.get_object().contains( "inline_traces" ) ) { + const auto& inline_traces = action["inline_traces"].get_array(); + for( const auto& t : inline_traces ) { + print_action_tree( t ); + } } } @@ -453,12 +455,13 @@ void print_result( const fc::variant& result ) { try { if (result.is_object() && result.get_object().contains("processed")) { const auto& processed = result["processed"]; const auto& transaction_id = processed["id"].as_string(); - string status = processed["receipt"].is_object() ? processed["receipt"]["status"].as_string() : "failed"; + string status = "failed"; int64_t net = -1; int64_t cpu = -1; if( processed.get_object().contains( "receipt" )) { const auto& receipt = processed["receipt"]; if( receipt.is_object()) { + status = receipt["status"].as_string(); net = receipt["net_usage_words"].as_int64() * 8; cpu = receipt["cpu_usage_us"].as_int64(); } diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 889effc9cb3..c94c9fdb2b3 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -218,7 +218,7 @@ transaction_trace_ptr CallAction(TESTER& test, T ac, const vector& } template -transaction_trace_ptr CallFunction(TESTER& test, T ac, const vector& data, const vector& scope = {N(testapi)}) { +transaction_trace_ptr CallFunction(TESTER& test, T ac, const vector& data, const vector& scope = {N(testapi)}, bool no_throw = false) { { signed_transaction trx; @@ -238,8 +238,10 @@ transaction_trace_ptr CallFunction(TESTER& test, T ac, const vector& data, flat_set keys; trx.get_signature_keys(test.control->get_chain_id(), fc::time_point::maximum(), keys); - auto res = test.push_transaction(trx); - BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); + auto res = test.push_transaction(trx, fc::time_point::maximum(), TESTER::DEFAULT_BILLED_CPU_TIME_US, no_throw); + if (!no_throw) { + BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); + } test.produce_block(); return res; } @@ -248,6 +250,7 @@ transaction_trace_ptr CallFunction(TESTER& test, T ac, const vector& data, #define CALL_TEST_FUNCTION(_TESTER, CLS, MTH, DATA) CallFunction(_TESTER, test_api_action{}, DATA) #define CALL_TEST_FUNCTION_SYSTEM(_TESTER, CLS, MTH, DATA) CallFunction(_TESTER, test_chain_action{}, DATA, {config::system_account_name} ) #define CALL_TEST_FUNCTION_SCOPE(_TESTER, CLS, MTH, DATA, ACCOUNT) CallFunction(_TESTER, test_api_action{}, DATA, ACCOUNT) +#define CALL_TEST_FUNCTION_NO_THROW(_TESTER, CLS, MTH, DATA) CallFunction(_TESTER, test_api_action{}, DATA, {N(testapi)}, true) #define CALL_TEST_FUNCTION_AND_CHECK_EXCEPTION(_TESTER, CLS, MTH, DATA, EXC, EXC_MESSAGE) \ BOOST_CHECK_EXCEPTION( \ CALL_TEST_FUNCTION( _TESTER, CLS, MTH, DATA), \ @@ -330,25 +333,25 @@ BOOST_FIXTURE_TEST_CASE(action_receipt_tests, TESTER) { try { auto result = push_reqauth( config::system_account_name, "active" ); BOOST_REQUIRE_EQUAL( result->receipt->status, transaction_receipt::executed ); - BOOST_REQUIRE( result->action_traces[0].receipt.auth_sequence.find( config::system_account_name ) - != result->action_traces[0].receipt.auth_sequence.end() ); - auto base_global_sequence_num = result->action_traces[0].receipt.global_sequence; - auto base_system_recv_seq_num = result->action_traces[0].receipt.recv_sequence; - auto base_system_auth_seq_num = result->action_traces[0].receipt.auth_sequence[config::system_account_name]; - auto base_system_code_seq_num = result->action_traces[0].receipt.code_sequence.value; - auto base_system_abi_seq_num = result->action_traces[0].receipt.abi_sequence.value; + BOOST_REQUIRE( result->action_traces[0].receipt->auth_sequence.find( config::system_account_name ) + != result->action_traces[0].receipt->auth_sequence.end() ); + auto base_global_sequence_num = result->action_traces[0].receipt->global_sequence; + auto base_system_recv_seq_num = result->action_traces[0].receipt->recv_sequence; + auto base_system_auth_seq_num = result->action_traces[0].receipt->auth_sequence[config::system_account_name]; + auto base_system_code_seq_num = result->action_traces[0].receipt->code_sequence.value; + auto base_system_abi_seq_num = result->action_traces[0].receipt->abi_sequence.value; uint64_t base_test_recv_seq_num = 0; uint64_t base_test_auth_seq_num = 0; call_doit_and_check( N(test), N(test), [&]( const transaction_trace_ptr& res ) { BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.global_sequence, base_global_sequence_num + 1 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.code_sequence.value, 1 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.abi_sequence.value, 0 ); - base_test_recv_seq_num = res->action_traces[0].receipt.recv_sequence; + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 1 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, 1 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, 0 ); + base_test_recv_seq_num = res->action_traces[0].receipt->recv_sequence; BOOST_CHECK( base_test_recv_seq_num > 0 ); base_test_recv_seq_num--; - const auto& m = res->action_traces[0].receipt.auth_sequence; + const auto& m = res->action_traces[0].receipt->auth_sequence; BOOST_CHECK_EQUAL( m.size(), 1 ); BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" ); base_test_auth_seq_num = m.begin()->second; @@ -361,11 +364,11 @@ BOOST_FIXTURE_TEST_CASE(action_receipt_tests, TESTER) { try { call_provereset_and_check( N(test), N(test), [&]( const transaction_trace_ptr& res ) { BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.global_sequence, base_global_sequence_num + 4 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.recv_sequence, base_test_recv_seq_num + 2 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.code_sequence.value, 2 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.abi_sequence.value, 0 ); - const auto& m = res->action_traces[0].receipt.auth_sequence; + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 4 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->recv_sequence, base_test_recv_seq_num + 2 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, 2 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, 0 ); + const auto& m = res->action_traces[0].receipt->auth_sequence; BOOST_CHECK_EQUAL( m.size(), 1 ); BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" ); BOOST_CHECK_EQUAL( m.begin()->second, base_test_auth_seq_num + 3 ); @@ -377,11 +380,11 @@ BOOST_FIXTURE_TEST_CASE(action_receipt_tests, TESTER) { try { call_doit_and_check( config::system_account_name, N(test), [&]( const transaction_trace_ptr& res ) { BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.global_sequence, base_global_sequence_num + 6 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.recv_sequence, base_system_recv_seq_num + 4 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.code_sequence.value, base_system_code_seq_num + 1 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.abi_sequence.value, base_system_abi_seq_num ); - const auto& m = res->action_traces[0].receipt.auth_sequence; + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 6 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->recv_sequence, base_system_recv_seq_num + 4 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, base_system_code_seq_num + 1 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, base_system_abi_seq_num ); + const auto& m = res->action_traces[0].receipt->auth_sequence; BOOST_CHECK_EQUAL( m.size(), 1 ); BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" ); BOOST_CHECK_EQUAL( m.begin()->second, base_test_auth_seq_num + 4 ); @@ -395,11 +398,11 @@ BOOST_FIXTURE_TEST_CASE(action_receipt_tests, TESTER) { try { call_doit_and_check( N(test), N(test), [&]( const transaction_trace_ptr& res ) { BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.global_sequence, base_global_sequence_num + 11 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.recv_sequence, base_test_recv_seq_num + 3 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.code_sequence.value, 4 ); - BOOST_CHECK_EQUAL( res->action_traces[0].receipt.abi_sequence.value, 1 ); - const auto& m = res->action_traces[0].receipt.auth_sequence; + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 11 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->recv_sequence, base_test_recv_seq_num + 3 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, 4 ); + BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, 1 ); + const auto& m = res->action_traces[0].receipt->auth_sequence; BOOST_CHECK_EQUAL( m.size(), 1 ); BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" ); BOOST_CHECK_EQUAL( m.begin()->second, base_test_auth_seq_num + 8 ); @@ -701,12 +704,12 @@ BOOST_FIXTURE_TEST_CASE(cf_action_tests, TESTER) { try { // test send context free action auto ttrace = CALL_TEST_FUNCTION( *this, "test_transaction", "send_cf_action", {} ); - BOOST_CHECK_EQUAL(ttrace->action_traces.size(), 1); - BOOST_CHECK_EQUAL(ttrace->action_traces[0].inline_traces.size(), 1); - BOOST_CHECK_EQUAL(ttrace->action_traces[0].inline_traces[0].receipt.receiver, account_name("dummy")); - BOOST_CHECK_EQUAL(ttrace->action_traces[0].inline_traces[0].act.account, account_name("dummy")); - BOOST_CHECK_EQUAL(ttrace->action_traces[0].inline_traces[0].act.name, account_name("event1")); - BOOST_CHECK_EQUAL(ttrace->action_traces[0].inline_traces[0].act.authorization.size(), 0); + BOOST_REQUIRE_EQUAL(ttrace->action_traces.size(), 2); + BOOST_CHECK_EQUAL((int)(ttrace->action_traces[1].creator_action_ordinal), 1); + BOOST_CHECK_EQUAL(ttrace->action_traces[1].receiver, account_name("dummy")); + BOOST_CHECK_EQUAL(ttrace->action_traces[1].act.account, account_name("dummy")); + BOOST_CHECK_EQUAL(ttrace->action_traces[1].act.name, account_name("event1")); + BOOST_CHECK_EQUAL(ttrace->action_traces[1].act.authorization.size(), 0); BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "send_cf_action_fail", {} ), eosio_assert_message_exception, @@ -2060,4 +2063,472 @@ BOOST_FIXTURE_TEST_CASE(eosio_assert_code_tests, TESTER) { try { BOOST_REQUIRE_EQUAL( validate(), true ); } FC_LOG_AND_RETHROW() } +/************************************************************************************* ++ * action_ordinal_test test cases ++ *************************************************************************************/ +BOOST_FIXTURE_TEST_CASE(action_ordinal_test, TESTER) { try { + + produce_blocks(1); + create_account(N(testapi) ); + set_code( N(testapi), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(bob) ); + set_code( N(bob), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(charlie) ); + set_code( N(charlie), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(david) ); + set_code( N(david), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(erin) ); + set_code( N(erin), contracts::test_api_wasm() ); + produce_blocks(1); + + transaction_trace_ptr txn_trace = CALL_TEST_FUNCTION_SCOPE( *this, "test_action", "test_action_ordinal1", + {}, vector{ N(testapi)}); + + BOOST_REQUIRE_EQUAL( validate(), true ); + + BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true); + BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 11); + + auto &atrace = txn_trace->action_traces; + BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0); + BOOST_REQUIRE_EQUAL((int)atrace[0].parent_action_ordinal, 0); + BOOST_REQUIRE_EQUAL(atrace[0].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), true); + int start_gseq = atrace[0].receipt->global_sequence; + + BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2); + BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[1].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[1].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[1].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[1].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); + + BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[2].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[2].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[2].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4); + + BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4); + BOOST_REQUIRE_EQUAL((int)atrace[3].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[3].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[3].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[3].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[3].act.name.value, TEST_METHOD("test_action", "test_action_ordinal3")); + BOOST_REQUIRE_EQUAL(atrace[3].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[3].receipt->global_sequence, start_gseq + 8); + + BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5); + BOOST_REQUIRE_EQUAL((int)atrace[4].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[4].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[4].receiver.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[4].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[4].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[4].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2); + + BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6); + BOOST_REQUIRE_EQUAL((int)atrace[5].creator_action_ordinal, 2); + BOOST_REQUIRE_EQUAL((int)atrace[5].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[5].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[5].act.account.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[5].act.name.value, TEST_METHOD("test_action", "test_action_ordinal_foo")); + BOOST_REQUIRE_EQUAL(atrace[5].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[5].receipt->global_sequence, start_gseq + 9); + + BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7); + BOOST_REQUIRE_EQUAL((int)atrace[6].creator_action_ordinal,2); + BOOST_REQUIRE_EQUAL((int)atrace[6].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[6].receiver.value, N(david)); + BOOST_REQUIRE_EQUAL(atrace[6].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[6].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[6].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3); + + BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8); + BOOST_REQUIRE_EQUAL((int)atrace[7].creator_action_ordinal, 5); + BOOST_REQUIRE_EQUAL((int)atrace[7].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[7].receiver.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[7].act.account.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[7].act.name.value, TEST_METHOD("test_action", "test_action_ordinal_bar")); + BOOST_REQUIRE_EQUAL(atrace[7].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[7].receipt->global_sequence, start_gseq + 10); + + BOOST_REQUIRE_EQUAL((int)atrace[8].action_ordinal, 9); + BOOST_REQUIRE_EQUAL((int)atrace[8].creator_action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[8].parent_action_ordinal, 3); + BOOST_REQUIRE_EQUAL(atrace[8].receiver.value, N(david)); + BOOST_REQUIRE_EQUAL(atrace[8].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[8].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[8].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5); + + BOOST_REQUIRE_EQUAL((int)atrace[9].action_ordinal, 10); + BOOST_REQUIRE_EQUAL((int)atrace[9].creator_action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[9].parent_action_ordinal, 3); + BOOST_REQUIRE_EQUAL(atrace[9].receiver.value, N(erin)); + BOOST_REQUIRE_EQUAL(atrace[9].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[9].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[9].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6); + + BOOST_REQUIRE_EQUAL((int)atrace[10].action_ordinal, 11); + BOOST_REQUIRE_EQUAL((int)atrace[10].creator_action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[10].parent_action_ordinal, 3); + BOOST_REQUIRE_EQUAL(atrace[10].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[10].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[10].act.name.value, TEST_METHOD("test_action", "test_action_ordinal4")); + BOOST_REQUIRE_EQUAL(atrace[10].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7); +} FC_LOG_AND_RETHROW() } + + +/************************************************************************************* ++ * action_ordinal_failtest1 test cases ++ *************************************************************************************/ +BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest1, TESTER) { try { + + produce_blocks(1); + create_account(N(testapi) ); + set_code( N(testapi), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(bob) ); + set_code( N(bob), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(charlie) ); + set_code( N(charlie), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(david) ); + set_code( N(david), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(erin) ); + set_code( N(erin), contracts::test_api_wasm() ); + produce_blocks(1); + + create_account(N(fail1) ); // <- make first action fails in the middle + produce_blocks(1); + + transaction_trace_ptr txn_trace = + CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_action_ordinal1", {}); + + BOOST_REQUIRE_EQUAL( validate(), true ); + + BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true); + BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 3); + + auto &atrace = txn_trace->action_traces; + + // fails here after creating one notify action and one inline action + BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0); + BOOST_REQUIRE_EQUAL((int)atrace[0].parent_action_ordinal, 0); + BOOST_REQUIRE_EQUAL(atrace[0].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[0].except->code(), 3050003); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal, 2); + BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[1].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[1].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[1].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[1].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[1].except.valid(), false); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[2].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[2].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[2].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[2].except.valid(), false); + +} FC_LOG_AND_RETHROW() } + +/************************************************************************************* ++ * action_ordinal_failtest2 test cases ++ *************************************************************************************/ +BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest2, TESTER) { try { + + produce_blocks(1); + create_account(N(testapi) ); + set_code( N(testapi), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(bob) ); + set_code( N(bob), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(charlie) ); + set_code( N(charlie), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(david) ); + set_code( N(david), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(erin) ); + set_code( N(erin), contracts::test_api_wasm() ); + produce_blocks(1); + + create_account(N(fail3) ); // <- make action 3 fails in the middle + produce_blocks(1); + + transaction_trace_ptr txn_trace = + CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_action_ordinal1", {}); + + BOOST_REQUIRE_EQUAL( validate(), true ); + + BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true); + BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 8); + + auto &atrace = txn_trace->action_traces; + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0); + BOOST_REQUIRE_EQUAL((int)atrace[0].parent_action_ordinal, 0); + BOOST_REQUIRE_EQUAL(atrace[0].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), false); + int start_gseq = atrace[0].receipt->global_sequence; + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2); + BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[1].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[1].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[1].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[1].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[2].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[2].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[2].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[2].except.valid(), false); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4); + BOOST_REQUIRE_EQUAL((int)atrace[3].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[3].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[3].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[3].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[3].act.name.value, TEST_METHOD("test_action", "test_action_ordinal3")); + BOOST_REQUIRE_EQUAL(atrace[3].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[3].except.valid(), false); + + // hey exception is here + BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5); + BOOST_REQUIRE_EQUAL((int)atrace[4].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[4].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[4].receiver.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[4].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[4].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[4].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[4].except.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[4].except->code(), 3050003); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6); + BOOST_REQUIRE_EQUAL((int)atrace[5].creator_action_ordinal, 2); + BOOST_REQUIRE_EQUAL((int)atrace[5].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[5].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[5].act.account.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[5].act.name.value, TEST_METHOD("test_action", "test_action_ordinal_foo")); + BOOST_REQUIRE_EQUAL(atrace[5].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[5].except.valid(), false); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7); + BOOST_REQUIRE_EQUAL((int)atrace[6].creator_action_ordinal,2); + BOOST_REQUIRE_EQUAL((int)atrace[6].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[6].receiver.value, N(david)); + BOOST_REQUIRE_EQUAL(atrace[6].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[6].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[6].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[6].except.valid(), false); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8); + BOOST_REQUIRE_EQUAL((int)atrace[7].creator_action_ordinal, 5); + BOOST_REQUIRE_EQUAL((int)atrace[7].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[7].receiver.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[7].act.account.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[7].act.name.value, TEST_METHOD("test_action", "test_action_ordinal_bar")); + BOOST_REQUIRE_EQUAL(atrace[7].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[7].except.valid(), false); + +} FC_LOG_AND_RETHROW() } + +/************************************************************************************* ++ * action_ordinal_failtest3 test cases ++ *************************************************************************************/ +BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, TESTER) { try { + + produce_blocks(1); + create_account(N(testapi) ); + set_code( N(testapi), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(bob) ); + set_code( N(bob), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(charlie) ); + set_code( N(charlie), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(david) ); + set_code( N(david), contracts::test_api_wasm() ); + produce_blocks(1); + create_account(N(erin) ); + set_code( N(erin), contracts::test_api_wasm() ); + produce_blocks(1); + + create_account(N(failnine) ); // <- make action 9 fails in the middle + produce_blocks(1); + + transaction_trace_ptr txn_trace = + CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_action_ordinal1", {}); + + BOOST_REQUIRE_EQUAL( validate(), true ); + + BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true); + BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 11); + + auto &atrace = txn_trace->action_traces; + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0); + BOOST_REQUIRE_EQUAL((int)atrace[0].parent_action_ordinal, 0); + BOOST_REQUIRE_EQUAL(atrace[0].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[0].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[0].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[0].except.valid(), false); + int start_gseq = atrace[0].receipt->global_sequence; + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2); + BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[1].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[1].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[1].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[1].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[1].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1); + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[2].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[2].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[2].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[2].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4); + + // fails here + BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4); + BOOST_REQUIRE_EQUAL((int)atrace[3].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[3].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[3].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[3].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[3].act.name.value, TEST_METHOD("test_action", "test_action_ordinal3")); + BOOST_REQUIRE_EQUAL(atrace[3].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[3].except.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[3].except->code(), 3050003); + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5); + BOOST_REQUIRE_EQUAL((int)atrace[4].creator_action_ordinal, 1); + BOOST_REQUIRE_EQUAL((int)atrace[4].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[4].receiver.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[4].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[4].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[4].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6); + BOOST_REQUIRE_EQUAL((int)atrace[5].creator_action_ordinal, 2); + BOOST_REQUIRE_EQUAL((int)atrace[5].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[5].receiver.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[5].act.account.value, N(bob)); + BOOST_REQUIRE_EQUAL(atrace[5].act.name.value, TEST_METHOD("test_action", "test_action_ordinal_foo")); + BOOST_REQUIRE_EQUAL(atrace[5].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[5].except.valid(), false); + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7); + BOOST_REQUIRE_EQUAL((int)atrace[6].creator_action_ordinal,2); + BOOST_REQUIRE_EQUAL((int)atrace[6].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[6].receiver.value, N(david)); + BOOST_REQUIRE_EQUAL(atrace[6].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[6].act.name.value, TEST_METHOD("test_action", "test_action_ordinal1")); + BOOST_REQUIRE_EQUAL(atrace[6].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3); + + // not executed + BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8); + BOOST_REQUIRE_EQUAL((int)atrace[7].creator_action_ordinal, 5); + BOOST_REQUIRE_EQUAL((int)atrace[7].parent_action_ordinal, 1); + BOOST_REQUIRE_EQUAL(atrace[7].receiver.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[7].act.account.value, N(charlie)); + BOOST_REQUIRE_EQUAL(atrace[7].act.name.value, TEST_METHOD("test_action", "test_action_ordinal_bar")); + BOOST_REQUIRE_EQUAL(atrace[7].receipt.valid(), false); + BOOST_REQUIRE_EQUAL(atrace[7].except.valid(), false); + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[8].action_ordinal, 9); + BOOST_REQUIRE_EQUAL((int)atrace[8].creator_action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[8].parent_action_ordinal, 3); + BOOST_REQUIRE_EQUAL(atrace[8].receiver.value, N(david)); + BOOST_REQUIRE_EQUAL(atrace[8].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[8].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[8].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5); + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[9].action_ordinal, 10); + BOOST_REQUIRE_EQUAL((int)atrace[9].creator_action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[9].parent_action_ordinal, 3); + BOOST_REQUIRE_EQUAL(atrace[9].receiver.value, N(erin)); + BOOST_REQUIRE_EQUAL(atrace[9].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[9].act.name.value, TEST_METHOD("test_action", "test_action_ordinal2")); + BOOST_REQUIRE_EQUAL(atrace[9].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6); + + // executed + BOOST_REQUIRE_EQUAL((int)atrace[10].action_ordinal, 11); + BOOST_REQUIRE_EQUAL((int)atrace[10].creator_action_ordinal, 3); + BOOST_REQUIRE_EQUAL((int)atrace[10].parent_action_ordinal, 3); + BOOST_REQUIRE_EQUAL(atrace[10].receiver.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[10].act.account.value, N(testapi)); + BOOST_REQUIRE_EQUAL(atrace[10].act.name.value, TEST_METHOD("test_action", "test_action_ordinal4")); + BOOST_REQUIRE_EQUAL(atrace[10].receipt.valid(), true); + BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/test-contracts/test_api/test_action.cpp b/unittests/test-contracts/test_api/test_action.cpp index bf1985ae3ef..e371f336cc0 100644 --- a/unittests/test-contracts/test_api/test_action.cpp +++ b/unittests/test-contracts/test_api/test_action.cpp @@ -259,3 +259,86 @@ void test_action::test_ram_billing_in_notify( uint64_t receiver, uint64_t code, db_store_i64( "notifytest"_n.value, "notifytest"_n.value, payer, "notifytest"_n.value, &to_notify, sizeof(to_notify) ); } } + +void test_action::test_action_ordinal1(uint64_t receiver, uint64_t code, uint64_t action) { + uint64_t _self = receiver; + if (receiver == "testapi"_n.value) { + print("exec 1"); + eosio::require_recipient( "bob"_n ); //-> exec 2 which would then cause execution of 4, 10 + + eosio::action act1({name(_self), "active"_n}, name(_self), + name(WASM_TEST_ACTION("test_action", "test_action_ordinal2")), + std::tuple<>()); + act1.send(); // -> exec 5 which would then cause execution of 6, 7, 8 + + if (is_account("fail1"_n)) { + eosio_assert(false, "fail at point 1"); + } + + eosio::action act2({name(_self), "active"_n}, name(_self), + name(WASM_TEST_ACTION("test_action", "test_action_ordinal3")), + std::tuple<>()); + act2.send(); // -> exec 9 + + eosio::require_recipient( "charlie"_n ); // -> exec 3 which would then cause execution of 11 + + } else if (receiver == "bob"_n.value) { + print("exec 2"); + eosio::action act1({name(_self), "active"_n}, name(_self), + name(WASM_TEST_ACTION("test_action", "test_action_ordinal_foo")), + std::tuple<>()); + act1.send(); // -> exec 10 + + eosio::require_recipient( "david"_n ); // -> exec 4 + } else if (receiver == "charlie"_n.value) { + print("exec 3"); + eosio::action act1({name(_self), "active"_n}, name(_self), + name(WASM_TEST_ACTION("test_action", "test_action_ordinal_bar")), + std::tuple<>()); // exec 11 + act1.send(); + + if (is_account("fail3"_n)) { + eosio_assert(false, "fail at point 3"); + } + + } else if (receiver == "david"_n.value) { + print("exec 4"); + } else { + eosio_assert(false, "assert failed at test_action::test_action_ordinal1"); + } +} +void test_action::test_action_ordinal2(uint64_t receiver, uint64_t code, uint64_t action) { + uint64_t _self = receiver; + if (receiver == "testapi"_n.value) { + print("exec 5"); + eosio::require_recipient( "david"_n ); // -> exec 6 + eosio::require_recipient( "erin"_n ); // -> exec 7 + + eosio::action act1({name(_self), "active"_n}, name(_self), + name(WASM_TEST_ACTION("test_action", "test_action_ordinal4")), + std::tuple<>()); + act1.send(); // -> exec 8 + } else if (receiver == "david"_n.value) { + print("exec 6"); + } else if (receiver == "erin"_n.value) { + print("exec 7"); + } else { + eosio_assert(false, "assert failed at test_action::test_action_ordinal2"); + } +} +void test_action::test_action_ordinal4(uint64_t receiver, uint64_t code, uint64_t action) { + print("exec 8"); +} +void test_action::test_action_ordinal3(uint64_t receiver, uint64_t code, uint64_t action) { + print("exec 9"); + + if (is_account("failnine"_n)) { + eosio_assert(false, "fail at point 9"); + } +} +void test_action::test_action_ordinal_foo(uint64_t receiver, uint64_t code, uint64_t action) { + print("exec 10"); +} +void test_action::test_action_ordinal_bar(uint64_t receiver, uint64_t code, uint64_t action) { + print("exec 11"); +} diff --git a/unittests/test-contracts/test_api/test_api.cpp b/unittests/test-contracts/test_api/test_api.cpp index 598990dc1a7..241d4762a00 100644 --- a/unittests/test-contracts/test_api/test_api.cpp +++ b/unittests/test-contracts/test_api/test_api.cpp @@ -64,6 +64,12 @@ extern "C" { WASM_TEST_HANDLER ( test_action, test_publication_time ); WASM_TEST_HANDLER ( test_action, test_assert_code ); WASM_TEST_HANDLER_EX( test_action, test_ram_billing_in_notify ); + WASM_TEST_HANDLER_EX( test_action, test_action_ordinal1 ); + WASM_TEST_HANDLER_EX( test_action, test_action_ordinal2 ); + WASM_TEST_HANDLER_EX( test_action, test_action_ordinal3 ); + WASM_TEST_HANDLER_EX( test_action, test_action_ordinal4 ); + WASM_TEST_HANDLER_EX( test_action, test_action_ordinal_foo ); + WASM_TEST_HANDLER_EX( test_action, test_action_ordinal_bar ); // test named actions // We enforce action name matches action data type name, so name mangling will not work for these tests. diff --git a/unittests/test-contracts/test_api/test_api.hpp b/unittests/test-contracts/test_api/test_api.hpp index 865923fcfb2..bbcf9965352 100644 --- a/unittests/test-contracts/test_api/test_api.hpp +++ b/unittests/test-contracts/test_api/test_api.hpp @@ -69,6 +69,12 @@ struct test_action { static void test_publication_time(); static void test_assert_code(); static void test_ram_billing_in_notify(uint64_t receiver, uint64_t code, uint64_t action); + static void test_action_ordinal1(uint64_t receiver, uint64_t code, uint64_t action); + static void test_action_ordinal2(uint64_t receiver, uint64_t code, uint64_t action); + static void test_action_ordinal3(uint64_t receiver, uint64_t code, uint64_t action); + static void test_action_ordinal4(uint64_t receiver, uint64_t code, uint64_t action); + static void test_action_ordinal_foo(uint64_t receiver, uint64_t code, uint64_t action); + static void test_action_ordinal_bar(uint64_t receiver, uint64_t code, uint64_t action); }; struct test_db { diff --git a/unittests/test-contracts/test_api/test_api.wasm b/unittests/test-contracts/test_api/test_api.wasm index c7a7601ace1..dc9a7125d82 100755 Binary files a/unittests/test-contracts/test_api/test_api.wasm and b/unittests/test-contracts/test_api/test_api.wasm differ diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index 5335ee037c4..c29ec8da8c4 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -92,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE( basic_test, TESTER ) try { auto result = push_transaction( trx ); BOOST_CHECK_EQUAL(result->receipt->status, transaction_receipt::executed); BOOST_CHECK_EQUAL(result->action_traces.size(), 1u); - BOOST_CHECK_EQUAL(result->action_traces.at(0).receipt.receiver.to_string(), name(N(asserter)).to_string() ); + BOOST_CHECK_EQUAL(result->action_traces.at(0).receiver.to_string(), name(N(asserter)).to_string() ); BOOST_CHECK_EQUAL(result->action_traces.at(0).act.account.to_string(), name(N(asserter)).to_string() ); BOOST_CHECK_EQUAL(result->action_traces.at(0).act.name.to_string(), name(N(procassert)).to_string() ); BOOST_CHECK_EQUAL(result->action_traces.at(0).act.authorization.size(), 1u );