Skip to content

Commit

Permalink
Merge pull request #551 from eosnetworkfoundation/skip_context_free_l…
Browse files Browse the repository at this point in the history
…ight_validation

[3.2] skip context free actions during light validation
  • Loading branch information
ClaytonCalabrese authored Jun 30, 2022
2 parents 8c24959 + 6be26f3 commit 2d10053
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 47 deletions.
84 changes: 43 additions & 41 deletions libraries/chain/apply_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,51 +72,53 @@ void apply_context::exec_one()
try {
action_return_value.clear();
receiver_account = &db.get<account_metadata_object,by_name>( 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)
);
}
}
}
}
Expand Down
14 changes: 8 additions & 6 deletions libraries/chain/transaction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<account_object, by_name>(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<account_object, by_name>( 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<account_name> actors;
Expand Down
82 changes: 82 additions & 0 deletions unittests/api_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<signed_block_ptr> 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<uint32_t>(100)); // verify payload matches context free data
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(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<permission_level>{{"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<const transaction_trace_ptr&, const packed_transaction_ptr&> 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
*************************************************************************************/
Expand Down

0 comments on commit 2d10053

Please sign in to comment.