diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 4a9611458c..2cdd7b5a91 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -72,51 +72,53 @@ void apply_context::exec_one() try { action_return_value.clear(); receiver_account = &db.get( receiver ); - privileged = receiver_account->is_privileged(); - 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 ); + if( !(context_free && control.skip_trx_checks()) ) { + privileged = receiver_account->is_privileged(); + 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 ); + } + (*native)( *this ); } - (*native)( *this ); - } - if( ( receiver_account->code_hash != digest_type() ) && - ( !( act->account == config::system_account_name - && act->name == "setcode"_n - && receiver == config::system_account_name ) - || control.is_builtin_activated( builtin_protocol_feature_t::forward_setcode ) - ) - ) { - if( trx_context.enforce_whiteblacklist && control.is_producing_block() ) { - control.check_contract_list( receiver ); - control.check_action_list( act->account, act->name ); + if( ( receiver_account->code_hash != digest_type() ) && + ( !( act->account == config::system_account_name + && act->name == "setcode"_n + && receiver == config::system_account_name ) + || control.is_builtin_activated( builtin_protocol_feature_t::forward_setcode ) + ) + ) { + if( trx_context.enforce_whiteblacklist && control.is_producing_block() ) { + control.check_contract_list( receiver ); + control.check_action_list( act->account, act->name ); + } + try { + control.get_wasm_interface().apply( receiver_account->code_hash, receiver_account->vm_type, receiver_account->vm_version, *this ); + } catch( const wasm_exit& ) {} } - try { - control.get_wasm_interface().apply( receiver_account->code_hash, receiver_account->vm_type, receiver_account->vm_version, *this ); - } catch( const wasm_exit& ) {} - } - if( !privileged && control.is_builtin_activated( builtin_protocol_feature_t::ram_restrictions ) ) { - const size_t checktime_interval = 10; - size_t counter = 0; - bool not_in_notify_context = (receiver == act->account); - const auto end = _account_ram_deltas.end(); - for( auto itr = _account_ram_deltas.begin(); itr != end; ++itr, ++counter ) { - if( counter == checktime_interval ) { - trx_context.checktime(); - counter = 0; - } - if( itr->delta > 0 && itr->account != receiver ) { - EOS_ASSERT( not_in_notify_context, unauthorized_ram_usage_increase, - "unprivileged contract cannot increase RAM usage of another account within a notify context: ${account}", - ("account", itr->account) - ); - EOS_ASSERT( has_authorization( itr->account ), unauthorized_ram_usage_increase, - "unprivileged contract cannot increase RAM usage of another account that has not authorized the action: ${account}", - ("account", itr->account) - ); + if( !privileged && control.is_builtin_activated( builtin_protocol_feature_t::ram_restrictions ) ) { + const size_t checktime_interval = 10; + size_t counter = 0; + bool not_in_notify_context = (receiver == act->account); + const auto end = _account_ram_deltas.end(); + for( auto itr = _account_ram_deltas.begin(); itr != end; ++itr, ++counter ) { + if( counter == checktime_interval ) { + trx_context.checktime(); + counter = 0; + } + if( itr->delta > 0 && itr->account != receiver ) { + EOS_ASSERT( not_in_notify_context, unauthorized_ram_usage_increase, + "unprivileged contract cannot increase RAM usage of another account within a notify context: ${account}", + ("account", itr->account) + ); + EOS_ASSERT( has_authorization( itr->account ), unauthorized_ram_usage_increase, + "unprivileged contract cannot increase RAM usage of another account that has not authorized the action: ${account}", + ("account", itr->account) + ); + } } } } diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 8e357777cf..7d28412e5c 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -723,12 +723,14 @@ namespace eosio { namespace chain { const auto& db = control.db(); const auto& auth_manager = control.get_authorization_manager(); - for( const auto& a : trx.context_free_actions ) { - auto* code = db.find(a.account); - EOS_ASSERT( code != nullptr, transaction_exception, - "action's code account '${account}' does not exist", ("account", a.account) ); - EOS_ASSERT( a.authorization.size() == 0, transaction_exception, - "context-free actions cannot have authorizations" ); + if( !trx.context_free_actions.empty() && !control.skip_trx_checks() ) { + for( const auto& a : trx.context_free_actions ) { + auto* code = db.find( a.account ); + EOS_ASSERT( code != nullptr, transaction_exception, + "action's code account '${account}' does not exist", ("account", a.account) ); + EOS_ASSERT( a.authorization.size() == 0, transaction_exception, + "context-free actions cannot have authorizations" ); + } } flat_set actors; diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 45ab8896e7..ffe9a87211 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -850,6 +850,88 @@ BOOST_FIXTURE_TEST_CASE(deferred_cfa_success, TESTER) try { BOOST_REQUIRE_EQUAL( validate(), true ); } FC_LOG_AND_RETHROW() +BOOST_AUTO_TEST_CASE(light_validation_skip_cfa) try { + tester chain(setup_policy::full); + + std::vector blocks; + blocks.push_back(chain.produce_block()); + + chain.create_account( "testapi"_n ); + chain.create_account( "dummy"_n ); + blocks.push_back(chain.produce_block()); + chain.set_code( "testapi"_n, contracts::test_api_wasm() ); + blocks.push_back(chain.produce_block()); + + cf_action cfa; + signed_transaction trx; + action act({}, cfa); + trx.context_free_actions.push_back(act); + trx.context_free_data.emplace_back(fc::raw::pack(100)); // verify payload matches context free data + trx.context_free_data.emplace_back(fc::raw::pack(200)); + // add a normal action along with cfa + dummy_action da = { DUMMY_ACTION_DEFAULT_A, DUMMY_ACTION_DEFAULT_B, DUMMY_ACTION_DEFAULT_C }; + action act1(vector{{"testapi"_n, config::active_name}}, da); + trx.actions.push_back(act1); + chain.set_transaction_headers(trx); + // run normal passing case + auto sigs = trx.sign(chain.get_private_key("testapi"_n, "active"), chain.control->get_chain_id()); + auto trace = chain.push_transaction(trx); + blocks.push_back(chain.produce_block()); + + BOOST_REQUIRE(trace->receipt); + BOOST_CHECK_EQUAL(trace->receipt->status, transaction_receipt::executed); + BOOST_CHECK_EQUAL(2, trace->action_traces.size()); + + BOOST_CHECK(trace->action_traces.at(0).context_free); // cfa + BOOST_CHECK_EQUAL("test\n", trace->action_traces.at(0).console); // cfa executed + + BOOST_CHECK(!trace->action_traces.at(1).context_free); // non-cfa + BOOST_CHECK_EQUAL("", trace->action_traces.at(1).console); + + + fc::temp_directory tempdir; + auto conf_genesis = tester::default_config( tempdir ); + + auto& cfg = conf_genesis.first; + cfg.trusted_producers = { "eosio"_n }; // light validation + + tester other( conf_genesis.first, conf_genesis.second ); + other.execute_setup_policy( setup_policy::full ); + + transaction_trace_ptr other_trace; + auto cc = other.control->applied_transaction.connect( [&](std::tuple x) { + auto& t = std::get<0>(x); + if( t && t->id == trace->id ) { + other_trace = t; + } + } ); + + for (auto& new_block : blocks) { + other.push_block(new_block); + } + blocks.clear(); + + BOOST_REQUIRE(other_trace); + BOOST_REQUIRE(other_trace->receipt); + BOOST_CHECK_EQUAL(other_trace->receipt->status, transaction_receipt::executed); + BOOST_CHECK(*trace->receipt == *other_trace->receipt); + BOOST_CHECK_EQUAL(2, other_trace->action_traces.size()); + + BOOST_CHECK(other_trace->action_traces.at(0).context_free); // cfa + BOOST_CHECK_EQUAL("", other_trace->action_traces.at(0).console); // cfa not executed for light validation (trusted producer) + BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->global_sequence, other_trace->action_traces.at(0).receipt->global_sequence); + BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->digest(), other_trace->action_traces.at(0).receipt->digest()); + + BOOST_CHECK(!other_trace->action_traces.at(1).context_free); // non-cfa + BOOST_CHECK_EQUAL("", other_trace->action_traces.at(1).console); + BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->global_sequence, other_trace->action_traces.at(1).receipt->global_sequence); + BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->digest(), other_trace->action_traces.at(1).receipt->digest()); + + + other.close(); + +} FC_LOG_AND_RETHROW() + /************************************************************************************* * checktime_tests test case *************************************************************************************/