Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #7089 from EOSIO/6332-only-bill-first-authorizer
Browse files Browse the repository at this point in the history
Implement ONLY_BILL_FIRST_AUTHORIZER protocol feature
  • Loading branch information
arhag committed Apr 10, 2019
2 parents fa3f720 + 7a5a373 commit 047a155
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 9 deletions.
8 changes: 7 additions & 1 deletion libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,13 @@ struct controller_impl {
trx_context.init_for_deferred_trx( gtrx.published );

if( trx_context.enforce_whiteblacklist && pending->_block_status == controller::block_status::incomplete ) {
check_actor_list( trx_context.bill_to_accounts ); // Assumes bill_to_accounts is the set of actors authorizing the transaction
flat_set<account_name> actors;
for( const auto& act : trx_context.trx.actions ) {
for( const auto& auth : act.authorization ) {
actors.insert( auth.actor );
}
}
check_actor_list( actors );
}

trx_context.exec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ enum class builtin_protocol_feature_t : uint32_t {
replace_deferred,
fix_linkauth_restriction,
disallow_empty_producer_schedule,
restrict_action_to_self
restrict_action_to_self,
only_bill_first_authorizer
};

struct protocol_feature_subjective_restrictions {
Expand Down
3 changes: 2 additions & 1 deletion libraries/chain/include/eosio/chain/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ namespace eosio { namespace chain {
bool allow_duplicate_keys = false) const;

uint32_t total_actions()const { return context_free_actions.size() + actions.size(); }
account_name first_authorizor()const {

account_name first_authorizer()const {
for( const auto& a : actions ) {
for( const auto& u : a.authorization )
return u.actor;
Expand Down
11 changes: 11 additions & 0 deletions libraries/chain/protocol_feature_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ Builtin protocol feature: RESTRICT_ACTION_TO_SELF
Disallows bypass of authorization checks by unprivileged contracts when sending inline actions or deferred transactions.
The original protocol rules allow a bypass of authorization checks for actions sent by a contract to itself.
This protocol feature removes that bypass.
*/
{}
} )
( builtin_protocol_feature_t::only_bill_first_authorizer, builtin_protocol_feature_spec{
"ONLY_BILL_FIRST_AUTHORIZER",
fc::variant("2f1f13e291c79da5a2bbad259ed7c1f2d34f697ea460b14b565ac33b063b73e2").as<digest_type>(),
// SHA256 hash of the raw message below within the comment delimiters (do not modify message below).
/*
Builtin protocol feature: ONLY_BILL_FIRST_AUTHORIZER
Adds CPU and network bandwidth usage to only the first authorizer of a transaction.
*/
{}
} )
Expand Down
12 changes: 8 additions & 4 deletions libraries/chain/transaction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,13 @@ namespace bacc = boost::accumulators;
validate_cpu_usage_to_bill( billed_cpu_time_us, false ); // Fail early if the amount to be billed is too high

// Record accounts to be billed for network and CPU usage
for( const auto& act : trx.actions ) {
for( const auto& auth : act.authorization ) {
bill_to_accounts.insert( auth.actor );
if( control.is_builtin_activated(builtin_protocol_feature_t::only_bill_first_authorizer) ) {
bill_to_accounts.insert( trx.first_authorizer() );
} else {
for( const auto& act : trx.actions ) {
for( const auto& auth : act.authorization ) {
bill_to_accounts.insert( auth.actor );
}
}
}
validate_ram_usage.reserve( bill_to_accounts.size() );
Expand Down Expand Up @@ -583,7 +587,7 @@ namespace bacc = boost::accumulators;
+ static_cast<uint64_t>(config::transaction_id_net_usage) ); // Will exit early if net usage cannot be payed.
}

auto first_auth = trx.first_authorizor();
auto first_auth = trx.first_authorizer();

uint32_t trx_size = 0;
const auto& cgto = control.mutable_db().create<generated_transaction_object>( [&]( auto& gto ) {
Expand Down
5 changes: 3 additions & 2 deletions unittests/auth_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ BOOST_AUTO_TEST_CASE( any_auth ) { try {

BOOST_AUTO_TEST_CASE(no_double_billing) {
try {
TESTER chain;
validating_tester chain( validating_tester::default_config() );
chain.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios );

chain.produce_block();

Expand Down Expand Up @@ -490,7 +491,7 @@ BOOST_AUTO_TEST_CASE( linkauth_special ) { try {
chain.create_account(N(tester));
chain.create_account(N(tester2));
chain.produce_blocks();

chain.push_action(config::system_account_name, updateauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("permission", "first")
Expand Down
107 changes: 107 additions & 0 deletions unittests/protocol_feature_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,4 +667,111 @@ BOOST_AUTO_TEST_CASE( restrict_action_to_self_test ) { try {

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( only_bill_to_first_authorizer ) { try {
tester chain( setup_policy::preactivate_feature_and_new_bios );

const auto& tester_account = N(tester);
const auto& tester_account2 = N(tester2);

chain.produce_blocks();
chain.create_account(tester_account);
chain.create_account(tester_account2);

chain.push_action(config::system_account_name, N(setalimits), config::system_account_name, fc::mutable_variant_object()
("account", name(tester_account).to_string())
("ram_bytes", 10000)
("net_weight", 1000)
("cpu_weight", 1000));

chain.push_action(config::system_account_name, N(setalimits), config::system_account_name, fc::mutable_variant_object()
("account", name(tester_account2).to_string())
("ram_bytes", 10000)
("net_weight", 1000)
("cpu_weight", 1000));

const resource_limits_manager& mgr = chain.control->get_resource_limits_manager();

chain.produce_blocks();

{
action act;
act.account = tester_account;
act.name = N(null);
act.authorization = vector<permission_level>{
{tester_account, config::active_name},
{tester_account2, config::active_name}
};

signed_transaction trx;
trx.actions.emplace_back(std::move(act));
chain.set_transaction_headers(trx);

trx.sign(get_private_key(tester_account, "active"), chain.control->get_chain_id());
trx.sign(get_private_key(tester_account2, "active"), chain.control->get_chain_id());


auto tester_cpu_limit0 = mgr.get_account_cpu_limit_ex(tester_account);
auto tester2_cpu_limit0 = mgr.get_account_cpu_limit_ex(tester_account2);
auto tester_net_limit0 = mgr.get_account_net_limit_ex(tester_account);
auto tester2_net_limit0 = mgr.get_account_net_limit_ex(tester_account2);

chain.push_transaction(trx);

auto tester_cpu_limit1 = mgr.get_account_cpu_limit_ex(tester_account);
auto tester2_cpu_limit1 = mgr.get_account_cpu_limit_ex(tester_account2);
auto tester_net_limit1 = mgr.get_account_net_limit_ex(tester_account);
auto tester2_net_limit1 = mgr.get_account_net_limit_ex(tester_account2);

BOOST_CHECK(tester_cpu_limit1.used > tester_cpu_limit0.used);
BOOST_CHECK(tester2_cpu_limit1.used > tester2_cpu_limit0.used);
BOOST_CHECK(tester_net_limit1.used > tester_net_limit0.used);
BOOST_CHECK(tester2_net_limit1.used > tester2_net_limit0.used);

BOOST_CHECK_EQUAL(tester_cpu_limit1.used - tester_cpu_limit0.used, tester2_cpu_limit1.used - tester2_cpu_limit0.used);
BOOST_CHECK_EQUAL(tester_net_limit1.used - tester_net_limit0.used, tester2_net_limit1.used - tester2_net_limit0.used);
}

const auto& pfm = chain.control->get_protocol_feature_manager();
const auto& d = pfm.get_builtin_digest( builtin_protocol_feature_t::only_bill_first_authorizer );
BOOST_REQUIRE( d );

chain.preactivate_protocol_features( {*d} );
chain.produce_blocks();

{
action act;
act.account = tester_account;
act.name = N(null2);
act.authorization = vector<permission_level>{
{tester_account, config::active_name},
{tester_account2, config::active_name}
};

signed_transaction trx;
trx.actions.emplace_back(std::move(act));
chain.set_transaction_headers(trx);

trx.sign(get_private_key(tester_account, "active"), chain.control->get_chain_id());
trx.sign(get_private_key(tester_account2, "active"), chain.control->get_chain_id());

auto tester_cpu_limit0 = mgr.get_account_cpu_limit_ex(tester_account);
auto tester2_cpu_limit0 = mgr.get_account_cpu_limit_ex(tester_account2);
auto tester_net_limit0 = mgr.get_account_net_limit_ex(tester_account);
auto tester2_net_limit0 = mgr.get_account_net_limit_ex(tester_account2);

chain.push_transaction(trx);

auto tester_cpu_limit1 = mgr.get_account_cpu_limit_ex(tester_account);
auto tester2_cpu_limit1 = mgr.get_account_cpu_limit_ex(tester_account2);
auto tester_net_limit1 = mgr.get_account_net_limit_ex(tester_account);
auto tester2_net_limit1 = mgr.get_account_net_limit_ex(tester_account2);

BOOST_CHECK(tester_cpu_limit1.used > tester_cpu_limit0.used);
BOOST_CHECK(tester2_cpu_limit1.used == tester2_cpu_limit0.used);
BOOST_CHECK(tester_net_limit1.used > tester_net_limit0.used);
BOOST_CHECK(tester2_net_limit1.used == tester2_net_limit0.used);
}

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 047a155

Please sign in to comment.