未验证 提交 1f2a28ea 编写于 作者: D Daniel Larimer 提交者: GitHub

Merge pull request #2947 from EOSIO/2898-subjective-billing-2

Progress on subjective CPU billing 
......@@ -348,12 +348,12 @@ bytes apply_context::get_packed_transaction() {
}
void apply_context::update_db_usage( const account_name& payer, int64_t delta ) {
if( (delta > 0) ) {
if( delta > 0 ) {
if( !(privileged || payer == account_name(receiver)) ) {
require_authorization( payer );
}
trx_context.add_ram_usage(payer, delta);
}
trx_context.add_ram_usage(payer, delta);
}
......
......@@ -395,10 +395,11 @@ struct controller_impl {
etrx.set_reference_block( self.head_block_id() );
transaction_context trx_context( self, etrx, etrx.id(), start );
trx_context.deadline = deadline;
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 );
trx_context.init_for_implicit_trx();
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 );
......@@ -433,8 +434,10 @@ struct controller_impl {
bool failure_is_subjective( const fc::exception& e ) {
auto code = e.code();
return (code == tx_soft_net_usage_exceeded::code_value) ||
(code == tx_deadline_exceeded::code_value);
return (code == block_net_usage_exceeded::code_value) ||
(code == block_cpu_usage_exceeded::code_value) ||
(code == deadline_exception::code_value) ||
(code == leeway_deadline_exception::code_value);
}
transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us ) {
......@@ -467,11 +470,12 @@ struct controller_impl {
fc::raw::unpack(ds,static_cast<transaction&>(dtrx) );
transaction_context trx_context( self, dtrx, gto.trx_id );
trx_context.deadline = deadline;
trx_context.billed_cpu_time_us = billed_cpu_time_us;
transaction_trace_ptr trace = trx_context.trace;
flat_set<account_name> bill_to_accounts;
try {
trx_context.init_for_deferred_trx( deadline, gto.published );
trx_context.init_for_deferred_trx( gto.published );
bill_to_accounts = trx_context.bill_to_accounts;
trx_context.exec();
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
......@@ -576,16 +580,16 @@ struct controller_impl {
transaction_trace_ptr trace;
try {
transaction_context trx_context(self, trx->trx, trx->id);
trx_context.deadline = deadline;
trx_context.billed_cpu_time_us = billed_cpu_time_us;
trace = trx_context.trace;
try {
if (implicit) {
trx_context.init_for_implicit_trx(deadline);
trx_context.init_for_implicit_trx();
} else {
trx_context.init_for_input_trx(deadline,
trx->packed_trx.get_unprunable_size(),
trx_context.init_for_input_trx( trx->packed_trx.get_unprunable_size(),
trx->packed_trx.get_prunable_size(),
trx->trx.signatures.size());
trx->trx.signatures.size() );
}
trx_context.delay = fc::seconds(trx->trx.delay_sec);
......@@ -1063,13 +1067,11 @@ void controller::push_confirmation( const header_confirmation& c ) {
}
transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline, uint32_t billed_cpu_time_us ) {
wdump((deadline)(billed_cpu_time_us));
return my->push_transaction(trx, deadline, false, billed_cpu_time_us);
}
transaction_trace_ptr controller::push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us )
{
wdump((deadline)(billed_cpu_time_us));
return my->push_scheduled_transaction( trxid, deadline, billed_cpu_time_us );
}
......
......@@ -203,16 +203,16 @@ namespace eosio { namespace chain {
3080001, "account using more than allotted RAM usage" )
FC_DECLARE_DERIVED_EXCEPTION( tx_net_usage_exceeded, resource_exhausted_exception,
3080002, "transaction exceeded the current network usage limit imposed on the transaction" )
FC_DECLARE_DERIVED_EXCEPTION( tx_soft_net_usage_exceeded, resource_exhausted_exception,
FC_DECLARE_DERIVED_EXCEPTION( block_net_usage_exceeded, resource_exhausted_exception,
3080003, "transaction network usage is too much for the remaining allowable usage of the current block" )
FC_DECLARE_DERIVED_EXCEPTION( tx_cpu_usage_exceeded, resource_exhausted_exception,
3080004, "transaction exceeded the current CPU usage limit imposed on the transaction" )
FC_DECLARE_DERIVED_EXCEPTION( tx_soft_cpu_usage_exceeded, resource_exhausted_exception,
FC_DECLARE_DERIVED_EXCEPTION( block_cpu_usage_exceeded, resource_exhausted_exception,
3080005, "transaction CPU usage is too much for the remaining allowable usage of the current block" )
FC_DECLARE_DERIVED_EXCEPTION( tx_deadline_exceeded, resource_exhausted_exception,
FC_DECLARE_DERIVED_EXCEPTION( deadline_exception, resource_exhausted_exception,
3080006, "transaction took too long" )
FC_DECLARE_DERIVED_EXCEPTION( action_cpu_usage_exceeded, resource_exhausted_exception,
3080007, "action took too long" )
FC_DECLARE_DERIVED_EXCEPTION( leeway_deadline_exception, deadline_exception,
3081001, "transaction reached the deadline set due to leeway on account CPU limits" )
FC_DECLARE_DERIVED_EXCEPTION( authorization_exception, chain_exception,
3090000, "Authorization exception" )
......
......@@ -15,17 +15,13 @@ namespace eosio { namespace chain {
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
);
void init_for_implicit_trx( uint64_t initial_net_usage = 0 );
void init_for_input_trx( fc::time_point deadline,
uint64_t packed_trx_unprunable_size,
void init_for_input_trx( uint64_t packed_trx_unprunable_size,
uint64_t packed_trx_prunable_size,
uint32_t num_signatures );
void init_for_deferred_trx( fc::time_point deadline,
fc::time_point published );
void init_for_deferred_trx( fc::time_point published );
void exec();
void finalize();
......@@ -50,6 +46,8 @@ namespace eosio { namespace chain {
void schedule_transaction();
void record_transaction( const transaction_id_type& id, fc::time_point_sec expire );
void validate_cpu_usage_to_bill( int64_t u, bool check_minimum = true )const;
/// Fields:
public:
......@@ -61,16 +59,12 @@ namespace eosio { namespace chain {
fc::time_point start;
fc::time_point published;
fc::time_point deadline = fc::time_point::maximum();
vector<action_receipt> executed;
flat_set<account_name> bill_to_accounts;
flat_set<account_name> validate_ram_usage;
/// 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;
/// 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;
......@@ -78,14 +72,22 @@ namespace eosio { namespace chain {
bool is_input = false;
bool apply_context_free = true;
uint32_t billed_cpu_time_us = 0;
fc::time_point deadline = fc::time_point::maximum();
fc::microseconds leeway = fc::microseconds(1000);
int64_t billed_cpu_time_us = 0;
private:
bool is_initialized = false;
uint64_t net_limit = 0;
bool net_limit_due_to_block = true;
uint64_t eager_net_limit = 0;
uint64_t& net_usage; /// reference to trace->net_usage
bool net_limit_due_to_block = false;
bool cpu_limit_due_to_block = false;
bool is_initialized = false;
fc::microseconds objective_duration_limit;
bool objective_duration_limit_due_to_block = true;
int64_t deadline_exception_code = block_cpu_usage_exceeded::code_value;
};
} }
......@@ -316,8 +316,12 @@ uint64_t resource_limits_manager::get_block_net_limit() const {
int64_t resource_limits_manager::get_account_cpu_limit( const account_name& name ) const {
const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);
const auto& limits = _db.get<resource_limits_object, by_owner>(boost::make_tuple(false, name));
if (limits.cpu_weight < 0) {
int64_t x;
int64_t cpu_weight;
get_account_limits( name, x, x, cpu_weight );
if( cpu_weight < 0 ) {
return -1;
}
......@@ -327,10 +331,9 @@ int64_t resource_limits_manager::get_account_cpu_limit( const account_name& name
uint128_t consumed_ex = (uint128_t)usage.cpu_usage.consumed * (uint128_t)config::rate_limiting_precision;
uint128_t virtual_capacity_ex = (uint128_t)state.virtual_cpu_limit * (uint128_t)config::rate_limiting_precision;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.cpu_weight) / (uint128_t)total_cpu_weight;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;
//idump((virtual_capacity_ex)(total_cpu_weight)(limits.cpu_weight)(usable_capacity_ex)(consumed_ex)(config::rate_limiting_precision) );
if (usable_capacity_ex < consumed_ex) {
if( usable_capacity_ex < consumed_ex ) {
return 0;
}
......@@ -341,8 +344,12 @@ account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const
const auto& cfg = _db.get<resource_limits_config_object>();
const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);
const auto& limits = _db.get<resource_limits_object, by_owner>(boost::make_tuple(false, name));
if (limits.cpu_weight < 0) {
int64_t x;
int64_t cpu_weight;
get_account_limits( name, x, x, cpu_weight );
if( cpu_weight < 0 ) {
return { -1, -1, -1 };
}
......@@ -352,15 +359,14 @@ account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const
uint128_t consumed_ex = (uint128_t)usage.cpu_usage.consumed * (uint128_t)config::rate_limiting_precision;
uint128_t virtual_capacity_ex = (uint128_t)state.virtual_cpu_limit * (uint128_t)config::rate_limiting_precision;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.cpu_weight) / (uint128_t)total_cpu_weight;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;
wdump((cfg.cpu_limit_parameters.target));
uint128_t real_capacity_ex = (uint128_t)cfg.cpu_limit_parameters.target * (uint128_t)config::rate_limiting_precision;
uint128_t guaranteed_capacity_ex = (uint128_t)(real_capacity_ex * limits.cpu_weight) / (uint128_t)total_cpu_weight;
uint128_t guaranteed_capacity_ex = (uint128_t)(real_capacity_ex * cpu_weight) / (uint128_t)total_cpu_weight;
uint128_t blocks_per_day = 86400 * 1000 / config::block_interval_ms;
if (usable_capacity_ex < consumed_ex) {
if( usable_capacity_ex < consumed_ex ) {
consumed_ex = usable_capacity_ex;
}
return { (int64_t)(std::min(usable_capacity_ex - consumed_ex, real_capacity_ex) / (uint128_t)config::rate_limiting_precision),
......@@ -372,8 +378,12 @@ account_resource_limit resource_limits_manager::get_account_cpu_limit_ex( const
int64_t resource_limits_manager::get_account_net_limit( const account_name& name ) const {
const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);
const auto& limits = _db.get<resource_limits_object, by_owner>(boost::make_tuple(false, name));
if (limits.net_weight < 0) {
int64_t x;
int64_t net_weight;
get_account_limits( name, x, net_weight, x );
if( net_weight < 0 ) {
return -1;
}
......@@ -383,9 +393,9 @@ int64_t resource_limits_manager::get_account_net_limit( const account_name& name
auto total_net_weight = state.total_net_weight;
if( total_net_weight == 0 ) total_net_weight = 1;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.net_weight) / (uint128_t)total_net_weight; // max
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * net_weight) / (uint128_t)total_net_weight; // max
if (usable_capacity_ex < consumed_ex) {
if( usable_capacity_ex < consumed_ex ) {
return 0;
}
......@@ -397,8 +407,12 @@ account_resource_limit resource_limits_manager::get_account_net_limit_ex( const
const auto& cfg = _db.get<resource_limits_config_object>();
const auto& state = _db.get<resource_limits_state_object>();
const auto& usage = _db.get<resource_usage_object, by_owner>(name);
const auto& limits = _db.get<resource_limits_object, by_owner>(boost::make_tuple(false, name));
if (limits.net_weight < 0) {
int64_t x;
int64_t net_weight;
get_account_limits( name, x, net_weight, x );
if( net_weight < 0 ) {
return { -1, -1, -1 };
}
......@@ -408,14 +422,14 @@ account_resource_limit resource_limits_manager::get_account_net_limit_ex( const
uint128_t consumed_ex = (uint128_t)usage.net_usage.consumed * (uint128_t)config::rate_limiting_precision;
uint128_t virtual_capacity_ex = (uint128_t)state.virtual_net_limit * (uint128_t)config::rate_limiting_precision;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.net_weight) / (uint128_t)total_net_weight; // max
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * net_weight) / (uint128_t)total_net_weight; // max
uint128_t real_capacity_ex = (uint128_t)cfg.net_limit_parameters.target * (uint128_t)config::rate_limiting_precision;
uint128_t guaranteed_capacity_ex = (uint128_t)(real_capacity_ex * limits.net_weight) / (uint128_t)total_net_weight;
uint128_t guaranteed_capacity_ex = (uint128_t)(real_capacity_ex * net_weight) / (uint128_t)total_net_weight;
uint128_t blocks_per_day = 86400 * 1000 / config::block_interval_ms;
if (usable_capacity_ex < consumed_ex) {
if( usable_capacity_ex < consumed_ex ) {
consumed_ex = usable_capacity_ex;
}
return { (int64_t)(std::min(usable_capacity_ex - consumed_ex, real_capacity_ex) / (uint128_t)config::rate_limiting_precision),
......
......@@ -29,99 +29,113 @@ namespace eosio { namespace chain {
void transaction_context::init(uint64_t initial_net_usage )
{
FC_ASSERT( !is_initialized, "cannot initialize twice" );
const static int64_t large_number_no_overflow = std::numeric_limits<int64_t>::max()/2;
// Record accounts to be billed for network and CPU usage
uint64_t determine_payers_cpu_cost = 0;
for( const auto& act : trx.actions ) {
for( const auto& auth : act.authorization ) {
bill_to_accounts.insert( auth.actor );
determine_payers_cpu_cost += config::determine_payers_cpu_overhead_per_authorization;
}
}
validate_ram_usage.reserve( bill_to_accounts.size() );
auto original_deadline = deadline;
// Calculate network and CPU usage limits and initial usage:
// Start with limits set in dynamic configuration
const auto& cfg = control.get_global_properties().configuration;
max_net = cfg.max_transaction_net_usage;
auto& rl = control.get_mutable_resource_limits_manager();
// 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 );
net_limit = rl.get_block_net_limit();
eager_net_limit = max_net;
objective_duration_limit = fc::microseconds( rl.get_block_cpu_limit() );
deadline = start + objective_duration_limit;
// Update usage values of accounts to reflect new time
auto& rl = control.get_mutable_resource_limits_manager();
rl.add_transaction_usage( bill_to_accounts, 0, 0, block_timestamp_type(control.pending_block_time()).slot );
// Check if deadline is limited by block deadline or caller-set deadline
if( original_deadline <= deadline ) {
deadline = original_deadline;
deadline_exception_code = deadline_exception::code_value;
}
uint64_t block_net_limit = rl.get_block_net_limit();
uint64_t block_cpu_limit = rl.get_block_cpu_limit();
// Possibly lower net_limit to the maximum net usage a transaction is allowed to be billed
if( cfg.max_transaction_net_usage <= net_limit ) {
net_limit = cfg.max_transaction_net_usage;
net_limit_due_to_block = false;
}
if( !billed_cpu_time_us ) {
idump((block_cpu_limit));
auto potential_deadline = start + fc::microseconds(block_cpu_limit);
if( potential_deadline < deadline ) deadline = potential_deadline;
// Possibly lower net_limit to optional limit 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 && trx_specified_net_usage_limit <= net_limit ) {
net_limit = trx_specified_net_usage_limit;
net_limit_due_to_block = false;
}
if( block_net_limit < eager_net_limit ) {
eager_net_limit = block_net_limit;
net_limit_due_to_block = true;
// Possibly lower objective_duration_limit to optional limit set in transaction header
if( trx.max_cpu_usage_ms > 0 ) {
auto trx_specified_cpu_usage_limit = fc::milliseconds(trx.max_cpu_usage_ms);
if( trx_specified_cpu_usage_limit <= objective_duration_limit ) {
objective_duration_limit = trx_specified_cpu_usage_limit;
objective_duration_limit_due_to_block = false;
}
}
// Initial billing for network usage
if( initial_net_usage > 0 )
add_net_usage( initial_net_usage );
// Possibly limit deadline if objective_duration_limit is not due to the block and does not exceed current delta
if( !objective_duration_limit_due_to_block && objective_duration_limit <= (deadline - start) ) {
deadline = start + objective_duration_limit;
deadline_exception_code = tx_cpu_usage_exceeded::code_value;
}
eager_net_limit = max_net;
net_limit_due_to_block = false;
if( billed_cpu_time_us > 0 )
validate_cpu_usage_to_bill( billed_cpu_time_us, false ); // Fail early if the amount to be billed is too high
// Lower limits to what the billed accounts can afford to pay
// 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 );
}
}
validate_ram_usage.reserve( bill_to_accounts.size() );
// Update usage values of accounts to reflect new time
rl.add_transaction_usage( bill_to_accounts, 0, 0, block_timestamp_type(control.pending_block_time()).slot );
if( !billed_cpu_time_us ) {
// Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed
int64_t account_net_limit = large_number_no_overflow;
int64_t account_cpu_limit = large_number_no_overflow;
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
account_net_limit = std::min( account_net_limit, net_limit );
auto cpu_limit = rl.get_account_cpu_limit(a);
if( cpu_limit > 0 ) {
auto potential_deadline = start + fc::microseconds(cpu_limit);
if( potential_deadline < deadline ) {
wdump((potential_deadline)(cpu_limit));
deadline = potential_deadline;
}
}
}
if( cpu_limit >= 0 )
account_cpu_limit = std::min( account_cpu_limit, cpu_limit );
}
eager_net_limit += cfg.net_usage_leeway;
eager_net_limit = std::min(eager_net_limit, max_net);
eager_net_limit = net_limit;
if( block_net_limit < eager_net_limit ) {
eager_net_limit = block_net_limit;
net_limit_due_to_block = true;
// Possible lower eager_net_limit to what the billed accounts can pay plus some (objective) leeway
auto new_eager_net_limit = std::min( eager_net_limit, static_cast<uint64_t>(account_net_limit + cfg.net_usage_leeway) );
if( new_eager_net_limit < eager_net_limit ) {
eager_net_limit = new_eager_net_limit;
net_limit_due_to_block = false;
}
// Possibly limit deadline if the duration accounts can be billed for (+ a subjective leeway) does not exceed current delta
if( ( fc::microseconds(account_cpu_limit) + leeway ) <= (deadline - start) ) {
deadline = start + fc::microseconds(account_cpu_limit) + leeway;
deadline_exception_code = leeway_deadline_exception::code_value;
}
// 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_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes) so check_net_usage can be efficient
if( initial_net_usage > 0 )
check_net_usage(); // Fail early if current net usage is already greater than the calculated limit
add_net_usage( initial_net_usage ); // Fail early if current net usage is already greater than the calculated limit
if( billed_cpu_time_us > 0 )
deadline = original_deadline; // Only change deadline if billed_cpu_time_us is not set
check_time(); // Fail early if deadline has already been exceeded
is_initialized = true;
}
void transaction_context::init_for_implicit_trx( fc::time_point d, uint64_t initial_net_usage )
void transaction_context::init_for_implicit_trx( uint64_t initial_net_usage )
{
published = control.pending_block_time();
deadline = d;
init( initial_net_usage );
}
void transaction_context::init_for_input_trx( fc::time_point d,
uint64_t packed_trx_unprunable_size,
void transaction_context::init_for_input_trx( uint64_t packed_trx_unprunable_size,
uint64_t packed_trx_prunable_size,
uint32_t num_signatures )
{
......@@ -148,7 +162,6 @@ namespace eosio { namespace chain {
}
published = control.pending_block_time();
deadline = d;
is_input = true;
control.validate_expiration( trx );
control.validate_tapos( trx );
......@@ -157,11 +170,9 @@ namespace eosio { namespace chain {
record_transaction( id, trx.expiration ); /// checks for dupes
}
void transaction_context::init_for_deferred_trx( fc::time_point d,
fc::time_point p )
void transaction_context::init_for_deferred_trx( fc::time_point p )
{
published = p;
deadline = d;
trace->scheduled = true;
apply_context_free = false;
init( 0 );
......@@ -189,6 +200,7 @@ namespace eosio { namespace chain {
void transaction_context::finalize() {
FC_ASSERT( is_initialized, "must first initialize" );
const static int64_t large_number_no_overflow = std::numeric_limits<int64_t>::max()/2;
if( is_input ) {
auto& am = control.get_mutable_authorization_manager();
......@@ -204,27 +216,43 @@ namespace eosio { namespace chain {
rl.verify_account_ram_usage( a );
}
eager_net_limit = max_net;
net_limit_due_to_block = false;
// Lower limits to what the billed accounts can afford to pay
// Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed
int64_t account_net_limit = large_number_no_overflow;
int64_t account_cpu_limit = large_number_no_overflow;
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
account_net_limit = std::min( account_net_limit, net_limit );
auto cpu_limit = rl.get_account_cpu_limit(a);
if( cpu_limit >= 0 )
account_cpu_limit = std::min( account_cpu_limit, cpu_limit );
}
// Possibly lower net_limit to what the billed accounts can pay
if( static_cast<uint64_t>(account_net_limit) <= net_limit ) {
net_limit = static_cast<uint64_t>(account_net_limit);
net_limit_due_to_block = false;
}
// Possibly lower objective_duration_limit to what the billed accounts can pay
if( account_cpu_limit <= objective_duration_limit.count() ) {
objective_duration_limit = fc::microseconds(account_cpu_limit);
objective_duration_limit_due_to_block = false;
}
net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes)
eager_net_limit = net_limit;
check_net_usage();
trace->elapsed = fc::time_point::now() - start;
if( !billed_cpu_time_us )
billed_cpu_time_us = trace->elapsed.count();
if( billed_cpu_time_us == 0 )
billed_cpu_time_us = std::max( trace->elapsed.count(), static_cast<int64_t>(config::default_min_transaction_cpu_usage_us) );
validate_cpu_usage_to_bill( billed_cpu_time_us );
rl.add_transaction_usage( bill_to_accounts, billed_cpu_time_us, net_usage,
rl.add_transaction_usage( bill_to_accounts, static_cast<uint64_t>(billed_cpu_time_us), net_usage,
block_timestamp_type(control.pending_block_time()).slot ); // Should never fail
}
......@@ -235,22 +263,60 @@ namespace eosio { namespace chain {
void transaction_context::check_net_usage()const {
if( BOOST_UNLIKELY(net_usage > eager_net_limit) ) {
if( BOOST_UNLIKELY( net_limit_due_to_block ) ) {
EOS_THROW( tx_soft_net_usage_exceeded,
"not enough space left in block: ${actual_net_usage} > ${net_usage_limit}",
("actual_net_usage", net_usage)("net_usage_limit", max_net) );
if( net_limit_due_to_block ) {
EOS_THROW( block_net_usage_exceeded,
"not enough space left in block: ${net_usage} > ${net_limit}",
("net_usage", net_usage)("net_limit", eager_net_limit) );
} else {
EOS_THROW( tx_net_usage_exceeded,
"net usage of transaction is too high: ${actual_net_usage} > ${net_usage_limit}",
("actual_net_usage", net_usage)("net_usage_limit", max_net) );
"net usage of transaction is too high: ${net_usage} > ${net_limit}",
("net_usage", net_usage)("net_limit", eager_net_limit) );
}
}
}
void transaction_context::check_time()const {
EOS_ASSERT( BOOST_LIKELY(fc::time_point::now() <= deadline), tx_deadline_exceeded,
"deadline exceeded",
("now",fc::time_point::now())("deadline",deadline)("start",start) );
auto now = fc::time_point::now();
if( BOOST_UNLIKELY( now > deadline ) ) {
if( billed_cpu_time_us > 0 || deadline_exception_code == deadline_exception::code_value ) {
EOS_THROW( deadline_exception, "deadline exceeded", ("now", now)("deadline", deadline)("start", start) );
} else if( deadline_exception_code == block_cpu_usage_exceeded::code_value ) {
EOS_THROW( block_cpu_usage_exceeded,
"not enough time left in block to complete executing transaction",
("now", now)("deadline", deadline)("start", start) );
} else if( deadline_exception_code == tx_cpu_usage_exceeded::code_value ) {
EOS_THROW( tx_cpu_usage_exceeded,
"transaction was executing for too long",
("now", now)("deadline", deadline)("start", start) );
} else if( deadline_exception_code == leeway_deadline_exception::code_value ) {
EOS_THROW( leeway_deadline_exception,
"the transaction was unable to complete by deadline, "
"but it is possible it could have succeeded if it were allow to run to completion",
("now", now)("deadline", deadline)("start", start) );
}
FC_ASSERT( false, "unexpected deadline exception code" );
}
}
void transaction_context::validate_cpu_usage_to_bill( int64_t billed_us, bool check_minimum )const {
#warning make min_transaction_cpu_us into a configuration parameter
EOS_ASSERT( !check_minimum || billed_us >= config::default_min_transaction_cpu_usage_us, transaction_exception,
"cannot bill CPU time less than the minimum of ${min_billable} us",
("min_billable", config::default_min_transaction_cpu_usage_us)("billed_cpu_time_us", billed_us)
);
if( objective_duration_limit_due_to_block ) {
EOS_ASSERT( billed_us <= objective_duration_limit.count(),
block_cpu_usage_exceeded,
"billed CPU time (${billed} us) is greater than the billable CPU time left in the block (${billable} us)",
("billed", billed_us)("billable", objective_duration_limit.count())
);
} else {
EOS_ASSERT( billed_us <= objective_duration_limit.count(),
tx_cpu_usage_exceeded,
"billed CPU time (${billed} us) is greater than the maximum billable CPU time for the transaction (${billable} us)",
("billed", billed_us)("billable", objective_duration_limit.count())
);
}
}
void transaction_context::add_ram_usage( account_name account, int64_t ram_delta ) {
......@@ -268,9 +334,6 @@ namespace eosio { namespace chain {
try {
acontext.exec();
} catch( const action_cpu_usage_exceeded& e ) {
trace = move(acontext.trace);
FC_ASSERT(false, "should not have reached here" );
} catch( ... ) {
trace = move(acontext.trace);
throw;
......
......@@ -87,8 +87,8 @@ namespace eosio { namespace testing {
void produce_blocks_until_end_of_round();
signed_block_ptr push_block(signed_block_ptr b);
transaction_trace_ptr push_transaction( packed_transaction& trx, uint32_t skip_flag = 0/*skip_nothing */, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US );
transaction_trace_ptr push_transaction( signed_transaction& trx, uint32_t skip_flag = 0/*skip_nothing*/, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US );
transaction_trace_ptr push_transaction( packed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US );
transaction_trace_ptr push_transaction( signed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(), uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US );
action_result push_action(action&& cert_act, uint64_t authorizer); // TODO/QUESTION: Is this needed?
transaction_trace_ptr push_action( const account_name& code,
......
......@@ -243,12 +243,10 @@ namespace eosio { namespace testing {
}
transaction_trace_ptr base_tester::push_transaction( packed_transaction& trx,
uint32_t skip_flag,
fc::time_point deadline,
uint32_t billed_cpu_time_us
)
{ try {
wlog((deadline));
if( !control->pending_block_state() )
_start_block(control->head_block_time() + fc::microseconds(config::block_interval_us));
auto r = control->push_transaction( std::make_shared<transaction_metadata>(trx), deadline, billed_cpu_time_us );
......@@ -258,12 +256,10 @@ namespace eosio { namespace testing {
} FC_CAPTURE_AND_RETHROW( (transaction_header(trx.get_transaction())) ) }
transaction_trace_ptr base_tester::push_transaction( signed_transaction& trx,
uint32_t skip_flag,
fc::time_point deadline,
uint32_t billed_cpu_time_us
)
{ try {
wlog((deadline));
if( !control->pending_block_state() )
_start_block(control->head_block_time() + fc::microseconds(config::block_interval_us));
auto c = packed_transaction::none;
......
......@@ -45,9 +45,10 @@ using namespace eosio::chain::plugin_interface;
namespace {
bool failure_is_subjective(const fc::exception& e, bool deadline_is_subjective) {
auto code = e.code();
return (code == tx_soft_cpu_usage_exceeded::code_value) ||
(code == tx_soft_net_usage_exceeded::code_value) ||
(code == tx_deadline_exceeded::code_value && deadline_is_subjective);
return (code == block_cpu_usage_exceeded::code_value) ||
(code == block_net_usage_exceeded::code_value) ||
(code == deadline_exception::code_value && deadline_is_subjective) ||
(code == leeway_deadline_exception::code_value && deadline_is_subjective);
}
}
......
......@@ -33,12 +33,10 @@ add_dependencies(unit_test asserter test_api test_api_mem test_api_db test_api_m
#To run unit_test with all log from blockchain displayed, put --verbose after --, i.e. unit_test -- --verbose
add_test(NAME unit_test_binaryen COMMAND unit_test
-t \!eosio_system_tests/*
-t \!resource_limits_test/enforce_block_limits_cpu
-t \!wasm_tests/weighted_cpu_limit_tests
--report_level=detailed --color_output -- --binaryen)
add_test(NAME unit_test_wavm COMMAND unit_test
-t \!eosio_system_tests/*
-t \!resource_limits_test/enforce_block_limits_cpu
-t \!wasm_tests/weighted_cpu_limit_tests
--report_level=detailed --color_output --catch_system_errors=no -- --wavm)
......
......@@ -25,6 +25,7 @@
#include <eosio/chain/block_summary_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <fc/crypto/digest.hpp>
#include <fc/crypto/sha256.hpp>
......@@ -207,8 +208,10 @@ bool is_page_memory_error(page_memory_error const &e) { return true; }
bool is_unsatisfied_authorization(unsatisfied_authorization const & e) { return true;}
bool is_wasm_execution_error(eosio::chain::wasm_execution_error const& e) {return true;}
bool is_tx_net_usage_exceeded(const tx_net_usage_exceeded& e) { return true; }
bool is_block_net_usage_exceeded(const tx_cpu_usage_exceeded& e) { return true; }
bool is_tx_cpu_usage_exceeded(const tx_cpu_usage_exceeded& e) { return true; }
bool is_tx_deadline_exceeded(const tx_deadline_exceeded& e) { return true; }
bool is_block_cpu_usage_exceeded(const tx_cpu_usage_exceeded& e) { return true; }
bool is_deadline_exception(const deadline_exception& e) { return true; }
/*
* register test suite `api_tests`
......@@ -647,8 +650,6 @@ BOOST_FIXTURE_TEST_CASE(checktime_pass_tests, TESTER) { try {
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
#if 0
BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
// TODO: This is an extremely fragile test. It needs improvements:
// 1) compilation of the smart contract should probably not count towards the CPU time of a transaction that first uses it;
......@@ -664,7 +665,11 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
ilog( "produce block" );
t.produce_blocks(1);
auto call_test = [](TESTER& test, auto ac) {
int64_t x; int64_t net; int64_t cpu;
t.control->get_resource_limits_manager().get_account_limits( N(testapi), x, net, cpu );
wdump((net)(cpu));
auto call_test = [](TESTER& test, auto ac, uint32_t billed_cpu_time_us, uint8_t max_cpu_usage_ms ) {
signed_transaction trx;
auto pl = vector<permission_level>{{N(testapi), config::active_name}};
......@@ -674,19 +679,29 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
trx.actions.push_back(act);
test.set_transaction_headers(trx);
trx.max_cpu_usage_ms = max_cpu_usage_ms;
auto sigs = trx.sign(test.get_private_key(N(testapi), "active"), chain_id_type());
trx.get_signature_keys(chain_id_type() );
auto res = test.push_transaction(trx);
auto res = test.push_transaction( trx, fc::time_point::now() + fc::milliseconds(200), billed_cpu_time_us );
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
test.produce_block();
};
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_CHECK_EXCEPTION( call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
5000, 0 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 50 ),
tx_cpu_usage_exceeded, is_tx_cpu_usage_exceeded );
BOOST_CHECK_EXCEPTION( call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 0 ),
block_cpu_usage_exceeded, is_block_cpu_usage_exceeded ); // Because the onblock uses up some of the CPU
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
#endif
/*************************************************************************************
* compiler_builtins_tests test case
......@@ -1017,7 +1032,6 @@ BOOST_FIXTURE_TEST_CASE(chain_tests, TESTER) { try {
vector<account_name> prods( control->active_producers().producers.size() );
for ( uint32_t i = 0; i < prods.size(); i++ ) {
prods[i] = control->active_producers().producers[i].producer_name;
produce_block();
}
CALL_TEST_FUNCTION( *this, "test_chain", "test_activeprods", fc::raw::pack(prods) );
......
......@@ -225,9 +225,7 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
// Issue the genesis supply of 1 billion EOS tokens to eosio.system
// Issue the genesis supply of 1 billion EOS tokens to eosio.system
ilog(".");
issue(N(eosio.token), config::system_account_name, config::system_account_name, initial_supply);
ilog(".");
auto actual = get_balance(config::system_account_name);
......@@ -247,9 +245,7 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
("memo", "" ) );
*/
}
ilog("set code....");
set_code_abi(N(eosio), eosio_system_wast, eosio_system_abi); //, &eosio_active_pk);
produce_blocks(1);
for( const auto& a : test_genesis ) {
auto ib = a.initial_balance;
......@@ -264,7 +260,6 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
);
BOOST_REQUIRE( !r->except_ptr );
wdump((a.aname)(net)(cpu));
r = base_tester::push_action(N(eosio), N(delegatebw), N(eosio), mutable_variant_object()
("from", "eosio" )
("receiver", name(a.aname))
......@@ -273,7 +268,6 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
("transfer", 1)
);
produce_blocks(1);
BOOST_REQUIRE( !r->except_ptr );
}
......
......@@ -206,7 +206,7 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
process_account_limit_updates();
const uint64_t increment = 1000;
const uint64_t expected_iterations = (config::default_max_block_cpu_usage + increment - 1 ) / increment;
const uint64_t expected_iterations = config::default_max_block_cpu_usage / increment;
for (int idx = 0; idx < expected_iterations; idx++) {
add_transaction_usage({account}, increment, 0, 0);
......@@ -223,9 +223,9 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
process_account_limit_updates();
const uint64_t increment = 1000;
const uint64_t expected_iterations = (config::default_max_block_net_usage + increment - 1 ) / increment;
const uint64_t expected_iterations = config::default_max_block_net_usage / increment;
for (int idx = 0; idx < expected_iterations - 1; idx++) {
for (int idx = 0; idx < expected_iterations; idx++) {
add_transaction_usage({account}, 0, increment, 0);
}
......
......@@ -616,7 +616,7 @@ BOOST_FIXTURE_TEST_CASE(weighted_cpu_limit_tests, tester ) try {
while (count < 4) {
signed_transaction trx;
for (int i = 0; i < 1000; ++i) {
for (int i = 0; i < 10; ++i) {
action act;
act.account = N(f_tests);
act.name = N() + (i * 16);
......@@ -628,12 +628,12 @@ BOOST_FIXTURE_TEST_CASE(weighted_cpu_limit_tests, tester ) try {
trx.sign(get_private_key( N(f_tests), "active" ), chain_id_type());
try {
push_transaction(trx);
push_transaction(trx, fc::time_point::maximum(), 0);
produce_blocks(1);
BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trx.id()));
pass = true;
count++;
} catch (eosio::chain::tx_deadline_exceeded &) {
} catch( eosio::chain::leeway_deadline_exception& ) {
BOOST_REQUIRE_EQUAL(count, 3);
break;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册