未验证 提交 f343de7c 编写于 作者: K Kevin Heifner 提交者: GitHub

Merge pull request #615 from brianjohnson5972/80-comp-rate-limiting

STAT-80: Computational Rate Limiting
......@@ -39,7 +39,6 @@
#include <chrono>
namespace eos { namespace chain {
bool chain_controller::is_known_block(const block_id_type& id)const
{
return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
......@@ -871,22 +870,34 @@ void chain_controller::validate_expiration(const Transaction& trx) const
("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
uint32_t chain_controller::_transaction_message_rate(uint32_t now, uint32_t last_update_sec, uint32_t rate_limit_time_frame_sec,
uint32_t rate_limit, uint32_t previous_rate, const char* type, const AccountName& name)
uint32_t chain_controller::_transaction_message_rate(const fc::time_point_sec& now, const fc::time_point_sec& last_update_sec, const fc::time_point_sec& rate_limit_time_frame_sec,
uint32_t rate_limit, uint32_t previous_rate, rate_limit_type type, const AccountName& name)
{
const auto delta_time = now - last_update_sec;
const fc::time_point_sec delta_time = fc::time_point(now - last_update_sec);
uint32_t message_count = 1;
if (delta_time <= rate_limit_time_frame_sec)
{
message_count += ( ( ( rate_limit_time_frame_sec - delta_time ) * fc::uint128( previous_rate ) )
/ rate_limit_time_frame_sec ).to_uint64();
EOS_ASSERT(message_count <= rate_limit, tx_msgs_exceeded,
"Rate limiting ${type}=${name} messages sent, ${count} exceeds ${max} messages limit per ${sec} seconds. Wait 1 second and try again",
("type",type)
("name",name)
("count",message_count)
("max",rate_limit)
("sec", rate_limit_time_frame_sec));
message_count += ( ( ( rate_limit_time_frame_sec - delta_time ).to_seconds() * fc::uint128( previous_rate ) )
/ rate_limit_time_frame_sec.sec_since_epoch() ).to_uint64();
#define RATE_LIMIT_ASSERT(tx_msgs_exceeded, type_str) \
EOS_ASSERT(message_count <= rate_limit, tx_msgs_exceeded, \
"Rate limiting ${type} account=${name} messages sent, ${count} exceeds ${max} messages limit per ${sec} seconds. Wait 1 second and try again", \
("type",type_str) \
("name",name) \
("count",message_count) \
("max",rate_limit) \
("sec", rate_limit_time_frame_sec))
switch (type)
{
case authorization_account:
RATE_LIMIT_ASSERT(tx_msgs_auth_exceeded, "authorization");
break;
case code_account:
RATE_LIMIT_ASSERT(tx_msgs_code_exceeded, "code");
break;
default:
FC_ASSERT(true, "Undefined rate_limit_type: ${rlt}", ("rlt", (uint32_t)type));
}
}
return message_count;
......@@ -894,29 +905,52 @@ uint32_t chain_controller::_transaction_message_rate(uint32_t now, uint32_t last
void chain_controller::rate_limit_message(const Message& message)
{ try {
const auto now = head_block_time();
// per authorization rate limiting
for (const auto& permission : message.authorization)
{
auto rate_limiting = _db.find<rate_limiting_object, by_name>(permission.account);
const auto now = head_block_time().sec_since_epoch();
if (rate_limiting == nullptr)
{
_db.create<rate_limiting_object>([&](rate_limiting_object& rlo) {
rlo.name = permission.account;
rlo.trans_msg_rate_per_account = 1;
rlo.last_update_sec = now;
rlo.per_auth_account_txn_msg_rate = 1;
rlo.per_auth_account_last_update_sec = now;
});
}
else
{
const auto message_rate =
_transaction_message_rate(now, rate_limiting->last_update_sec, _per_scope_trans_msg_rate_limit_time_frame_sec,
_per_scope_trans_msg_rate_limit, rate_limiting->trans_msg_rate_per_account, "account", permission.account);
_transaction_message_rate(now, rate_limiting->per_auth_account_last_update_sec, _per_auth_account_txn_msg_rate_limit_time_frame_sec,
_per_auth_account_txn_msg_rate_limit, rate_limiting->per_auth_account_txn_msg_rate, authorization_account, permission.account);
_db.modify(*rate_limiting, [&] (rate_limiting_object& rlo) {
rlo.trans_msg_rate_per_account = message_rate;
rlo.last_update_sec = now;
rlo.per_auth_account_txn_msg_rate = message_rate;
rlo.per_auth_account_last_update_sec = now;
});
}
}
// per code rate limiting
auto rate_limiting = _db.find<rate_limiting_object, by_name>(message.code);
if (rate_limiting == nullptr)
{
_db.create<rate_limiting_object>([&](rate_limiting_object& rlo) {
rlo.name = message.code;
rlo.per_code_account_txn_msg_rate = 1;
rlo.per_code_account_last_update_sec = now;
});
}
else
{
const auto message_rate =
_transaction_message_rate(now, rate_limiting->per_code_account_last_update_sec, _per_code_account_txn_msg_rate_limit_time_frame_sec,
_per_code_account_txn_msg_rate_limit, rate_limiting->per_code_account_txn_msg_rate, code_account, message.code);
_db.modify(*rate_limiting, [&] (rate_limiting_object& rlo) {
rlo.per_code_account_txn_msg_rate = message_rate;
rlo.per_code_account_last_update_sec = now;
});
}
} FC_CAPTURE_AND_RETHROW((message)) }
void chain_controller::process_message(const Transaction& trx, AccountName code,
......@@ -986,10 +1020,10 @@ void chain_controller::apply_message(apply_context& context)
//idump((context.code)(context.msg.type));
const uint32_t execution_time =
_skip_flags | received_block
? _rcvd_block_trans_execution_time
? _rcvd_block_txn_execution_time
: _skip_flags | created_block
? _create_block_trans_execution_time
: _trans_execution_time;
? _create_block_txn_execution_time
: _txn_execution_time;
const bool is_received_block = _skip_flags & received_block;
wasm_interface::get().apply(context, execution_time, is_received_block);
}
......@@ -1205,12 +1239,15 @@ void chain_controller::initialize_chain(chain_initializer_interface& starter)
chain_controller::chain_controller(database& database, fork_database& fork_db, block_log& blocklog,
chain_initializer_interface& starter, unique_ptr<chain_administration_interface> admin,
uint32_t trans_execution_time, uint32_t rcvd_block_trans_execution_time,
uint32_t create_block_trans_execution_time, uint32_t per_scope_trans_msg_rate_limit_time_frame_sec,
uint32_t per_scope_trans_msg_rate_limit)
: _db(database), _fork_db(fork_db), _block_log(blocklog), _admin(std::move(admin)), _trans_execution_time(trans_execution_time),
_rcvd_block_trans_execution_time(rcvd_block_trans_execution_time), _create_block_trans_execution_time(create_block_trans_execution_time),
_per_scope_trans_msg_rate_limit_time_frame_sec(per_scope_trans_msg_rate_limit_time_frame_sec), _per_scope_trans_msg_rate_limit(per_scope_trans_msg_rate_limit) {
uint32_t txn_execution_time, uint32_t rcvd_block_txn_execution_time,
uint32_t create_block_txn_execution_time,
const txn_msg_rate_limits& rate_limit)
: _db(database), _fork_db(fork_db), _block_log(blocklog), _admin(std::move(admin)), _txn_execution_time(txn_execution_time),
_rcvd_block_txn_execution_time(rcvd_block_txn_execution_time), _create_block_txn_execution_time(create_block_txn_execution_time),
_per_auth_account_txn_msg_rate_limit_time_frame_sec(rate_limit.per_auth_account_time_frame_sec),
_per_auth_account_txn_msg_rate_limit(rate_limit.per_auth_account),
_per_code_account_txn_msg_rate_limit_time_frame_sec(rate_limit.per_code_account_time_frame_sec),
_per_code_account_txn_msg_rate_limit(rate_limit.per_code_account) {
initialize_indexes();
starter.register_types(*this, _db);
......
......@@ -36,11 +36,13 @@ namespace eos { namespace chain {
*/
class chain_controller {
public:
struct txn_msg_rate_limits;
chain_controller(database& database, fork_database& fork_db, block_log& blocklog,
chain_initializer_interface& starter, unique_ptr<chain_administration_interface> admin,
uint32_t trans_execution_time, uint32_t rcvd_block_trans_execution_time,
uint32_t create_block_trans_execution_time, uint32_t per_scope_trans_msg_rate_limit_time_frame_sec,
uint32_t per_scope_trans_msg_rate_limit);
uint32_t txn_execution_time, uint32_t rcvd_block_txn_execution_time,
uint32_t create_block_txn_execution_time,
const txn_msg_rate_limits& rate_limit);
chain_controller(chain_controller&&) = default;
~chain_controller();
......@@ -257,6 +259,15 @@ namespace eos { namespace chain {
const deque<SignedTransaction>& pending()const { return _pending_transactions; }
/**
* Enum to indicate what type of rate limiting is being performed.
*/
enum rate_limit_type
{
authorization_account,
code_account
};
/**
* Determine what the current message rate is.
* @param now The current block time seconds
......@@ -264,13 +275,22 @@ namespace eos { namespace chain {
* @param rate_limit_time_frame_sec The time frame, in seconds, that the rate limit is over
* @param rate_limit The rate that is not allowed to be exceeded
* @param previous_rate The rate at the last_update_sec
* @param type The string type description (for logging errors)
* @param type The type of the rate limit
* @param name The account name associated with this rate (for logging errors)
* @return the calculated rate at this time
* @throws tx_msgs_exceeded if current message rate exceeds the passed in rate_limit
* @throws tx_msgs_auth_exceeded if current message rate exceeds the passed in rate_limit, and type is authorization_account
* @throws tx_msgs_code_exceeded if current message rate exceeds the passed in rate_limit, and type is code_account
*/
static uint32_t _transaction_message_rate(uint32_t now, uint32_t last_update_sec, uint32_t rate_limit_time_frame_sec,
uint32_t rate_limit, uint32_t previous_rate, const char* type, const AccountName& name);
static uint32_t _transaction_message_rate(const fc::time_point_sec& now, const fc::time_point_sec& last_update_sec, const fc::time_point_sec& rate_limit_time_frame_sec,
uint32_t rate_limit, uint32_t previous_rate, rate_limit_type type, const AccountName& name);
struct txn_msg_rate_limits {
fc::time_point_sec per_auth_account_time_frame_sec = fc::time_point_sec(config::DefaultPerAuthAccountTimeFrameSeconds);
uint32_t per_auth_account = config::DefaultPerAuthAccount;
fc::time_point_sec per_code_account_time_frame_sec = fc::time_point_sec(config::DefaultPerCodeAccountTimeFrameSeconds);
uint32_t per_code_account = config::DefaultPerCodeAccount;
};
private:
/// Reset the object graph in-memory
......@@ -347,7 +367,8 @@ namespace eos { namespace chain {
/**
* Calculate all rates associated with the given message and enforce rate limiting.
* @param message The message to calculate
* @throws tx_msgs_exceeded if any of the calculated message rates exceed the configured rate limit
* @throws tx_msgs_auth_exceeded if any of the calculated message rates exceed the configured authorization account rate limit
* @throws tx_msgs_code_exceeded if the calculated message rate exceed the configured code account rate limit
*/
void rate_limit_message(const Message& message);
......@@ -388,11 +409,13 @@ namespace eos { namespace chain {
bool _currently_applying_block = false;
uint64_t _skip_flags = 0;
const uint32_t _trans_execution_time;
const uint32_t _rcvd_block_trans_execution_time;
const uint32_t _create_block_trans_execution_time;
const uint32_t _per_scope_trans_msg_rate_limit_time_frame_sec;
const uint32_t _per_scope_trans_msg_rate_limit;
const uint32_t _txn_execution_time;
const uint32_t _rcvd_block_txn_execution_time;
const uint32_t _create_block_txn_execution_time;
const fc::time_point_sec _per_auth_account_txn_msg_rate_limit_time_frame_sec;
const uint32_t _per_auth_account_txn_msg_rate_limit;
const fc::time_point_sec _per_code_account_txn_msg_rate_limit_time_frame_sec;
const uint32_t _per_code_account_txn_msg_rate_limit;
flat_map<uint32_t,block_id_type> _checkpoints;
......
......@@ -34,6 +34,11 @@ const static int BlockIntervalSeconds = 3;
const static int Percent100 = 10000;
const static int Percent1 = 100;
const static int DefaultPerAuthAccountTimeFrameSeconds = 18;
const static int DefaultPerAuthAccount = 1800;
const static int DefaultPerCodeAccountTimeFrameSeconds = 18;
const static int DefaultPerCodeAccount = 18000;
const static UInt32 DefaultMaxBlockSize = 5 * 1024 * 1024;
const static UInt32 DefaultTargetBlockSize = 128 * 1024;
const static UInt64 DefaultMaxStorageSize = 10 * 1024;
......
......@@ -42,7 +42,8 @@ namespace eos { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( tx_resource_exhausted, eos::chain::transaction_exception, 3030015, "transaction exhausted allowed resources" )
FC_DECLARE_DERIVED_EXCEPTION( page_memory_error, eos::chain::transaction_exception, 3030016, "error in WASM page memory" )
FC_DECLARE_DERIVED_EXCEPTION( unsatisfied_permission, eos::chain::transaction_exception, 3030017, "Unsatisfied permission" )
FC_DECLARE_DERIVED_EXCEPTION( tx_msgs_exceeded, eos::chain::transaction_exception, 3030018, "Number of transaction messages per authorized account has been exceeded" )
FC_DECLARE_DERIVED_EXCEPTION( tx_msgs_auth_exceeded, eos::chain::transaction_exception, 3030018, "Number of transaction messages per authorized account has been exceeded" )
FC_DECLARE_DERIVED_EXCEPTION( tx_msgs_code_exceeded, eos::chain::transaction_exception, 3030019, "Number of transaction messages per code account has been exceeded" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, eos::chain::utility_exception, 3060001, "invalid pts address" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, eos::chain::chain_exception, 37006, "insufficient feeds" )
......
......@@ -23,8 +23,10 @@ namespace eos { namespace chain {
OBJECT_CTOR(rate_limiting_object)
id_type id;
AccountName name;
uint32_t last_update_sec = 0;
uint32_t trans_msg_rate_per_account = 0;
fc::time_point_sec per_auth_account_last_update_sec;
uint32_t per_auth_account_txn_msg_rate = 0;
fc::time_point_sec per_code_account_last_update_sec;
uint32_t per_code_account_txn_msg_rate = 0;
};
using rate_limiting_id_type = rate_limiting_object::id_type;
......@@ -43,4 +45,4 @@ CHAINBASE_SET_INDEX_TYPE(eos::chain::rate_limiting_object, eos::chain::rate_limi
FC_REFLECT(chainbase::oid<eos::chain::rate_limiting_object>, (_id))
FC_REFLECT(eos::chain::rate_limiting_object, (id)(name)(last_update_sec)(trans_msg_rate_per_account))
FC_REFLECT(eos::chain::rate_limiting_object, (id)(name)(per_auth_account_last_update_sec)(per_auth_account_txn_msg_rate)(per_code_account_last_update_sec)(per_code_account_txn_msg_rate))
......@@ -37,6 +37,7 @@ using chain::key64x64x64_value_object;
using chain::by_name;
using chain::by_scope_primary;
using chain::uint128_t;
using txn_msg_rate_limits = chain_controller::txn_msg_rate_limits;
class chain_plugin_impl {
......@@ -52,27 +53,22 @@ public:
fc::optional<block_log> block_logger;
fc::optional<chain_controller> chain;
chain_id_type chain_id;
uint32_t rcvd_block_trans_execution_time;
uint32_t trans_execution_time;
uint32_t create_block_trans_execution_time;
uint32_t per_scope_trans_msg_rate_limit_time_frame_sec;
uint32_t per_scope_trans_msg_rate_limit;
uint32_t rcvd_block_txn_execution_time;
uint32_t txn_execution_time;
uint32_t create_block_txn_execution_time;
txn_msg_rate_limits rate_limits;
};
#ifdef NDEBUG
const uint32_t chain_plugin::DEFAULT_RECEIVED_BLOCK_TRANSACTION_EXECUTION_TIME = 12;
const uint32_t chain_plugin::DEFAULT_TRANSACTION_EXECUTION_TIME = 3;
const uint32_t chain_plugin::DEFAULT_CREATE_BLOCK_TRANSACTION_EXECUTION_TIME = 3;
const uint32_t chain_plugin::default_received_block_transaction_execution_time = 12;
const uint32_t chain_plugin::default_transaction_execution_time = 3;
const uint32_t chain_plugin::default_create_block_transaction_execution_time = 3;
#else
const uint32_t chain_plugin::DEFAULT_RECEIVED_BLOCK_TRANSACTION_EXECUTION_TIME = 72;
const uint32_t chain_plugin::DEFAULT_TRANSACTION_EXECUTION_TIME = 18;
const uint32_t chain_plugin::DEFAULT_CREATE_BLOCK_TRANSACTION_EXECUTION_TIME = 18;
const uint32_t chain_plugin::default_received_block_transaction_execution_time = 72;
const uint32_t chain_plugin::default_transaction_execution_time = 18;
const uint32_t chain_plugin::default_create_block_transaction_execution_time = 18;
#endif
const uint32_t chain_plugin::DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT_TIME_FRAME_SECONDS = 18;
const uint32_t chain_plugin::DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT = 1800;
chain_plugin::chain_plugin()
:my(new chain_plugin_impl()) {
......@@ -88,16 +84,20 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
("block-log-dir", bpo::value<bfs::path>()->default_value("blocks"),
"the location of the block log (absolute path or relative to application data dir)")
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("rcvd-block-trans-execution-time", bpo::value<uint32_t>()->default_value(DEFAULT_RECEIVED_BLOCK_TRANSACTION_EXECUTION_TIME),
("rcvd-block-trans-execution-time", bpo::value<uint32_t>()->default_value(default_received_block_transaction_execution_time),
"Limits the maximum time (in milliseconds) that is allowed a transaction's code to execute from a received block.")
("trans-execution-time", bpo::value<uint32_t>()->default_value(DEFAULT_TRANSACTION_EXECUTION_TIME),
("trans-execution-time", bpo::value<uint32_t>()->default_value(default_transaction_execution_time),
"Limits the maximum time (in milliseconds) that is allowed a pushed transaction's code to execute.")
("create-block-trans-execution-time", bpo::value<uint32_t>()->default_value(DEFAULT_CREATE_BLOCK_TRANSACTION_EXECUTION_TIME),
("create-block-trans-execution-time", bpo::value<uint32_t>()->default_value(default_create_block_transaction_execution_time),
"Limits the maximum time (in milliseconds) that is allowed a transaction's code to execute while creating a block.")
("per-scope-transaction-msg-rate-limit-time-frame-sec", bpo::value<uint32_t>()->default_value(DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT_TIME_FRAME_SECONDS),
"The time frame, in seconds, that the per-scope-transaction-msg-rate-limit is imposed over.")
("per-scope-transaction-msg-rate-limit", bpo::value<uint32_t>()->default_value(DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT),
"Limits the maximum rate of transaction messages that an account is allowed each per-scope-transaction-msg-rate-limit-time-frame-sec.")
("per-authorized-account-transaction-msg-rate-limit-time-frame-sec", bpo::value<uint32_t>()->default_value(config::DefaultPerAuthAccountTimeFrameSeconds),
"The time frame, in seconds, that the per-authorized-account-transaction-msg-rate-limit is imposed over.")
("per-authorized-account-transaction-msg-rate-limit", bpo::value<uint32_t>()->default_value(config::DefaultPerAuthAccount),
"Limits the maximum rate of transaction messages that an account is allowed each per-authorized-account-transaction-msg-rate-limit-time-frame-sec.")
("per-code-account-transaction-msg-rate-limit-time-frame-sec", bpo::value<uint32_t>()->default_value(config::DefaultPerCodeAccountTimeFrameSeconds),
"The time frame, in seconds, that the per-code-account-transaction-msg-rate-limit is imposed over.")
("per-code-account-transaction-msg-rate-limit", bpo::value<uint32_t>()->default_value(config::DefaultPerCodeAccount),
"Limits the maximum rate of transaction messages that an account's code is allowed each per-code-account-transaction-msg-rate-limit-time-frame-sec.")
;
cli.add_options()
("replay-blockchain", bpo::bool_switch()->default_value(false),
......@@ -173,12 +173,15 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
}
}
my->rcvd_block_trans_execution_time = options.at("rcvd-block-trans-execution-time").as<uint32_t>() * 1000;
my->trans_execution_time = options.at("trans-execution-time").as<uint32_t>() * 1000;
my->create_block_trans_execution_time = options.at("create-block-trans-execution-time").as<uint32_t>() * 1000;
my->rcvd_block_txn_execution_time = options.at("rcvd-block-trans-execution-time").as<uint32_t>() * 1000;
my->txn_execution_time = options.at("trans-execution-time").as<uint32_t>() * 1000;
my->create_block_txn_execution_time = options.at("create-block-trans-execution-time").as<uint32_t>() * 1000;
my->rate_limits.per_auth_account_time_frame_sec = fc::time_point_sec(options.at("per-authorized-account-transaction-msg-rate-limit-time-frame-sec").as<uint32_t>());
my->rate_limits.per_auth_account = options.at("per-authorized-account-transaction-msg-rate-limit").as<uint32_t>();
my->per_scope_trans_msg_rate_limit_time_frame_sec = options.at("per-scope-transaction-msg-rate-limit-time-frame-sec").as<uint32_t>();
my->per_scope_trans_msg_rate_limit = options.at("per-scope-transaction-msg-rate-limit").as<uint32_t>();
my->rate_limits.per_code_account_time_frame_sec = fc::time_point_sec(options.at("per-code-account-transaction-msg-rate-limit-time-frame-sec").as<uint32_t>());
my->rate_limits.per_code_account = options.at("per-code-account-transaction-msg-rate-limit").as<uint32_t>();
}
void chain_plugin::plugin_startup()
......@@ -201,11 +204,10 @@ void chain_plugin::plugin_startup()
my->chain_id = genesis.compute_chain_id();
my->chain = chain_controller(db, *my->fork_db, *my->block_logger,
initializer, native_contract::make_administrator(),
my->trans_execution_time,
my->rcvd_block_trans_execution_time,
my->create_block_trans_execution_time,
my->per_scope_trans_msg_rate_limit_time_frame_sec,
my->per_scope_trans_msg_rate_limit);
my->txn_execution_time,
my->rcvd_block_txn_execution_time,
my->create_block_txn_execution_time,
my->rate_limits);
if(!my->readonly) {
ilog("starting chain in read/write mode");
......
......@@ -298,12 +298,9 @@ public:
void get_chain_id (chain::chain_id_type &cid) const;
static const uint32_t DEFAULT_RECEIVED_BLOCK_TRANSACTION_EXECUTION_TIME;
static const uint32_t DEFAULT_TRANSACTION_EXECUTION_TIME;
static const uint32_t DEFAULT_CREATE_BLOCK_TRANSACTION_EXECUTION_TIME;
static const uint32_t DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT_TIME_FRAME_SECONDS;
static const uint32_t DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT;
static const uint32_t default_received_block_transaction_execution_time;
static const uint32_t default_transaction_execution_time;
static const uint32_t default_create_block_transaction_execution_time;
private:
unique_ptr<class chain_plugin_impl> my;
......
......@@ -92,25 +92,13 @@ flat_set<public_key_type> testing_fixture::available_keys() const {
}
testing_blockchain::testing_blockchain(chainbase::database& db, fork_database& fork_db, block_log& blocklog,
chain_initializer_interface& initializer, testing_fixture& fixture)
chain_initializer_interface& initializer, testing_fixture& fixture,
const chain_controller::txn_msg_rate_limits& rate_limits)
: chain_controller(db, fork_db, blocklog, initializer, native_contract::make_administrator(),
::eos::chain_plugin::DEFAULT_TRANSACTION_EXECUTION_TIME * 1000,
::eos::chain_plugin::DEFAULT_RECEIVED_BLOCK_TRANSACTION_EXECUTION_TIME * 1000,
::eos::chain_plugin::DEFAULT_CREATE_BLOCK_TRANSACTION_EXECUTION_TIME * 1000,
::eos::chain_plugin::DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT_TIME_FRAME_SECONDS,
::eos::chain_plugin::DEFAULT_PER_SCOPE_TRANSACTION_MSG_RATE_LIMIT),
db(db),
fixture(fixture) {}
testing_blockchain::testing_blockchain(chainbase::database& db, fork_database& fork_db, block_log& blocklog,
chain_initializer_interface& initializer, testing_fixture& fixture,
uint32_t rate_limit_time_frame_sec, uint32_t rate_limit)
: chain_controller(db, fork_db, blocklog, initializer, native_contract::make_administrator(),
::eos::chain_plugin::DEFAULT_TRANSACTION_EXECUTION_TIME * 1000,
::eos::chain_plugin::DEFAULT_RECEIVED_BLOCK_TRANSACTION_EXECUTION_TIME * 1000,
::eos::chain_plugin::DEFAULT_CREATE_BLOCK_TRANSACTION_EXECUTION_TIME * 1000,
rate_limit_time_frame_sec,
rate_limit),
::eos::chain_plugin::default_transaction_execution_time * 1000,
::eos::chain_plugin::default_received_block_transaction_execution_time * 1000,
::eos::chain_plugin::default_create_block_transaction_execution_time * 1000,
rate_limits),
db(db),
fixture(fixture) {}
......
......@@ -125,11 +125,8 @@ protected:
class testing_blockchain : public chain_controller {
public:
testing_blockchain(chainbase::database& db, fork_database& fork_db, block_log& blocklog,
chain_initializer_interface& initializer, testing_fixture& fixture);
testing_blockchain(chainbase::database& db, fork_database& fork_db, block_log& blocklog,
chain_initializer_interface& initializer, testing_fixture& fixture,
uint32_t rate_limit_time_frame_sec, uint32_t rate_limit);
chain_initializer_interface& initializer, testing_fixture& fixture,
const chain_controller::txn_msg_rate_limits& rate_limits = chain_controller::txn_msg_rate_limits());
/**
* @brief Publish the provided contract to the blockchain, owned by owner
......
......@@ -25,12 +25,17 @@
native_contract::native_contract_chain_initializer name ## _initializer(genesis_state()); \
testing_blockchain name(name ## _db, name ## _fdb, name ## _log, name ## _initializer, *this); \
BOOST_TEST_CHECKPOINT("Created blockchain " << #name);
#define MKCHAIN3(name,rate_limit_time_frame_sec,rate_limit) \
#define MKCHAIN5(name,per_auth_account_rate_limit_time_frame_sec,per_auth_account_rate_limit,per_code_account_rate_limit_time_frame_sec,per_code_account_rate_limit) \
chainbase::database name ## _db(get_temp_dir(), chainbase::database::read_write, TEST_DB_SIZE); \
block_log name ## _log(get_temp_dir() / "blocklog"); \
fork_database name ## _fdb; \
native_contract::native_contract_chain_initializer name ## _initializer(genesis_state()); \
testing_blockchain name(name ## _db, name ## _fdb, name ## _log, name ## _initializer, *this,rate_limit_time_frame_sec,rate_limit); \
chain_controller::txn_msg_rate_limits rate_limits; \
rate_limits.per_auth_account_time_frame_sec = per_auth_account_rate_limit_time_frame_sec; \
rate_limits.per_auth_account = per_auth_account_rate_limit; \
rate_limits.per_code_account_time_frame_sec = per_code_account_rate_limit_time_frame_sec; \
rate_limits.per_code_account = per_code_account_rate_limit; \
testing_blockchain name(name ## _db, name ## _fdb, name ## _log, name ## _initializer, *this, rate_limits); \
BOOST_TEST_CHECKPOINT("Created blockchain " << #name);
#define MKCHAINS_MACRO(x, y, name) Make_Blockchain(name)
......
......@@ -314,7 +314,7 @@ void WithdrawCurrency( testing_blockchain& chain, AccountName from, AccountName
//Test account script processing
BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture)
{ try {
Make_Blockchain(chain, 10, 100000);
Make_Blockchain(chain, fc::time_point_sec(10), 100000, fc::time_point_sec(10), 100000);
chain.produce_blocks(10);
Make_Account(chain, currency);
chain.produce_blocks(1);
......
......@@ -19,7 +19,7 @@
using namespace eos;
using namespace chain;
using rate_limiting_type = eos::chain::testing_blockchain::rate_limit_type;
BOOST_AUTO_TEST_SUITE(chain_tests)
......@@ -55,130 +55,132 @@ BOOST_FIXTURE_TEST_CASE(get_required_keys, testing_fixture)
} FC_LOG_AND_RETHROW() }
// Test chain_controller::_transaction_message_rate message rate calculation
BOOST_FIXTURE_TEST_CASE(transaction_msg_rate_calculation, testing_fixture)
template< typename tx_msgs_exceeded >
void transaction_msg_rate_calculation(rate_limiting_type account_type)
{ try {
uint32_t now = 0;
uint32_t last_update_sec = 0;
uint32_t rate_limit_time_frame_sec = 10;
fc::time_point_sec now(0);
auto last_update_sec = now;
fc::time_point_sec rate_limit_time_frame_sec(10);
uint32_t rate_limit = 10;
uint32_t previous_rate = 9;
auto rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(10, rate);
previous_rate = 10;
BOOST_CHECK_EXCEPTION(eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name)),\
rate_limit, previous_rate, account_type, N(my.name)),\
tx_msgs_exceeded,
[](tx_msgs_exceeded const & e) -> bool { return true; } );
last_update_sec = 10;
now = 11;
last_update_sec = fc::time_point_sec(10);
now = fc::time_point_sec(11);
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(10, rate);
now = 12;
now = fc::time_point_sec(12);
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(9, rate);
now = 13;
now = fc::time_point_sec(13);
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(8, rate);
now = 19;
now = fc::time_point_sec(19);
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(2, rate);
now = 19;
now = fc::time_point_sec(19);
// our scenario will never have a previous_rate higher than max (since it was limited) but just checking algorithm
previous_rate = 90;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(10, rate);
now = 20;
now = fc::time_point_sec(20);
// our scenario will never have a previous_rate higher than max (since it was limited) but just checking algorithm
previous_rate = 10000;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(1, rate);
now = 2000;
now = fc::time_point_sec(2000);
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(1, rate);
rate_limit_time_frame_sec = 10000;
now = 2010;
rate_limit_time_frame_sec = fc::time_point_sec(10000);
now = fc::time_point_sec(2010);
previous_rate = 10;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(9, rate);
rate_limit = 10000;
now = 10000;
last_update_sec = 9999;
now = fc::time_point_sec(10000);
last_update_sec = fc::time_point_sec(9999);
previous_rate = 10000;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name));
rate_limit, previous_rate, account_type, N(my.name));
BOOST_CHECK_EQUAL(10000, rate);
last_update_sec = 10000;
last_update_sec = fc::time_point_sec(10000);
BOOST_CHECK_EXCEPTION(eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, previous_rate, "account", N(my.name)),\
rate_limit, previous_rate, account_type, N(my.name)),\
tx_msgs_exceeded,
[](tx_msgs_exceeded const & e) -> bool { return true; } );
} FC_LOG_AND_RETHROW() }
// Test chain_controller::_transaction_message_rate message rate calculation
BOOST_FIXTURE_TEST_CASE(transaction_msg_rate_running_calculation, testing_fixture)
template< typename tx_msgs_exceeded >
void transaction_msg_rate_running_calculation(rate_limiting_type account_type)
{ try {
uint32_t now = 1000;
uint32_t last_update_sec = 1000;
uint32_t rate_limit_time_frame_sec = 1000;
fc::time_point_sec now(1000);
auto last_update_sec = now;
auto rate_limit_time_frame_sec = now;
uint32_t rate_limit = 1000;
uint32_t rate = 0;
for (uint32_t i = 0; i < 1000; ++i)
{
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
}
BOOST_REQUIRE_EQUAL(1000, rate);
BOOST_REQUIRE_EXCEPTION(eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name)),\
rate_limit, rate, account_type, N(my.name)),\
tx_msgs_exceeded,
[](tx_msgs_exceeded const & e) -> bool { return true; } );
++now;
now += 1;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
BOOST_REQUIRE_EQUAL(1000, rate);
last_update_sec = now;
BOOST_REQUIRE_EXCEPTION(eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name)),\
rate_limit, rate, account_type, N(my.name)),\
tx_msgs_exceeded,
[](tx_msgs_exceeded const & e) -> bool { return true; } );
now += 10;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
last_update_sec = now;
for (uint32_t i = 0; i < 9; ++i)
{
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
}
BOOST_REQUIRE_EQUAL(1000, rate);
BOOST_REQUIRE_EXCEPTION(eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name)),\
rate_limit, rate, account_type, N(my.name)),\
tx_msgs_exceeded,
[](tx_msgs_exceeded const & e) -> bool { return true; } );
......@@ -187,26 +189,46 @@ BOOST_FIXTURE_TEST_CASE(transaction_msg_rate_running_calculation, testing_fixtur
now += 10;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
last_update_sec = now;
for (uint32_t i = 0; i < 9; ++i)
{
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
}
BOOST_REQUIRE_EQUAL(1000, rate);
BOOST_REQUIRE_EXCEPTION(eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name)),\
rate_limit, rate, account_type, N(my.name)),\
tx_msgs_exceeded,
[](tx_msgs_exceeded const & e) -> bool { return true; } );
}
now += 100;
rate = eos::chain::chain_controller::_transaction_message_rate(now, last_update_sec, rate_limit_time_frame_sec,
rate_limit, rate, "account", N(my.name));
rate_limit, rate, account_type, N(my.name));
BOOST_REQUIRE_EQUAL(901, rate);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(authorization_transaction_msg_rate_calculation, testing_fixture)
{
transaction_msg_rate_calculation<tx_msgs_auth_exceeded>(rate_limiting_type::authorization_account);
}
BOOST_FIXTURE_TEST_CASE(authorization_transaction_msg_rate_running_calculation, testing_fixture)
{
transaction_msg_rate_running_calculation<tx_msgs_auth_exceeded>(rate_limiting_type::authorization_account);
}
BOOST_FIXTURE_TEST_CASE(code_transaction_msg_rate_calculation, testing_fixture)
{
transaction_msg_rate_calculation<tx_msgs_code_exceeded>(rate_limiting_type::code_account);
}
BOOST_FIXTURE_TEST_CASE(code_transaction_msg_rate_running_calculation, testing_fixture)
{
transaction_msg_rate_running_calculation<tx_msgs_code_exceeded>(rate_limiting_type::code_account);
}
BOOST_AUTO_TEST_SUITE_END()
......@@ -4,40 +4,27 @@
*/
#include <eoslib/message.h>
#include <eoslib/types.hpp>
#include "rate_limit_auth.hpp"
#include <currency/currency.hpp>
extern "C" {
void init()
{
}
void test_auths2(const rate_limit_auth::Auths2& auth)
void test_auths(const currency::Transfer& auth)
{
eos::print("auths2\n");
requireAuth( auth.acct1 );
requireAuth( auth.acct2 );
}
void test_auths3(const rate_limit_auth::Auths3& auth)
{
eos::print("auths3\n");
requireAuth( auth.acct1 );
requireAuth( auth.acct2 );
requireAuth( auth.acct3 );
requireAuth( auth.from );
requireAuth( auth.to );
}
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t action )
{
if( code == N(test1) )
if( code == N(test1) || code == N(test5) )
{
if( action == N(auth2) )
{
test_auths2( eos::currentMessage< rate_limit_auth::Auths2 >() );
}
else if( action == N(auth3) )
if( action == N(transfer) )
{
test_auths3( eos::currentMessage< rate_limit_auth::Auths3 >() );
test_auths( eos::currentMessage< currency::Transfer >() );
}
}
}
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eoslib/eos.hpp>
#include <eoslib/token.hpp>
#include <eoslib/db.hpp>
/**
* @defgroup testcontract Test Contract
* @brief Test Contract
* @ingroup contractapi
*
*/
namespace rate_limit_auth {
/**
* @defgroup ratelimitcontract Test Contract for rate limiting
* @brief Test Contract for verifying rate limiting on all authorities
* @ingroup testcontract
*
* @{
*/
/**
* Mesage with 2 authorities
*/
struct Auths2 {
AccountName acct1;
AccountName acct2;
};
/**
* Mesage with 3 authorities
*/
struct Auths3 {
AccountName acct1;
AccountName acct2;
AccountName acct3;
};
} /// @} /// ratelimitcontract
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册