未验证 提交 8a15c1ab 编写于 作者: M Matt Witherspoon 提交者: GitHub

Merge branch 'slim' into yet_more_builtins

......@@ -57,8 +57,9 @@ tests/chain_bench
tests/chain_test
tests/intense_test
tests/performance_test
tests/tests/config.hpp
tests/config.hpp
unittests/config.hpp
doxygen
wallet.json
......
......@@ -71,11 +71,69 @@ namespace eosiosystem {
EOSLIB_SERIALIZE( refund_request, (owner)(request_time)(amount) )
};
/**
* These tables are designed to be constructed in the scope of the relevant user, this
* facilitates simpler API for per-user queries
*/
typedef eosio::multi_index< N(userres), user_resources> user_resources_table;
typedef eosio::multi_index< N(delband), delegated_bandwidth> del_bandwidth_table;
typedef eosio::multi_index< N(refunds), refund_request> refunds_table;
/**
* Called after a new account is created. This code enforces resource-limits rules
* for new accounts as well as new account naming conventions.
*
* 1. accounts cannot contain '.' symbols which forces all acccounts to be 12
* characters long without '.' until a future account auction process is implemented
* which prevents name squatting.
*
* 2. new accounts must stake a minimal number of tokens (as set in system parameters)
* therefore, this method will execute an inline buyram from receiver for newacnt in
* an amount equal to the current new account creation fee.
*/
void native::newaccount( account_name creator,
account_name newact
/* no need to parse authorites
const authority& owner,
const authority& active,
const authority& recovery*/ ) {
eosio::print( eosio::name{creator}, " created ", eosio::name{newact}, "\n");
user_resources_table userres( _self, newact);
auto r = userres.emplace( newact, [&]( auto& res ) {
res.owner = newact;
});
set_resource_limits( newact,
0,// r->storage_bytes,
0, 0 );
// r->net_weight.amount,
// r->cpu_weight.amount );
}
/**
* This action will buy an exact amount of ram and bill the payer the current market price.
*/
void system_contract::buyrambytes( account_name payer, account_name receiver, uint32_t bytes ) {
const double system_token_supply = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name()).amount;
const double unstaked_token_supply = system_token_supply - _gstate.total_storage_stake.amount;
const double R = unstaked_token_supply;
const double C = _gstate.free_ram() + bytes;
const double F = _gstate.storage_reserve_ratio / 10000.0;
const double T = bytes;
const double ONE(1.0);
double E = -R * (ONE - std::pow( ONE + T/C, F ) );
int64_t tokens_out = int64_t(E*1.0105);
print( "desired ram: ", bytes, "\n" );
buyram( payer, receiver, asset(tokens_out) );
}
/**
* When buying ram the payer irreversiblly transfers quant to system contract and only
......@@ -87,34 +145,35 @@ namespace eosiosystem {
*/
void system_contract::buyram( account_name payer, account_name receiver, asset quant )
{
print( "\n payer: ", eosio::name{payer}, " buys ram for ", eosio::name{receiver}, " with ", quant, "\n" );
require_auth( payer );
eosio_assert( quant.amount > 0, "must purchase a positive amount" );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)},
{ payer, N(eosio), quant, std::string("buy ram") } );
global_state_singleton gstate_table( _self, _self );
auto gstate = gstate_table.exists() ? gstate_table.get() : get_default_parameters();
if( payer != N(eosio) ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)},
{ payer, N(eosio), quant, std::string("buy ram") } );
}
const double system_token_supply = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name()).amount;
const double unstaked_token_supply = system_token_supply - gstate.total_storage_stake.amount;
const double unstaked_token_supply = system_token_supply - _gstate.total_storage_stake.amount;
print( "free ram: ", _gstate.free_ram(), " tokens: ", system_token_supply, " unstaked: ", unstaked_token_supply, "\n" );
const double E = quant.amount;
const double R = unstaked_token_supply - E;
const double C = gstate.free_ram(); //free_ram;
const double F = .10; /// 10% reserve ratio pricing, assumes only 10% of tokens will ever want to stake for ram
const double C = _gstate.free_ram(); //free_ram;
const double F = 1./(_gstate.storage_reserve_ratio/10000.0); /// 10% reserve ratio pricing, assumes only 10% of tokens will ever want to stake for ram
const double ONE(1.0);
double T = C * (std::pow( ONE + E/R, F ) - ONE);
T *= .99; /// 1% fee on every conversion
int64_t bytes_out = static_cast<int64_t>(T);
print( "ram bytes out: ", bytes_out, "\n" );
eosio_assert( bytes_out > 0, "must reserve a positive amount" );
gstate.total_storage_bytes_reserved += uint64_t(bytes_out);
gstate.total_storage_stake.amount += quant.amount;
gstate_table.set( gstate, _self );
_gstate.total_storage_bytes_reserved += uint64_t(bytes_out);
_gstate.total_storage_stake.amount += quant.amount;
user_resources_table userres( _self, receiver );
auto res_itr = userres.find( receiver );
......@@ -128,7 +187,7 @@ namespace eosiosystem {
res.storage_bytes += uint64_t(bytes_out);
});
}
set_resource_limits( res_itr->owner, res_itr->storage_bytes, uint64_t(res_itr->net_weight.amount), uint64_t(res_itr->cpu_weight.amount) );
set_resource_limits( res_itr->owner, res_itr->storage_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount );
}
......@@ -137,21 +196,18 @@ namespace eosiosystem {
* refunds the purchase price to the account. In this way there is no profit to be made through buying
* and selling ram.
*/
void system_contract::sellram( account_name account, uint64_t bytes ) {
void system_contract::sellram( account_name account, uint32_t bytes ) {
user_resources_table userres( _self, account );
auto res_itr = userres.find( account );
eosio_assert( res_itr != userres.end(), "no resource row" );
eosio_assert( res_itr->storage_bytes >= bytes, "insufficient quota" );
global_state_singleton gstate_table( _self, _self );
auto gstate = gstate_table.exists() ? gstate_table.get() : get_default_parameters();
const double system_token_supply = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name()).amount;
const double unstaked_token_supply = system_token_supply - gstate.total_storage_stake.amount;
const double unstaked_token_supply = system_token_supply - _gstate.total_storage_stake.amount;
const double R = unstaked_token_supply;
const double C = gstate.free_ram() + bytes;
const double F = .10;
const double C = _gstate.free_ram() + bytes;
const double F = _gstate.storage_reserve_ratio / 10000.0;
const double T = bytes;
const double ONE(1.0);
......@@ -163,18 +219,18 @@ namespace eosiosystem {
int64_t tokens_out = int64_t(E);
eosio_assert( tokens_out > 0, "must free at least one token" );
gstate.total_storage_bytes_reserved -= bytes;
gstate.total_storage_stake.amount -= tokens_out;
gstate_table.set( gstate, _self );
_gstate.total_storage_bytes_reserved -= bytes;
_gstate.total_storage_stake.amount -= tokens_out;
userres.modify( res_itr, account, [&]( auto& res ) {
res.storage_bytes -= bytes;
});
set_resource_limits( res_itr->owner, res_itr->storage_bytes, uint64_t(res_itr->net_weight.amount), uint64_t(res_itr->cpu_weight.amount) );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio),N(active)},
{ N(eosio), account, asset(tokens_out), std::string("sell ram") } );
if( N(eosio) != account ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio),N(active)},
{ N(eosio), account, asset(tokens_out), std::string("sell ram") } );
}
}
void system_contract::delegatebw( account_name from, account_name receiver,
......@@ -183,13 +239,15 @@ namespace eosiosystem {
{
require_auth( from );
print( "from: ", eosio::name{from}, " to: ", eosio::name{receiver}, " net: ", stake_net_quantity, " cpu: ", stake_cpu_quantity );
eosio_assert( stake_cpu_quantity.amount >= 0, "must stake a positive amount" );
eosio_assert( stake_net_quantity.amount >= 0, "must stake a positive amount" );
eosio_assert( stake_cpu_quantity >= asset(0), "must stake a positive amount" );
eosio_assert( stake_net_quantity >= asset(0), "must stake a positive amount" );
asset total_stake = stake_cpu_quantity + stake_net_quantity;
eosio_assert( total_stake.amount > 0, "must stake a positive amount" );
auto total_stake = stake_cpu_quantity.amount + stake_net_quantity.amount;
eosio_assert( total_stake > 0, "must stake a positive amount" );
print( "deltable" );
del_bandwidth_table del_tbl( _self, from );
auto itr = del_tbl.find( receiver );
if( itr == del_tbl.end() ) {
......@@ -207,6 +265,7 @@ namespace eosiosystem {
});
}
print( "totals" );
user_resources_table totals_tbl( _self, receiver );
auto tot_itr = totals_tbl.find( receiver );
if( tot_itr == totals_tbl.end() ) {
......@@ -224,35 +283,55 @@ namespace eosiosystem {
set_resource_limits( tot_itr->owner, tot_itr->storage_bytes, uint64_t(tot_itr->net_weight.amount), uint64_t(tot_itr->cpu_weight.amount) );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {from,N(active)},
{ from, N(eosio), total_stake, std::string("stake bandwidth") } );
if( N(eosio) != from) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {from,N(active)},
{ from, N(eosio), asset(total_stake), std::string("stake bandwidth") } );
}
print( "voters" );
auto from_voter = _voters.find(from);
if( from_voter == _voters.end() ) {
print( " create voter" );
from_voter = _voters.emplace( from, [&]( auto& v ) {
v.owner = from;
v.staked = uint64_t(total_stake);
});
} else {
_voters.modify( from_voter, 0, [&]( auto& v ) {
v.staked += uint64_t(total_stake);
});
}
adjust_voting_power( from, (stake_net_quantity.amount + stake_cpu_quantity.amount) );
print( "voteproducer" );
voteproducer( from, from_voter->proxy, from_voter->producers );
} // delegatebw
void system_contract::undelegatebw( account_name from, account_name receiver,
asset unstake_net_quantity, asset unstake_cpu_quantity )
{
eosio_assert( unstake_cpu_quantity.amount >= 0, "must unstake a positive amount" );
eosio_assert( unstake_net_quantity.amount >= 0, "must unstake a positive amount" );
eosio_assert( unstake_cpu_quantity >= asset(), "must unstake a positive amount" );
eosio_assert( unstake_net_quantity >= asset(), "must unstake a positive amount" );
require_auth( from );
//eosio_assert( is_account( receiver ), "can only delegate resources to an existing account" );
del_bandwidth_table del_tbl( _self, from );
const auto& dbw = del_tbl.get( receiver );
eosio_assert( dbw.net_weight >= unstake_net_quantity, "insufficient staked net bandwidth" );
eosio_assert( dbw.cpu_weight >= unstake_cpu_quantity, "insufficient staked cpu bandwidth" );
eosio_assert( dbw.net_weight.amount >= unstake_net_quantity.amount, "insufficient staked net bandwidth" );
eosio_assert( dbw.cpu_weight.amount >= unstake_cpu_quantity.amount, "insufficient staked cpu bandwidth" );
auto total_refund = unstake_cpu_quantity.amount + unstake_net_quantity.amount;
_voters.modify( _voters.get(from), 0, [&]( auto& v ) {
v.staked -= uint64_t(total_refund);
});
eosio::asset total_refund = unstake_cpu_quantity + unstake_net_quantity;
eosio_assert( total_refund.amount > 0, "must unstake a positive amount" );
eosio_assert( total_refund > 0, "must unstake a positive amount" );
del_tbl.modify( dbw, from, [&]( auto& dbo ){
dbo.net_weight -= unstake_net_quantity;
dbo.cpu_weight -= unstake_cpu_quantity;
});
});
user_resources_table totals_tbl( _self, receiver );
......@@ -260,9 +339,9 @@ namespace eosiosystem {
totals_tbl.modify( totals, 0, [&]( auto& tot ) {
tot.net_weight -= unstake_net_quantity;
tot.cpu_weight -= unstake_cpu_quantity;
});
});
set_resource_limits( totals.owner, totals.storage_bytes, uint64_t(totals.net_weight.amount), uint64_t(totals.cpu_weight.amount) );
set_resource_limits( receiver, totals.storage_bytes, totals.net_weight.amount, totals.cpu_weight.amount );
refunds_table refunds_tbl( _self, from );
//create refund request
......@@ -279,6 +358,7 @@ namespace eosiosystem {
r.request_time = now();
});
}
//create or replace deferred transaction
//refund act;
//act.owner = from;
......@@ -287,8 +367,8 @@ namespace eosiosystem {
out.delay_sec = refund_delay;
out.send( from, receiver );
adjust_voting_power( from, -(unstake_net_quantity.amount + unstake_cpu_quantity.amount) );
const auto& fromv = _voters.get( from );
voteproducer( from, fromv.proxy, fromv.producers );
} // undelegatebw
......@@ -309,20 +389,16 @@ namespace eosiosystem {
refunds_tbl.erase( req );
}
void system_contract::setparams( uint64_t max_storage_size, uint32_t storage_reserve_ratio ) {
require_auth( _self );
eosio_assert( storage_reserve_ratio > 0, "invalid reserve ratio" );
require_auth( _self );
global_state_singleton gs( _self, _self );
auto parameters = gs.exists() ? gs.get() : get_default_parameters();
eosio_assert( storage_reserve_ratio > 0, "invalid reserve ratio" );
eosio_assert( max_storage_size > parameters.total_storage_bytes_reserved, "attempt to set max below reserved" );
eosio_assert( max_storage_size > _gstate.total_storage_bytes_reserved, "attempt to set max below reserved" );
parameters.max_storage_size = max_storage_size;
parameters.storage_reserve_ratio = storage_reserve_ratio;
gs.set( parameters, _self );
_gstate.max_storage_size = max_storage_size;
_gstate.storage_reserve_ratio = storage_reserve_ratio;
_global.set( _gstate, _self );
}
} //namespace eosiosystem
......@@ -7,34 +7,27 @@
{"name":"value", "type":"string"}
]
},{
"name": "transfer",
"name": "buyrambytes",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"to", "type":"account_name"},
{"name":"quantity", "type":"asset"},
{"name":"memo", "type":"string"}
{"name":"payer", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"bytes", "type":"uint32"}
]
},{
"name": "issue",
"base": "",
"fields": [
{"name":"to", "type":"account_name"},
{"name":"quantity", "type":"asset"}
]
},{
"name": "account",
"name": "sellram",
"base": "",
"fields": [
{"name":"currency", "type":"uint64"},
{"name":"balance", "type":"uint64"}
{"name":"account", "type":"account_name"},
{"name":"bytes", "type":"uint32"}
]
},{
"name": "currency_stats",
"name": "buyram",
"base": "",
"fields": [
{"name":"currency", "type":"uint64"},
{"name":"supply", "type":"uint64"}
{"name":"payer", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"quant", "type":"asset"}
]
},{
"name": "delegatebw",
......@@ -43,8 +36,7 @@
{"name":"from", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"stake_net_quantity", "type":"asset"},
{"name":"stake_cpu_quantity", "type":"asset"},
{"name":"stake_storage_quantity", "type":"asset"}
{"name":"stake_cpu_quantity", "type":"asset"}
]
},{
"name": "undelegatebw",
......@@ -53,8 +45,7 @@
{"name":"from", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"unstake_net_quantity", "type":"asset"},
{"name":"unstake_cpu_quantity", "type":"asset"},
{"name":"unstake_storage_bytes", "type":"uint64"}
{"name":"unstake_cpu_quantity", "type":"asset"}
]
},{
"name": "refund",
......@@ -70,7 +61,6 @@
{"name":"to", "type":"account_name"},
{"name":"net_weight", "type":"uint64"},
{"name":"cpu_weight", "type":"uint64"},
{"name":"storage_stake", "type":"uint64"},
{"name":"storage_bytes", "type":"uint64"}
]
},{
......@@ -80,7 +70,6 @@
{"name":"owner", "type":"account_name"},
{"name":"net_weight", "type":"asset"},
{"name":"cpu_weight", "type":"asset"},
{"name":"storage_stake", "type":"asset"},
{"name":"storage_bytes", "type":"uint64"}
]
},{
......@@ -95,12 +84,13 @@
"name": "blockchain_parameters",
"base": "",
"fields": [
{"name":"max_block_net_usage", "type": "uint64"},
{"name":"max_block_net_usage", "type": "uint32"},
{"name":"target_block_net_usage_pct", "type": "uint32"},
{"name":"max_transaction_net_usage", "type":"uint32"},
{"name":"base_per_transaction_net_usage", "type":"uint32"},
{"name":"context_free_discount_net_usage_num", "type":"uint64"},
{"name":"context_free_discount_net_usage_den", "type":"uint64"},
{"name":"net_usage_leeway", "type":"uint32"},
{"name":"context_free_discount_net_usage_num", "type":"uint32"},
{"name":"context_free_discount_net_usage_den", "type":"uint32"},
{"name":"max_block_cpu_usage", "type": "uint64"},
{"name":"target_block_cpu_usage_pct", "type": "uint32"},
{"name":"max_transaction_cpu_usage", "type":"uint32"},
......@@ -108,8 +98,9 @@
{"name":"base_per_action_cpu_usage", "type":"uint32"},
{"name":"base_setcode_cpu_usage", "type":"uint32"},
{"name":"per_signature_cpu_usage", "type":"uint32"},
{"name":"context_free_discount_cpu_usage_num", "type":"uint64"},
{"name":"context_free_discount_cpu_usage_den", "type":"uint64"},
{"name":"cpu_usage_leeway", "type":"uint32"},
{"name":"context_free_discount_cpu_usage_num", "type":"uint32"},
{"name":"context_free_discount_cpu_usage_den", "type":"uint32"},
{"name":"max_transaction_lifetime", "type":"uint32"},
{"name":"deferred_trx_expiration_window", "type":"uint32"},
{"name":"max_transaction_delay", "type":"uint32"},
......@@ -169,13 +160,8 @@
"name": "regproxy",
"base": "",
"fields": [
{"name":"proxy", "type":"account_name"}
]
},{
"name": "unregproxy",
"base": "",
"fields": [
{"name":"proxy", "type":"account_name"}
{"name":"proxy", "type":"account_name"},
{"name":"isproxy", "type":"bool"}
]
},{
"name": "voteproducer",
......@@ -189,17 +175,16 @@
"name": "voter_info",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"proxy", "type":"account_name"},
{"name":"last_update", "type":"time"},
{"name":"is_proxy", "type":"uint32"},
{"name":"staked", "type":"asset"},
{"name":"unstaking", "type":"asset"},
{"name":"unstake_per_week", "type":"asset"},
{"name":"proxied_votes", "type":"uint128"},
{"name":"producers", "type":"account_name[]"},
{"name":"deferred_trx_id", "type":"uint32"},
{"name":"last_unstake", "type":"uint32"}
{"name":"owner", "type":"account_name"},
{"name":"proxy", "type":"account_name"},
{"name":"producers", "type":"account_name[]"},
{"name":"staked", "type":"uint64"},
{"name":"last_vote_weight", "type":"float64"},
{"name":"proxied_vote_weight", "type":"float64"},
{"name":"is_proxy", "type":"bool"},
{"name":"deferred_trx_id", "type":"uint32"},
{"name":"last_unstake_time", "type":"time"},
{"name":"unstaking", "type":"asset"}
]
},{
"name": "claimrewards",
......@@ -209,13 +194,18 @@
]
}
],
"actions": [{
"name": "transfer",
"type": "transfer",
"actions": [
{
"name": "buyrambytes",
"type": "buyrambytes",
"ricardian_contract": ""
},{
"name": "issue",
"type": "issue",
"name": "buyram",
"type": "buyram",
"ricardian_contract": ""
},{
"name": "sellram",
"type": "sellram",
"ricardian_contract": ""
},{
"name": "delegatebw",
......@@ -245,10 +235,6 @@
"name": "regproxy",
"type": "regproxy",
"ricardian_contract": ""
},{
"name": "unregproxy",
"type": "unregproxy",
"ricardian_contract": ""
},{
"name": "voteproducer",
"type": "voteproducer",
......
......@@ -5,20 +5,48 @@
#include "producer_pay.cpp"
#include "voting.cpp"
namespace eosiosystem {
system_contract::system_contract( account_name s )
:native(s),
_voters(_self,_self),
_producers(_self,_self),
_global(_self,_self)
{
print( "construct system\n" );
_gstate = _global.exists() ? _global.get() : get_default_parameters();
}
eosio_global_state system_contract::get_default_parameters() {
eosio_global_state dp;
get_blockchain_parameters(dp);
return dp;
}
system_contract::~system_contract() {
print( "destruct system\n" );
_global.set( _gstate, _self );
eosio_exit(0);
}
} /// eosio.system
EOSIO_ABI( eosiosystem::system_contract,
(setparams)
// delegate_bandwith.cpp
(delegatebw)(undelegatebw)(refund)
(buyram)(sellram)
(regproxy)
// voting.cpp
(unregproxy)(regproducer)(unregprod)(voteproducer)
// producer_pay.cpp
(claimrewards)
// native.hpp
//XXX
(onblock)
(newaccount)(updateauth)(deleteauth)(linkauth)(unlinkauth)(postrecovery)(passrecovery)(vetorecovery)(onerror)(canceldelay)
// defined in eosio.system.hpp
(nonce)
(setparams)
// delegate_bandwith.cpp
(delegatebw)(undelegatebw)(refund)
(buyram)(buyrambytes)(sellram)
// voting.cpp
(regproxy)(regproducer)(unregprod)(voteproducer)
// producer_pay.cpp
(claimrewards)
// native.hpp
//XXX
(onblock)
(newaccount)(updateauth)(deleteauth)(linkauth)(unlinkauth)(postrecovery)(passrecovery)(vetorecovery)(onerror)(canceldelay)
// defined in eosio.system.hpp
(nonce)
)
......@@ -18,9 +18,9 @@ namespace eosiosystem {
using eosio::const_mem_fun;
struct eosio_parameters : eosio::blockchain_parameters {
uint64_t max_storage_size = 1024 * 1024 * 1024;
uint64_t max_storage_size = 64ll*1024 * 1024 * 1024;
uint32_t percent_of_max_inflation_rate = 0;
uint32_t storage_reserve_ratio = 2000; // ratio * 1000
uint32_t storage_reserve_ratio = 100; // ratio * 10000
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE_DERIVED( eosio_parameters, eosio::blockchain_parameters, (max_storage_size)(percent_of_max_inflation_rate)(storage_reserve_ratio) )
......@@ -65,7 +65,41 @@ namespace eosiosystem {
(time_became_active)(last_produced_block_time) )
};
typedef eosio::multi_index< N(producerinfo), producer_info,
struct voter_info {
account_name owner = 0; /// the voter
account_name proxy = 0; /// the proxy set by the voter, if any
std::vector<account_name> producers; /// the producers approved by this voter if no proxy set
uint64_t staked = 0;
/**
* Every time a vote is cast we must first "undo" the last vote weight, before casting the
* new vote weight. Vote weight is calculated as:
*
* stated.amount * 2 ^ ( weeks_since_launch/weeks_per_year)
*/
double last_vote_weight = 0; /// the vote weight cast the last time the vote was updated
/**
* Total vote weight delegated to this voter.
*/
double proxied_vote_weight= 0; /// the total vote weight delegated to this voter as a proxy
bool is_proxy = 0; /// whether the voter is a proxy for others
uint32_t deferred_trx_id = 0; /// the ID of the 3-day delay deferred transaction
time last_unstake_time = 0; /// the time when the deferred_trx_id was sent
eosio::asset unstaking; /// the total unstaking (pending 3 day delay)
uint64_t primary_key()const { return owner; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(producers)(staked)(last_vote_weight)(proxied_vote_weight)(is_proxy)(deferred_trx_id)(last_unstake_time)(unstaking) )
};
typedef eosio::multi_index< N(voters), voter_info> voters_table;
typedef eosio::multi_index< N(producers), producer_info,
indexed_by<N(prototalvote), const_mem_fun<producer_info, double, &producer_info::by_votes> >
> producers_table;
......@@ -76,11 +110,19 @@ namespace eosiosystem {
static constexpr uint64_t system_token_symbol = S(4,EOS);
class system_contract : public native {
private:
voters_table _voters;
producers_table _producers;
global_state_singleton _global;
eosio_global_state _gstate;
public:
using native::native;
system_contract( account_name s );
[[noreturn]] ~system_contract();
// Actions:
void onblock( const block_id_type&, uint32_t timestamp_slot, account_name producer );
void onblock( uint32_t timestamp_slot, account_name producer );
// const block_header& header ); /// only parse first 3 fields of block header
// functions defined in delegate_bandwidth.cpp
......@@ -114,12 +156,13 @@ namespace eosiosystem {
* tokens will be executed.
*/
void buyram( account_name buyer, account_name receiver, asset tokens );
void buyrambytes( account_name buyer, account_name receiver, uint32_t bytes );
/**
* Reduces quota my bytes and then performs an inline transfer of tokens
* to receiver based upon the average purchase price of the original quota.
*/
void sellram( account_name receiver, uint64_t bytes );
void sellram( account_name receiver, uint32_t bytes );
/**
* This action is called after the delegation-period to claim all pending
......@@ -135,12 +178,9 @@ namespace eosiosystem {
void setparams( uint64_t max_storage_size, uint32_t storage_reserve_ratio );
void voteproducer( const account_name voter, const account_name proxy, const std::vector<account_name>& producers );
void regproxy( const account_name proxy );
void unregproxy( const account_name proxy );
void regproxy( const account_name proxy, bool isproxy );
void nonce( const std::string& /*value*/ ) {}
......
......@@ -46,17 +46,18 @@ namespace eosiosystem {
};
struct block_header {
block_id_type previous;
uint32_t timestamp;
account_name producer;
uint32_t schedule_version = 0;
uint16_t confirmed = 0;
block_id_type previous;
checksum256 transaction_mroot;
checksum256 action_mroot;
uint32_t schedule_version = 0;
eosio::optional<eosio::producer_schedule> new_producers;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(block_header, (previous)(timestamp)(producer)(schedule_version)(transaction_mroot)(action_mroot)
(new_producers))
EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot)
(schedule_version)(new_producers))
};
......@@ -85,10 +86,7 @@ namespace eosiosystem {
/* no need to parse authorites
const authority& owner,
const authority& active,
const authority& recovery*/ ) {
eosio::print( eosio::name{creator}, " created ", eosio::name{newact});
set_resource_limits( newact, 3000, 0, 0 );
}
const authority& recovery*/ );
void updateauth( /*account_name account,
......
......@@ -7,7 +7,7 @@ namespace eosiosystem {
static const uint32_t num_of_payed_producers = 121;
void system_contract::onblock( const block_id_type&, block_timestamp timestamp, account_name producer ) {
void system_contract::onblock( block_timestamp timestamp, account_name producer ) {
global_state_singleton gs( _self, _self );
auto parameters = gs.exists() ? gs.get() : get_default_parameters();
......
此差异已折叠。
此差异已折叠。
......@@ -64,7 +64,8 @@ void token::transfer( account_name from,
asset quantity,
string /*memo*/ )
{
print( "transfer" );
print( "transfer from ", eosio::name{from}, " to ", eosio::name{to}, " ", quantity, "\n" );
eosio_assert( from != to, "cannot transfer to self" );
require_auth( from );
eosio_assert( is_account( to ), "to account does not exist");
auto sym = quantity.symbol.name();
......@@ -77,6 +78,9 @@ void token::transfer( account_name from,
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must transfer positive quantity" );
if ( quantity.symbol.precision() != st.supply.symbol.precision() )
quantity.adjust_precision( st.supply.symbol );
sub_balance( from, quantity, st );
add_balance( to, quantity, st, from );
}
......@@ -99,6 +103,7 @@ void token::sub_balance( account_name owner, asset value, const currency_stats&
from_acnts.modify( from, owner, [&]( auto& a ) {
a.balance -= value;
print( eosio::name{owner}, " balance: ", a.balance, "\n" );
});
}
......@@ -110,11 +115,13 @@ void token::add_balance( account_name owner, asset value, const currency_stats&
eosio_assert( !st.enforce_whitelist, "can only transfer to white listed accounts" );
to_acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
print( eosio::name{owner}, " balance: ", a.balance, "\n" );
});
} else {
eosio_assert( !st.enforce_whitelist || to->whitelist, "receiver requires whitelist by issuer" );
to_acnts.modify( to, 0, [&]( auto& a ) {
a.balance += value;
print( eosio::name{owner}, " balance: ", a.balance, "\n" );
});
}
}
......
......@@ -251,6 +251,18 @@ namespace eosio {
symbol.print(false);
}
void adjust_precision(const symbol_type& ref_sym) {
eosio_assert(this->symbol.name() == ref_sym.name(), "comparison of symbols with different names is not allowed");
if (this->symbol.precision() == ref_sym.precision())
return;
eosio_assert(ref_sym.precision() >= this->symbol.precision(), "asset symbol has higher precision than expected");
for (uint8_t i = 0; i < ref_sym.precision() - this->symbol.precision(); ++i) {
printi(amount);
this->amount *= 10;
}
this->symbol.value = ref_sym.value;
}
EOSLIB_SERIALIZE( asset, (amount)(symbol) )
};
......
......@@ -18,7 +18,7 @@ extern "C" {
* @{
*/
void set_resource_limits( account_name account, uint64_t ram_bytes, uint64_t net_weight, uint64_t cpu_weight );
void set_resource_limits( account_name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight );
bool set_active_producers( char *producer_data, uint32_t producer_data_size );
......
......@@ -10,18 +10,20 @@ namespace eosio {
uint32_t target_block_net_usage_pct;
uint32_t max_transaction_net_usage;
uint32_t base_per_transaction_net_usage;
uint64_t context_free_discount_net_usage_num;
uint64_t context_free_discount_net_usage_den;
uint32_t net_usage_leeway;
uint32_t context_free_discount_net_usage_num;
uint32_t context_free_discount_net_usage_den;
uint64_t max_block_cpu_usage;
uint32_t max_block_cpu_usage;
uint32_t target_block_cpu_usage_pct;
uint32_t max_transaction_cpu_usage;
uint32_t base_per_transaction_cpu_usage;
uint32_t base_per_action_cpu_usage;
uint32_t base_setcode_cpu_usage;
uint32_t per_signature_cpu_usage;
uint64_t context_free_discount_cpu_usage_num;
uint64_t context_free_discount_cpu_usage_den;
uint32_t cpu_usage_leeway;
uint32_t context_free_discount_cpu_usage_num;
uint32_t context_free_discount_cpu_usage_den;
uint32_t max_transaction_lifetime;
uint32_t deferred_trx_expiration_window;
......@@ -33,12 +35,12 @@ namespace eosio {
EOSLIB_SERIALIZE( blockchain_parameters,
(max_block_net_usage)(target_block_net_usage_pct)
(max_transaction_net_usage)(base_per_transaction_net_usage)
(max_transaction_net_usage)(base_per_transaction_net_usage)(net_usage_leeway)
(context_free_discount_net_usage_num)(context_free_discount_net_usage_den)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_transaction_cpu_usage)(base_per_transaction_cpu_usage)
(base_per_action_cpu_usage)(base_setcode_cpu_usage)(per_signature_cpu_usage)
(base_per_action_cpu_usage)(base_setcode_cpu_usage)(per_signature_cpu_usage)(cpu_usage_leeway)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_lifetime)(deferred_trx_expiration_window)(max_transaction_delay)
......
......@@ -91,15 +91,14 @@ extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
if ( code == N(eosio)) {
if (action == N(onerror)) {
apply_onerror(receiver, deferred_transaction::from_current_action());
}
} else if ( code == N(eosio.token) ) {
if ( code == N(eosio.token) ) {
if( action == N(transfer) ) {
apply_transfer(receiver, code, unpack_action_data<eosio::token::transfer_args>());
}
} else if (code == receiver ) {
if (action == N(onerror)) {
apply_onerror(receiver, deferred_transaction::from_current_action());
}
if ( action == N(setowner)) {
apply_setowner(receiver, unpack_action_data<set_owner>());
}
......
......@@ -41,7 +41,8 @@ extern "C" {
}
WASM_TEST_HANDLER(test_action, assert_true_cf);
require_auth(code);
if (action != WASM_TEST_ACTION("test_transaction", "stateful_api") && action != WASM_TEST_ACTION("test_transaction", "context_free_api"))
require_auth(code);
//test_types
WASM_TEST_HANDLER(test_types, types_size);
......@@ -139,6 +140,8 @@ extern "C" {
WASM_TEST_HANDLER(test_transaction, cancel_deferred_transaction);
WASM_TEST_HANDLER(test_transaction, send_cf_action);
WASM_TEST_HANDLER(test_transaction, send_cf_action_fail);
WASM_TEST_HANDLER(test_transaction, stateful_api);
WASM_TEST_HANDLER(test_transaction, context_free_api);
//test chain
WASM_TEST_HANDLER(test_chain, test_activeprods);
......
......@@ -170,6 +170,8 @@ struct test_transaction {
static void cancel_deferred_transaction();
static void send_cf_action();
static void send_cf_action_fail();
static void stateful_api();
static void context_free_api();
};
struct test_chain {
......
......@@ -287,3 +287,13 @@ void test_transaction::send_cf_action_fail() {
act.send_context_free();
eosio_assert(false, "send_cfa_action_fail() should've thrown an error");
}
void test_transaction::stateful_api() {
char buf[4] = {1};
db_store_i64(N(test_transaction), N(table), N(test_transaction), 0, buf, 4);
}
void test_transaction::context_free_api() {
char buf[128] = {0};
get_context_free_data(0, buf, sizeof(buf));
}
......@@ -81,6 +81,7 @@ namespace eosio { namespace chain {
built_in_types.emplace("uint128", pack_unpack<boost::multiprecision::uint128_t>());
built_in_types.emplace("uint256", pack_unpack<boost::multiprecision::uint256_t>());
built_in_types.emplace("varuint32", pack_unpack<fc::unsigned_int>());
built_in_types.emplace("bool", pack_unpack<uint8_t>());
built_in_types.emplace("int8", pack_unpack<int8_t>());
built_in_types.emplace("int16", pack_unpack<int16_t>());
built_in_types.emplace("int32", pack_unpack<int32_t>());
......
......@@ -36,8 +36,14 @@ action_trace apply_context::exec_one()
{
auto start = fc::time_point::now();
cpu_usage = 0;
checktime( control.get_global_properties().configuration.base_per_action_cpu_usage );
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 {
if( act.account == config::system_account_name && act.name == N(setcode) && receiver == config::system_account_name ) {
checktime( cfg.base_setcode_cpu_usage );
}
const auto &a = control.get_account(receiver);
privileged = a.privileged;
auto native = control.find_apply_handler(receiver, act.account, act.name);
......@@ -45,7 +51,7 @@ action_trace apply_context::exec_one()
(*native)(*this);
}
if (a.code.size() > 0 && !(act.name == N(setcode) && act.account == config::system_account_name)) {
if( a.code.size() > 0 && !(act.account == config::system_account_name && act.name == N(setcode)) ) {
try {
control.get_wasm_interface().apply(a.code_version, a.code, *this);
} catch ( const wasm_exit& ){}
......@@ -63,6 +69,8 @@ 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;
......@@ -70,8 +78,7 @@ action_trace apply_context::exec_one()
t.total_cpu_usage = cpu_usage;
t.console = _pending_console_output.str();
executed.emplace_back( move(r) );
total_cpu_usage += cpu_usage;
trx_context.executed.emplace_back( move(r) );
print_debug(receiver, t);
......@@ -97,22 +104,13 @@ void apply_context::exec()
}
for( const auto& inline_action : _cfa_inline_actions ) {
apply_context ncontext( control, trx_context, inline_action, recurse_depth + 1 );
ncontext.context_free = true;
ncontext.exec();
fc::move_append( executed, move(ncontext.executed) );
total_cpu_usage += ncontext.total_cpu_usage;
trace.total_cpu_usage += ncontext.trace.total_cpu_usage;
trace.inline_traces.emplace_back(ncontext.trace);
trace.inline_traces.emplace_back( trx_context.dispatch_action( 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 ) {
apply_context ncontext( control, trx_context, inline_action, recurse_depth + 1 );
ncontext.exec();
fc::move_append( executed, move(ncontext.executed) );
total_cpu_usage += ncontext.total_cpu_usage;
trace.total_cpu_usage += ncontext.trace.total_cpu_usage;
trace.inline_traces.emplace_back(ncontext.trace);
trace.inline_traces.emplace_back( trx_context.dispatch_action( inline_action, inline_action.account, false, recurse_depth + 1 ) );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
} /// exec()
......@@ -144,7 +142,7 @@ void apply_context::require_authorization( const account_name& account ) {
return;
}
}
EOS_ASSERT( false, tx_missing_auth, "missing authority of ${account}", ("account",account));
EOS_ASSERT( false, missing_auth_exception, "missing authority of ${account}", ("account",account));
}
bool apply_context::has_authorization( const account_name& account )const {
......@@ -163,7 +161,7 @@ void apply_context::require_authorization(const account_name& account,
return;
}
}
EOS_ASSERT( false, tx_missing_auth, "missing authority of ${account}/${permission}",
EOS_ASSERT( false, missing_auth_exception, "missing authority of ${account}/${permission}",
("account",account)("permission",permission) );
}
......@@ -352,7 +350,8 @@ void apply_context::reset_console() {
void apply_context::checktime(uint32_t instruction_count) {
cpu_usage += instruction_count;
trx_context.add_cpu_usage_and_check_time( instruction_count );
EOS_ASSERT( BOOST_LIKELY(cpu_usage <= cpu_usage_limit), action_cpu_usage_exceeded, "action cpu usage exceeded" );
trx_context.check_time();
}
bytes apply_context::get_packed_transaction() {
......
......@@ -6,7 +6,6 @@
#include <boost/rational.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <fc/reflect/variant.hpp>
#include <eosio/chain/exceptions.hpp>
namespace eosio { namespace chain {
typedef boost::multiprecision::int128_t int128_t;
......@@ -36,8 +35,6 @@ string asset::to_string()const {
asset asset::from_string(const string& from)
{
try {
asset result;
string s = fc::trim(from);
// Find space in order to split amount and symbol
......@@ -59,11 +56,12 @@ asset asset::from_string(const string& from)
} else {
precision_digit_str = "0";
}
string symbol_part = precision_digit_str + ',' + symbol_str;
result.sym = symbol::from_string(symbol_part);
symbol sym = symbol::from_string(symbol_part);
// Parse amount
int64_t int_part, fract_part = 0;
safe<int64_t> int_part, fract_part;
if (dot_pos != string::npos) {
int_part = fc::to_int64(amount_str.substr(0, dot_pos));
fract_part = fc::to_int64(amount_str.substr(dot_pos + 1));
......@@ -71,11 +69,12 @@ asset asset::from_string(const string& from)
} else {
int_part = fc::to_int64(amount_str);
}
result.amount = int_part;
result.amount *= int64_t(result.precision());
result.amount += fract_part;
return result;
safe<int64_t> amount = int_part;
amount *= safe<int64_t>(sym.precision());
amount += fract_part;
return asset(amount.value, sym);
}
FC_CAPTURE_LOG_AND_RETHROW( (from) )
}
......
......@@ -137,10 +137,10 @@ namespace eosio { namespace chain {
const vector<permission_level>& auths
)const
{
EOS_ASSERT( auths.size() == 1, tx_irrelevant_auth,
EOS_ASSERT( auths.size() == 1, irrelevant_auth_exception,
"updateauth action should only have one declared authorization" );
const auto& auth = auths[0];
EOS_ASSERT( auth.actor == update.account, tx_irrelevant_auth,
EOS_ASSERT( auth.actor == update.account, irrelevant_auth_exception,
"the owner of the affected permission needs to be the actor of the declared authorization" );
const auto* min_permission = find_permission({update.account, update.permission});
......@@ -153,7 +153,7 @@ namespace eosio { namespace chain {
const auto delay = get_permission(auth).satisfies( *min_permission,
_db.get_index<permission_index>().indices() );
EOS_ASSERT( delay.valid(),
tx_irrelevant_auth,
irrelevant_auth_exception,
"updateauth action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", auth)("min", permission_level{update.account, min_permission->name}) );
......@@ -164,17 +164,17 @@ namespace eosio { namespace chain {
const vector<permission_level>& auths
)const
{
EOS_ASSERT( auths.size() == 1, tx_irrelevant_auth,
EOS_ASSERT( auths.size() == 1, irrelevant_auth_exception,
"deleteauth action should only have one declared authorization" );
const auto& auth = auths[0];
EOS_ASSERT( auth.actor == del.account, tx_irrelevant_auth,
EOS_ASSERT( auth.actor == del.account, irrelevant_auth_exception,
"the owner of the permission to delete needs to be the actor of the declared authorization" );
const auto& min_permission = get_permission({del.account, del.permission});
const auto delay = get_permission(auth).satisfies( min_permission,
_db.get_index<permission_index>().indices() );
EOS_ASSERT( delay.valid(),
tx_irrelevant_auth,
irrelevant_auth_exception,
"updateauth action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", auth)("min", permission_level{min_permission.owner, min_permission.name}) );
......@@ -185,10 +185,10 @@ namespace eosio { namespace chain {
const vector<permission_level>& auths
)const
{
EOS_ASSERT( auths.size() == 1, tx_irrelevant_auth,
EOS_ASSERT( auths.size() == 1, irrelevant_auth_exception,
"link action should only have one declared authorization" );
const auto& auth = auths[0];
EOS_ASSERT( auth.actor == link.account, tx_irrelevant_auth,
EOS_ASSERT( auth.actor == link.account, irrelevant_auth_exception,
"the owner of the linked permission needs to be the actor of the declared authorization" );
EOS_ASSERT( link.type != updateauth::get_name(), action_validate_exception,
......@@ -211,7 +211,7 @@ namespace eosio { namespace chain {
_db.get_index<permission_index>().indices() );
EOS_ASSERT( delay.valid(),
tx_irrelevant_auth,
irrelevant_auth_exception,
"link action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", auth)("min", permission_level{link.account, *linked_permission_name}) );
......@@ -222,10 +222,10 @@ namespace eosio { namespace chain {
const vector<permission_level>& auths
)const
{
EOS_ASSERT( auths.size() == 1, tx_irrelevant_auth,
EOS_ASSERT( auths.size() == 1, irrelevant_auth_exception,
"unlink action should only have one declared authorization" );
const auto& auth = auths[0];
EOS_ASSERT( auth.actor == unlink.account, tx_irrelevant_auth,
EOS_ASSERT( auth.actor == unlink.account, irrelevant_auth_exception,
"the owner of the linked permission needs to be the actor of the declared authorization" );
const auto unlinked_permission_name = lookup_linked_permission(unlink.account, unlink.code, unlink.type);
......@@ -240,7 +240,7 @@ namespace eosio { namespace chain {
_db.get_index<permission_index>().indices() );
EOS_ASSERT( delay.valid(),
tx_irrelevant_auth,
irrelevant_auth_exception,
"unlink action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", auth)("min", permission_level{unlink.account, *unlinked_permission_name}) );
......@@ -251,14 +251,14 @@ namespace eosio { namespace chain {
const vector<permission_level>& auths
)const
{
EOS_ASSERT( auths.size() == 1, tx_irrelevant_auth,
EOS_ASSERT( auths.size() == 1, irrelevant_auth_exception,
"canceldelay action should only have one declared authorization" );
const auto& auth = auths[0];
const auto delay = get_permission(auth).satisfies( get_permission(cancel.canceling_auth),
_db.get_index<permission_index>().indices() );
EOS_ASSERT( delay.valid(),
tx_irrelevant_auth,
irrelevant_auth_exception,
"canceldelay action declares irrelevant authority '${auth}'; specified authority to satisfy is ${min}",
("auth", auth)("min", cancel.canceling_auth) );
}
......@@ -372,7 +372,7 @@ namespace eosio { namespace chain {
auto delay = get_permission(declared_auth).satisfies( min_permission,
_db.get_index<permission_index>().indices() );
EOS_ASSERT( delay.valid(),
tx_irrelevant_auth,
irrelevant_auth_exception,
"action declares irrelevant authority '${auth}'; minimum authority is ${min}",
("auth", declared_auth)("min", permission_level{min_permission.owner, min_permission.name}) );
max_delay = std::max( max_delay, *delay );
......
......@@ -4,6 +4,7 @@
namespace eosio { namespace chain {
/*
uint32_t block_header_state::calc_dpos_last_irreversible()const {
if( producer_to_last_produced.size() == 0 )
return 0;
......@@ -18,11 +19,13 @@ namespace eosio { namespace chain {
return irb[offset];
}
*/
bool block_header_state::is_active_producer( account_name n )const {
return producer_to_last_produced.find(n) != producer_to_last_produced.end();
}
/*
block_timestamp_type block_header_state::get_slot_time( uint32_t slot_num )const {
auto t = header.timestamp;
FC_ASSERT( std::numeric_limits<decltype(t.slot)>::max() - t.slot >= slot_num, "block timestamp overflow" );
......@@ -40,6 +43,7 @@ namespace eosio { namespace chain {
producer_key block_header_state::get_scheduled_producer( uint32_t slot_num )const {
return get_scheduled_producer( get_slot_time(slot_num) );
}
*/
producer_key block_header_state::get_scheduled_producer( block_timestamp_type t )const {
auto index = t.slot % (active_schedule.producers.size() * config::producer_repetitions);
......@@ -47,10 +51,12 @@ namespace eosio { namespace chain {
return active_schedule.producers[index];
}
/*
uint32_t block_header_state::producer_participation_rate()const
{
return static_cast<uint32_t>(config::percent_100); // Ignore participation rate for now until we construct a better metric.
}
*/
/**
......@@ -79,20 +85,18 @@ namespace eosio { namespace chain {
result.block_num = block_num + 1;
result.producer_to_last_produced = producer_to_last_produced;
result.producer_to_last_produced[prokey.producer_name] = result.block_num;
result.dpos_last_irreversible_blocknum = result.calc_dpos_last_irreversible();
result.bft_irreversible_blocknum =
std::max(bft_irreversible_blocknum,result.dpos_last_irreversible_blocknum);
result.blockroot_merkle = blockroot_merkle;
result.blockroot_merkle.append( id );
auto block_mroot = result.blockroot_merkle.get_root();
result.active_schedule = active_schedule;
result.pending_schedule = pending_schedule;
result.active_schedule = active_schedule;
result.pending_schedule = pending_schedule;
result.dpos_irreversible_blocknum = dpos_irreversible_blocknum;
result.bft_irreversible_blocknum = bft_irreversible_blocknum;
if( result.pending_schedule.producers.size() &&
result.dpos_last_irreversible_blocknum >= pending_schedule_lib_num ) {
result.dpos_irreversible_blocknum >= pending_schedule_lib_num ) {
result.active_schedule = move( result.pending_schedule );
flat_map<account_name,uint32_t> new_producer_to_last_produced;
......@@ -101,13 +105,29 @@ namespace eosio { namespace chain {
if( existing != producer_to_last_produced.end() ) {
new_producer_to_last_produced[pro.producer_name] = existing->second;
} else {
new_producer_to_last_produced[pro.producer_name] = result.dpos_last_irreversible_blocknum;
new_producer_to_last_produced[pro.producer_name] = result.dpos_irreversible_blocknum;
}
}
result.producer_to_last_produced = move( new_producer_to_last_produced );
result.producer_to_last_produced[prokey.producer_name] = result.block_num;
}
/// grow the confirmed count
static_assert(std::numeric_limits<uint8_t>::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations");
auto num_active_producers = result.active_schedule.producers.size();
uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1;
if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) {
result.confirm_count.reserve( confirm_count.size() + 1 );
result.confirm_count = confirm_count;
result.confirm_count.resize( confirm_count.size() + 1 );
result.confirm_count.back() = (uint8_t)required_confs;
} else {
result.confirm_count.resize( confirm_count.size() );
memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 );
result.confirm_count.back() = (uint8_t)required_confs;
}
return result;
} /// generate_next
......@@ -140,6 +160,12 @@ namespace eosio { namespace chain {
FC_ASSERT( result.header.producer == h.producer, "wrong producer specified" );
FC_ASSERT( result.header.schedule_version == h.schedule_version, "schedule_version in signed block is corrupted" );
//idump((h.producer)(h.block_num()-h.confirmed)(h.block_num()));
auto itr = producer_to_last_produced.find(h.producer);
if( itr != producer_to_last_produced.end() ) {
FC_ASSERT( itr->second <= result.block_num - h.confirmed, "producer double-confirming known range" );
}
// FC_ASSERT( result.header.block_mroot == h.block_mroot, "mistmatch block merkle root" );
/// below this point is state changes that cannot be validated with headers alone, but never-the-less,
......@@ -148,9 +174,14 @@ namespace eosio { namespace chain {
result.set_new_producers( *h.new_producers );
}
result.set_confirmed( h.confirmed );
// idump( (result.confirm_count.size()) );
result.header.action_mroot = h.action_mroot;
result.header.transaction_mroot = h.transaction_mroot;
result.header.producer_signature = h.producer_signature;
//idump((result.header));
result.id = result.header.id();
FC_ASSERT( result.block_signing_key == result.signee(), "block not signed by expected key",
......@@ -159,6 +190,41 @@ namespace eosio { namespace chain {
return result;
} /// next
void block_header_state::set_confirmed( uint16_t num_prev_blocks ) {
/*
idump((num_prev_blocks)(confirm_count.size()));
for( uint32_t i = 0; i < confirm_count.size(); ++i ) {
std::cerr << "confirm_count["<<i<<"] = " << int(confirm_count[i]) << "\n";
}
*/
header.confirmed = num_prev_blocks;
int32_t i = (int32_t)(confirm_count.size() - 1);
uint32_t blocks_to_confirm = num_prev_blocks + 1; /// confirm the head block too
while( i >= 0 && blocks_to_confirm ) {
--confirm_count[i];
//idump((confirm_count[i]));
if( confirm_count[i] == 0 )
{
uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i);
dpos_irreversible_blocknum = block_num_for_i;
//idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));
if (i == confirm_count.size() - 1) {
confirm_count.resize(0);
} else {
memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i - 1);
confirm_count.resize( confirm_count.size() - i - 1 );
}
return;
}
--i;
--blocks_to_confirm;
}
}
digest_type block_header_state::sig_digest()const {
auto header_bmroot = digest_type::hash( std::make_pair( header.digest(), blockroot_merkle.get_root() ) );
return digest_type::hash( std::make_pair(header_bmroot, pending_schedule_hash) );
......
#include <eosio/chain/controller.hpp>
#include <eosio/chain/block_context.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/block_log.hpp>
......@@ -35,6 +36,7 @@ struct pending_state {
vector<action_receipt> _actions;
block_context _block_ctx;
void push() {
_db_session.push();
......@@ -346,6 +348,7 @@ struct controller_impl {
if( add_to_fork_db ) {
pending->_pending_block_state->validated = true;
auto new_bsp = fork_db.add( pending->_pending_block_state );
emit( self.accepted_block_header, pending->_pending_block_state );
head = fork_db.head();
FC_ASSERT( new_bsp == head, "committed block did not become the new head in fork database" );
}
......@@ -370,9 +373,11 @@ 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_trace_ptr trace;
transaction_context trx_context( self, etrx, etrx.id() );
transaction_trace_ptr trace = trx_context.trace;
try {
transaction_context trx_context( trace, self, etrx, etrx.id(), deadline, true, 0, cpu_usage );
trx_context.init_for_implicit_trx( deadline, 0, cpu_usage );
trx_context.exec(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
......@@ -428,17 +433,19 @@ struct controller_impl {
}
auto start = fc::time_point::now();
transaction_trace_ptr trace;
signed_transaction dtrx;
fc::raw::unpack(ds,static_cast<transaction&>(dtrx) );
transaction_context trx_context( self, dtrx, gto.trx_id );
transaction_trace_ptr trace = trx_context.trace;
flat_set<account_name> bill_to_accounts;
uint64_t max_cpu;
uint64_t max_cpu = 0;
bool abort_on_error = false;
try {
signed_transaction dtrx;
fc::raw::unpack(ds,static_cast<transaction&>(dtrx) );
trx_context.init_for_deferred_trx( deadline, gto.published );
transaction_context trx_context( trace, self, dtrx, gto.trx_id, deadline, gto.published );
bill_to_accounts = trx_context.bill_to_accounts;
max_cpu = trx_context.max_cpu;
max_cpu = trx_context.initial_max_billable_cpu;
trx_context.exec(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
......@@ -467,10 +474,9 @@ struct controller_impl {
trace->soft_except_ptr = std::current_exception();
trace->elapsed = fc::time_point::now() - start;
}
// Only soft or hard failure logic below:
trx_context.undo_session.undo();
// Make sure failure was not due to problems with deserializing the deferred transaction.
FC_ASSERT( bool(trace), "failed to deserialize transaction" );
// Only soft or hard failure logic below:
if( gto.sender != account_name() ) {
// Attempt error handling for the generated transaction.
......@@ -503,6 +509,7 @@ struct controller_impl {
emit( self.applied_transaction, trace );
undo_session.squash();
} FC_CAPTURE_AND_RETHROW() } /// push_scheduled_transaction
......@@ -557,12 +564,19 @@ struct controller_impl {
}
auto start = fc::time_point::now();
transaction_trace_ptr trace;
transaction_context trx_context( self, trx->trx, trx->id);
transaction_trace_ptr trace = trx_context.trace;
try {
unapplied_transactions.erase( trx->signed_id );
transaction_context trx_context( trace, self, trx->trx, trx->id, deadline,
implicit, ( implicit ? 0 : trx->packed_trx.get_billable_size() ) );
if( implicit ) {
trx_context.init_for_implicit_trx( deadline );
} else {
trx_context.init_for_input_trx( deadline,
trx->packed_trx.get_unprunable_size(),
trx->packed_trx.get_prunable_size(),
trx->trx.signatures.size() );
}
fc::microseconds required_delay(0);
if( !implicit ) {
......@@ -573,8 +587,6 @@ struct controller_impl {
"authorization imposes a delay (${required_delay} sec) greater than the delay specified in transaction header (${specified_delay} sec)",
("required_delay", required_delay.to_seconds())("specified_delay", trx_context.delay.to_seconds()) );
emit( self.accepted_transaction, trx);
trx_context.exec(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
......@@ -615,7 +627,7 @@ struct controller_impl {
} FC_CAPTURE_AND_RETHROW() } /// push_transaction
void start_block( block_timestamp_type when ) {
void start_block( block_timestamp_type when, uint16_t confirm_block_count ) {
FC_ASSERT( !pending );
FC_ASSERT( db.revision() == head->block_num, "",
......@@ -629,7 +641,7 @@ struct controller_impl {
const auto& gpo = db.get<global_property_object>();
if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...
( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_last_irreversible_blocknum ) && // ... that has now become irreversible ...
( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...
pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...
head->pending_schedule.producers.size() == 0 // ... and not just because it was promoted to active at the start of this block, then:
)
......@@ -637,7 +649,7 @@ struct controller_impl {
// Promote proposed schedule to pending schedule.
ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ",
("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num)
("lib", pending->_pending_block_state->dpos_last_irreversible_blocknum)
("lib", pending->_pending_block_state->dpos_irreversible_blocknum)
("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );
pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );
db.modify( gpo, [&]( auto& gp ) {
......@@ -646,6 +658,8 @@ struct controller_impl {
});
}
pending->_pending_block_state->set_confirmed(confirm_block_count);
try {
auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
push_transaction( onbtrx, fc::time_point::maximum(), true );
......@@ -667,7 +681,7 @@ struct controller_impl {
void apply_block( const signed_block_ptr& b ) { try {
try {
start_block( b->timestamp );
start_block( b->timestamp, b->confirmed );
for( const auto& receipt : b->transactions ) {
if( receipt.trx.contains<packed_transaction>() ) {
......@@ -698,16 +712,17 @@ struct controller_impl {
void push_block( const signed_block_ptr& b ) {
FC_ASSERT(!pending, "it is not valid to push a block when there is a pending block");
try {
if( pending ) abort_block();
FC_ASSERT( b );
auto new_header_state = fork_db.add( b );
emit( self.accepted_block_header, new_header_state );
maybe_switch_forks();
} FC_LOG_AND_RETHROW()
} FC_LOG_AND_RETHROW( )
}
void push_confirmation( const header_confirmation& c ) {
FC_ASSERT(!pending, "it is not valid to push a confirmation when there is a pending block");
fork_db.add( c );
emit( self.accepted_confirmation, c );
maybe_switch_forks();
......@@ -718,7 +733,6 @@ struct controller_impl {
if( new_head->header.previous == head->id ) {
try {
abort_block();
apply_block( new_head->block );
fork_db.mark_in_current_chain( new_head, true );
fork_db.set_validity( new_head, true );
......@@ -810,8 +824,10 @@ struct controller_impl {
void finalize_block()
{ try {
if( !pending ) self.start_block();
{
FC_ASSERT(pending, "it is not valid to finalize when there is no pending block");
try {
/*
ilog( "finalize block ${n} (${id}) at ${t} by ${p} (${signing_key}); schedule_version: ${v} lib: ${lib} #dtrxs: ${ndtrxs} ${np}",
......@@ -821,7 +837,7 @@ struct controller_impl {
("p",pending->_pending_block_state->header.producer)
("signing_key", pending->_pending_block_state->block_signing_key)
("v",pending->_pending_block_state->header.schedule_version)
("lib",pending->_pending_block_state->dpos_last_irreversible_blocknum)
("lib",pending->_pending_block_state->dpos_irreversible_blocknum)
("ndtrxs",db.get_index<generated_transaction_multi_index,by_trx_id>().size())
("np",pending->_pending_block_state->header.new_producers)
);
......@@ -990,8 +1006,8 @@ void controller::startup() {
chainbase::database& controller::db()const { return my->db; }
void controller::start_block( block_timestamp_type when ) {
my->start_block(when);
void controller::start_block( block_timestamp_type when, uint16_t confirm_block_count ) {
my->start_block(when, confirm_block_count);
}
void controller::finalize_block() {
......@@ -1084,11 +1100,11 @@ time_point controller::pending_block_time()const {
}
uint32_t controller::last_irreversible_block_num() const {
return my->head->bft_irreversible_blocknum;
return std::max(my->head->bft_irreversible_blocknum, my->head->dpos_irreversible_blocknum);
}
block_id_type controller::last_irreversible_block_id() const {
auto lib_num = my->head->bft_irreversible_blocknum;
auto lib_num = last_irreversible_block_num();
const auto& tapos_block_summary = db().get<block_summary_object>((uint16_t)lib_num);
if( block_header::num_from_id(tapos_block_summary.block_id) == lib_num )
......@@ -1118,7 +1134,7 @@ void controller::log_irreversible_blocks() {
my->blog.read_head();
const auto& log_head = my->blog.head();
auto lib = my->head->dpos_last_irreversible_blocknum;
auto lib = my->head->dpos_irreversible_blocknum;
if( lib > 2 ) {
......
......@@ -32,7 +32,7 @@ namespace eosio { namespace chain {
>,
ordered_non_unique< tag<by_lib_block_num>,
composite_key< block_header_state,
member<block_header_state,uint32_t,&block_header_state::dpos_last_irreversible_blocknum>,
member<block_header_state,uint32_t,&block_header_state::dpos_irreversible_blocknum>,
member<block_header_state,uint32_t,&block_header_state::bft_irreversible_blocknum>,
member<block_header_state,uint32_t,&block_header_state::block_num>
>,
......@@ -98,7 +98,7 @@ namespace eosio { namespace chain {
/// we cannot normally prune the lib if it is the head block because
/// the next block needs to build off of the head block. We are exiting
/// now so we can prune this block as irreversible before exiting.
auto lib = my->head->dpos_last_irreversible_blocknum;
auto lib = my->head->dpos_irreversible_blocknum;
auto oldest = *my->index.get<by_block_num>().begin();
if( oldest->block_num <= lib ) {
prune( oldest );
......@@ -131,7 +131,7 @@ namespace eosio { namespace chain {
my->head = *my->index.get<by_lib_block_num>().begin();
auto lib = my->head->dpos_last_irreversible_blocknum;
auto lib = my->head->dpos_irreversible_blocknum;
auto oldest = *my->index.get<by_block_num>().begin();
if( oldest->block_num < lib ) {
......
......@@ -491,7 +491,7 @@ class apply_context {
* This method will check that @ref account is listed in the message's declared authorizations, and marks the
* authorization as used. Note that all authorizations on a message must be used, or the message is invalid.
*
* @throws tx_missing_auth If no sufficient permission was found
* @throws missing_auth_exception If no sufficient permission was found
*/
void require_authorization(const account_name& account);
bool has_authorization(const account_name& account) const;
......@@ -606,11 +606,10 @@ class apply_context {
generic_index<index_double_object> idx_double;
generic_index<index_long_double_object> idx_long_double;
vector<action_receipt> executed;
action_trace trace;
uint64_t cpu_usage = 0;
uint64_t total_cpu_usage = 0;
uint64_t cpu_usage_limit = 0;
private:
......
......@@ -3,7 +3,7 @@
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <fc/exception/exception.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/types.hpp>
#include <eosio/chain/symbol.hpp>
......@@ -25,15 +25,21 @@ with amount = 10 and symbol(4,"CUR")
*/
struct asset
{
explicit asset(share_type a = 0, symbol id = EOS_SYMBOL)
:amount(a), sym(id){}
static constexpr int64_t max_amount = (1LL << 62) - 1;
explicit asset(share_type a = 0, symbol id = EOS_SYMBOL) :amount(a), sym(id) {
EOS_ASSERT( is_amount_within_range(), asset_type_exception, "magnitude of asset amount must be less than 2^62" );
EOS_ASSERT( sym.valid(), asset_type_exception, "invalid symbol" );
}
share_type amount;
symbol sym;
bool is_amount_within_range()const { return -max_amount <= amount && amount <= max_amount; }
bool is_valid()const { return is_amount_within_range() && sym.valid(); }
double to_real()const { return static_cast<double>(amount) / precision(); }
uint8_t decimals()const;
......
......@@ -57,7 +57,7 @@ namespace eosio { namespace chain {
vector<transaction_receipt> transactions; /// new or generated transactions
};
typedef std::shared_ptr<signed_block> signed_block_ptr;
using signed_block_ptr = std::shared_ptr<signed_block>;
struct producer_confirmation {
block_id_type block_id;
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
namespace eosio { namespace chain {
class block_context {
public:
enum block_status {
dpos_irreversible = 0, ///< this block has already been applied before by this node and is considered irreversible by DPOS standards
bft_irreversible = 1, ///< this block has already been applied before by this node and is considered irreversible by BFT standards (but not yet DPOS standards)
validated_block = 2, ///< this is a complete block signed by a valid producer and has been previously applied by this node and therefore validated but it is not yet irreversible
completed_block = 3, ///< this is a complete block signed by a valid producer but is not yet irreversible nor has it yet been applied by this node
producing_block = 4, ///< this is an incomplete block that is being produced by a valid producer for their time slot and will be signed by them after the block is completed
speculative_block = 5 ///< this is an incomplete block that is only being speculatively produced by the node (whether it is the node of an active producer or not)
};
block_status status = speculative_block;
bool is_active_producer = false; ///< whether the node applying the block is an active producer (this further modulates behavior when the block status is completed_block)
};
} }
......@@ -6,13 +6,25 @@ namespace eosio { namespace chain {
struct block_header
{
block_id_type previous;
block_timestamp_type timestamp;
account_name producer;
/**
* By signing this block this producer is confirming blocks [block_num() - confirmed, blocknum())
* as being the best blocks for that range and that he has not signed any other
* statements that would contradict.
*
* No producer should sign a block with overlapping ranges or it is proof of byzantine
* behavior. When producing a block a producer is always confirming at least the block he
* is building off of. A producer cannot confirm "this" block, only prior blocks.
*/
uint16_t confirmed = 1;
block_id_type previous;
checksum256_type transaction_mroot; /// mroot of cycles_summary
checksum256_type action_mroot; /// mroot of all delivered action receipts
account_name producer;
/** The producer schedule version that should validate this block, this is used to
* indicate that the prior block which included new_producers->version has been marked
......@@ -42,7 +54,8 @@ namespace eosio { namespace chain {
} } /// namespace eosio::chain
FC_REFLECT(eosio::chain::block_header, (previous)(timestamp)
FC_REFLECT(eosio::chain::block_header,
(timestamp)(producer)(confirmed)(previous)
(transaction_mroot)(action_mroot)
(producer)(schedule_version)(new_producers))
......
......@@ -12,7 +12,8 @@ struct block_header_state {
block_id_type id;
uint32_t block_num = 0;
signed_block_header header;
uint32_t dpos_last_irreversible_blocknum = 0;
uint32_t dpos_irreversible_blocknum = 0;
uint32_t bft_irreversible_blocknum = 0;
uint32_t pending_schedule_lib_num = 0; /// last irr block num
digest_type pending_schedule_hash;
producer_schedule_type pending_schedule;
......@@ -20,26 +21,29 @@ struct block_header_state {
incremental_merkle blockroot_merkle;
flat_map<account_name,uint32_t> producer_to_last_produced;
public_key_type block_signing_key;
vector<uint8_t> confirm_count;
vector<header_confirmation> confirmations;
block_header_state next( const signed_block_header& h )const;
block_header_state generate_next( block_timestamp_type when )const;
void set_new_producers( producer_schedule_type next_pending );
void set_confirmed( uint16_t num_prev_blocks );
void add_confirmation( const header_confirmation& c );
uint32_t bft_irreversible_blocknum = 0;
vector<header_confirmation> confirmations;
bool has_pending_producers()const { return pending_schedule.producers.size(); }
uint32_t calc_dpos_last_irreversible()const;
//uint32_t calc_dpos_last_irreversible()const;
bool is_active_producer( account_name n )const;
/*
block_timestamp_type get_slot_time( uint32_t slot_num )const;
uint32_t get_slot_at_time( block_timestamp_type t )const;
producer_key get_scheduled_producer( uint32_t slot_num )const;
producer_key get_scheduled_producer( block_timestamp_type t )const;
uint32_t producer_participation_rate()const;
*/
producer_key get_scheduled_producer( block_timestamp_type t )const;
const block_id_type& prev()const { return header.previous; }
digest_type sig_digest()const;
void sign( const std::function<signature_type(const digest_type&)>& signer );
......@@ -51,8 +55,8 @@ struct block_header_state {
} } /// namespace eosio::chain
FC_REFLECT( eosio::chain::block_header_state,
(id)(block_num)(header)(dpos_last_irreversible_blocknum)
(id)(block_num)(header)(dpos_irreversible_blocknum)(bft_irreversible_blocknum)
(pending_schedule_lib_num)(pending_schedule_hash)
(pending_schedule)(active_schedule)(blockroot_merkle)
(producer_to_last_produced)(block_signing_key)
(bft_irreversible_blocknum)(confirmations) )
(confirm_count)(confirmations) )
......@@ -27,7 +27,7 @@ namespace eosio { namespace chain {
vector<transaction_metadata_ptr> trxs;
};
typedef std::shared_ptr<block_state> block_state_ptr;
using block_state_ptr = std::shared_ptr<block_state>;
} } /// namespace eosio::chain
......
......@@ -30,6 +30,17 @@ namespace eosio { namespace chain {
static block_timestamp maximum() { return block_timestamp( 0xffff ); }
static block_timestamp min() { return block_timestamp(0); }
block_timestamp next() const {
FC_ASSERT( std::numeric_limits<uint32_t>::max() - slot >= 1, "block timestamp overflow" );
auto result = block_timestamp(*this);
result.slot += 1;
return result;
}
fc::time_point to_time_point() const {
return (fc::time_point)(*this);
}
operator fc::time_point() const {
int64_t msec = slot * (int64_t)IntervalMs;
msec += EpochMs;
......
......@@ -21,18 +21,20 @@ struct chain_config {
uint32_t target_block_net_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum net usage; exceeding this triggers congestion handling
uint32_t max_transaction_net_usage; ///< the maximum objectively measured net usage that the chain will allow regardless of account limits
uint32_t base_per_transaction_net_usage; ///< the base amount of net usage billed for a transaction to cover incidentals
uint64_t context_free_discount_net_usage_num; ///< the numerator for the discount on net usage of context-free data
uint64_t context_free_discount_net_usage_den; ///< the denominator for the discount on net usage of context-free data
uint32_t net_usage_leeway;
uint32_t context_free_discount_net_usage_num; ///< the numerator for the discount on net usage of context-free data
uint32_t context_free_discount_net_usage_den; ///< the denominator for the discount on net usage of context-free data
uint64_t max_block_cpu_usage; ///< the maxiumum cpu usage in instructions for a block
uint32_t max_block_cpu_usage; ///< the maxiumum cpu usage in instructions for a block
uint32_t target_block_cpu_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum cpu usage; exceeding this triggers congestion handling
uint32_t max_transaction_cpu_usage; ///< the maximum objectively measured cpu usage that the chain will allow regardless of account limits
uint32_t base_per_transaction_cpu_usage; ///< the base amount of cpu usage billed for a transaction to cover incidentals
uint32_t base_per_action_cpu_usage; ///< the base amount of cpu usage billed for an action to cover incidentals
uint32_t base_setcode_cpu_usage; ///< the base amount of cpu usage billed for a setcode action to cover compilation/etc
uint32_t per_signature_cpu_usage; ///< the cpu usage billed for every signature on a transaction
uint64_t context_free_discount_cpu_usage_num; ///< the numerator for the discount on cpu usage of context-free actions
uint64_t context_free_discount_cpu_usage_den; ///< the denominator for the discount on cpu usage of context-free actions
uint32_t cpu_usage_leeway;
uint32_t context_free_discount_cpu_usage_num; ///< the numerator for the discount on cpu usage of context-free actions
uint32_t context_free_discount_cpu_usage_den; ///< the denominator for the discount on cpu usage of context-free actions
uint32_t max_transaction_lifetime; ///< the maximum number of seconds that an input transaction's expiration can be ahead of the time of the block in which it is first included
uint32_t deferred_trx_expiration_window; ///< the number of seconds after the time a deferred transaction can first execute until it expires
......@@ -48,6 +50,7 @@ struct chain_config {
<< "Target Block Net Usage Percent: " << ((double)c.target_block_net_usage_pct / (double)config::percent_1) << "%, "
<< "Max Transaction Net Usage: " << c.max_transaction_net_usage << ", "
<< "Base Per-Transaction Net Usage: " << c.base_per_transaction_net_usage << ", "
<< "Net Usage Leeway: " << c.net_usage_leeway << ", "
<< "Context-Free Data Net Usage Discount: " << (double)c.context_free_discount_net_usage_num * 100.0 / (double)c.context_free_discount_net_usage_den << "% , "
<< "Max Block CPU Usage: " << c.max_block_cpu_usage << ", "
......@@ -57,6 +60,7 @@ struct chain_config {
<< "Base Per-Action CPU Usage: " << c.base_per_action_cpu_usage << ", "
<< "Base Setcode CPU Usage: " << c.base_setcode_cpu_usage << ", "
<< "Per-Signature CPU Usage: " << c.per_signature_cpu_usage << ", "
<< "CPU Usage Leeway: " << c.cpu_usage_leeway << ", "
<< "Context-Free Action CPU Usage Discount: " << (double)c.context_free_discount_cpu_usage_num * 100.0 / (double)c.context_free_discount_cpu_usage_den << "% , "
<< "Max Transaction Lifetime: " << c.max_transaction_lifetime << ", "
......@@ -76,12 +80,12 @@ inline bool operator!=(const chain_config& a, const chain_config& b) { return !(
FC_REFLECT(eosio::chain::chain_config,
(max_block_net_usage)(target_block_net_usage_pct)
(max_transaction_net_usage)(base_per_transaction_net_usage)
(max_transaction_net_usage)(base_per_transaction_net_usage)(net_usage_leeway)
(context_free_discount_net_usage_num)(context_free_discount_net_usage_den)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_transaction_cpu_usage)(base_per_transaction_cpu_usage)
(base_per_action_cpu_usage)(base_setcode_cpu_usage)(per_signature_cpu_usage)
(base_per_action_cpu_usage)(base_setcode_cpu_usage)(per_signature_cpu_usage)(cpu_usage_leeway)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_lifetime)(deferred_trx_expiration_window)(max_transaction_delay)
......
......@@ -58,8 +58,9 @@ const static uint32_t default_max_block_net_usage = 1024 * 102
const static uint32_t default_target_block_net_usage_pct = 10 * percent_1; /// we target 1000 TPS
const static uint32_t default_max_transaction_net_usage = default_max_block_net_usage / 10;
const static uint32_t default_base_per_transaction_net_usage = 12; // 12 bytes (11 bytes for worst case of transaction_receipt_header + 1 byte for static_variant tag)
const static uint64_t default_context_free_discount_net_usage_num = 20; // TODO: is this reasonable?
const static uint64_t default_context_free_discount_net_usage_den = 100;
const static uint32_t default_net_usage_leeway = 500; // TODO: is this reasonable?
const static uint32_t default_context_free_discount_net_usage_num = 20; // TODO: is this reasonable?
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 = 100 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst
......@@ -69,8 +70,9 @@ const static uint32_t default_base_per_transaction_cpu_usage = 512;
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
const static uint32_t default_per_signature_cpu_usage = 100 * 1024; // TODO: is this reasonable?
const static uint64_t default_context_free_discount_cpu_usage_num = 20;
const static uint64_t default_context_free_discount_cpu_usage_den = 100;
const static uint32_t default_cpu_usage_leeway = 2048; // TODO: is this reasonable?
const static uint32_t default_context_free_discount_cpu_usage_num = 20;
const static uint32_t default_context_free_discount_cpu_usage_den = 100;
const static uint32_t default_max_trx_lifetime = 60*60; // 1 hour
const static uint32_t default_deferred_trx_expiration_window = 10*60; // 10 minutes
......@@ -86,6 +88,8 @@ const static uint32_t resource_processing_cpu_overhead_per_billed_account = 25
const static uint32_t determine_payers_cpu_overhead_per_authorization = 64; // TODO: is this reasonable?
const static uint32_t ram_usage_validation_overhead_per_account = 64; // TODO: is this reasonable?
const static uint32_t fixed_net_overhead_of_packed_trx = 16; // TODO: is this reasonable?
const static uint32_t overhead_per_row_per_index_ram_bytes = 32; ///< overhead accounts for basic tracking structures in a row per index
const static uint32_t overhead_per_account_ram_bytes = 2*1024; ///< overhead accounts for basic account storage and pre-pays features like account recovery
const static uint32_t setcode_ram_bytes_multiplier = 10; ///< multiplier on contract size to account for multiple copies and cached compilation
......@@ -96,6 +100,11 @@ const static eosio::chain::wasm_interface::vm_type default_wasm_runtime = eosio:
* The number of sequential blocks produced by a single producer
*/
const static int producer_repetitions = 12;
const static int max_producers = 125;
const static size_t maximum_tracked_dpos_confirmations = 1024; ///<
static_assert(maximum_tracked_dpos_confirmations >= ((max_producers * 2 / 3) + 1) * producer_repetitions, "Settings never allow for DPOS irreversibility" );
/**
* The number of blocks produced per round is based upon all producers having a chance
......
......@@ -37,7 +37,6 @@ namespace eosio { namespace chain {
struct runtime_limits {
fc::microseconds max_push_block_us = fc::microseconds(100000);
fc::microseconds max_push_transaction_us = fc::microseconds(1000'000);
fc::microseconds max_deferred_transactions_us = fc::microseconds(100000);
};
path block_log_dir = chain::config::default_block_log_dir;
......@@ -60,7 +59,7 @@ namespace eosio { namespace chain {
* Starts a new pending block session upon which new transactions can
* be pushed.
*/
void start_block( block_timestamp_type time = block_timestamp_type() );
void start_block( block_timestamp_type time = block_timestamp_type(), uint16_t confirm_block_count = 0 );
void abort_block();
......@@ -196,7 +195,7 @@ namespace eosio { namespace chain {
} } /// eosio::chain
FC_REFLECT( eosio::chain::controller::config::runtime_limits, (max_push_block_us)(max_push_transaction_us)(max_deferred_transactions_us) )
FC_REFLECT( eosio::chain::controller::config::runtime_limits, (max_push_block_us)(max_push_transaction_us) )
FC_REFLECT( eosio::chain::controller::config,
(block_log_dir)
(shared_memory_dir)(shared_memory_size)(read_only)
......
......@@ -77,8 +77,6 @@ namespace eosio { namespace chain {
>
>;
typedef chainbase::generic_index<generated_transaction_multi_index> generated_transaction_index;
namespace config {
template<>
struct billable_size<generated_transaction_object> {
......
......@@ -21,6 +21,7 @@ struct genesis_state {
.target_block_net_usage_pct = config::default_target_block_net_usage_pct,
.max_transaction_net_usage = config::default_max_transaction_net_usage,
.base_per_transaction_net_usage = config::default_base_per_transaction_net_usage,
.net_usage_leeway = config::default_net_usage_leeway,
.context_free_discount_net_usage_num = config::default_context_free_discount_net_usage_num,
.context_free_discount_net_usage_den = config::default_context_free_discount_net_usage_den,
......@@ -31,6 +32,7 @@ struct genesis_state {
.base_per_action_cpu_usage = config::default_base_per_action_cpu_usage,
.base_setcode_cpu_usage = config::default_base_setcode_cpu_usage,
.per_signature_cpu_usage = config::default_per_signature_cpu_usage,
.cpu_usage_leeway = config::default_cpu_usage_leeway,
.context_free_discount_cpu_usage_num = config::default_context_free_discount_cpu_usage_num,
.context_free_discount_cpu_usage_den = config::default_context_free_discount_cpu_usage_den,
......
......@@ -22,6 +22,8 @@ namespace eosio { namespace chain { namespace resource_limits {
uint32_t max_multiplier; // the multiplier by which virtual space can oversell usage when uncongested
ratio contract_rate; // the rate at which a congested resource contracts its limit
ratio expand_rate; // the rate at which an uncongested resource expands its limits
void validate()const; // throws if the parameters do not satisfy basic sanity checks
};
class resource_limits_manager {
......
......@@ -27,6 +27,8 @@ namespace eosio { namespace chain { namespace resource_limits {
template<uint64_t Precision = config::rate_limiting_precision>
struct exponential_moving_average_accumulator
{
static_assert( Precision > 0, "Precision must be positive" );
exponential_moving_average_accumulator()
: last_ordinal(0)
, value_ex(0)
......@@ -45,11 +47,14 @@ namespace eosio { namespace chain { namespace resource_limits {
return value_ex / Precision;
}
void add( uint64_t units, uint32_t ordinal, uint32_t window_size )
void add( uint64_t units, uint32_t ordinal, uint32_t window_size /* must be positive */ )
{
if( last_ordinal != ordinal ) {
if (last_ordinal + window_size > ordinal) {
const auto delta = ordinal - last_ordinal;
if( ordinal <= last_ordinal )
wdump((ordinal)(last_ordinal));
FC_ASSERT( ordinal > last_ordinal, "new ordinal cannot be less than the previous ordinal" );
if( (uint64_t)last_ordinal + window_size > (uint64_t)ordinal ) {
const auto delta = ordinal - last_ordinal; // clearly 0 < delta < window_size
const auto decay = make_ratio(
(uint64_t)window_size - delta,
(uint64_t)window_size
......@@ -131,6 +136,13 @@ namespace eosio { namespace chain { namespace resource_limits {
OBJECT_CTOR(resource_limits_config_object);
id_type id;
static_assert( config::block_interval_ms > 0, "config::block_interval_ms must be positive" );
static_assert( config::block_cpu_usage_average_window_ms >= config::block_interval_ms,
"config::block_cpu_usage_average_window_ms cannot be less than config::block_interval_ms" );
static_assert( config::block_size_average_window_ms >= config::block_interval_ms,
"config::block_size_average_window_ms cannot be less than config::block_interval_ms" );
elastic_limit_parameters cpu_limit_parameters = {EOS_PERCENT(config::default_max_block_cpu_usage, config::default_target_block_cpu_usage_pct), config::default_max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}};
elastic_limit_parameters net_limit_parameters = {EOS_PERCENT(config::default_max_block_net_usage, config::default_target_block_net_usage_pct), config::default_max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}};
};
......
......@@ -59,8 +59,15 @@ namespace eosio {
class symbol {
public:
explicit symbol(uint8_t p, const char* s): m_value(string_to_symbol(p, s)) { }
explicit symbol(uint64_t v = SY(4, EOS)): m_value(v) { }
static constexpr uint8_t max_precision = 18;
explicit symbol(uint8_t p, const char* s): m_value(string_to_symbol(p, s)) {
FC_ASSERT(valid(), "invalid symbol", ("s",s));
}
explicit symbol(uint64_t v = SY(4, EOS)): m_value(v) {
FC_ASSERT(valid(), "invalid symbol", ("name",name()));
}
static symbol from_string(const string& from)
{
try {
......@@ -71,6 +78,7 @@ namespace eosio {
auto prec_part = s.substr(0, comma_pos);
uint8_t p = fc::to_int64(prec_part);
string name_part = s.substr(comma_pos + 1);
FC_ASSERT( p <= max_precision, "precision should be <= 18");
return symbol(string_to_symbol(p, name_part.c_str()));
} FC_CAPTURE_LOG_AND_RETHROW((from))
}
......@@ -78,7 +86,7 @@ namespace eosio {
bool valid() const
{
const auto& s = name();
return valid_name(s);
return decimals() <= max_precision && valid_name(s);
}
static bool valid_name(const string& name)
{
......
......@@ -31,7 +31,7 @@ namespace eosio { namespace chain {
};
struct transaction_trace;
typedef std::shared_ptr<transaction_trace> transaction_trace_ptr;
using transaction_trace_ptr = std::shared_ptr<transaction_trace>;
struct transaction_trace {
transaction_id_type id;
......@@ -55,7 +55,7 @@ namespace eosio { namespace chain {
uint64_t cpu_usage;
vector<transaction_trace_ptr> trx_traces;
};
typedef std::shared_ptr<block_trace> block_trace_ptr;
using block_trace_ptr = std::shared_ptr<block_trace>;
} } /// namespace eosio::chain
......
......@@ -118,7 +118,8 @@ namespace eosio { namespace chain {
set_transaction(t, std::move(t.context_free_data), _compression);
}
uint32_t get_billable_size()const;
uint32_t get_unprunable_size()const;
uint32_t get_prunable_size()const;
digest_type packed_digest()const;
......@@ -136,6 +137,7 @@ namespace eosio { namespace chain {
void set_transaction(const transaction& t, const vector<bytes>& cfd, compression_type _compression = none);
};
using packed_transaction_ptr = std::shared_ptr<packed_transaction>;
/**
* When a transaction is generated it can be scheduled to occur
......
......@@ -4,48 +4,37 @@
namespace eosio { namespace chain {
class transaction_context {
private:
// Common private constructor
void init( uint64_t initial_net_usage, uint64_t initial_cpu_usage );
public:
transaction_context( controller& c,
transaction_trace_ptr& trace_ptr,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point deadline,
fc::time_point published,
uint64_t initial_net_usage,
uint64_t initial_cpu_usage );
const transaction_id_type& trx_id );
static uint64_t calculate_initial_net_usage( const controller& c,
const signed_transaction& t,
uint64_t packed_trx_billable_size );
void init_for_implicit_trx( fc::time_point deadline,
uint64_t initial_net_usage = 0,
uint64_t initial_cpu_usage = 0 );
public:
// For deferred transactions
transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point deadline,
fc::time_point published );
void init_for_input_trx( fc::time_point deadline,
uint64_t packed_trx_unprunable_size,
uint64_t packed_trx_prunable_size,
uint32_t num_signatures );
// For implicit or input transactions
transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point deadline,
bool is_implicit,
uint64_t packed_trx_billable_size,
uint64_t initial_cpu_usage = 0 );
void init_for_deferred_trx( fc::time_point deadline,
fc::time_point published );
void exec();
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(); }
inline void add_cpu_usage_and_check_time( uint64_t u ) { check_time(); cpu_usage += u; check_cpu_usage(); }
/// 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;
......@@ -55,9 +44,11 @@ namespace eosio { namespace chain {
private:
void dispatch_action( const action& a, account_name receiver, bool context_free = false );
inline void dispatch_action( const action& a, bool context_free = false ) {
dispatch_action(a, a.account, context_free);
friend class apply_context;
action_trace dispatch_action( const action& a, account_name receiver, bool context_free = false, uint32_t recurse_depth = 0 );
inline action_trace dispatch_action( const action& a, bool context_free = false ) {
return dispatch_action(a, a.account, context_free);
};
void schedule_transaction();
void record_transaction( const transaction_id_type& id, fc::time_point_sec expire );
......@@ -70,14 +61,23 @@ namespace eosio { namespace chain {
transaction_id_type id;
chainbase::database::session undo_session;
transaction_trace_ptr trace;
fc::time_point deadline;
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;
uint64_t max_net = 0; /// the maximum number of network usage bytes the transaction can consume
uint64_t max_cpu = 0; /// the maximum number of CPU instructions the transaction may consume
/// 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
/// 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;
fc::microseconds delay;
bool is_input = false;
bool apply_context_free = true;
......@@ -86,6 +86,9 @@ namespace eosio { namespace chain {
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;
};
......
......@@ -48,6 +48,6 @@ class transaction_metadata {
uint32_t total_actions()const { return trx.context_free_actions.size() + trx.actions.size(); }
};
typedef std::shared_ptr<transaction_metadata> transaction_metadata_ptr;
using transaction_metadata_ptr = std::shared_ptr<transaction_metadata>;
} } // eosio::chain
......@@ -7,6 +7,8 @@
namespace eosio { namespace chain { namespace resource_limits {
static_assert( config::rate_limiting_precision > 0, "config::rate_limiting_precision must be positive" );
static uint64_t update_elastic_limit(uint64_t current_limit, uint64_t average_usage, const elastic_limit_parameters& params) {
uint64_t result = current_limit;
if (average_usage > params.target ) {
......@@ -18,6 +20,15 @@ static uint64_t update_elastic_limit(uint64_t current_limit, uint64_t average_us
return std::min(std::max(result, params.max), params.max * params.max_multiplier);
}
void elastic_limit_parameters::validate()const {
// At the very least ensure parameters are not set to values that will cause divide by zero errors later on.
// Stricter checks for sensible values can be added later.
FC_ASSERT( periods > 0, "elastic limit parameter 'periods' cannot be zero" );
FC_ASSERT( contract_rate.denominator > 0, "elastic limit parameter 'contract_rate' is not a well-defined ratio" );
FC_ASSERT( expand_rate.denominator > 0, "elastic limit parameter 'expand_rate' is not a well-defined ratio" );
}
void resource_limits_state_object::update_virtual_cpu_limit( const resource_limits_config_object& cfg ) {
virtual_cpu_limit = update_elastic_limit(virtual_cpu_limit, average_block_cpu_usage.average(), cfg.cpu_limit_parameters);
}
......@@ -58,6 +69,8 @@ void resource_limits_manager::initialize_account(const account_name& account) {
}
void resource_limits_manager::set_block_parameters(const elastic_limit_parameters& cpu_limit_parameters, const elastic_limit_parameters& net_limit_parameters ) {
cpu_limit_parameters.validate();
net_limit_parameters.validate();
const auto& config = _db.get<resource_limits_config_object>();
_db.modify(config, [&](resource_limits_config_object& c){
c.cpu_limit_parameters = cpu_limit_parameters;
......@@ -84,7 +97,7 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
uint128_t capacity_cpu_ex = state.virtual_cpu_limit * config::rate_limiting_precision;
EOS_ASSERT( state.total_cpu_weight > 0 && (consumed_cpu_ex * state.total_cpu_weight) <= (limits.cpu_weight * capacity_cpu_ex),
tx_resource_exhausted,
tx_cpu_usage_exceeded,
"authorizing account '${n}' has insufficient cpu resources for this transaction",
("n", name(a))
("consumed", (double)consumed_cpu_ex/(double)config::rate_limiting_precision)
......@@ -99,7 +112,7 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
uint128_t capacity_net_ex = state.virtual_net_limit * config::rate_limiting_precision;
EOS_ASSERT( state.total_net_weight > 0 && (consumed_net_ex * state.total_net_weight) <= (limits.net_weight * capacity_net_ex),
tx_resource_exhausted,
tx_net_usage_exceeded,
"authorizing account '${n}' has insufficient net resources for this transaction",
("n", name(a))
("consumed", (double)consumed_net_ex/(double)config::rate_limiting_precision)
......@@ -142,10 +155,10 @@ void resource_limits_manager::verify_account_ram_usage( const account_name accou
get_account_limits( account, ram_bytes, net_weight, cpu_weight );
const auto& usage = _db.get<resource_usage_object,by_owner>( account );
if( ram_bytes >= 0 && usage.ram_usage > ram_bytes ) {
tx_resource_exhausted e(FC_LOG_MESSAGE(error, "account ${a} has insufficient ram bytes", ("a", account)));
e.append_log(FC_LOG_MESSAGE(error, "needs ${d} has ${m}", ("d",usage.ram_usage)("m",ram_bytes)));
throw e;
if( ram_bytes >= 0 ) {
EOS_ASSERT( usage.ram_usage <= ram_bytes, ram_usage_exceeded,
"account ${account} has insufficient ram bytes; needs ${available} has ${needs}",
("account", account)("available",usage.ram_usage)("needs",ram_bytes) );
}
}
......
......@@ -130,8 +130,16 @@ flat_set<public_key_type> signed_transaction::get_signature_keys( const chain_id
return transaction::get_signature_keys(signatures, chain_id, context_free_data, allow_duplicate_keys);
}
uint32_t packed_transaction::get_billable_size()const {
auto size = fc::raw::pack_size(*this);
uint32_t packed_transaction::get_unprunable_size()const {
uint64_t size = config::fixed_net_overhead_of_packed_trx;
size += packed_trx.size();
FC_ASSERT( size <= std::numeric_limits<uint32_t>::max(), "packed_transaction is too big" );
return static_cast<uint32_t>(size);
}
uint32_t packed_transaction::get_prunable_size()const {
uint64_t size = fc::raw::pack_size(signatures);
size += packed_context_free_data.size();
FC_ASSERT( size <= std::numeric_limits<uint32_t>::max(), "packed_transaction is too big" );
return static_cast<uint32_t>(size);
}
......
......@@ -9,26 +9,23 @@
namespace eosio { namespace chain {
transaction_context::transaction_context( controller& c,
transaction_trace_ptr& trace_ptr,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point d,
fc::time_point p,
uint64_t initial_net_usage,
uint64_t initial_cpu_usage )
const transaction_id_type& trx_id )
:control(c)
,trx(t)
,id(trx_id)
,undo_session(c.db().start_undo_session(true))
,trace(std::make_shared<transaction_trace>())
,deadline(d)
,published(p)
,net_usage(trace->net_usage)
,cpu_usage(trace->cpu_usage)
{
trace->id = id;
trace_ptr = trace;
executed.reserve( trx.total_actions() );
}
void transaction_context::init(uint64_t initial_net_usage, uint64_t initial_cpu_usage )
{
FC_ASSERT( !is_initialized, "cannot initialize twice" );
// Record accounts to be billed for network and CPU usage
uint64_t determine_payers_cpu_cost = 0;
......@@ -55,6 +52,25 @@ namespace eosio { namespace chain {
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;
// 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 );
uint64_t block_net_limit = rl.get_block_net_limit();
uint64_t block_cpu_limit = rl.get_block_cpu_limit();
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;
}
// Initial billing for network usage
if( initial_net_usage > 0 )
add_net_usage( initial_net_usage );
......@@ -66,73 +82,104 @@ namespace eosio { namespace chain {
+ 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).
// 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 );
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
max_net = std::min( max_net, rl.get_block_net_limit() );
max_cpu = std::min( max_cpu, rl.get_block_cpu_limit() );
for( const auto& a : bill_to_accounts ) {
auto net_limit = rl.get_account_net_limit(a);
if( net_limit >= 0 )
max_net = std::min( max_net, static_cast<uint64_t>(net_limit) ); // reduce max_net to the amount the account is able to pay
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 )
max_cpu = std::min( max_cpu, static_cast<uint64_t>(cpu_limit) ); // reduce max_cpu to the amount the account is able to pay
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
eager_net_limit += cfg.net_usage_leeway;
eager_net_limit = std::min(eager_net_limit, max_net);
eager_cpu_limit += cfg.cpu_usage_leeway;
eager_cpu_limit = std::min(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
max_net = (max_net/8)*8; // Round down to nearest multiple of word size (8 bytes)
max_cpu = (max_cpu/1024)*1024; // Round down to nearest multiple of 1024
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
if( initial_net_usage > 0 )
check_net_usage();
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
is_initialized = true;
}
uint64_t transaction_context::calculate_initial_net_usage( const controller& c,
const signed_transaction& t,
uint64_t packed_trx_billable_size ) {
const auto& cfg = c.get_global_properties().configuration;
uint64_t initial_net_usage = static_cast<uint64_t>(cfg.base_per_transaction_net_usage) + packed_trx_billable_size;
if( t.delay_sec.value > 0 ) {
void transaction_context::init_for_implicit_trx( fc::time_point d, uint64_t initial_net_usage, uint64_t initial_cpu_usage )
{
published = control.pending_block_time();
deadline = d;
init( initial_net_usage, initial_cpu_usage );
}
void transaction_context::init_for_input_trx( fc::time_point d,
uint64_t packed_trx_unprunable_size,
uint64_t packed_trx_prunable_size,
uint32_t num_signatures )
{
const auto& cfg = control.get_global_properties().configuration;
uint64_t discounted_size_for_pruned_data = packed_trx_prunable_size;
if( cfg.context_free_discount_net_usage_den > 0
&& cfg.context_free_discount_net_usage_num < cfg.context_free_discount_net_usage_den )
{
discounted_size_for_pruned_data *= cfg.context_free_discount_net_usage_num;
discounted_size_for_pruned_data = ( discounted_size_for_pruned_data + cfg.context_free_discount_net_usage_den - 1)
/ cfg.context_free_discount_net_usage_den; // rounds up
}
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
// whether that be by successfully executing, soft failure, hard failure, or expiration.
initial_net_usage += static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
+ static_cast<uint64_t>(config::transaction_id_net_usage);
}
return initial_net_usage;
published = control.pending_block_time();
deadline = d;
is_input = true;
init( initial_net_usage, initial_cpu_usage );
}
transaction_context::transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point d,
fc::time_point p )
:transaction_context( c, trace_ptr, t, trx_id, d, p, 0, 0 )
void transaction_context::init_for_deferred_trx( fc::time_point d,
fc::time_point p )
{
published = p;
deadline = d;
trace->scheduled = true;
is_input = false;
apply_context_free = false;
}
transaction_context::transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point d,
bool is_implicit,
uint64_t packed_trx_billable_size,
uint64_t initial_cpu_usage )
:transaction_context( c, trace_ptr, t, trx_id, d, c.pending_block_time(),
calculate_initial_net_usage(c, t, packed_trx_billable_size), initial_cpu_usage )
{
trace->scheduled = false;
is_input = !is_implicit;
init( 0, 0 );
}
void transaction_context::exec() {
FC_ASSERT( is_initialized, "must first initialize" );
//trx.validate(); // Not needed anymore since overflow is prevented by using uint64_t instead of uint32_t
control.validate_tapos( trx );
control.validate_referenced_accounts( trx );
......@@ -144,13 +191,15 @@ namespace eosio { namespace chain {
if( apply_context_free ) {
for( const auto& act : trx.context_free_actions ) {
dispatch_action( act, true );
trace->action_traces.emplace_back();
trace->action_traces.back() = dispatch_action( act, true );
}
}
if( delay == fc::microseconds() ) {
for( const auto& act : trx.actions ) {
dispatch_action( act );
trace->action_traces.emplace_back();
trace->action_traces.back() = dispatch_action( act );
}
} else {
schedule_transaction();
......@@ -163,34 +212,98 @@ namespace eosio { namespace chain {
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
control.get_mutable_resource_limits_manager()
.add_transaction_usage( bill_to_accounts, cpu_usage, net_usage,
block_timestamp_type(control.pending_block_time()).slot );
check_net_usage();
check_cpu_usage();
rl.add_transaction_usage( bill_to_accounts, cpu_usage, net_usage,
block_timestamp_type(control.pending_block_time()).slot ); // Should never fail
}
void transaction_context::squash() {
undo_session.squash();
}
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 {
EOS_ASSERT( BOOST_LIKELY(net_usage <= max_net), tx_resource_exhausted,
"net usage of transaction is too high: ${actual_net_usage} > ${net_usage_limit}",
("actual_net_usage", net_usage)("net_usage_limit", max_net) );
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) );
} 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) );
}
}
}
void transaction_context::check_cpu_usage()const {
EOS_ASSERT( BOOST_LIKELY(cpu_usage <= max_cpu), tx_resource_exhausted,
"cpu usage of transaction is too high: ${actual_net_usage} > ${cpu_usage_limit}",
("actual_net_usage", cpu_usage)("cpu_usage_limit", max_cpu) );
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} > ${cpu_usage_limit}",
("actual_cpu_usage", cpu_usage)("cpu_usage_limit", max_cpu) );
}
}
}
void transaction_context::check_time()const {
if( BOOST_UNLIKELY(fc::time_point::now() > deadline) ) {
wlog( "deadline passed" );
throw checktime_exceeded();
}
EOS_ASSERT( BOOST_LIKELY(fc::time_point::now() <= deadline), tx_deadline_exceeded, "deadline exceeded" );
}
void transaction_context::add_ram_usage( account_name account, int64_t ram_delta ) {
......@@ -201,15 +314,19 @@ namespace eosio { namespace chain {
}
}
void transaction_context::dispatch_action( const action& a, account_name receiver, bool context_free ) {
apply_context acontext( control, *this, a );
action_trace transaction_context::dispatch_action( const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) {
apply_context acontext( control, *this, a, recurse_depth );
acontext.context_free = context_free;
acontext.receiver = receiver;
acontext.exec();
fc::move_append(executed, move(acontext.executed) );
try {
acontext.exec();
} catch( const action_cpu_usage_exceeded& e ) {
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" );
}
trace->action_traces.emplace_back( move(acontext.trace) );
return move(acontext.trace);
}
void transaction_context::schedule_transaction() {
......@@ -245,7 +362,7 @@ namespace eosio { namespace chain {
transaction.expiration = expire;
});
} catch ( ... ) {
EOS_ASSERT( false, transaction_exception,
EOS_ASSERT( false, tx_duplicate,
"duplicate transaction ${id}", ("id", id ) );
}
} /// record_transaction
......
......@@ -146,6 +146,7 @@ class privileged_api : public context_aware_api {
datastream<const char*> ds( packed_producer_schedule, datalen );
vector<producer_key> producers;
fc::raw::unpack(ds, producers);
EOS_ASSERT(producers.size() <= config::max_producers, wasm_execution_error, "Producer schedule exceeds the maximum producer count for this chain");
// check that producers are unique
std::set<account_name> unique_producers;
for (const auto& p: producers) {
......@@ -194,6 +195,7 @@ class privileged_api : public context_aware_api {
};
/*
class checktime_api : public context_aware_api {
public:
explicit checktime_api( apply_context& ctx )
......@@ -203,6 +205,7 @@ public:
context.checktime(instruction_count);
}
};
*/
class softfloat_api : public context_aware_api {
public:
......@@ -1617,7 +1620,7 @@ REGISTER_INTRINSICS(privileged_api,
(set_privileged, void(int64_t, int) )
);
REGISTER_INJECTED_INTRINSICS(checktime_api,
REGISTER_INJECTED_INTRINSICS(apply_context,
(checktime, void(int))
);
......
......@@ -444,6 +444,23 @@ namespace fc
wdump( __VA_ARGS__ ); \
}
#define FC_LOG_AND_DROP( ... ) \
catch( fc::exception& er ) { \
wlog( "${details}", ("details",er.to_detail_string()) ); \
} catch( const std::exception& e ) { \
fc::exception fce( \
FC_LOG_MESSAGE( warn, "rethrow ${what}: ",FC_FORMAT_ARG_PARAMS( __VA_ARGS__ )("what",e.what()) ), \
fc::std_exception_code,\
BOOST_CORE_TYPEID(e).name(), \
e.what() ) ; \
wlog( "${details}", ("details",fce.to_detail_string()) ); \
} catch( ... ) { \
fc::unhandled_exception e( \
FC_LOG_MESSAGE( warn, "rethrow", FC_FORMAT_ARG_PARAMS( __VA_ARGS__) ), \
std::current_exception() ); \
wlog( "${details}", ("details",e.to_detail_string()) ); \
}
/**
* @def FC_RETHROW_EXCEPTIONS(LOG_LEVEL,FORMAT,...)
......
......@@ -5,7 +5,6 @@
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/abi_serializer.hpp>
#include <fc/io/json.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/test/unit_test.hpp>
#include <iosfwd>
......@@ -61,6 +60,8 @@ namespace eosio { namespace testing {
void copy_row(const chain::key_value_object& obj, vector<char>& data);
bool expect_assert_message(const fc::exception& ex, string expected);
/**
* @class tester
* @brief provides utility function to simplify the creation of unit tests
......@@ -92,6 +93,10 @@ namespace eosio { namespace testing {
transaction_trace_ptr push_action( const account_name& code, const action_name& acttype, const vector<account_name>& actors, const variant_object& data, uint32_t expiration = DEFAULT_EXPIRATION_DELTA, uint32_t delay_sec = 0 );
transaction_trace_ptr push_action( const account_name& code, const action_name& acttype, const vector<permission_level>& auths, const variant_object& data, uint32_t expiration = DEFAULT_EXPIRATION_DELTA, uint32_t delay_sec = 0 );
action get_action( account_name code, action_name acttype, vector<permission_level> auths,
const variant_object& data )const;
void set_transaction_headers(signed_transaction& trx,
uint32_t expiration = DEFAULT_EXPIRATION_DELTA,
uint32_t delay_sec = 0)const;
......@@ -177,15 +182,15 @@ namespace eosio { namespace testing {
static action_result error(const string& msg) { return msg; }
auto get_resolver() {
return [this](const account_name &name) -> optional<abi_serializer> {
return [this]( const account_name& name ) -> optional<abi_serializer> {
try {
const auto &accnt = control->db().get<account_object, by_name>(name);
const auto& accnt = control->db().get<account_object, by_name>( name );
abi_def abi;
if (abi_serializer::to_abi(accnt.abi, abi)) {
return abi_serializer(abi);
if( abi_serializer::to_abi( accnt.abi, abi )) {
return abi_serializer( abi );
}
return optional<abi_serializer>();
} FC_RETHROW_EXCEPTIONS(error, "Failed to find or parse ABI for ${name}", ("name", name))
} FC_RETHROW_EXCEPTIONS( error, "Failed to find or parse ABI for ${name}", ("name", name))
};
}
......@@ -218,6 +223,7 @@ namespace eosio { namespace testing {
protected:
signed_block_ptr _produce_block( fc::microseconds skip_time, bool skip_pending_trxs = false, uint32_t skip_flag = 0 );
void _start_block(fc::time_point block_time);
// Fields:
protected:
......@@ -229,6 +235,7 @@ namespace eosio { namespace testing {
protected:
controller::config cfg;
map<transaction_id_type, transaction_receipt> chain_transactions;
map<account_name, block_id_type> last_produced_block;
};
class tester : public base_tester {
......@@ -321,36 +328,79 @@ namespace eosio { namespace testing {
};
/**
* Utility predicate to check whether an FC_ASSERT message ends with a given string
* Utility predicate to check whether an fc::exception message is equivalent to a given string
*/
struct assert_message_ends_with {
assert_message_ends_with(string expected)
:expected(expected)
{}
bool operator()( const fc::exception& ex ) {
auto message = ex.get_log().at(0).get_message();
return boost::algorithm::ends_with(message, expected);
}
string expected;
};
struct fc_exception_message_is {
fc_exception_message_is( const string& msg )
: expected( msg ) {}
/**
* Utility predicate to check whether an FC_ASSERT message contains a given string
*/
struct assert_message_contains {
assert_message_contains(string expected)
:expected(expected)
{}
bool operator()( const fc::exception& ex ) {
auto message = ex.get_log().at(0).get_message();
return boost::algorithm::contains(message, expected);
}
bool operator()( const fc::exception& ex );
string expected;
};
/**
* Utility predicate to check whether an fc::exception message starts with a given string
*/
struct fc_exception_message_starts_with {
fc_exception_message_starts_with( const string& msg )
: expected( msg ) {}
bool operator()( const fc::exception& ex );
string expected;
};
/**
* Utility predicate to check whether an fc::assert_exception message is equivalent to a given string
*/
struct fc_assert_exception_message_is {
fc_assert_exception_message_is( const string& msg )
: expected( msg ) {}
bool operator()( const fc::assert_exception& ex );
string expected;
};
/**
* Utility predicate to check whether an fc::assert_exception message starts with a given string
*/
struct fc_assert_exception_message_starts_with {
fc_assert_exception_message_starts_with( const string& msg )
: expected( msg ) {}
bool operator()( const fc::assert_exception& ex );
string expected;
};
/**
* Utility predicate to check whether an eosio_assert message is equivalent to a given string
*/
struct eosio_assert_message_is {
eosio_assert_message_is( const string& msg )
: expected( "assertion failed: " ) {
expected.append( msg );
}
bool operator()( const fc::assert_exception& ex );
string expected;
};
/**
* Utility predicate to check whether an eosio_assert message starts with a given string
*/
struct eosio_assert_message_starts_with {
eosio_assert_message_starts_with( const string& msg )
: expected( "assertion failed: " ) {
expected.append( msg );
}
bool operator()( const fc::assert_exception& ex );
string expected;
};
} } /// eosio::testing
#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/wast_to_wasm.hpp>
#include <eosio/chain/eosio_contract.hpp>
......@@ -8,6 +9,11 @@
namespace eosio { namespace testing {
bool expect_assert_message(const fc::exception& ex, string expected) {
BOOST_TEST_MESSAGE("LOG : " << "expected: " << expected << ", actual: " << ex.get_log().at(0).get_message());
return (ex.get_log().at(0).get_message().find(expected) != std::string::npos);
}
fc::variant_object filter_fields(const fc::variant_object& filter, const fc::variant_object& value) {
fc::mutable_variant_object res;
for( auto& entry : filter ) {
......@@ -72,17 +78,24 @@ namespace eosio { namespace testing {
for( const auto& receipt : block_state->block->transactions ) {
if( receipt.trx.contains<packed_transaction>() ) {
auto &pt = receipt.trx.get<packed_transaction>();
chain_transactions.emplace(pt.get_transaction().id(), receipt);
chain_transactions[pt.get_transaction().id()] = receipt;
} else {
auto& id = receipt.trx.get<transaction_id_type>();
chain_transactions.emplace(id, receipt);
chain_transactions[id] = receipt;
}
}
});
}
signed_block_ptr base_tester::push_block(signed_block_ptr b) {
control->abort_block();
control->push_block(b);
auto itr = last_produced_block.find(b->producer);
if (itr == last_produced_block.end() || block_header::num_from_id(b->id()) > block_header::num_from_id(itr->second)) {
last_produced_block[b->producer] = b->id();
}
return b;
}
......@@ -91,12 +104,8 @@ namespace eosio { namespace testing {
auto head_time = control->head_block_time();
auto next_time = head_time + skip_time;
if( !control->pending_block_state() ) {
control->start_block( next_time );
} else if( control->pending_block_state()->header.timestamp != next_time ) {
wlog( "aborting current pending block and starting a new one" );
control->abort_block();
control->start_block( next_time );
if( !control->pending_block_state() || control->pending_block_state()->header.timestamp != next_time ) {
_start_block( next_time );
}
auto producer = control->head_block_state()->get_scheduled_producer(next_time);
......@@ -117,6 +126,8 @@ namespace eosio { namespace testing {
while( control->push_next_scheduled_transaction( fc::time_point::maximum() ) );
}
control->finalize_block();
control->sign_block( [&]( digest_type d ) {
return priv_key.sign(d);
......@@ -124,10 +135,27 @@ namespace eosio { namespace testing {
control->commit_block();
control->log_irreversible_blocks();
control->start_block( next_time + fc::microseconds(config::block_interval_us));
last_produced_block[control->head_block_state()->header.producer] = control->head_block_state()->id;
_start_block( next_time + fc::microseconds(config::block_interval_us));
return control->head_block_state()->block;
}
void base_tester::_start_block(fc::time_point block_time) {
auto head_block_number = control->head_block_num();
auto producer = control->head_block_state()->get_scheduled_producer(block_time);
auto last_produced_block_num = control->last_irreversible_block_num();
auto itr = last_produced_block.find(producer.producer_name);
if (itr != last_produced_block.end()) {
last_produced_block_num = block_header::num_from_id(itr->second);
}
control->abort_block();
control->start_block( block_time, head_block_number - last_produced_block_num );
}
void base_tester::produce_blocks( uint32_t n, bool empty ) {
if( empty ) {
for( uint32_t i = 0; i < n; ++i )
......@@ -184,7 +212,7 @@ namespace eosio { namespace testing {
transaction_trace_ptr base_tester::push_transaction( packed_transaction& trx, uint32_t skip_flag, fc::time_point deadline ) { try {
if( !control->pending_block_state() )
control->start_block();
_start_block(control->head_block_time() + fc::microseconds(config::block_interval_us));
auto r = control->sync_push( std::make_shared<transaction_metadata>(trx), deadline );
if( r->hard_except_ptr ) std::rethrow_exception( r->hard_except_ptr );
if( r->soft_except_ptr ) std::rethrow_exception( r->soft_except_ptr );
......@@ -195,7 +223,7 @@ namespace eosio { namespace testing {
transaction_trace_ptr base_tester::push_transaction( signed_transaction& trx, uint32_t skip_flag, fc::time_point deadline ) { try {
if( !control->pending_block_state() )
control->start_block();
_start_block(control->head_block_time() + fc::microseconds(config::block_interval_us));
auto r = control->sync_push( std::make_shared<transaction_metadata>(trx), deadline );
if( r->hard_except_ptr ) std::rethrow_exception( r->hard_except_ptr );
if( r->soft_except_ptr ) std::rethrow_exception( r->soft_except_ptr );
......@@ -218,7 +246,8 @@ namespace eosio { namespace testing {
try {
push_transaction(trx);
} catch (const fc::exception& ex) {
return error(ex.top_message());
return error(ex.top_message()); // top_message() is assumed by many tests; otherwise they fail
//return error(ex.to_detail_string());
}
produce_block();
BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trx.id()));
......@@ -264,6 +293,18 @@ namespace eosio { namespace testing {
)
{ try {
signed_transaction trx;
trx.actions.emplace_back(get_action( code, acttype, auths, data ));
set_transaction_headers(trx, expiration, delay_sec);
for (const auto& auth : auths) {
trx.sign(get_private_key(auth.actor, auth.permission.to_string()), chain_id_type());
}
return push_transaction(trx);
} FC_CAPTURE_AND_RETHROW( (code)(acttype)(auths)(data)(expiration) ) }
action base_tester::get_action( account_name code, action_name acttype, vector<permission_level> auths,
const variant_object& data )const {
const auto& acnt = control->db().get<account_object,by_name>(code);
auto abi = acnt.get_abi();
chain::abi_serializer abis(abi);
......@@ -278,17 +319,8 @@ namespace eosio { namespace testing {
act.name = acttype;
act.authorization = auths;
act.data = abis.variant_to_binary(action_type_name, data);
signed_transaction trx;
trx.actions.emplace_back(std::move(act));
set_transaction_headers(trx, expiration, delay_sec);
for (const auto& auth : auths) {
trx.sign(get_private_key(auth.actor, auth.permission.to_string()), chain_id_type());
}
return push_transaction(trx);
} FC_CAPTURE_AND_RETHROW( (code)(acttype)(auths)(data)(expiration) ) }
return act;
}
transaction_trace_ptr base_tester::push_reqauth( account_name from, const vector<permission_level>& auths, const vector<private_key_type>& keys ) {
variant pretty_trx = fc::mutable_variant_object()
("actions", fc::variants({
......@@ -635,6 +667,7 @@ namespace eosio { namespace testing {
for( int i = 1; i <= a.control->head_block_num(); ++i ) {
auto block = a.control->fetch_block_by_number(i);
if( block ) { //&& !b.control->is_known_block(block->id()) ) {
b.control->abort_block();
b.control->push_block(block); //, eosio::chain::validation_steps::created_block);
}
}
......@@ -646,7 +679,7 @@ namespace eosio { namespace testing {
void base_tester::push_genesis_block() {
set_code(config::system_account_name, eosio_bios_wast);
set_abi(config::system_account_name, eosio_bios_abi);
//produce_block();
}
......@@ -673,7 +706,81 @@ namespace eosio { namespace testing {
return tid;
}
} } /// eosio::test
bool fc_exception_message_is::operator()( const fc::exception& ex ) {
auto message = ex.get_log().at( 0 ).get_message();
bool match = (message == expected);
if( !match ) {
BOOST_TEST_MESSAGE( "LOG: expected: " << expected << ", actual: " << message );
}
return match;
}
bool fc_exception_message_starts_with::operator()( const fc::exception& ex ) {
auto message = ex.get_log().at( 0 ).get_message();
bool match = boost::algorithm::starts_with( message, expected );
if( !match ) {
BOOST_TEST_MESSAGE( "LOG: expected: " << expected << ", actual: " << message );
}
return match;
}
bool fc_assert_exception_message_is::operator()( const fc::assert_exception& ex ) {
auto message = ex.get_log().at( 0 ).get_message();
bool match = false;
auto pos = message.find( ": " );
if( pos != std::string::npos ) {
message = message.substr( pos + 2 );
match = (message == expected);
}
if( !match ) {
BOOST_TEST_MESSAGE( "LOG: expected: " << expected << ", actual: " << message );
}
return match;
}
bool fc_assert_exception_message_starts_with::operator()( const fc::assert_exception& ex ) {
auto message = ex.get_log().at( 0 ).get_message();
bool match = false;
auto pos = message.find( ": " );
if( pos != std::string::npos ) {
message = message.substr( pos + 2 );
match = boost::algorithm::starts_with( message, expected );
}
if( !match ) {
BOOST_TEST_MESSAGE( "LOG: expected: " << expected << ", actual: " << message );
}
return match;
}
bool eosio_assert_message_is::operator()( const fc::assert_exception& ex ) {
auto message = ex.get_log().at( 0 ).get_message();
bool match = false;
auto pos = message.find( ": " );
if( pos != std::string::npos ) {
message = message.substr( pos + 2 );
match = (message == expected);
}
if( !match ) {
BOOST_TEST_MESSAGE( "LOG: expected: " << expected << ", actual: " << message );
}
return match;
}
bool eosio_assert_message_starts_with::operator()( const fc::assert_exception& ex ) {
auto message = ex.get_log().at( 0 ).get_message();
bool match = false;
auto pos = message.find( ": " );
if( pos != std::string::npos ) {
message = message.substr( pos + 2 );
match = boost::algorithm::starts_with( message, expected );
}
if( !match ) {
BOOST_TEST_MESSAGE( "LOG: expected: " << expected << ", actual: " << message );
}
return match;
}
} } /// eosio::testing
std::ostream& operator<<( std::ostream& osm, const fc::variant& v ) {
//fc::json::to_stream( osm, v );
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#define EOS_ASSERT( expr, exc_type, FORMAT, ... ) \
FC_MULTILINE_MACRO_BEGIN \
if( !(expr) ) \
FC_THROW_EXCEPTION( exc_type, FORMAT, __VA_ARGS__ ); \
FC_MULTILINE_MACRO_END
#define EOS_THROW( exc_type, FORMAT, ... ) \
throw exc_type( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) );
/**
* Macro inspired from FC_RETHROW_EXCEPTIONS
* The main difference here is that if the exception caught isn't of type "eosio::chain::chain_exception"
* This macro will rethrow the exception as the specified "exception_type"
*/
#define EOS_RETHROW_EXCEPTIONS(exception_type, FORMAT, ... ) \
catch (eosio::chain::chain_exception& e) { \
FC_RETHROW_EXCEPTION( e, warn, FORMAT, __VA_ARGS__ ); \
} catch (fc::exception& e) { \
exception_type new_exception(FC_LOG_MESSAGE( warn, FORMAT, __VA_ARGS__ )); \
for (const auto& log: e.get_log()) { \
new_exception.append_log(log); \
} \
throw new_exception; \
} catch( const std::exception& e ) { \
exception_type fce(FC_LOG_MESSAGE( warn, FORMAT" (${what})" ,__VA_ARGS__("what",e.what()))); \
throw fce;\
} catch( ... ) { \
throw fc::unhandled_exception( \
FC_LOG_MESSAGE( warn, FORMAT,__VA_ARGS__), \
std::current_exception() ); \
}
/**
* Macro inspired from FC_CAPTURE_AND_RETHROW
* The main difference here is that if the exception caught isn't of type "eosio::chain::chain_exception"
* This macro will rethrow the exception as the specified "exception_type"
*/
#define EOS_CAPTURE_AND_RETHROW( exception_type, ... ) \
catch (eosio::chain::chain_exception& e) { \
FC_RETHROW_EXCEPTION( e, warn, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \
} catch (fc::exception& e) { \
exception_type new_exception(e.get_log()); \
throw new_exception; \
} catch( const std::exception& e ) { \
exception_type fce( \
FC_LOG_MESSAGE( warn, "${what}: ",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)("what",e.what())), \
fc::std_exception_code,\
BOOST_CORE_TYPEID(decltype(e)).name(), \
e.what() ) ; throw fce;\
} catch( ... ) { \
throw fc::unhandled_exception( \
FC_LOG_MESSAGE( warn, "",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)), \
std::current_exception() ); \
}
#define EOS_DECLARE_OP_BASE_EXCEPTIONS( op_name ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _validate_exception, \
eosio::chain::message_validate_exception, \
3040000 + 100 * operation::tag< op_name ## _operation >::value, \
#op_name "_operation validation exception" \
) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _evaluate_exception, \
eosio::chain::message_evaluate_exception, \
3050000 + 100 * operation::tag< op_name ## _operation >::value, \
#op_name "_operation evaluation exception" \
)
#define EOS_DECLARE_OP_VALIDATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _ ## exc_name, \
eosio::chain::op_name ## _validate_exception, \
3040000 + 100 * operation::tag< op_name ## _operation >::value \
+ seqnum, \
msg \
)
#define EOS_DECLARE_OP_EVALUATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _ ## exc_name, \
eosio::chain::op_name ## _evaluate_exception, \
3050000 + 100 * operation::tag< op_name ## _operation >::value \
+ seqnum, \
msg \
)
#define EOS_RECODE_EXC( cause_type, effect_type ) \
catch( const cause_type& e ) \
{ throw( effect_type( e.what(), e.get_log() ) ); }
......@@ -25,11 +25,14 @@ namespace eosio { namespace chain { namespace plugin_interface {
using accepted_transaction = channel_decl<struct accepted_transaction_tag, transaction_metadata_ptr>;
using applied_transaction = channel_decl<struct applied_transaction_tag, transaction_trace_ptr>;
using accepted_confirmation = channel_decl<struct accepted_confirmation_tag, header_confirmation>;
using incoming_block = channel_decl<struct incoming_blocks_tag, signed_block_ptr>;
using incoming_transaction = channel_decl<struct incoming_transactions_tag, packed_transaction_ptr>;
}
namespace methods {
using get_block_by_number = method_decl<chain_plugin_interface, const signed_block_ptr&(uint32_t block_num)>;
using get_block_by_id = method_decl<chain_plugin_interface, const signed_block_ptr&(const block_id_type& block_id)>;
using get_block_by_number = method_decl<chain_plugin_interface, signed_block_ptr(uint32_t block_num)>;
using get_block_by_id = method_decl<chain_plugin_interface, signed_block_ptr(const block_id_type& block_id)>;
using get_head_block_id = method_decl<chain_plugin_interface, block_id_type ()>;
using get_last_irreversible_block_number = method_decl<chain_plugin_interface, uint32_t ()>;
......
......@@ -45,6 +45,7 @@ public:
,accepted_transaction_channel(app().get_channel<channels::accepted_transaction>())
,applied_transaction_channel(app().get_channel<channels::applied_transaction>())
,accepted_confirmation_channel(app().get_channel<channels::accepted_confirmation>())
,incoming_block_channel(app().get_channel<channels::incoming_block>())
{}
bfs::path block_log_dir;
......@@ -61,7 +62,6 @@ public:
chain_id_type chain_id;
int32_t max_reversible_block_time_ms;
int32_t max_pending_transaction_time_ms;
int32_t max_deferred_transaction_time_ms;
//txn_msg_rate_limits rate_limits;
fc::optional<vm_type> wasm_runtime;
......@@ -72,6 +72,7 @@ public:
channels::accepted_transaction::channel_type& accepted_transaction_channel;
channels::applied_transaction::channel_type& applied_transaction_channel;
channels::accepted_confirmation::channel_type& accepted_confirmation_channel;
channels::incoming_block::channel_type& incoming_block_channel;
// method provider handles
methods::get_block_by_number::method_type::handle get_block_by_number_provider;
......@@ -79,6 +80,8 @@ public:
methods::get_head_block_id::method_type::handle get_head_block_id_provider;
methods::get_last_irreversible_block_number::method_type::handle get_last_irreversible_block_number_provider;
// things we subscribe to
channels::incoming_transaction::channel_type::handle incoming_transaction_subscription;
};
chain_plugin::chain_plugin()
......@@ -97,10 +100,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("max-reversible-block-time", bpo::value<int32_t>()->default_value(-1),
"Limits the maximum time (in milliseconds) that a reversible block is allowed to run before being considered invalid")
("max-pending-transaction-time", bpo::value<int32_t>()->default_value(-1),
("max-pending-transaction-time", bpo::value<int32_t>()->default_value(30),
"Limits the maximum time (in milliseconds) that is allowed a pushed transaction's code to execute before being considered invalid")
("max-deferred-transaction-time", bpo::value<int32_t>()->default_value(20),
"Limits the maximum time (in milliseconds) that is allowed a to push deferred transactions at the start of a block")
("wasm-runtime", bpo::value<eosio::chain::wasm_interface::vm_type>()->value_name("wavm/binaryen"), "Override default WASM runtime")
("shared-memory-size-mb", bpo::value<uint64_t>()->default_value(config::default_shared_memory_size / (1024 * 1024)), "Maximum size MB of database shared memory file")
......@@ -182,7 +183,6 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
my->max_reversible_block_time_ms = options.at("max-reversible-block-time").as<int32_t>();
my->max_pending_transaction_time_ms = options.at("max-pending-transaction-time").as<int32_t>();
my->max_deferred_transaction_time_ms = options.at("max-deferred-transaction-time").as<int32_t>();
if(options.count("wasm-runtime"))
my->wasm_runtime = options.at("wasm-runtime").as<vm_type>();
......@@ -209,21 +209,17 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
my->chain_config->limits.max_push_transaction_us = fc::milliseconds(my->max_pending_transaction_time_ms);
}
if (my->max_deferred_transaction_time_ms > 0 ) {
my->chain_config->limits.max_deferred_transactions_us = fc::milliseconds(my->max_deferred_transaction_time_ms);
}
if(my->wasm_runtime)
my->chain_config->wasm_runtime = *my->wasm_runtime;
my->chain.emplace(*my->chain_config);
// set up method providers
my->get_block_by_number_provider = app().get_method<methods::get_block_by_number>().register_provider([this](uint32_t block_num) -> const signed_block_ptr& {
my->get_block_by_number_provider = app().get_method<methods::get_block_by_number>().register_provider([this](uint32_t block_num) -> signed_block_ptr {
return my->chain->fetch_block_by_number(block_num);
});
my->get_block_by_id_provider = app().get_method<methods::get_block_by_id>().register_provider([this](block_id_type id) -> const signed_block_ptr& {
my->get_block_by_id_provider = app().get_method<methods::get_block_by_id>().register_provider([this](block_id_type id) -> signed_block_ptr {
return my->chain->fetch_block_by_id(id);
});
......@@ -259,6 +255,12 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
my->chain->accepted_confirmation.connect([this](const header_confirmation& conf){
my->accepted_confirmation_channel.publish(conf);
});
my->incoming_transaction_subscription = app().get_channel<channels::incoming_transaction>().subscribe([this](const packed_transaction_ptr& ptrx){
try {
my->chain->push_transaction(std::make_shared<transaction_metadata>(*ptrx), get_transaction_deadline());
} FC_LOG_AND_DROP();
});
}
void chain_plugin::plugin_startup()
......@@ -286,7 +288,7 @@ chain_apis::read_write chain_plugin::get_read_write_api() {
}
void chain_plugin::accept_block(const signed_block_ptr& block ) {
chain().push_block( block );
my->incoming_block_channel.publish(block);
}
void chain_plugin::accept_transaction(const packed_transaction& trx) {
......
......@@ -239,8 +239,6 @@ struct faucet_testnet_plugin_impl {
trx.sign(_create_account_private_key, chainid);
try {
if( !cc.pending_block_state() )
cc.start_block();
cc.push_transaction( std::make_shared<transaction_metadata>(trx) );
} catch (const account_name_exists_exception& ) {
// another transaction ended up adding the account, so look for alternates
......
......@@ -7,4 +7,4 @@ add_library( producer_plugin
target_link_libraries( producer_plugin chain_plugin appbase eosio_chain eos_utilities )
target_include_directories( producer_plugin
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../chain_interface/include" )
......@@ -4,6 +4,7 @@
*/
#include <eosio/producer_plugin/producer_plugin.hpp>
#include <eosio/chain/producer_object.hpp>
#include <eosio/chain/plugin_interface.hpp>
#include <fc/io/json.hpp>
#include <fc/smart_ref_impl.hpp>
......@@ -24,11 +25,13 @@ namespace eosio {
static appbase::abstract_plugin& _producer_plugin = app().register_plugin<producer_plugin>();
using namespace eosio::chain;
using namespace eosio::chain::plugin_interface;
class producer_plugin_impl {
public:
producer_plugin_impl(boost::asio::io_service& io)
: _timer(io) {}
:_timer(io)
{}
void schedule_production_loop();
block_production_condition::block_production_condition_enum block_production_loop();
......@@ -43,6 +46,8 @@ class producer_plugin_impl {
std::set<chain::account_name> _producers;
boost::asio::deadline_timer _timer;
int32_t _max_deferred_transaction_time_ms;
block_production_condition::block_production_condition_enum _prev_result = block_production_condition::produced;
uint32_t _prev_result_count = 0;
......@@ -52,6 +57,8 @@ class producer_plugin_impl {
producer_plugin* _self = nullptr;
channels::incoming_block::channel_type::handle _incoming_block_subscription;
void on_block( const block_state_ptr& bsp ) {
if( bsp->header.timestamp <= _last_signed_block_time ) return;
if( bsp->header.timestamp <= _start_time ) return;
......@@ -59,8 +66,12 @@ class producer_plugin_impl {
const auto& active_producer_to_signing_key = bsp->active_schedule.producers;
auto active_producers = boost::adaptors::keys( boost::make_iterator_range( bsp->producer_to_last_produced.begin(),
bsp->producer_to_last_produced.end() ) );
flat_set<account_name> active_producers;
active_producers.reserve(bsp->active_schedule.producers.size());
for (const auto& p: bsp->active_schedule.producers) {
active_producers.insert(p.producer_name);
}
std::set_intersection( _producers.begin(), _producers.end(),
active_producers.begin(), active_producers.end(),
boost::make_function_output_iterator( [&]( const chain::account_name& producer )
......@@ -83,6 +94,20 @@ class producer_plugin_impl {
}
} ) );
}
void on_incoming_block(const signed_block_ptr& block) {
chain::controller& chain = app().get_plugin<chain_plugin>().chain();
// abort the pending block
chain.abort_block();
try {
// push the new block
chain.push_block(block);
} FC_LOG_AND_DROP();
// restart our production loop
schedule_production_loop();
}
};
void new_chain_banner(const eosio::chain::controller& db)
......@@ -97,7 +122,7 @@ void new_chain_banner(const eosio::chain::controller& db)
"*******************************\n"
"\n";
if( db.head_block_state()->get_slot_at_time(fc::time_point::now()) > 200 )
if( db.head_block_state()->header.timestamp.to_time_point() < (fc::time_point::now() - fc::milliseconds(200 * config::block_interval_ms)))
{
std::cerr << "Your genesis seems to have an old timestamp\n"
"Please consider using the --genesis-timestamp option to give your genesis a recent timestamp\n"
......@@ -125,6 +150,8 @@ void producer_plugin::set_program_options(
producer_options.add_options()
("enable-stale-production,e", boost::program_options::bool_switch()->notifier([this](bool e){my->_production_enabled = e;}), "Enable block production, even if the chain is stale.")
("max-deferred-transaction-time", bpo::value<int32_t>()->default_value(20),
"Limits the maximum time (in milliseconds) that is allowed a to push deferred transactions at the start of a block")
("required-participation", boost::program_options::value<uint32_t>()
->default_value(uint32_t(config::required_producer_participation/config::percent_1))
->notifier([this](uint32_t e) {
......@@ -186,6 +213,13 @@ void producer_plugin::plugin_initialize(const boost::program_options::variables_
my->_private_keys[key_id_to_wif_pair.first] = key_id_to_wif_pair.second;
}
}
my->_max_deferred_transaction_time_ms = options.at("max-deferred-transaction-time").as<int32_t>();
my->_incoming_block_subscription = app().get_channel<channels::incoming_block>().subscribe([this](const signed_block_ptr& block){
my->on_incoming_block(block);
});
} FC_LOG_AND_RETHROW() }
void producer_plugin::plugin_startup()
......@@ -206,7 +240,7 @@ void producer_plugin::plugin_startup()
my->schedule_production_loop();
} else
elog("No producers configured! Please add producer IDs and private keys to configuration.");
ilog("producer plugin: plugin_startup() end");
ilog("producer plugin: plugin_startup() end");
} FC_CAPTURE_AND_RETHROW() }
void producer_plugin::plugin_shutdown() {
......@@ -218,6 +252,8 @@ void producer_plugin::plugin_shutdown() {
}
void producer_plugin_impl::schedule_production_loop() {
_timer.cancel();
//Schedule for the next second's tick regardless of chain state
// If we would wait less than 50ms (1/10 of block_interval), wait for the whole block interval.
fc::time_point now = fc::time_point::now();
......@@ -229,14 +265,42 @@ void producer_plugin_impl::schedule_production_loop() {
time_to_next_block_time += config::block_interval_us;
}
fc::time_point block_time = now + fc::microseconds(time_to_next_block_time);
static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
_timer.expires_at( epoch + boost::posix_time::microseconds(block_time.time_since_epoch().count()));
chain::controller& chain = app().get_plugin<chain_plugin>().chain();
try {
chain.abort_block();
chain.start_block(block_time);
} FC_LOG_AND_DROP();
chain.abort_block();
chain.start_block( now + fc::microseconds(time_to_next_block_time) );
if (chain.pending_block_state()) {
// TODO: BIG BAD WARNING, THIS WILL HAPPILY BLOW PAST DEADLINES BUT CONTROLLER IS NOT YET SAFE FOR DEADLINE USAGE
try {
while (chain.push_next_unapplied_transaction(fc::time_point::maximum()));
} FC_LOG_AND_DROP();
_timer.expires_from_now( boost::posix_time::microseconds(time_to_next_block_time) );
//_timer.async_wait(boost::bind(&producer_plugin_impl::block_production_loop, this));
_timer.async_wait( [&](const boost::system::error_code&){ block_production_loop(); } );
try {
while (chain.push_next_scheduled_transaction(fc::time_point::maximum()));
} FC_LOG_AND_DROP();
//_timer.async_wait(boost::bind(&producer_plugin_impl::block_production_loop, this));
_timer.async_wait([&](const boost::system::error_code& ec) {
if (ec != boost::asio::error::operation_aborted) {
block_production_loop();
}
});
} else {
elog("Failed to start a pending block, will try again later");
// we failed to start a block, so try again later?
_timer.async_wait([&](const boost::system::error_code& ec) {
if (ec != boost::asio::error::operation_aborted) {
schedule_production_loop();
}
});
}
}
block_production_condition::block_production_condition_enum producer_plugin_impl::block_production_loop() {
......@@ -320,19 +384,13 @@ block_production_condition::block_production_condition_enum producer_plugin_impl
// If the next block production opportunity is in the present or future, we're synced.
if( !_production_enabled )
{
if( hbs.get_slot_time(1) >= now )
if( hbs.header.timestamp.next().to_time_point() >= now )
_production_enabled = true;
else
return block_production_condition::not_synced;
}
// is anyone scheduled to produce now or one second in the future?
uint32_t slot = hbs.get_slot_at_time( now );
if( slot == 0 )
{
capture("next_time", hbs.get_slot_time(1));
return block_production_condition::not_time_yet;
}
auto pending_block_timestamp = chain.pending_block_state()->header.timestamp;
//
// this assert should not fail, because now <= db.head_block_time()
......@@ -344,7 +402,7 @@ block_production_condition::block_production_condition_enum producer_plugin_impl
//
assert( now > chain.head_block_time() );
const auto& scheduled_producer = hbs.get_scheduled_producer( slot );
const auto& scheduled_producer = hbs.get_scheduled_producer( pending_block_timestamp );
// we must control the producer scheduled to produce the next block.
if( _producers.find( scheduled_producer.producer_name ) == _producers.end() )
{
......@@ -352,7 +410,6 @@ block_production_condition::block_production_condition_enum producer_plugin_impl
return block_production_condition::not_my_turn;
}
auto scheduled_time = hbs.get_slot_time( slot );
auto private_key_itr = _private_keys.find( scheduled_producer.block_signing_key );
if( private_key_itr == _private_keys.end() )
......@@ -361,16 +418,16 @@ block_production_condition::block_production_condition_enum producer_plugin_impl
return block_production_condition::no_private_key;
}
uint32_t prate = hbs.producer_participation_rate();
/*uint32_t prate = hbs.producer_participation_rate();
if( prate < _required_producer_participation )
{
capture("pct", uint32_t(prate / config::percent_1));
return block_production_condition::low_participation;
}
}*/
if( llabs(( time_point(scheduled_time) - now).count()) > fc::milliseconds( config::block_interval_ms ).count() )
if( llabs(( pending_block_timestamp.to_time_point() - now).count()) > fc::milliseconds( config::block_interval_ms ).count() )
{
capture("scheduled_time", scheduled_time)("now", now);
capture("scheduled_time", pending_block_timestamp)("now", now);
return block_production_condition::lag;
}
......
......@@ -73,8 +73,6 @@ using namespace eosio::chain;
struct txn_test_gen_plugin_impl {
transaction_trace_ptr push_transaction( signed_transaction& trx ) { try {
controller& cc = app().get_plugin<chain_plugin>().chain();
if( !cc.pending_block_state() )
cc.start_block();
return cc.push_transaction( std::make_shared<transaction_metadata>(trx) );
} FC_CAPTURE_AND_RETHROW( (transaction_header(trx)) ) }
......
......@@ -410,6 +410,7 @@ fc::variant regproducer_variant(const account_name& producer,
("target_block_net_usage_pct", config::default_target_block_net_usage_pct)
("max_transaction_net_usage", config::default_max_transaction_net_usage)
("base_per_transaction_net_usage", config::default_base_per_transaction_net_usage)
("net_usage_leeway", config::default_net_usage_leeway)
("context_free_discount_net_usage_num", config::default_context_free_discount_net_usage_num)
("context_free_discount_net_usage_den", config::default_context_free_discount_net_usage_den)
("max_block_cpu_usage", config::default_max_block_cpu_usage)
......@@ -419,6 +420,7 @@ fc::variant regproducer_variant(const account_name& producer,
("base_per_action_cpu_usage", config::default_base_per_action_cpu_usage)
("base_setcode_cpu_usage", config::default_base_setcode_cpu_usage)
("per_signature_cpu_usage", config::default_per_signature_cpu_usage)
("cpu_usage_leeway", config::default_cpu_usage_leeway)
("context_free_discount_cpu_usage_num", config::default_context_free_discount_cpu_usage_num)
("context_free_discount_cpu_usage_den", config::default_context_free_discount_cpu_usage_den)
("max_transaction_lifetime", config::default_max_trx_lifetime)
......@@ -1654,7 +1656,7 @@ int main( int argc, char** argv ) {
if (!proposer.empty()) {
accountPermissions = vector<permission_level>{{proposer, config::active_name}};
} else {
EOS_THROW(tx_missing_auth, "Authority is not provided (either by multisig parameter <proposer> or -p)");
EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <proposer> or -p)");
}
}
if (proposer.empty()) {
......@@ -1787,7 +1789,7 @@ int main( int argc, char** argv ) {
if (!canceler.empty()) {
accountPermissions = vector<permission_level>{{canceler, config::active_name}};
} else {
EOS_THROW(tx_missing_auth, "Authority is not provided (either by multisig parameter <canceler> or -p)");
EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <canceler> or -p)");
}
}
if (canceler.empty()) {
......@@ -1819,7 +1821,7 @@ int main( int argc, char** argv ) {
if (!executer.empty()) {
accountPermissions = vector<permission_level>{{executer, config::active_name}};
} else {
EOS_THROW(tx_missing_auth, "Authority is not provided (either by multisig parameter <executer> or -p)");
EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <executer> or -p)");
}
}
if (executer.empty()) {
......
......@@ -1227,9 +1227,8 @@ BOOST_AUTO_TEST_CASE(account_ram_limit) { try {
BOOST_REQUIRE_EXCEPTION(
chain.create_account(N(acc4), acc1),
tx_resource_exhausted,
[] (const tx_resource_exhausted &e)->bool {
BOOST_REQUIRE_EQUAL(std::string("transaction exhausted allowed resources"), e.what());
ram_usage_exceeded,
[] (const ram_usage_exceeded &e)->bool {
return true;
}
);
......
......@@ -52,9 +52,11 @@ function build_contract {
done
($PRINT_CMDS; @WASM_LLVM_LINK@ -only-needed -o $workdir/linked.bc $workdir/built/* \
${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/libc.bc \
${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/eosiolib.bc \
${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/libc++.bc \
${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/eosiolib.bc
${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/libc.bc
)
($PRINT_CMDS; @WASM_LLC@ -thread-model=single --asm-verbose=false -o $workdir/assembly.s $workdir/linked.bc)
($PRINT_CMDS; ${EOSIO_INSTALL_DIR}/bin/eosio-s2wasm -o $outname -s 16384 $workdir/assembly.s)
......
......@@ -31,8 +31,18 @@ add_dependencies(unit_test asserter test_api test_api_mem test_api_db test_api_m
#Manually run unit_test for all supported runtimes
#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 \!auth_tests/linkauth_special -t \!eosio_system_tests/* -t \!delay_tests/* --report_level=detailed --color_output -- --binaryen)
add_test(NAME unit_test_wavm COMMAND unit_test -t \!auth_tests/linkauth_special -t \!eosio_system_tests/* -t \!delay_tests/* --report_level=detailed --color_output --catch_system_errors=no -- --wavm)
add_test(NAME unit_test_binaryen COMMAND unit_test
-t \!eosio_system_tests/*
-t \!delay_tests/*
-t \!currency_tests/test_deferred_failure
-t \!abi_tests/abigen_all_types
--report_level=detailed --color_output -- --binaryen)
add_test(NAME unit_test_wavm COMMAND unit_test
-t \!eosio_system_tests/*
-t \!delay_tests/*
-t \!currency_tests/test_deferred_failure
-t \!abi_tests/abigen_all_types
--report_level=detailed --color_output --catch_system_errors=no -- --wavm)
if(ENABLE_COVERAGE_TESTING)
......
......@@ -457,9 +457,7 @@ BOOST_FIXTURE_TEST_CASE(abigen_unknown_type, abi_gen_helper)
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(abigen_all_types, abi_gen_helper)
{
#if 0
try {
{ try {
const char* all_types = R"=====(
#include <eosiolib/types.hpp>
......@@ -663,9 +661,7 @@ BOOST_FIXTURE_TEST_CASE(abigen_all_types, abi_gen_helper)
)=====";
BOOST_TEST( generate_abi(all_types, all_types_abi) == true);
} FC_LOG_AND_RETHROW()
#endif
}
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(abigen_double_base, abi_gen_helper)
{ try {
......
此差异已折叠。
此差异已折叠。
......@@ -220,14 +220,18 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) {
}
ilog(".");
#warning Complete this test
/*
// Set code eosio.system from eosio.bios to eosio.system
set_code_abi(config::system_account_name, eosio_system_wast, eosio_system_abi);
ilog(".");
// Register these genesis accts as producer account
for (auto gen_acc : gen_accounts) {
// BOOST_REQUIRE_EQUAL(success(), regproducer(gen_acc));
}
*/
} FC_LOG_AND_RETHROW()
}
......
......@@ -78,7 +78,7 @@ BOOST_AUTO_TEST_SUITE(database_tests)
// Check the last irreversible block number is set correctly
const auto expected_last_irreversible_block_number = calc_exp_last_irr_block_num(num_of_blocks_to_prod) + 1;
BOOST_TEST(test.control->head_block_state()->dpos_last_irreversible_blocknum == expected_last_irreversible_block_number);
BOOST_TEST(test.control->head_block_state()->dpos_irreversible_blocknum == expected_last_irreversible_block_number);
// Check that block 201 cannot be found (only 20 blocks exist)
BOOST_TEST(test.control->fetch_block_by_number(num_of_blocks_to_prod + 1 + 1) == nullptr);
......@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_SUITE(database_tests)
const auto next_expected_last_irreversible_block_number = calc_exp_last_irr_block_num(
num_of_blocks_to_prod + next_num_of_blocks_to_prod) + 1;
// Check the last irreversible block number is updated correctly
BOOST_TEST(test.control->head_block_state()->dpos_last_irreversible_blocknum == next_expected_last_irreversible_block_number);
BOOST_TEST(test.control->head_block_state()->dpos_irreversible_blocknum == next_expected_last_irreversible_block_number);
// Check that block 201 can now be found
BOOST_CHECK_NO_THROW(test.control->fetch_block_by_number(num_of_blocks_to_prod + 1));
// Check the latest head block match
......
......@@ -2163,7 +2163,7 @@ BOOST_AUTO_TEST_CASE( canceldelay_test2 ) { try {
chain::canceldelay{{N(tester), N(first)}, trx_id});
chain.set_transaction_headers(trx);
trx.sign(chain.get_private_key(N(tester), "second"), chain_id_type());
BOOST_REQUIRE_THROW( chain.push_transaction(trx), tx_irrelevant_auth );
BOOST_REQUIRE_THROW( chain.push_transaction(trx), irrelevant_auth_exception );
}
// canceldelay with "active" permission for delayed transfer of 1.0000 CUR
......@@ -2289,7 +2289,7 @@ BOOST_AUTO_TEST_CASE( canceldelay_test2 ) { try {
chain::canceldelay{{N(tester), config::owner_name}, trx_id});
chain.set_transaction_headers(trx);
trx.sign(chain.get_private_key(N(tester), "active"), chain_id_type());
BOOST_REQUIRE_THROW( chain.push_transaction(trx), tx_irrelevant_auth );
BOOST_REQUIRE_THROW( chain.push_transaction(trx), irrelevant_auth_exception );
}
// canceldelay with "owner" permission for delayed transfer of 10.0000 CUR
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE( tic_tac_toe_game ) try {
("host", "player1")
("by", "player1")
("mvt", mvt)
), assert_exception, assert_message_contains("not your turn"));
), assert_exception, eosio_assert_message_starts_with("it's not your turn yet"));
mvt = mutable_variant_object()
("row", 1)
......@@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE( tic_tac_toe_game ) try {
("host", "player1")
("by", "player2")
("mvt", mvt)
), assert_exception, assert_message_contains("not a valid movement"));
), assert_exception, eosio_assert_message_starts_with("not a valid movement"));
mvt = mutable_variant_object()
("row", 0)
......@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE( tic_tac_toe_game ) try {
("host", "player1")
("by", "player2")
("mvt", mvt)
), assert_exception, assert_message_contains("the game has ended"));
), assert_exception, eosio_assert_message_starts_with("the game has ended"));
game current;
chain.get_table_entry(current, N(tic.tac.toe), N(player1), N(games), N(player2));
......@@ -181,13 +181,13 @@ BOOST_AUTO_TEST_CASE( tic_tac_toe_game ) try {
("host", "player1")
("by", "player2")
("mvt", mvt)
), assert_exception, assert_message_contains("game doesn't exists"));
), assert_exception, eosio_assert_message_starts_with("game doesn't exists"));
BOOST_CHECK_EXCEPTION(chain.push_action(N(tic.tac.toe), N(restart), N(player2), mutable_variant_object()
("challenger", "player2")
("host", "player1")
("by", "player2")
), assert_exception, assert_message_contains("game doesn't exists"));
), assert_exception, eosio_assert_message_starts_with("game doesn't exists"));
chain.push_action(N(tic.tac.toe), N(create), N(player2), mutable_variant_object()
("challenger", "player1")
......@@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE( tic_tac_toe_game ) try {
("host", "player2")
("by", "player2")
("mvt", mvt)
), assert_exception, assert_message_contains("not your turn"));
), assert_exception, eosio_assert_message_starts_with("it's not your turn yet"));
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册