提交 c1aa8039 编写于 作者: D Daniel Larimer

Switch to subjective billing in us #2898

- some unit tests no longer pass due to new semantics around deadline
上级 c50969ca
......@@ -34,10 +34,8 @@ static inline void print_debug(account_name receiver, const action_trace& ar) {
action_trace apply_context::exec_one()
{
auto start = fc::time_point::now();
cpu_usage = 0;
cpu_usage_limit = trx_context.get_action_cpu_usage_limit( context_free );
const auto& cfg = control.get_global_properties().configuration;
checktime( cfg.base_per_action_cpu_usage );
try {
const auto &a = control.get_account(receiver);
privileged = a.privileged;
......@@ -53,6 +51,7 @@ action_trace apply_context::exec_one()
} catch ( const wasm_exit& ){}
}
} FC_CAPTURE_AND_RETHROW((_pending_console_output.str()));
action_receipt r;
......@@ -65,13 +64,9 @@ action_trace apply_context::exec_one()
r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor );
}
cpu_usage = trx_context.add_action_cpu_usage( cpu_usage, context_free );
action_trace t(r);
t.trx_id = trx_context.id;
t.act = act;
t.cpu_usage = cpu_usage;
t.total_cpu_usage = cpu_usage;
t.console = _pending_console_output.str();
trx_context.executed.emplace_back( move(r) );
......@@ -91,7 +86,6 @@ void apply_context::exec()
for( uint32_t i = 1; i < _notified.size(); ++i ) {
receiver = _notified[i];
trace.inline_traces.emplace_back( exec_one() );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
if( _cfa_inline_actions.size() > 0 || _inline_actions.size() > 0 ) {
......@@ -102,13 +96,11 @@ void apply_context::exec()
for( const auto& inline_action : _cfa_inline_actions ) {
trace.inline_traces.emplace_back();
trx_context.dispatch_action( trace.inline_traces.back(), inline_action, inline_action.account, true, recurse_depth + 1 );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
for( const auto& inline_action : _inline_actions ) {
trace.inline_traces.emplace_back();
trx_context.dispatch_action( trace.inline_traces.back(), inline_action, inline_action.account, false, recurse_depth + 1 );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
} /// exec()
......@@ -362,8 +354,6 @@ void apply_context::reset_console() {
}
void apply_context::checktime(uint32_t instruction_count) {
cpu_usage += instruction_count;
EOS_ASSERT( BOOST_LIKELY(cpu_usage <= cpu_usage_limit), action_cpu_usage_exceeded, "action cpu usage exceeded" );
trx_context.check_time();
}
......
......@@ -356,7 +356,7 @@ struct controller_impl {
FC_ASSERT( new_bsp == head, "committed block did not become the new head in fork database" );
}
//ilog((fc::json::to_pretty_string(*pending->_pending_block_state->block)));
// ilog((fc::json::to_pretty_string(*pending->_pending_block_state->block)));
emit( self.accepted_block, pending->_pending_block_state );
pending->push();
pending.reset();
......@@ -385,8 +385,8 @@ struct controller_impl {
transaction_trace_ptr apply_onerror( const generated_transaction_object& gto,
fc::time_point deadline,
uint64_t cpu_usage,
fc::time_point start ) {
fc::time_point start,
uint32_t billed_cpu_time_us) {
signed_transaction etrx;
// Deliver onerror action containing the failed deferred transaction directly back to the sender.
etrx.actions.emplace_back( vector<permission_level>{},
......@@ -394,18 +394,19 @@ struct controller_impl {
etrx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to avoid appearing expired
etrx.set_reference_block( self.head_block_id() );
transaction_context trx_context( self, etrx, etrx.id() );
transaction_context trx_context( self, etrx, etrx.id(), start );
trx_context.billed_cpu_time_us = billed_cpu_time_us;
transaction_trace_ptr trace = trx_context.trace;
try {
trx_context.init_for_implicit_trx( deadline, 0, cpu_usage );
trx_context.init_for_implicit_trx( deadline, 0 );
trx_context.published = gto.published;
trx_context.trace->action_traces.emplace_back();
trx_context.dispatch_action( trx_context.trace->action_traces.back(), etrx.actions.back(), gto.sender );
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
auto restore = make_block_restore_point();
trace->receipt = push_receipt( gto.trx_id, transaction_receipt::soft_fail, trace->cpu_usage, trace->net_usage );
trace->receipt = push_receipt( gto.trx_id, transaction_receipt::soft_fail,
trx_context.billed_cpu_time_us, trace->net_usage );
fc::move_append( pending->_actions, move(trx_context.executed) );
emit( self.applied_transaction, trace );
......@@ -432,12 +433,18 @@ struct controller_impl {
bool failure_is_subjective( const fc::exception& e ) {
auto code = e.code();
return (code == tx_soft_cpu_usage_exceeded::code_value) ||
(code == tx_soft_net_usage_exceeded::code_value) ||
return (code == tx_soft_net_usage_exceeded::code_value) ||
(code == tx_deadline_exceeded::code_value);
}
transaction_trace_ptr push_scheduled_transaction( const generated_transaction_object& gto, fc::time_point deadline )
transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us ) {
const auto& idx = db.get_index<generated_transaction_multi_index,by_trx_id>();
auto itr = idx.find( trxid );
FC_ASSERT( itr != idx.end(), "unknown transaction" );
return push_scheduled_transaction( *itr, deadline, billed_cpu_time_us );
}
transaction_trace_ptr push_scheduled_transaction( const generated_transaction_object& gto, fc::time_point deadline, uint32_t billed_cpu_time_us )
{ try {
auto undo_session = db.start_undo_session(true);
fc::datastream<const char*> ds( gto.packed_trx.data(), gto.packed_trx.size() );
......@@ -452,31 +459,28 @@ struct controller_impl {
auto trace = std::make_shared<transaction_trace>();
trace->id = gto.trx_id;
trace->scheduled = false;
trace->receipt = push_receipt( gto.trx_id, transaction_receipt::expired, 0, 0 ); // expire the transaction
trace->receipt = push_receipt( gto.trx_id, transaction_receipt::expired, billed_cpu_time_us, 0 ); // expire the transaction
return trace;
}
auto start = fc::time_point::now();
signed_transaction dtrx;
fc::raw::unpack(ds,static_cast<transaction&>(dtrx) );
transaction_context trx_context( self, dtrx, gto.trx_id );
trx_context.billed_cpu_time_us = billed_cpu_time_us;
transaction_trace_ptr trace = trx_context.trace;
flat_set<account_name> bill_to_accounts;
uint64_t max_cpu = 0;
try {
trx_context.init_for_deferred_trx( deadline, gto.published );
bill_to_accounts = trx_context.bill_to_accounts;
max_cpu = trx_context.initial_max_billable_cpu;
trx_context.exec();
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
auto restore = make_block_restore_point();
trace->receipt = push_receipt( gto.trx_id,
transaction_receipt::executed,
trace->cpu_usage,
trx_context.billed_cpu_time_us,
trace->net_usage );
fc::move_append( pending->_actions, move(trx_context.executed) );
......@@ -490,7 +494,7 @@ struct controller_impl {
} catch( const fc::exception& e ) {
trace->except = e;
trace->except_ptr = std::current_exception();
trace->elapsed = fc::time_point::now() - start;
trace->elapsed = fc::time_point::now() - trx_context.start;
}
trx_context.undo_session.undo();
......@@ -499,7 +503,7 @@ struct controller_impl {
if( gto.sender != account_name() && !failure_is_subjective(*trace->except)) {
// Attempt error handling for the generated transaction.
edump((trace->except->to_detail_string()));
auto error_trace = apply_onerror( gto, deadline, trace->cpu_usage, start );
auto error_trace = apply_onerror( gto, deadline, trx_context.start, trx_context.billed_cpu_time_us );
error_trace->failed_dtrx_trace = trace;
trace = error_trace;
if( !trace->except_ptr ) {
......@@ -510,18 +514,17 @@ struct controller_impl {
// Only hard failure OR subjective failure logic below:
trace->cpu_usage = ((trace->cpu_usage + 1023)/1024)*1024; // Round up cpu_usage to nearest multiple of 1024
trace->cpu_usage = std::min(trace->cpu_usage, max_cpu);
resource_limits.add_transaction_usage( bill_to_accounts, trace->cpu_usage, 0,
trace->elapsed = fc::time_point::now() - trx_context.start;
resource_limits.add_transaction_usage( bill_to_accounts, trx_context.billed_cpu_time_us, 0,
block_timestamp_type(self.pending_block_time()).slot ); // Should never fail
trace->elapsed = fc::time_point::now() - start;
if (failure_is_subjective(*trace->except)) {
// this is a subjective failure, don't remove the retained state so it can be
// retried at a later time and don't include any artifact of the transaction in the pending block
remove_retained_state.cancel();
} else {
trace->receipt = push_receipt(gto.trx_id, transaction_receipt::hard_fail, trace->cpu_usage, 0);
trace->receipt = push_receipt(gto.trx_id, transaction_receipt::hard_fail, trx_context.billed_cpu_time_us, 0);
emit( self.applied_transaction, trace );
undo_session.squash();
}
......@@ -535,14 +538,12 @@ struct controller_impl {
*/
template<typename T>
const transaction_receipt& push_receipt( const T& trx, transaction_receipt_header::status_enum status,
uint64_t cpu_usage, uint64_t net_usage ) {
uint64_t kcpu_usage = cpu_usage / 1024;
uint64_t cpu_usage_us, uint64_t net_usage ) {
uint64_t net_usage_words = net_usage / 8;
FC_ASSERT( kcpu_usage*1024 == cpu_usage, "cpu_usage is not divisible by 1024" );
FC_ASSERT( net_usage_words*8 == net_usage, "net_usage is not divisible by 8" );
pending->_pending_block_state->block->transactions.emplace_back( trx );
transaction_receipt& r = pending->_pending_block_state->block->transactions.back();
r.kcpu_usage = kcpu_usage;
r.cpu_usage_us = cpu_usage_us;
r.net_usage_words = net_usage_words;
r.status = status;
return r;
......@@ -567,14 +568,15 @@ struct controller_impl {
*/
transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx,
fc::time_point deadline,
bool implicit = false )
bool implicit,
uint32_t billed_cpu_time_us )
{
FC_ASSERT(deadline != fc::time_point(), "deadline cannot be uninitialized");
transaction_trace_ptr trace;
try {
auto start = fc::time_point::now();
transaction_context trx_context(self, trx->trx, trx->id);
trx_context.billed_cpu_time_us = billed_cpu_time_us;
trace = trx_context.trace;
try {
if (implicit) {
......@@ -594,15 +596,15 @@ struct controller_impl {
trx->recover_keys(),
{},
trx_context.delay,
std::bind(&transaction_context::add_cpu_usage_and_check_time, &trx_context,
std::placeholders::_1),
[](uint32_t){}
/*std::bind(&transaction_context::add_cpu_usage_and_check_time, &trx_context,
std::placeholders::_1)*/,
false
);
}
trx_context.exec();
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
auto restore = make_block_restore_point();
......@@ -610,12 +612,12 @@ struct controller_impl {
transaction_receipt::status_enum s = (trx_context.delay == fc::seconds(0))
? transaction_receipt::executed
: transaction_receipt::delayed;
trace->receipt = push_receipt(trx->packed_trx, s, trace->cpu_usage, trace->net_usage);
trace->receipt = push_receipt(trx->packed_trx, s, trx_context.billed_cpu_time_us, trace->net_usage);
pending->_pending_block_state->trxs.emplace_back(trx);
} else {
transaction_receipt_header r;
r.status = transaction_receipt::executed;
r.kcpu_usage = trace->cpu_usage / 1024;
r.cpu_usage_us = trx_context.billed_cpu_time_us;
r.net_usage_words = trace->net_usage / 8;
trace->receipt = r;
}
......@@ -682,7 +684,7 @@ struct controller_impl {
try {
auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
push_transaction( onbtrx, fc::time_point::maximum(), true );
push_transaction( onbtrx, fc::time_point::maximum(), true, config::default_min_transaction_cpu_usage_us);
} catch ( ... ) {
ilog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
}
......@@ -696,7 +698,12 @@ struct controller_impl {
void sign_block( const std::function<signature_type( const digest_type& )>& signer_callback ) {
auto p = pending->_pending_block_state;
try {
p->sign( signer_callback );
} catch ( ... ) {
edump(( fc::json::to_pretty_string( *p->block ) ) );
throw;
}
static_cast<signed_block_header&>(*p->block) = p->header;
} /// sign_block
......@@ -709,10 +716,10 @@ struct controller_impl {
if( receipt.trx.contains<packed_transaction>() ) {
auto& pt = receipt.trx.get<packed_transaction>();
auto mtrx = std::make_shared<transaction_metadata>(pt);
push_transaction( mtrx, fc::time_point::maximum() );
push_transaction( mtrx, fc::time_point::maximum(), false, receipt.cpu_usage_us );
}
else if( receipt.trx.contains<transaction_id_type>() ) {
self.push_scheduled_transaction( receipt.trx.get<transaction_id_type>(), fc::time_point::maximum() );
push_scheduled_transaction( receipt.trx.get<transaction_id_type>(), fc::time_point::maximum(), receipt.cpu_usage_us );
}
}
......@@ -734,6 +741,7 @@ struct controller_impl {
void push_block( const signed_block_ptr& b ) {
// idump((fc::json::to_pretty_string(*b)));
FC_ASSERT(!pending, "it is not valid to push a block when there is a pending block");
try {
FC_ASSERT( b );
......@@ -1055,14 +1063,11 @@ void controller::push_confirmation( const header_confirmation& c ) {
}
transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline ) {
return my->push_transaction(trx, deadline);
return my->push_transaction(trx, deadline, false, 0);
}
transaction_trace_ptr controller::push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline ) {
const auto& idx = db().get_index<generated_transaction_multi_index,by_trx_id>();
auto itr = idx.find( trxid );
FC_ASSERT( itr != idx.end(), "unknown transaction" );
return my->push_scheduled_transaction( *itr, deadline );
return my->push_scheduled_transaction( trxid, deadline, 0 );
}
uint32_t controller::head_block_num()const {
......
......@@ -22,7 +22,7 @@ namespace eosio { namespace chain {
transaction_receipt_header( status_enum s ):status(s){}
fc::enum_type<uint8_t,status_enum> status;
fc::unsigned_int kcpu_usage; ///< total billed CPU usage
uint32_t cpu_usage_us; ///< total billed CPU usage (microseconds)
fc::unsigned_int net_usage_words; ///< total billed NET usage, so we can reconstruct resource state when skipping context free data... hard failures...
};
......@@ -37,7 +37,7 @@ namespace eosio { namespace chain {
digest_type digest()const {
digest_type::encoder enc;
fc::raw::pack( enc, status );
fc::raw::pack( enc, kcpu_usage );
fc::raw::pack( enc, cpu_usage_us );
fc::raw::pack( enc, net_usage_words );
if( trx.contains<transaction_id_type>() )
fc::raw::pack( enc, trx.get<transaction_id_type>() );
......@@ -72,6 +72,6 @@ namespace eosio { namespace chain {
FC_REFLECT_ENUM( eosio::chain::transaction_receipt::status_enum,
(executed)(soft_fail)(hard_fail)(delayed)(expired) )
FC_REFLECT(eosio::chain::transaction_receipt_header, (status)(kcpu_usage)(net_usage_words) )
FC_REFLECT(eosio::chain::transaction_receipt_header, (status)(cpu_usage_us)(net_usage_words) )
FC_REFLECT_DERIVED(eosio::chain::transaction_receipt, (eosio::chain::transaction_receipt_header), (trx) )
FC_REFLECT_DERIVED(eosio::chain::signed_block, (eosio::chain::signed_block_header), (transactions)(block_extensions) )
......@@ -63,9 +63,10 @@ const static uint32_t default_context_free_discount_net_usage_num = 20; // TO
const static uint32_t default_context_free_discount_net_usage_den = 100;
const static uint32_t transaction_id_net_usage = 32; // 32 bytes for the size of a transaction id
const static uint32_t default_max_block_cpu_usage = 1024 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst
const static uint32_t default_target_block_cpu_usage_pct = 10 * percent_1; /// target 1000 TPS
const static uint32_t default_max_transaction_cpu_usage = default_max_block_cpu_usage / 10;
const static uint32_t default_max_block_cpu_usage = 100'000; /// max block cpu usage in microseconds
const static uint32_t default_target_block_cpu_usage_pct = 5 * percent_1; /// target 1000 TPS
const static uint32_t default_max_transaction_cpu_usage = default_max_block_cpu_usage;
const static uint32_t default_min_transaction_cpu_usage_us = 100; /// 10000 TPS equiv
const static uint32_t default_base_per_transaction_cpu_usage = 512; // TODO: is this reasonable?
const static uint32_t default_base_per_action_cpu_usage = 1024;
const static uint32_t default_base_setcode_cpu_usage = 2 * 1024 * 1024; /// overbilling cpu usage for setcode to cover incidental
......
......@@ -38,7 +38,6 @@ namespace eosio { namespace chain {
fc::optional<transaction_receipt_header> receipt;
fc::microseconds elapsed;
uint64_t net_usage = 0;
uint64_t cpu_usage = 0;
bool scheduled = false;
vector<action_trace> action_traces; ///< disposable
......@@ -49,7 +48,7 @@ namespace eosio { namespace chain {
struct block_trace {
fc::microseconds elapsed;
uint64_t cpu_usage;
uint64_t billed_cpu_usage_us;
vector<transaction_trace_ptr> trx_traces;
};
using block_trace_ptr = std::shared_ptr<block_trace>;
......@@ -62,6 +61,6 @@ FC_REFLECT( eosio::chain::base_action_trace,
FC_REFLECT_DERIVED( eosio::chain::action_trace,
(eosio::chain::base_action_trace), (inline_traces) )
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(net_usage)(cpu_usage)(scheduled)
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(net_usage)(scheduled)
(action_traces)(failed_dtrx_trace)(except) )
FC_REFLECT( eosio::chain::block_trace, (elapsed)(cpu_usage)(trx_traces) )
FC_REFLECT( eosio::chain::block_trace, (elapsed)(billed_cpu_usage_us)(trx_traces) )
......@@ -6,17 +6,18 @@ namespace eosio { namespace chain {
class transaction_context {
private:
void init( uint64_t initial_net_usage, uint64_t initial_cpu_usage );
void init( uint64_t initial_net_usage );
public:
transaction_context( controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id );
const transaction_id_type& trx_id,
fc::time_point start = fc::time_point::now() );
void init_for_implicit_trx( fc::time_point deadline,
uint64_t initial_net_usage = 0,
uint64_t initial_cpu_usage = 0 );
uint64_t initial_net_usage = 0
);
void init_for_input_trx( fc::time_point deadline,
uint64_t packed_trx_unprunable_size,
......@@ -31,15 +32,8 @@ namespace eosio { namespace chain {
void squash();
inline void add_net_usage( uint64_t u ) { net_usage += u; check_net_usage(); }
inline void add_cpu_usage( uint64_t u ) { cpu_usage += u; check_cpu_usage(); }
void add_cpu_usage_and_check_time( uint32_t u );
/// returns cpu usage amount that was actually added
uint64_t add_action_cpu_usage( uint64_t u, bool context_free );
uint64_t get_action_cpu_usage_limit( bool context_free )const;
void check_net_usage()const;
void check_cpu_usage()const;
void check_time()const;
void add_ram_usage( account_name account, int64_t ram_delta );
......@@ -64,6 +58,8 @@ namespace eosio { namespace chain {
transaction_id_type id;
chainbase::database::session undo_session;
transaction_trace_ptr trace;
fc::time_point start;
fc::time_point published;
fc::time_point deadline = fc::time_point::maximum();
......@@ -73,11 +69,8 @@ namespace eosio { namespace chain {
/// the maximum number of network usage bytes the transaction can consume (ignoring what billable accounts can pay and ignoring the remaining usage available in the block)
uint64_t max_net = 0;
uint64_t eager_net_limit = 0; ///< net usage limit (in bytes) to check against eagerly
uint64_t eager_net_limit = 0;
/// the maximum number of virtual CPU instructions the transaction may consume (ignoring what billable accounts can pay and ignoring the remaining usage available in the block)
uint64_t max_cpu = 0;
uint64_t eager_cpu_limit = 0; ///< cpu usage limit (in virtual CPU instructions) to check against eagerly
/// the maximum number of virtual CPU instructions of the transaction that can be safely billed to the billable accounts
uint64_t initial_max_billable_cpu = 0;
......@@ -85,10 +78,10 @@ namespace eosio { namespace chain {
bool is_input = false;
bool apply_context_free = true;
uint32_t billed_cpu_time_us = 0;
private:
uint64_t& net_usage; /// reference to trace->net_usage
uint64_t& cpu_usage; /// reference to trace->cpu_usage
bool net_limit_due_to_block = false;
bool cpu_limit_due_to_block = false;
bool is_initialized = false;
......
......@@ -11,21 +11,22 @@ namespace eosio { namespace chain {
transaction_context::transaction_context( controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id )
const transaction_id_type& trx_id,
fc::time_point s )
:control(c)
,trx(t)
,id(trx_id)
,undo_session(c.db().start_undo_session(true))
,trace(std::make_shared<transaction_trace>())
,start(s)
,net_usage(trace->net_usage)
,cpu_usage(trace->cpu_usage)
{
trace->id = id;
executed.reserve( trx.total_actions() );
FC_ASSERT( trx.transaction_extensions.size() == 0, "we don't support any extensions yet" );
}
void transaction_context::init(uint64_t initial_net_usage, uint64_t initial_cpu_usage )
void transaction_context::init(uint64_t initial_net_usage )
{
FC_ASSERT( !is_initialized, "cannot initialize twice" );
......@@ -44,20 +45,13 @@ namespace eosio { namespace chain {
// Start with limits set in dynamic configuration
const auto& cfg = control.get_global_properties().configuration;
max_net = cfg.max_transaction_net_usage;
max_cpu = cfg.max_transaction_cpu_usage;
// Potentially lower limits to what is optionally set in the transaction header
uint64_t trx_specified_net_usage_limit = static_cast<uint64_t>(trx.max_net_usage_words.value)*8;
if( trx_specified_net_usage_limit > 0 )
max_net = std::min( max_net, trx_specified_net_usage_limit );
uint64_t trx_specified_cpu_usage_limit = 1000 * uint64_t( trx.max_cpu_usage_ms );
if( trx_specified_cpu_usage_limit > 0 )
max_cpu = std::min( max_cpu, trx_specified_cpu_usage_limit );
eager_net_limit = max_net;
eager_cpu_limit = max_cpu;
//idump((eager_cpu_limit)(max_cpu));
// Update usage values of accounts to reflect new time
auto& rl = control.get_mutable_resource_limits_manager();
......@@ -66,82 +60,57 @@ namespace eosio { namespace chain {
uint64_t block_net_limit = rl.get_block_net_limit();
uint64_t block_cpu_limit = rl.get_block_cpu_limit();
if( !billed_cpu_time_us ) {
wdump((block_cpu_limit));
auto potential_deadline = fc::time_point::now() + fc::microseconds(block_cpu_limit);
if( potential_deadline < deadline ) deadline = potential_deadline;
}
if( block_net_limit < eager_net_limit ) {
eager_net_limit = block_net_limit;
net_limit_due_to_block = true;
}
if( block_cpu_limit < eager_cpu_limit ) {
eager_cpu_limit = block_cpu_limit;
//idump((eager_cpu_limit)(max_cpu));
cpu_limit_due_to_block = true;
}
// Initial billing for network usage
if( initial_net_usage > 0 )
add_net_usage( initial_net_usage );
// Initial billing for CPU usage known to be soon consumed
add_cpu_usage( initial_cpu_usage
+ static_cast<uint64_t>(cfg.base_per_transaction_cpu_usage)
+ determine_payers_cpu_cost
+ bill_to_accounts.size() * config::resource_processing_cpu_overhead_per_billed_account );
// Fails early if current CPU usage is already greater than the current limit (which may still go lower).
eager_net_limit = max_net;
eager_cpu_limit = max_cpu;
//idump((eager_cpu_limit)(max_cpu));
net_limit_due_to_block = false;
cpu_limit_due_to_block = false;
// Lower limits to what the billed accounts can afford to pay
for( const auto& a : bill_to_accounts ) {
auto net_limit = rl.get_account_net_limit(a);
if( net_limit >= 0 )
eager_net_limit = std::min( eager_net_limit, static_cast<uint64_t>(net_limit) ); // reduce max_net to the amount the account is able to pay
auto cpu_limit = rl.get_account_cpu_limit(a);
if( cpu_limit >= 0 ) {
eager_cpu_limit = std::min( eager_cpu_limit, static_cast<uint64_t>(cpu_limit) ); // reduce max_cpu to the amount the account is able to pay
}
}
initial_max_billable_cpu = eager_cpu_limit; // Possibly used for hard failure purposes
auto potential_deadline = fc::time_point::now() + fc::microseconds(block_cpu_limit);
if( potential_deadline < deadline ) deadline = potential_deadline;
}
eager_net_limit += cfg.net_usage_leeway;
eager_net_limit = std::min(eager_net_limit, max_net);
eager_cpu_limit += cfg.cpu_usage_leeway;
// idump((eager_cpu_limit)(max_cpu));
eager_cpu_limit = std::min(eager_cpu_limit, max_cpu);
// idump((eager_cpu_limit)(max_cpu));
if( block_net_limit < eager_net_limit ) {
eager_net_limit = block_net_limit;
net_limit_due_to_block = true;
}
if( block_cpu_limit < eager_cpu_limit ) {
eager_cpu_limit = block_cpu_limit;
cpu_limit_due_to_block = true;
}
// Round down network and CPU usage limits so that comparison to actual usage is more efficient
eager_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes)
eager_cpu_limit = (eager_cpu_limit/1024)*1024; // Round down to nearest multiple of 1024
//idump((eager_cpu_limit));
if( initial_net_usage > 0 )
check_net_usage(); // Fail early if current net usage is already greater than the calculated limit
check_cpu_usage(); // Fail early if current CPU usage is already greater than the calculated limit
//idump((eager_cpu_limit));
is_initialized = true;
}
void transaction_context::init_for_implicit_trx( fc::time_point d, uint64_t initial_net_usage, uint64_t initial_cpu_usage )
void transaction_context::init_for_implicit_trx( fc::time_point d, uint64_t initial_net_usage )
{
published = control.pending_block_time();
deadline = d;
init( initial_net_usage, initial_cpu_usage );
init( initial_net_usage );
}
void transaction_context::init_for_input_trx( fc::time_point d,
......@@ -163,7 +132,6 @@ namespace eosio { namespace chain {
uint64_t initial_net_usage = static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
+ packed_trx_unprunable_size + discounted_size_for_pruned_data;
uint64_t initial_cpu_usage = num_signatures * cfg.per_signature_cpu_usage;
if( trx.delay_sec.value > 0 ) {
// If delayed, also charge ahead of time for the additional net usage needed to retire the delayed transaction
......@@ -178,7 +146,7 @@ namespace eosio { namespace chain {
control.validate_expiration( trx );
control.validate_tapos( trx );
control.validate_referenced_accounts( trx );
init( initial_net_usage, initial_cpu_usage );
init( initial_net_usage );
record_transaction( id, trx.expiration ); /// checks for dupes
}
......@@ -189,7 +157,7 @@ namespace eosio { namespace chain {
deadline = d;
trace->scheduled = true;
apply_context_free = false;
init( 0, 0 );
init( 0 );
}
void transaction_context::exec() {
......@@ -224,37 +192,32 @@ namespace eosio { namespace chain {
}
}
add_cpu_usage( validate_ram_usage.size() * config::ram_usage_validation_overhead_per_account );
auto& rl = control.get_mutable_resource_limits_manager();
for( auto a : validate_ram_usage ) {
rl.verify_account_ram_usage( a );
}
eager_net_limit = max_net;
eager_cpu_limit = max_cpu;
net_limit_due_to_block = false;
cpu_limit_due_to_block = false;
// Lower limits to what the billed accounts can afford to pay
for( const auto& a : bill_to_accounts ) {
auto net_limit = rl.get_account_net_limit(a);
if( net_limit >= 0 )
eager_net_limit = std::min( eager_net_limit, static_cast<uint64_t>(net_limit) ); // reduce max_net to the amount the account is able to pay
auto cpu_limit = rl.get_account_cpu_limit(a);
if( cpu_limit >= 0 )
eager_cpu_limit = std::min( eager_cpu_limit, static_cast<uint64_t>(cpu_limit) ); // reduce max_cpu to the amount the account is able to pay
}
net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes)
cpu_usage = ((cpu_usage + 1023)/1024)*1024; // Round up to nearest multiple of 1024
check_net_usage();
check_cpu_usage();
rl.add_transaction_usage( bill_to_accounts, cpu_usage, net_usage,
trace->elapsed = fc::time_point::now() - start;
if( !billed_cpu_time_us )
billed_cpu_time_us = trace->elapsed.count();
rl.add_transaction_usage( bill_to_accounts, billed_cpu_time_us, net_usage,
block_timestamp_type(control.pending_block_time()).slot ); // Should never fail
}
......@@ -262,42 +225,6 @@ namespace eosio { namespace chain {
undo_session.squash();
}
void transaction_context::add_cpu_usage_and_check_time( uint32_t u ) {
add_cpu_usage( u );
check_time();
}
uint64_t transaction_context::add_action_cpu_usage( uint64_t u, bool context_free ) {
const auto& cfg = control.get_global_properties().configuration;
uint64_t discounted_cpu_usage = u;
if( context_free && cfg.context_free_discount_cpu_usage_den > 0
&& cfg.context_free_discount_cpu_usage_num < cfg.context_free_discount_cpu_usage_den )
{
discounted_cpu_usage *= cfg.context_free_discount_cpu_usage_num;
discounted_cpu_usage = ( discounted_cpu_usage + cfg.context_free_discount_cpu_usage_den - 1)
/ cfg.context_free_discount_cpu_usage_den; // rounds up
}
add_cpu_usage( discounted_cpu_usage );
return discounted_cpu_usage;
}
uint64_t transaction_context::get_action_cpu_usage_limit( bool context_free )const {
check_cpu_usage();
uint64_t diff = max_cpu - cpu_usage;
if( !context_free ) return diff;
const auto& cfg = control.get_global_properties().configuration;
uint64_t n = cfg.context_free_discount_cpu_usage_num;
uint64_t d = cfg.context_free_discount_cpu_usage_den;
if( d == 0 || n >= d ) return diff;
if( n == 0 ) return std::numeric_limits<uint64_t>::max();
return (diff * d)/n;
}
void transaction_context::check_net_usage()const {
if( BOOST_UNLIKELY(net_usage > eager_net_limit) ) {
......@@ -313,22 +240,10 @@ namespace eosio { namespace chain {
}
}
void transaction_context::check_cpu_usage()const {
if( BOOST_UNLIKELY(cpu_usage > eager_cpu_limit) ) {
if( BOOST_UNLIKELY( cpu_limit_due_to_block ) ) {
EOS_THROW( tx_soft_cpu_usage_exceeded,
"not enough CPU usage allotment left in block: ${actual_cpu_usage} > ${cpu_usage_limit}",
("actual_cpu_usage", cpu_usage)("cpu_usage_limit", max_cpu) );
} else {
EOS_THROW( tx_cpu_usage_exceeded,
"cpu usage of transaction is too high: ${actual_cpu_usage} > ${eager_cpu_limit}",
("actual_cpu_usage", cpu_usage)("cpu_usage_limit", max_cpu)("eager_cpu_limit", eager_cpu_limit) );
}
}
}
void transaction_context::check_time()const {
EOS_ASSERT( BOOST_LIKELY(fc::time_point::now() <= deadline), tx_deadline_exceeded, "deadline exceeded" );
EOS_ASSERT( BOOST_LIKELY(fc::time_point::now() <= deadline), tx_deadline_exceeded,
"deadline exceeded",
("now",fc::time_point::now())("deadline",deadline)("start",start) );
}
void transaction_context::add_ram_usage( account_name account, int64_t ram_delta ) {
......@@ -348,7 +263,6 @@ namespace eosio { namespace chain {
acontext.exec();
} catch( const action_cpu_usage_exceeded& e ) {
trace = move(acontext.trace);
add_action_cpu_usage( acontext.cpu_usage, context_free ); // Will update cpu_usage to latest value and throw appropriate exception
FC_ASSERT(false, "should not have reached here" );
} catch( ... ) {
trace = move(acontext.trace);
......
......@@ -655,8 +655,11 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
TESTER t;
t.produce_blocks(2);
ilog( "create account" );
t.create_account( N(testapi) );
ilog( "set code" );
t.set_code( N(testapi), test_api_wast );
ilog( "produce block" );
t.produce_blocks(1);
auto call_test = [](TESTER& test, auto ac) {
......@@ -665,6 +668,8 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
auto pl = vector<permission_level>{{N(testapi), config::active_name}};
action act(pl, ac);
ilog( "call test" );
trx.actions.push_back(act);
test.set_transaction_headers(trx);
auto sigs = trx.sign(test.get_private_key(N(testapi), "active"), chain_id_type());
......@@ -674,7 +679,8 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
test.produce_block();
};
BOOST_CHECK_EXCEPTION(call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{}), tx_cpu_usage_exceeded, is_tx_cpu_usage_exceeded /*tx_deadline_exceeded, is_tx_deadline_exceeded*/);
BOOST_CHECK_EXCEPTION(call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{}), tx_deadline_exceeded, is_tx_cpu_usage_exceeded /*tx_deadline_exceeded, is_tx_deadline_exceeded*/);
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
......
......@@ -261,7 +261,7 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
);
}
produce_blocks(10);
produce_blocks(10000);
for( auto pro : { N(p1), N(p2), N(p3), N(p4), N(p5) } ) {
base_tester::push_action(N(eosio), N(regproducer), pro, mutable_variant_object()
......@@ -270,7 +270,7 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
("url", "" )
);
}
produce_blocks(100);
produce_blocks(10);
auto votepro = [&]( account_name voter, vector<account_name> producers ) {
std::sort( producers.begin(), producers.end() );
......
......@@ -633,7 +633,7 @@ BOOST_FIXTURE_TEST_CASE(weighted_cpu_limit_tests, tester ) try {
BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trx.id()));
pass = true;
count++;
} catch (eosio::chain::tx_cpu_usage_exceeded &) {
} catch (eosio::chain::tx_deadline_exceeded &) {
BOOST_REQUIRE_EQUAL(count, 3);
break;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册