Skip to content

Commit 1fdd7ad

Browse files
authored
Merge branch 'GH-984-whitelist-eos-vm-oc-limits' into GH-981-oc-whitelist
2 parents 485df7b + d429989 commit 1fdd7ad

24 files changed

+721
-66
lines changed

libraries/chain/controller.cpp

+49-8
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,8 @@ struct controller_impl {
10061006
async_t async_aggregation = async_t::yes; // by default we process incoming votes asynchronously
10071007
my_finalizers_t my_finalizers;
10081008
std::atomic<bool> writing_snapshot = false;
1009+
std::atomic<bool> applying_block = false;
1010+
platform_timer& main_thread_timer;
10091011

10101012
thread_local static platform_timer timer; // a copy for main thread and each read-only thread
10111013
#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED)
@@ -1285,6 +1287,7 @@ struct controller_impl {
12851287
read_mode( cfg.read_mode ),
12861288
thread_pool(),
12871289
my_finalizers(cfg.finalizers_dir / config::safety_filename),
1290+
main_thread_timer(timer), // assumes constructor is called from main thread
12881291
wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() )
12891292
{
12901293
assert(cfg.chain_thread_pool_size > 0);
@@ -1542,10 +1545,14 @@ struct controller_impl {
15421545
auto mark_branch_irreversible = [&, this](auto& fork_db) {
15431546
assert(!irreversible_mode() || fork_db.head());
15441547
const auto& head_id = irreversible_mode() ? fork_db.head()->id() : chain_head.id();
1548+
const auto head_num = block_header::num_from_id(head_id);
15451549
// verifies lib is on head branch, otherwise returns an empty branch
1546-
// The new lib needs to be on the head branch because the fork_db.advance_root() below could purge blocks that
1547-
// would be needed to be re-applied on a fork switch from the exiting chain_head.
1548-
auto branch = fork_db.fetch_branch(head_id, new_lib_id);
1550+
// The new lib needs to be on the head branch because the forkdb.advance_root() below could purge blocks that
1551+
// would be needed to be re-applied on a fork switch from the exiting chain_head.
1552+
// Pending LIB can be greater than chain head, for example when syncing, in that case fetch branch from the
1553+
// pending LIB. If the pending LIB not found on the head branch then fetch_branch returns an empty branch.
1554+
// Otherwise fetch_branch will return from chain_head to root iff chain_head on pending LIB branch.
1555+
auto branch = new_lib_num <= head_num ? fork_db.fetch_branch(head_id, new_lib_id) : fork_db.fetch_branch(new_lib_id, head_id);
15491556
try {
15501557
auto should_process = [&](auto& bsp) {
15511558
// Only make irreversible blocks that have been validated. Blocks in the fork database may not be on our current best head
@@ -3780,6 +3787,9 @@ struct controller_impl {
37803787
}
37813788
}
37823789

3790+
applying_block = true;
3791+
auto apply = fc::make_scoped_exit([&](){ applying_block = false; });
3792+
37833793
transaction_trace_ptr trace;
37843794

37853795
size_t packed_idx = 0;
@@ -3806,7 +3816,11 @@ struct controller_impl {
38063816
std::holds_alternative<transaction_id_type>(receipt.trx);
38073817

38083818
if( transaction_failed && !transaction_can_fail) {
3809-
edump((*trace));
3819+
if (trace->except->code() == interrupt_exception::code_value) {
3820+
ilog("Interrupt of trx id: ${id}", ("id", trace->id));
3821+
} else {
3822+
edump((*trace));
3823+
}
38103824
throw *trace->except;
38113825
}
38123826

@@ -3875,7 +3889,8 @@ struct controller_impl {
38753889
} catch ( const boost::interprocess::bad_alloc& ) {
38763890
throw;
38773891
} catch ( const fc::exception& e ) {
3878-
edump((e.to_detail_string()));
3892+
if (e.code() != interrupt_exception::code_value)
3893+
edump((e.to_detail_string()));
38793894
abort_block();
38803895
throw;
38813896
} catch ( const std::exception& e ) {
@@ -4365,7 +4380,15 @@ struct controller_impl {
43654380
log_irreversible();
43664381
transition_to_savanna_if_needed();
43674382
return controller::apply_blocks_result::complete;
4368-
} FC_LOG_AND_RETHROW( )
4383+
} catch (fc::exception& e) {
4384+
if (e.code() != interrupt_exception::code_value) {
4385+
wlog("${d}", ("d",e.to_detail_string()));
4386+
FC_RETHROW_EXCEPTION(e, warn, "rethrow");
4387+
}
4388+
throw;
4389+
} catch (...) {
4390+
try { throw; } FC_LOG_AND_RETHROW()
4391+
}
43694392
}
43704393

43714394
controller::apply_blocks_result maybe_apply_blocks( const forked_callback_t& forked_cb, const trx_meta_cache_lookup& trx_lookup )
@@ -4437,8 +4460,12 @@ struct controller_impl {
44374460
} catch ( const boost::interprocess::bad_alloc& ) {
44384461
throw;
44394462
} catch (const fc::exception& e) {
4440-
elog("exception thrown while applying block ${bn} : ${id}, previous ${p}, error: ${e}",
4441-
("bn", bsp->block_num())("id", bsp->id())("p", bsp->previous())("e", e.to_detail_string()));
4463+
if (e.code() == interrupt_exception::code_value) {
4464+
ilog("interrupt while applying block ${bn} : ${id}", ("bn", bsp->block_num())("id", bsp->id()));
4465+
} else {
4466+
elog("exception thrown while applying block ${bn} : ${id}, previous ${p}, error: ${e}",
4467+
("bn", bsp->block_num())("id", bsp->id())("p", bsp->previous())("e", e.to_detail_string()));
4468+
}
44424469
except = std::current_exception();
44434470
} catch (const std::exception& e) {
44444471
elog("exception thrown while applying block ${bn} : ${id}, previous ${p}, error: ${e}",
@@ -4501,6 +4528,16 @@ struct controller_impl {
45014528
return applied_trxs;
45024529
}
45034530

4531+
void interrupt_transaction() {
4532+
// Only interrupt transaction if applying a block. Speculative trxs already have a deadline set so they
4533+
// have limited run time already. This is to allow killing a long-running transaction in a block being
4534+
// validated.
4535+
if (applying_block) {
4536+
ilog("Interrupting apply block");
4537+
main_thread_timer.expire_now();
4538+
}
4539+
}
4540+
45044541
// @param if_active true if instant finality is active
45054542
static checksum256_type calc_merkle( deque<digest_type>&& digests, bool if_active ) {
45064543
if (if_active) {
@@ -5261,6 +5298,10 @@ deque<transaction_metadata_ptr> controller::abort_block() {
52615298
return my->abort_block();
52625299
}
52635300

5301+
void controller::interrupt_transaction() {
5302+
my->interrupt_transaction();
5303+
}
5304+
52645305
boost::asio::io_context& controller::get_thread_pool() {
52655306
return my->thread_pool.get_executor();
52665307
}

libraries/chain/include/eosio/chain/controller.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ namespace eosio::chain {
207207
*/
208208
deque<transaction_metadata_ptr> abort_block();
209209

210+
/// Expected to be called from signal handler
211+
void interrupt_transaction();
212+
210213
/**
211214
*
212215
*/

libraries/chain/include/eosio/chain/exceptions.hpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ namespace eosio { namespace chain {
381381
3080005, "Transaction CPU usage is too much for the remaining allowable usage of the current block" )
382382
FC_DECLARE_DERIVED_EXCEPTION( deadline_exception, resource_exhausted_exception,
383383
3080006, "Transaction took too long" )
384+
FC_DECLARE_DERIVED_EXCEPTION( leeway_deadline_exception, deadline_exception,
385+
3081001, "Transaction reached the deadline set due to leeway on account CPU limits" )
386+
384387
FC_DECLARE_DERIVED_EXCEPTION( greylist_net_usage_exceeded, resource_exhausted_exception,
385388
3080007, "Transaction exceeded the current greylisted account network usage limit" )
386389
FC_DECLARE_DERIVED_EXCEPTION( greylist_cpu_usage_exceeded, resource_exhausted_exception,
@@ -389,9 +392,8 @@ namespace eosio { namespace chain {
389392
3080009, "Read-only transaction eos-vm-oc compile temporary failure" )
390393
FC_DECLARE_DERIVED_EXCEPTION( ro_trx_vm_oc_compile_permanent_failure, resource_exhausted_exception,
391394
3080010, "Read-only transaction eos-vm-oc compile permanent failure" )
392-
393-
FC_DECLARE_DERIVED_EXCEPTION( leeway_deadline_exception, deadline_exception,
394-
3081001, "Transaction reached the deadline set due to leeway on account CPU limits" )
395+
FC_DECLARE_DERIVED_EXCEPTION( interrupt_exception, resource_exhausted_exception,
396+
3080011, "Transaction interrupted by signal" )
395397

396398
FC_DECLARE_DERIVED_EXCEPTION( authorization_exception, chain_exception,
397399
3090000, "Authorization exception" )

libraries/chain/include/eosio/chain/platform_timer.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct platform_timer {
1717

1818
void start(fc::time_point tp);
1919
void stop();
20+
void expire_now();
2021

2122
/* Sets a callback for when timer expires. Be aware this could might fire from a signal handling context and/or
2223
on any particular thread. Only a single callback can be registered at once; trying to register more will

libraries/chain/platform_timer_asio_fallback.cpp

+12-15
Original file line numberDiff line numberDiff line change
@@ -57,39 +57,36 @@ platform_timer::~platform_timer() {
5757

5858
void platform_timer::start(fc::time_point tp) {
5959
if(tp == fc::time_point::maximum()) {
60-
expired = 0;
60+
expired = false;
6161
return;
6262
}
6363
fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch();
6464
if(x.count() <= 0)
65-
expired = 1;
65+
expired = true;
6666
else {
67-
#if 0
68-
std::promise<void> p;
69-
auto f = p.get_future();
70-
checktime_ios->post([&p,this]() {
71-
expired = 0;
72-
p.set_value();
73-
});
74-
f.get();
75-
#endif
76-
expired = 0;
67+
expired = false;
7768
my->timer->expires_after(std::chrono::microseconds(x.count()));
7869
my->timer->async_wait([this](const boost::system::error_code& ec) {
7970
if(ec)
8071
return;
81-
expired = 1;
82-
call_expiration_callback();
72+
expire_now();
8373
});
8474
}
8575
}
8676

77+
void platform_timer::expire_now() {
78+
bool expected = false;
79+
if (expired.compare_exchange_strong(expected, true)) {
80+
call_expiration_callback();
81+
}
82+
}
83+
8784
void platform_timer::stop() {
8885
if(expired)
8986
return;
9087

9188
my->timer->cancel();
92-
expired = 1;
89+
expired = true;
9390
}
9491

9592
}}

libraries/chain/platform_timer_kqueue.cpp

+13-7
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ platform_timer::platform_timer() {
5858

5959
if(c == 1 && anEvent.filter == EVFILT_TIMER) {
6060
platform_timer* self = (platform_timer*)anEvent.udata;
61-
self->expired = 1;
62-
self->call_expiration_callback();
61+
self->expire_now();
6362
}
6463
else if(c == 1 && anEvent.filter == EVFILT_USER)
6564
return;
@@ -90,19 +89,26 @@ platform_timer::~platform_timer() {
9089

9190
void platform_timer::start(fc::time_point tp) {
9291
if(tp == fc::time_point::maximum()) {
93-
expired = 0;
92+
expired = false;
9493
return;
9594
}
9695
fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch();
9796
if(x.count() <= 0)
98-
expired = 1;
97+
expired = true;
9998
else {
10099
struct kevent64_s aTimerEvent;
101100
EV_SET64(&aTimerEvent, my->timerid, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_USECONDS|NOTE_CRITICAL, x.count(), (uint64_t)this, 0, 0);
102101

103-
expired = 0;
102+
expired = false;
104103
if(kevent64(kqueue_fd, &aTimerEvent, 1, NULL, 0, KEVENT_FLAG_IMMEDIATE, NULL) != 0)
105-
expired = 1;
104+
expired = true;
105+
}
106+
}
107+
108+
void platform_timer::expire_now() {
109+
bool expected = false;
110+
if (expired.compare_exchange_strong(expected, true)) {
111+
call_expiration_callback();
106112
}
107113
}
108114

@@ -113,7 +119,7 @@ void platform_timer::stop() {
113119
struct kevent64_s stop_timer_event;
114120
EV_SET64(&stop_timer_event, my->timerid, EVFILT_TIMER, EV_DELETE, 0, 0, 0, 0, 0);
115121
kevent64(kqueue_fd, &stop_timer_event, 1, NULL, 0, KEVENT_FLAG_IMMEDIATE, NULL);
116-
expired = 1;
122+
expired = true;
117123
}
118124

119125
}}

libraries/chain/platform_timer_posix.cpp

+19-11
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
#include <fc/fwd_impl.hpp>
66
#include <fc/exception/exception.hpp>
77

8+
#include <atomic>
89
#include <mutex>
910

1011
#include <signal.h>
1112
#include <time.h>
13+
#include <sys/types.h>
1214

13-
namespace eosio { namespace chain {
15+
namespace eosio::chain {
1416

1517
static_assert(std::atomic_bool::is_always_lock_free, "Only lock-free atomics AS-safe.");
1618

@@ -19,18 +21,17 @@ struct platform_timer::impl {
1921

2022
static void sig_handler(int, siginfo_t* si, void*) {
2123
platform_timer* self = (platform_timer*)si->si_value.sival_ptr;
22-
self->expired = 1;
23-
self->call_expiration_callback();
24+
self->expire_now();
2425
}
2526
};
2627

2728
platform_timer::platform_timer() {
2829
static_assert(sizeof(impl) <= fwd_size);
2930

3031
static bool initialized;
31-
static std::mutex initalized_mutex;
32+
static std::mutex initialized_mutex;
3233

33-
if(std::lock_guard guard(initalized_mutex); !initialized) {
34+
if(std::lock_guard guard(initialized_mutex); !initialized) {
3435
struct sigaction act;
3536
sigemptyset(&act.sa_mask);
3637
act.sa_sigaction = impl::sig_handler;
@@ -55,19 +56,26 @@ platform_timer::~platform_timer() {
5556

5657
void platform_timer::start(fc::time_point tp) {
5758
if(tp == fc::time_point::maximum()) {
58-
expired = 0;
59+
expired = false;
5960
return;
6061
}
6162
fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch();
6263
if(x.count() <= 0)
63-
expired = 1;
64+
expired = true;
6465
else {
6566
time_t secs = x.count() / 1000000;
6667
long nsec = (x.count() - (secs*1000000)) * 1000;
6768
struct itimerspec enable = {{0, 0}, {secs, nsec}};
68-
expired = 0;
69+
expired = false;
6970
if(timer_settime(my->timerid, 0, &enable, NULL) != 0)
70-
expired = 1;
71+
expired = true;
72+
}
73+
}
74+
75+
void platform_timer::expire_now() {
76+
bool expected = false;
77+
if (expired.compare_exchange_strong(expected, true)) {
78+
call_expiration_callback();
7179
}
7280
}
7381

@@ -76,7 +84,7 @@ void platform_timer::stop() {
7684
return;
7785
struct itimerspec disable = {{0, 0}, {0, 0}};
7886
timer_settime(my->timerid, 0, &disable, NULL);
79-
expired = 1;
87+
expired = true;
8088
}
8189

82-
}}
90+
}

libraries/chain/transaction_context.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,10 @@ namespace eosio::chain {
459459
return;
460460

461461
auto now = fc::time_point::now();
462-
if( explicit_billed_cpu_time || deadline_exception_code == deadline_exception::code_value ) {
462+
if (explicit_billed_cpu_time && block_deadline > now) {
463+
EOS_THROW( interrupt_exception, "interrupt signaled, ran ${bt}us, start ${s}",
464+
("bt", now - pseudo_start)("s", start) );
465+
} else if( explicit_billed_cpu_time || deadline_exception_code == deadline_exception::code_value ) {
463466
EOS_THROW( deadline_exception, "deadline exceeded ${billing_timer}us",
464467
("billing_timer", now - pseudo_start)("now", now)("deadline", _deadline)("start", start) );
465468
} else if( deadline_exception_code == block_cpu_usage_exceeded::code_value ) {

plugins/test_control_api_plugin/test_control_api_plugin.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ void test_control_api_plugin::plugin_startup() {
5151
TEST_CONTROL_RW_CALL(throw_on, 202, http_params_types::params_required)
5252
}, appbase::exec_queue::read_write);
5353

54+
app().get_plugin<http_plugin>().add_api({
55+
TEST_CONTROL_RW_CALL(swap_action, 202, http_params_types::params_required)
56+
}, appbase::exec_queue::read_write);
57+
5458
}
5559

5660
void test_control_api_plugin::plugin_shutdown() {}

plugins/test_control_plugin/include/eosio/test_control_plugin/test_control_plugin.hpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,17 @@ class read_write {
3333
};
3434
empty throw_on(const throw_on_params& params) const;
3535

36-
private:
36+
// produce a next block with `from` action replaced with `to` action
37+
// requires Savanna to be active, this assumes blocks are is_proper_svnn_block
38+
struct swap_action_params {
39+
chain::name from; // replace from action in block to `to` action
40+
chain::name to;
41+
fc::crypto::private_key trx_priv_key;
42+
fc::crypto::private_key blk_priv_key;
43+
};
44+
empty swap_action(const swap_action_params& params) const;
45+
46+
private:
3747
test_control_ptr my;
3848
};
3949

@@ -68,3 +78,4 @@ class test_control_plugin : public plugin<test_control_plugin> {
6878
FC_REFLECT(eosio::test_control_apis::empty, )
6979
FC_REFLECT(eosio::test_control_apis::read_write::kill_node_on_producer_params, (producer)(where_in_sequence)(based_on_lib) )
7080
FC_REFLECT(eosio::test_control_apis::read_write::throw_on_params, (signal)(exception) )
81+
FC_REFLECT(eosio::test_control_apis::read_write::swap_action_params, (from)(to)(trx_priv_key)(blk_priv_key) )

0 commit comments

Comments
 (0)