Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.1.0 -> main] Fix deferred trx processing #1153

Merged
merged 8 commits into from
Feb 10, 2025
3 changes: 2 additions & 1 deletion benchmark/bls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ struct interface_in_benchmark {
timer = std::make_unique<platform_timer>();
trx_timer = std::make_unique<transaction_checktime_timer>(*timer);
trx_ctx = std::make_unique<transaction_context>(*chain->control.get(), *ptrx, ptrx->id(), std::move(*trx_timer),
action_digests_t::store_which_t::legacy);
action_digests_t::store_which_t::legacy, fc::time_point::now(),
transaction_metadata::trx_type::input);
trx_ctx->max_transaction_time_subjective = fc::microseconds::maximum();
trx_ctx->init_for_input_trx( ptrx->get_unprunable_size(), ptrx->get_prunable_size() );
trx_ctx->exec(); // this is required to generate action traces to be used by apply_context constructor
Expand Down
8 changes: 6 additions & 2 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2644,7 +2644,8 @@ struct controller_impl {

transaction_checktime_timer trx_timer(timer);
const packed_transaction trx( std::move( etrx ) );
transaction_context trx_context( self, trx, trx.id(), std::move(trx_timer), bb.action_receipt_digests().store_which(), start );
transaction_context trx_context( self, trx, trx.id(), std::move(trx_timer), bb.action_receipt_digests().store_which(),
start, transaction_metadata::trx_type::implicit );

if (auto dm_logger = get_deep_mind_logger(trx_context.is_transient())) {
dm_logger->on_onerror(etrx);
Expand Down Expand Up @@ -2690,6 +2691,8 @@ struct controller_impl {
} catch ( const boost::interprocess::bad_alloc& ) {
throw;
} catch( const fc::exception& e ) {
// apply_onerror for deferred trxs is implicit so interrupt oc not allowed
assert(e.code() != interrupt_oc_exception::code_value);
handle_exception(e);
} catch ( const std::exception& e ) {
auto wrapper = fc::std_exception_wrapper::from_current_exception(e);
Expand Down Expand Up @@ -2815,7 +2818,8 @@ struct controller_impl {
auto& bb = std::get<building_block>(pending->_block_stage);

transaction_checktime_timer trx_timer( timer );
transaction_context trx_context( self, *trx->packed_trx(), gtrx.trx_id, std::move(trx_timer), bb.action_receipt_digests().store_which() );
transaction_context trx_context( self, *trx->packed_trx(), gtrx.trx_id, std::move(trx_timer), bb.action_receipt_digests().store_which(),
start, transaction_metadata::trx_type::scheduled );
trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource
trx_context.block_deadline = block_deadline;
trx_context.max_transaction_time_subjective = max_transaction_time;
Expand Down
6 changes: 4 additions & 2 deletions libraries/chain/include/eosio/chain/transaction_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ namespace eosio::chain {
const transaction_id_type& trx_id, // trx_id diff than t.id() before replace_deferred
transaction_checktime_timer&& timer,
action_digests_t::store_which_t sad,
fc::time_point start = fc::time_point::now(),
transaction_metadata::trx_type type = transaction_metadata::trx_type::input);
fc::time_point start,
transaction_metadata::trx_type type);
~transaction_context();

void init_for_implicit_trx();
Expand Down Expand Up @@ -162,6 +162,8 @@ namespace eosio::chain {
bool is_dry_run()const { return trx_type == transaction_metadata::trx_type::dry_run; };
bool is_read_only()const { return trx_type == transaction_metadata::trx_type::read_only; };
bool is_transient()const { return trx_type == transaction_metadata::trx_type::read_only || trx_type == transaction_metadata::trx_type::dry_run; };
bool is_implicit()const { return trx_type == transaction_metadata::trx_type::implicit; };
bool is_scheduled()const { return trx_type == transaction_metadata::trx_type::scheduled; };
bool has_undo()const;

int64_t set_proposed_producers(vector<producer_authority> producers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,14 @@ struct eosvmoc_tier {
}
}
#endif
const bool allow_oc_interrupt = attempt_tierup && context.is_applying_block() && context.trx_context.has_undo();
// Do not allow oc interrupt if no undo as the transaction needs to be undone to restart it.
// Do not allow oc interrupt if implicit or scheduled. There are two implicit trxs: onblock and onerror.
// The onerror trx of deferred trxs is implicit. Interrupt needs to be disabled for deferred trxs because
// they capture all exceptions, explicitly handle undo stack, and directly call trx_context.execute_action.
// Not allowing interrupt for onblock seems rather harmless, so instead of distinguishing between onerror and
// onblock, just disallow for all implicit.
const bool allow_oc_interrupt = attempt_tierup && context.is_applying_block() &&
context.trx_context.has_undo() && !context.trx_context.is_implicit() && !context.trx_context.is_scheduled();
auto ex = fc::make_scoped_exit([&]() {
if (allow_oc_interrupt) {
eos_vm_oc_compile_interrupt = false;
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/transaction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace eosio::chain {
undo();
*trace = transaction_trace{}; // reset trace
initialize();
transaction_timer.stop();
resume_billing_timer(start);

auto sw = executed_action_receipts.store_which();
Expand Down
3 changes: 2 additions & 1 deletion libraries/chain/webassembly/runtimes/eos-vm-oc/executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ void executor::execute(const code_descriptor& code, memory& mem, apply_context&
syscall(SYS_mprotect, self->code_mapping, self->code_mapping_size, PROT_NONE);
self->mapping_is_executable = false;
}, this);
context.trx_context.checktime(); //catch any expiration that might have occurred before setting up callback

auto cleanup = fc::make_scoped_exit([cb, &tt=context.trx_context.transaction_timer, &mem=mem](){
cb->is_running = false;
Expand All @@ -245,6 +244,8 @@ void executor::execute(const code_descriptor& code, memory& mem, apply_context&
}
});

context.trx_context.checktime(); //catch any expiration that might have occurred before setting up callback

void(*apply_func)(uint64_t, uint64_t, uint64_t) = (void(*)(uint64_t, uint64_t, uint64_t))(cb->running_code_base + code.apply_offset);

switch(sigsetjmp(*cb->jmp, 0)) {
Expand Down