diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 741a5a5c550..15ff989cc41 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -359,6 +359,7 @@ void apply_context::execute_inline( action&& a ) { try { control.get_authorization_manager() .check_authorization( {a}, + {}, {}, {{receiver, config::eosio_code_name}}, control.pending_block_time() - trx_context.published, @@ -525,6 +526,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a try { control.get_authorization_manager() .check_authorization( trx.actions, + {}, {}, {{receiver, config::eosio_code_name}}, delay, diff --git a/libraries/chain/authorization_manager.cpp b/libraries/chain/authorization_manager.cpp index 1427b3c3bff..b09910345b7 100644 --- a/libraries/chain/authorization_manager.cpp +++ b/libraries/chain/authorization_manager.cpp @@ -483,6 +483,7 @@ namespace eosio { namespace chain { void authorization_manager::check_authorization( const vector& actions, const flat_set& provided_keys, + std::optional payer, const flat_set& provided_permissions, fc::microseconds provided_delay, const std::function& _checktime, @@ -552,6 +553,9 @@ namespace eosio { namespace chain { } } } + if (payer) { + permissions_to_satisfy.emplace(permission_level{payer->payer, config::active_name},effective_provided_delay); + } // Now verify that all the declared authorizations are satisfied: diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c5d55772145..1b285dadc66 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1179,6 +1179,7 @@ struct controller_impl { { EOS_ASSERT(deadline != fc::time_point(), transaction_exception, "deadline cannot be uninitialized"); + transaction_trace_ptr trace; try { auto start = fc::time_point::now(); @@ -1216,6 +1217,7 @@ struct controller_impl { try { const transaction& trn = trx->packed_trx()->get_transaction(); + if( trx->implicit ) { EOS_ASSERT( !explicit_net_usage_words, transaction_exception, "NET usage cannot be explicitly set for implicit transactions" ); trx_context.init_for_implicit_trx(); @@ -1234,9 +1236,12 @@ struct controller_impl { trx_context.delay = fc::seconds(trn.delay_sec); if( check_auth ) { + auto payer = trn.resource_payer_info(self.is_builtin_activated(builtin_protocol_feature_t::resource_payer)); + authorization.check_authorization( trn.actions, trx->recovered_keys(), + payer, {}, trx_context.delay, [&trx_context](){ trx_context.checktime(); }, diff --git a/libraries/chain/include/eosio/chain/authorization_manager.hpp b/libraries/chain/include/eosio/chain/authorization_manager.hpp index a61770c3fdb..32c2f997770 100644 --- a/libraries/chain/include/eosio/chain/authorization_manager.hpp +++ b/libraries/chain/include/eosio/chain/authorization_manager.hpp @@ -79,6 +79,7 @@ namespace eosio { namespace chain { void check_authorization( const vector& actions, const flat_set& provided_keys, + std::optional payer, const flat_set& provided_permissions = flat_set(), fc::microseconds provided_delay = fc::microseconds(0), const std::function& checktime = std::function(), diff --git a/libraries/chain/webassembly/permission.cpp b/libraries/chain/webassembly/permission.cpp index c5b521b8513..c16edec43d4 100644 --- a/libraries/chain/webassembly/permission.cpp +++ b/libraries/chain/webassembly/permission.cpp @@ -34,6 +34,7 @@ namespace eosio { namespace chain { namespace webassembly { .get_authorization_manager() .check_authorization( trx.actions, provided_keys, + {}, provided_permissions, fc::seconds(trx.delay_sec), std::bind(&transaction_context::checktime, &context.trx_context), diff --git a/unittests/resource_limits_test.cpp b/unittests/resource_limits_test.cpp index 5346dccdde8..d79f15b8dd9 100644 --- a/unittests/resource_limits_test.cpp +++ b/unittests/resource_limits_test.cpp @@ -559,6 +559,7 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test) namespace { name respyr_acct = "respyr"_n; + name payee_acct = "payee"_n; template Transaction populate() { @@ -584,6 +585,31 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test) return txn; } + + template + Transaction populate_payer_payee() { + Transaction txn; + txn.ref_block_num = 1; + txn.ref_block_prefix = 2; + txn.expiration.from_iso_string("2021-12-20T15:30"); + txn.actions.emplace_back( vector{{payee_acct, config::active_name}}, payee_acct, "doit"_n, bytes{} ); + txn.max_net_usage_words = 32; + txn.max_cpu_usage_ms = 10; + + // Resource Payer Transaction Extension (with net and cpu different from those in transaction) + resource_payer resource_payer_trx_extension; + resource_payer_trx_extension.payer = respyr_acct; + resource_payer_trx_extension.max_net_bytes = 1024; + resource_payer_trx_extension.max_cpu_us = 30000; + resource_payer_trx_extension.max_memory_bytes = 0; + emplace_extension( + txn.transaction_extensions, + resource_payer::extension_id(), + fc::raw::pack( resource_payer_trx_extension ) + ); + + return txn; + } } BOOST_FIXTURE_TEST_CASE(resource_payer_cpu_validation, resource_limits_fixture) try { @@ -694,5 +720,67 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test) } } FC_LOG_AND_RETHROW() + BOOST_FIXTURE_TEST_CASE(resource_payer_not_signed, resource_limits_fixture) try { + tester t( setup_policy::preactivate_feature_and_new_bios ); + + auto txn = populate_payer_payee(); + + const auto& pfm = t.control->get_protocol_feature_manager(); + const auto& d = pfm.get_builtin_digest(builtin_protocol_feature_t::resource_payer); + BOOST_REQUIRE(d); + + t.preactivate_protocol_features( {*d} ); + t.produce_block(); + + // now, get resource payer info having activated resource_payer protcol feature + auto res_pyr = txn.resource_payer_info( t.control->is_builtin_activated(builtin_protocol_feature_t::resource_payer) ); + BOOST_CHECK_EQUAL(res_pyr != std::nullopt, true); + + t.produce_blocks(2); + t.create_account( res_pyr->payer ); + t.create_account( payee_acct); + + t.set_code( res_pyr->payer, contracts::payloadless_wasm() ); + t.produce_blocks(1); + + t.set_transaction_headers( txn, t.DEFAULT_EXPIRATION_DELTA ); + txn.sign( t.get_private_key(payee_acct, "active"), t.control->get_chain_id() ); + + BOOST_REQUIRE_THROW(t.push_transaction(txn), unsatisfied_authorization); + + } FC_LOG_AND_RETHROW() + + BOOST_FIXTURE_TEST_CASE(resource_payer_fully_signed, resource_limits_fixture) try { + tester t( setup_policy::preactivate_feature_and_new_bios ); + auto txn = populate_payer_payee(); + + const auto& pfm = t.control->get_protocol_feature_manager(); + const auto& d = pfm.get_builtin_digest(builtin_protocol_feature_t::resource_payer); + BOOST_REQUIRE(d); + + t.preactivate_protocol_features( {*d} ); + t.produce_block(); + + // now, get resource payer info having activated resource_payer protcol feature + auto res_pyr = txn.resource_payer_info( t.control->is_builtin_activated(builtin_protocol_feature_t::resource_payer) ); + BOOST_CHECK_EQUAL(res_pyr != std::nullopt, true); + + t.produce_blocks(2); + t.create_account( res_pyr->payer ); + t.create_account( payee_acct); + + t.set_code( res_pyr->payer, contracts::payloadless_wasm() ); + t.produce_blocks(1); + + t.set_transaction_headers( txn, t.DEFAULT_EXPIRATION_DELTA ); + txn.sign( t.get_private_key(res_pyr->payer, "active"), t.control->get_chain_id() ); + txn.sign( t.get_private_key(payee_acct, "active"), t.control->get_chain_id() ); + + auto res = t.push_transaction(txn); + BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); + BOOST_REQUIRE_EQUAL(res->bill_to_accounts.size(), 1); + BOOST_REQUIRE_EQUAL(*res->bill_to_accounts.begin(), respyr_acct); + + } FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END()