提交 32c5af26 编写于 作者: A arhag

Merge branch 'master' into enforce-new-transaction-header-parameters

......@@ -4,10 +4,11 @@ Simple and fast setup of EOS.IO on Docker is also available.
## Install Dependencies
- [Docker](https://docs.docker.com) Docker 17.05 or higher is required
- [docker-compose](https://docs.docker.com/compose/) version >= 1.10.0
## Docker Requirement
- At least 8GB RAM (Docker -> Preferences -> Advanced -> Memory -> 8GB or above)
## Build eos image
```bash
......@@ -40,13 +41,15 @@ docker run --name nodeos -v /path-to-data-dir:/opt/eosio/bin/data-dir -p 8888:88
curl http://127.0.0.1:8888/v1/chain/get_info
```
## Start both nodeos and walletd containers
## Start both nodeos and keosd containers
```bash
docker-compose up
docker volume create --name=nodeos-data-volume
docker volume create --name=keosd-data-volume
docker-compose up -d
```
After `docker-compose up`, two services named nodeos and walletd will be started. nodeos service would expose ports 8888 and 9876 to the host. walletd service does not expose any port to the host, it is only accessible to cleos when runing cleos is running inside the walletd container as described in "Execute cleos commands" section.
After `docker-compose up -d`, two services named `nodeosd` and `keosd` will be started. nodeos service would expose ports 8888 and 9876 to the host. keosd service does not expose any port to the host, it is only accessible to cleos when runing cleos is running inside the keosd container as described in "Execute cleos commands" section.
### Execute cleos commands
......@@ -54,7 +57,7 @@ After `docker-compose up`, two services named nodeos and walletd will be started
You can run the `cleos` commands via a bash alias.
```bash
alias cleos='docker-compose exec walletd /opt/eosio/bin/cleos -H nodeos'
alias cleos='docker-compose exec keosd /opt/eosio/bin/cleos -H nodeosd'
cleos get info
cleos get account inita
```
......@@ -65,10 +68,10 @@ Upload sample exchange contract
cleos set contract exchange contracts/exchange/exchange.wast contracts/exchange/exchange.abi
```
If you don't need walletd afterwards, you can stop the walletd service using
If you don't need keosd afterwards, you can stop the keosd service using
```bash
docker-compose stop walletd
docker-compose stop keosd
```
### Change default configuration
......@@ -95,5 +98,6 @@ docker-compose up
The data volume created by docker-compose can be deleted as follows:
```bash
docker volume rm docker_nodeos-data-volume
docker volume rm nodeos-data-volume
docker volume rm keosd-data-volume
```
version: "4"
version: "3"
services:
builder:
......@@ -11,6 +11,7 @@ services:
context: .
image: eosio/eos
command: /opt/eosio/bin/nodeosd.sh
hostname: nodeosd
ports:
- 8888:8888
- 9876:9876
......@@ -19,14 +20,17 @@ services:
volumes:
- nodeos-data-volume:/opt/eosio/bin/data-dir
walletd:
keosd:
image: eosio/eos
command: /opt/eosio/bin/keosd
hostname: keosd
links:
- nodeosd
volumes:
- walletd-data-volume:/opt/eosio/bin/data-dir
- keosd-data-volume:/opt/eosio/bin/data-dir
volumes:
nodeos-data-volume:
walletd-data-volume:
external: true
keosd-data-volume:
external: true
......@@ -262,7 +262,7 @@ namespace eosiosystem {
act.owner = del.from;
transaction out( now() + refund_delay + refund_expiration_time );
out.actions.emplace_back( permission_level{ del.from, N(active) }, self, N(refund), act );
out.send( del.from, 0, now() + refund_delay );
out.send( del.from, receiver, now() + refund_delay );
if ( asset(0) < del.unstake_net_quantity + del.unstake_cpu_quantity ) {
voting<SystemAccount>::decrease_voting_power( del.from, del.unstake_net_quantity + del.unstake_cpu_quantity );
......
......@@ -97,6 +97,10 @@
{"name":"context_free_discount_cpu_usage_den", "type":"uint64"},
{"name":"max_transaction_cpu_usage", "type":"uint32"},
{"name":"max_transaction_net_usage", "type":"uint32"},
{"name":"max_block_cpu_usage", "type": "uint64"},
{"name":"target_block_cpu_usage_pct", "type": "uint32"},
{"name":"max_block_net_usage", "type": "uint64"},
{"name":"target_block_net_usage_pct", "type": "uint32"},
{"name":"max_transaction_lifetime", "type":"uint32"},
{"name":"max_transaction_exec_time", "type":"uint32"},
{"name":"max_authority_depth", "type":"uint16"},
......
......@@ -252,6 +252,10 @@ namespace eosiosystem {
std::array<uint64_t, 21> context_free_discount_cpu_usage_den;
std::array<uint32_t, 21> max_transaction_cpu_usage;
std::array<uint32_t, 21> max_transaction_net_usage;
std::array<uint64_t, 21> max_block_cpu_usage;
std::array<uint32_t, 21> target_block_cpu_usage_pct;
std::array<uint64_t, 21> max_block_net_usage;
std::array<uint32_t, 21> target_block_net_usage_pct;
std::array<uint32_t, 21> max_transaction_lifetime;
std::array<uint16_t, 21> max_authority_depth;
std::array<uint32_t, 21> max_transaction_exec_time;
......@@ -282,6 +286,10 @@ namespace eosiosystem {
context_free_discount_cpu_usage_den[n] = it->prefs.context_free_discount_cpu_usage_den;
max_transaction_cpu_usage[n] = it->prefs.max_transaction_cpu_usage;
max_transaction_net_usage[n] = it->prefs.max_transaction_net_usage;
max_block_cpu_usage[n] = it->prefs.max_block_cpu_usage;
target_block_cpu_usage_pct[n] = it->prefs.target_block_cpu_usage_pct;
max_block_net_usage[n] = it->prefs.max_block_net_usage;
target_block_net_usage_pct[n] = it->prefs.target_block_net_usage_pct;
max_transaction_lifetime[n] = it->prefs.max_transaction_lifetime;
max_authority_depth[n] = it->prefs.max_authority_depth;
max_transaction_exec_time[n] = it->prefs.max_transaction_exec_time;
......@@ -309,6 +317,10 @@ namespace eosiosystem {
std::sort( context_free_discount_cpu_usage_den.begin(), context_free_discount_cpu_usage_den.begin()+n );
std::sort( max_transaction_cpu_usage.begin(), max_transaction_cpu_usage.begin()+n );
std::sort( max_transaction_net_usage.begin(), max_transaction_net_usage.begin()+n );
std::sort( max_block_cpu_usage.begin(), max_block_cpu_usage.begin()+n );
std::sort( target_block_cpu_usage_pct.begin(), target_block_cpu_usage_pct.begin()+n );
std::sort( max_block_net_usage.begin(), max_block_net_usage.begin()+n );
std::sort( target_block_net_usage_pct.begin(), target_block_net_usage_pct.begin()+n );
std::sort( max_transaction_lifetime.begin(), max_transaction_lifetime.begin()+n );
std::sort( max_transaction_exec_time.begin(), max_transaction_exec_time.begin()+n );
std::sort( max_authority_depth.begin(), max_authority_depth.begin()+n );
......@@ -338,6 +350,10 @@ namespace eosiosystem {
parameters.context_free_discount_cpu_usage_den = context_free_discount_cpu_usage_den[median];
parameters.max_transaction_cpu_usage = max_transaction_cpu_usage[median];
parameters.max_transaction_net_usage = max_transaction_net_usage[median];
parameters.max_block_cpu_usage = max_block_cpu_usage[median];
parameters.target_block_cpu_usage_pct = target_block_cpu_usage_pct[median];
parameters.max_block_net_usage = max_block_net_usage[median];
parameters.target_block_net_usage_pct = target_block_net_usage_pct[median];
parameters.max_transaction_lifetime = max_transaction_lifetime[median];
parameters.max_transaction_exec_time = max_transaction_exec_time[median];
parameters.max_authority_depth = max_authority_depth[median];
......
......@@ -16,6 +16,10 @@ namespace eosio {
uint64_t context_free_discount_cpu_usage_den;
uint32_t max_transaction_cpu_usage;
uint32_t max_transaction_net_usage;
uint64_t max_block_cpu_usage;
uint32_t target_block_cpu_usage_pct;
uint64_t max_block_net_usage;
uint32_t target_block_net_usage_pct;
uint32_t max_transaction_lifetime;
uint32_t max_transaction_exec_time;
uint16_t max_authority_depth;
......@@ -29,6 +33,8 @@ namespace eosio {
(base_setcode_cpu_usage)(per_signature_cpu_usage)(per_lock_net_usage)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_cpu_usage)(max_transaction_net_usage)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_block_net_usage)(target_block_net_usage_pct)
(max_transaction_lifetime)(max_transaction_exec_time)(max_authority_depth)
(max_inline_depth)(max_inline_action_size)(max_generated_transaction_size)
(max_generated_transaction_count)
......
......@@ -28,7 +28,7 @@ namespace eosio {
:expiration(exp),region(r)
{}
void send(uint64_t sender_id, account_name payer = account_name(0), time delay_until = now()) const {
void send(uint64_t sender_id, account_name payer, time delay_until = now()) const {
auto serialize = pack(*this);
send_deferred(sender_id, payer, delay_until, serialize.data(), serialize.size());
}
......
......@@ -52,7 +52,7 @@ namespace proxy {
transaction out;
out.actions.emplace_back(permission_level{self, N(active)}, N(currency), N(transfer), new_transfer);
out.send(id, 0, now() + code_config.delay);
out.send(id, self, now() + code_config.delay);
}
}
......@@ -78,7 +78,7 @@ namespace proxy {
configs::store(code_config, self);
eosio::print("Resending Transaction: ", failed_dtrx.sender_id, " as ", id, "\n");
failed_dtrx.send(id, 0, now() + code_config.delay);
failed_dtrx.send(id, self, now() + code_config.delay);
}
}
......
......@@ -18,6 +18,8 @@
#include "test_checktime.cpp"
#include "test_permission.cpp"
account_name global_receiver;
extern "C" {
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
if ( action == N(cf_action) ) {
......@@ -118,13 +120,13 @@ extern "C" {
WASM_TEST_HANDLER(test_transaction, send_action_recurse);
WASM_TEST_HANDLER(test_transaction, test_read_transaction);
WASM_TEST_HANDLER(test_transaction, test_transaction_size);
WASM_TEST_HANDLER(test_transaction, send_transaction);
WASM_TEST_HANDLER(test_transaction, send_transaction_empty);
WASM_TEST_HANDLER(test_transaction, send_transaction_large);
WASM_TEST_HANDLER(test_transaction, send_action_sender);
WASM_TEST_HANDLER(test_transaction, send_transaction_expiring_late);
WASM_TEST_HANDLER_EX(test_transaction, send_transaction);
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_empty);
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_large);
WASM_TEST_HANDLER_EX(test_transaction, send_action_sender);
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_expiring_late);
WASM_TEST_HANDLER(test_transaction, deferred_print);
WASM_TEST_HANDLER(test_transaction, send_deferred_transaction);
WASM_TEST_HANDLER_EX(test_transaction, send_deferred_transaction);
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);
......
......@@ -196,14 +196,14 @@ struct test_transaction {
static void send_action_inline_fail();
static void test_read_transaction();
static void test_transaction_size();
static void send_transaction();
static void send_transaction_empty();
static void send_transaction(uint64_t receiver, uint64_t code, uint64_t action);
static void send_transaction_empty(uint64_t receiver, uint64_t code, uint64_t action);
static void send_transaction_max();
static void send_transaction_large();
static void send_transaction_expiring_late();
static void send_action_sender();
static void send_transaction_large(uint64_t receiver, uint64_t code, uint64_t action);
static void send_transaction_expiring_late(uint64_t receiver, uint64_t code, uint64_t action);
static void send_action_sender(uint64_t receiver, uint64_t code, uint64_t action);
static void deferred_print();
static void send_deferred_transaction();
static void send_deferred_transaction(uint64_t receiver, uint64_t code, uint64_t action);
static void cancel_deferred_transaction();
static void send_cf_action();
static void send_cf_action_fail();
......
......@@ -93,7 +93,7 @@ void test_transaction::send_action_empty() {
*/
void test_transaction::send_action_large() {
using namespace eosio;
char large_message[8 * 1024];
static char large_message[8 * 1024];
test_action_action<N(testapi), WASM_TEST_ACTION("test_action", "read_action_normal")> test_action;
copy_data(large_message, 8*1024, test_action.data);
action act(vector<permission_level>{{N(testapi), N(active)}}, test_action);
......@@ -161,7 +161,7 @@ void test_transaction::test_transaction_size() {
eosio_assert( trans_size == transaction_size(), "transaction size does not match" );
}
void test_transaction::send_transaction() {
void test_transaction::send_transaction(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
dummy_action payload = {DUMMY_ACTION_DEFAULT_A, DUMMY_ACTION_DEFAULT_B, DUMMY_ACTION_DEFAULT_C};
......@@ -170,10 +170,10 @@ void test_transaction::send_transaction() {
auto trx = transaction();
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.send(0);
trx.send(0, receiver);
}
void test_transaction::send_action_sender() {
void test_transaction::send_action_sender(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
account_name cur_send;
read_action_data( &cur_send, sizeof(account_name) );
......@@ -182,13 +182,13 @@ void test_transaction::send_action_sender() {
auto trx = transaction();
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.send(0);
trx.send(0, receiver);
}
void test_transaction::send_transaction_empty() {
void test_transaction::send_transaction_empty(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
auto trx = transaction();
trx.send(0);
trx.send(0, receiver);
eosio_assert(false, "send_transaction_empty() should've thrown an error");
}
......@@ -196,7 +196,7 @@ void test_transaction::send_transaction_empty() {
/**
* cause failure due to a large transaction size
*/
void test_transaction::send_transaction_large() {
void test_transaction::send_transaction_large(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
auto trx = transaction();
for (int i = 0; i < 32; i ++) {
......@@ -206,12 +206,12 @@ void test_transaction::send_transaction_large() {
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
}
trx.send(0);
trx.send(0, receiver);
eosio_assert(false, "send_transaction_large() should've thrown an error");
}
void test_transaction::send_transaction_expiring_late() {
void test_transaction::send_transaction_expiring_late(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
account_name cur_send;
read_action_data( &cur_send, sizeof(account_name) );
......@@ -220,7 +220,7 @@ void test_transaction::send_transaction_expiring_late() {
auto trx = transaction(now() + 60*60*24*365);
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.send(0);
trx.send(0, receiver);
eosio_assert(false, "send_transaction_expiring_late() should've thrown an error");
}
......@@ -232,12 +232,12 @@ void test_transaction::deferred_print() {
eosio::print("deferred executed\n");
}
void test_transaction::send_deferred_transaction() {
void test_transaction::send_deferred_transaction(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
auto trx = transaction();
test_action_action<N(testapi), WASM_TEST_ACTION("test_transaction", "deferred_print")> test_action;
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.send( 0xffffffffffffffff, 0, now()+2 );
trx.send( 0xffffffffffffffff, receiver, now()+2 );
}
void test_transaction::cancel_deferred_transaction() {
......@@ -277,7 +277,7 @@ void test_transaction::read_inline_action() {
eosio_assert(res != -1, "get_action error");
action tmp;
datastream<char *> ds(buffer, res);
datastream<char *> ds(buffer, (size_t)res);
ds >> tmp.account;
ds >> tmp.name;
ds >> tmp.authorization;
......@@ -307,7 +307,7 @@ void test_transaction::read_inline_cf_action() {
eosio_assert(res != -1, "get_action error");
action tmp;
datastream<char *> ds(buffer, res);
datastream<char *> ds(buffer, (size_t)res);
ds >> tmp.account;
ds >> tmp.name;
ds >> tmp.authorization;
......
......@@ -260,10 +260,6 @@ void apply_context::execute_deferred( deferred_transaction&& trx ) {
controller.validate_uniqueness(trx); // TODO: Move this out of here when we have concurrent shards to somewhere we can check for conflicts between shards.
if (trx.payer != receiver) {
require_authorization(trx.payer);
}
const auto& gpo = controller.get_global_properties();
FC_ASSERT( results.deferred_transactions_count < gpo.configuration.max_generated_transaction_count );
......@@ -271,6 +267,10 @@ void apply_context::execute_deferred( deferred_transaction&& trx ) {
// privileged accounts can do anything, no need to check auth
if( !privileged ) {
// check to make sure the payer has authorized this deferred transaction's storage in RAM
if (trx.payer != receiver) {
require_authorization(trx.payer);
}
// if a contract is deferring only actions to itself then there is no need
// to check permissions, it could have done everything anyway.
......@@ -312,7 +312,7 @@ const contracts::table_id_object* apply_context::find_table( name code, name sco
return db.find<table_id_object, contracts::by_code_scope_table>(boost::make_tuple(code, scope, table));
}
const contracts::table_id_object& apply_context::find_or_create_table( name code, name scope, name table ) {
const contracts::table_id_object& apply_context::find_or_create_table( name code, name scope, name table, const account_name &payer ) {
require_read_lock(code, scope);
const auto* existing_tid = db.find<contracts::table_id_object, contracts::by_code_scope_table>(boost::make_tuple(code, scope, table));
if (existing_tid != nullptr) {
......@@ -320,13 +320,22 @@ const contracts::table_id_object& apply_context::find_or_create_table( name code
}
require_write_lock(scope);
update_db_usage(payer, config::billable_size_v<contracts::table_id_object>, "New Table ${c},${s},${t}", _V("c",code)("s",scope)("t",table));
return mutable_db.create<contracts::table_id_object>([&](contracts::table_id_object &t_id){
t_id.code = code;
t_id.scope = scope;
t_id.table = table;
t_id.payer = payer;
});
}
void apply_context::remove_table( const contracts::table_id_object& tid ) {
update_db_usage(tid.payer, - config::billable_size_v<contracts::table_id_object>);
mutable_db.remove(tid);
}
vector<account_name> apply_context::get_active_producers() const {
const auto& gpo = controller.get_global_properties();
vector<account_name> accounts;
......@@ -362,7 +371,7 @@ const bytes& apply_context::get_packed_transaction() {
void apply_context::update_db_usage( const account_name& payer, int64_t delta, const char* use_format, const fc::variant_object& args ) {
require_write_lock( payer );
if( (delta > 0) ) {
if (payer != account_name(receiver)) {
if (!(privileged || payer == account_name(receiver))) {
require_authorization( payer );
}
......@@ -443,7 +452,7 @@ int apply_context::db_store_i64( uint64_t scope, uint64_t table, const account_n
int apply_context::db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size ) {
require_write_lock( scope );
const auto& tab = find_or_create_table( code, scope, table );
const auto& tab = find_or_create_table( code, scope, table, payer );
auto tableid = tab.id;
FC_ASSERT( payer != account_name(), "must specify a valid account to pay for new record" );
......@@ -460,7 +469,7 @@ int apply_context::db_store_i64( uint64_t code, uint64_t scope, uint64_t table,
++t.count;
});
int64_t billable_size = (int64_t)(buffer_size + sizeof(key_value_object) + config::overhead_per_row_ram_bytes);
int64_t billable_size = (int64_t)(buffer_size + config::billable_size_v<key_value_object>);
update_db_usage( payer, billable_size, "New Row ${id} in (${c},${s},${t})", _V("id", obj.primary_key)("c",receiver)("s",scope)("t",table));
keyval_cache.cache_table( tab );
......@@ -473,7 +482,7 @@ void apply_context::db_update_i64( int iterator, account_name payer, const char*
const auto& tab = keyval_cache.get_table( obj.t_id );
require_write_lock( tab.scope );
const int64_t overhead = sizeof(key_value_object) + config::overhead_per_row_ram_bytes;
const int64_t overhead = config::billable_size_v<key_value_object>;
int64_t old_size = (int64_t)(obj.value.size() + overhead);
int64_t new_size = (int64_t)(buffer_size + overhead);
......@@ -498,7 +507,7 @@ void apply_context::db_update_i64( int iterator, account_name payer, const char*
void apply_context::db_remove_i64( int iterator ) {
const key_value_object& obj = keyval_cache.get( iterator );
update_db_usage( obj.payer, -(obj.value.size() + config::overhead_per_row_ram_bytes) );
update_db_usage( obj.payer, -(obj.value.size() + config::billable_size_v<key_value_object>) );
const auto& table_obj = keyval_cache.get_table( obj.t_id );
require_write_lock( table_obj.scope );
......@@ -508,6 +517,10 @@ void apply_context::db_remove_i64( int iterator ) {
});
mutable_db.remove( obj );
if (table_obj.count == 0) {
remove_table(table_obj);
}
keyval_cache.remove( iterator );
}
......
......@@ -374,8 +374,19 @@ transaction_trace chain_controller::delayed_transaction_processing( const transa
time_point_sec execute_after = head_block_time();
execute_after += mtrx.delay;
//TODO: !!! WHO GETS BILLED TO STORE THE DELAYED TX?
deferred_transaction dtrx(context.get_next_sender_id(), config::system_account_name, config::system_account_name, execute_after, trx);
// TODO: update to better method post RC1?
account_name payer;
for(const auto& act : mtrx.trx().actions ) {
if (act.authorization.size() > 0) {
payer = act.authorization.at(0).actor;
break;
}
}
FC_ASSERT(!payer.empty(), "Failed to find a payer for delayed transaction!");
deferred_transaction dtrx(context.get_next_sender_id(), config::system_account_name, payer, execute_after, trx);
FC_ASSERT( dtrx.execute_after < dtrx.expiration, "transaction expires before it can execute" );
result.deferred_transaction_requests.push_back(std::move(dtrx));
......@@ -593,6 +604,12 @@ void chain_controller::_finalize_block( const block_trace& trace, const producer
update_last_irreversible_block();
_resource_limits.process_account_limit_updates();
const auto& chain_config = this->get_global_properties().configuration;
_resource_limits.set_block_parameters(
{EOS_PERCENT(chain_config.max_block_cpu_usage, chain_config.target_block_cpu_usage_pct), chain_config.max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}},
{EOS_PERCENT(chain_config.max_block_net_usage, chain_config.target_block_net_usage_pct), chain_config.max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}}
);
// trigger an update of our elastic values for block limits
_resource_limits.process_block_usage(b.block_num());
......@@ -1199,7 +1216,7 @@ static uint32_t calculate_transaction_cpu_usage( const transaction_trace& trace,
context_free_cpu_usage +
signature_cpu_usage;
uint32_t cpu_usage_limit = meta.trx().max_kcpu_usage.value * 1024; // overflow checked in validate_transaction_without_state
uint32_t cpu_usage_limit = meta.trx().max_kcpu_usage.value * 1024UL; // overflow checked in validate_transaction_without_state
EOS_ASSERT( cpu_usage_limit == 0 || actual_cpu_usage <= cpu_usage_limit, tx_resource_exhausted,
"declared cpu usage limit of transaction is too low: ${actual_cpu_usage} > ${declared_limit}",
("actual_cpu_usage", actual_cpu_usage)("declared_limit",cpu_usage_limit) );
......@@ -1216,7 +1233,7 @@ static uint32_t calculate_transaction_net_usage( const transaction_trace& trace,
lock_net_usage;
uint32_t net_usage_limit = meta.trx().max_net_usage_words.value * 8; // overflow checked in validate_transaction_without_state
uint32_t net_usage_limit = meta.trx().max_net_usage_words.value * 8UL; // overflow checked in validate_transaction_without_state
EOS_ASSERT( net_usage_limit == 0 || actual_net_usage <= net_usage_limit, tx_resource_exhausted,
"declared net usage limit of transaction is too low: ${actual_net_usage} > ${declared_limit}",
("actual_net_usage", actual_net_usage)("declared_limit",net_usage_limit) );
......@@ -1319,8 +1336,8 @@ void chain_controller::validate_transaction_without_state( const transaction& tr
EOS_ASSERT( act.authorization.empty(), cfa_irrelevant_auth, "context-free actions cannot require authorization" );
}
EOS_ASSERT( trx.max_net_usage_words.value < 0x20000000, transaction_exception, "overflow of max_net_usage_words" );
EOS_ASSERT( trx.max_kcpu_usage.value < 0x400000, transaction_exception, "overflow of max_kcpu_usage" );
EOS_ASSERT( trx.max_kcpu_usage.value < UINT32_MAX / 1024UL, transaction_exception, "declared max_kcpu_usage overflows when expanded to max cpu usage" );
EOS_ASSERT( trx.max_net_usage_words.value < UINT32_MAX / 8UL, transaction_exception, "declared max_net_usage_words overflows when expanded to max net usage" );
} FC_CAPTURE_AND_RETHROW((trx)) }
......@@ -1943,6 +1960,9 @@ transaction_trace chain_controller::_apply_transaction( transaction_metadata& me
return result;
} catch (...) {
if (meta.is_implicit) {
try {
throw;
} FC_CAPTURE_AND_LOG((meta.id));
transaction_trace result(meta.id);
result.status = transaction_trace::hard_fail;
return result;
......@@ -2009,7 +2029,7 @@ transaction_trace chain_controller::_apply_error( transaction_metadata& meta ) {
void chain_controller::_destroy_generated_transaction( const generated_transaction_object& gto ) {
auto& generated_transaction_idx = _db.get_mutable_index<generated_transaction_multi_index>();
_resource_limits.add_account_ram_usage(gto.payer, -(sizeof(generated_transaction_object) + gto.packed_trx.size() + config::overhead_per_row_ram_bytes));
_resource_limits.add_account_ram_usage(gto.payer, -( config::billable_size_v<generated_transaction_object> + gto.packed_trx.size()));
generated_transaction_idx.remove(gto);
}
......@@ -2018,7 +2038,7 @@ void chain_controller::_create_generated_transaction( const deferred_transaction
size_t trx_size = fc::raw::pack_size(dto);
_resource_limits.add_account_ram_usage(
dto.payer,
(sizeof(generated_transaction_object) + (int64_t)trx_size + config::overhead_per_row_ram_bytes),
(config::billable_size_v<generated_transaction_object> + (int64_t)trx_size),
"Generated Transaction ${id} from ${s}", _V("id", dto.sender_id)("s",dto.sender)
);
......
......@@ -76,7 +76,7 @@ void apply_eosio_newaccount(apply_context& context) {
});
resources.initialize_account(create.name);
resources.add_account_ram_usage(
create.name,
create.creator,
(int64_t)config::overhead_per_account_ram_bytes,
"New Account ${n}", _V("n", create.name)
);
......@@ -91,7 +91,7 @@ void apply_eosio_newaccount(apply_context& context) {
resources.add_account_ram_usage(
owner,
(int64_t)(sizeof(permission_object) + result.auth.get_billable_size()),
(int64_t)(config::billable_size_v<permission_object> + result.auth.get_billable_size()),
"New Permission ${a}@${p}", _V("a", owner)("p",name)
);
......@@ -248,10 +248,7 @@ void apply_eosio_updateauth(apply_context& context) {
"Changing parent authority is not currently supported");
// TODO: Depending on an implementation detail like sizeof(permission_object) for consensus-affecting side effects like
// RAM usage seems like a bad idea. For example, an upgrade of the implementation of boost::interprocess::vector
// could cause a hardfork unless the old size calculation behavior was also carefully replicated in the same upgrade.
int64_t old_size = (int64_t)(sizeof(permission_object) + permission->auth.get_billable_size());
int64_t old_size = (int64_t)(config::billable_size_v<permission_object> + permission->auth.get_billable_size());
// TODO/QUESTION: If we are updating an existing permission, should we check if the message declared
// permission satisfies the permission we want to modify?
......@@ -262,7 +259,7 @@ void apply_eosio_updateauth(apply_context& context) {
po.delay = fc::seconds(update.delay);
});
int64_t new_size = (int64_t)(sizeof(permission_object) + permission->auth.get_billable_size());
int64_t new_size = (int64_t)(config::billable_size_v<permission_object> + permission->auth.get_billable_size());
resources.add_account_ram_usage(
permission->owner,
......@@ -283,7 +280,7 @@ void apply_eosio_updateauth(apply_context& context) {
resources.add_account_ram_usage(
p.owner,
(int64_t)(sizeof(permission_object) + p.auth.get_billable_size()),
(int64_t)(config::billable_size_v<permission_object> + p.auth.get_billable_size()),
"New Permission ${a}@${p}", _V("a", p.owner)("p",p.name)
);
......@@ -320,7 +317,7 @@ void apply_eosio_deleteauth(apply_context& context) {
resources.add_account_ram_usage(
permission.owner,
-(int64_t)(sizeof(permission_object) + permission.auth.get_billable_size())
-(int64_t)(config::billable_size_v<permission_object> + permission.auth.get_billable_size())
);
db.remove(permission);
}
......@@ -365,7 +362,7 @@ void apply_eosio_linkauth(apply_context& context) {
resources.add_account_ram_usage(
l.account,
(int64_t)(sizeof(permission_link_object)),
(int64_t)(config::billable_size_v<permission_link_object>),
"New Permission Link ${code}::${act} -> ${a}@${p}", _V("code", l.code)("act",l.message_type)("a", l.account)("p",l.required_permission)
);
}
......@@ -384,7 +381,7 @@ void apply_eosio_unlinkauth(apply_context& context) {
EOS_ASSERT(link != nullptr, action_validate_exception, "Attempting to unlink authority, but no link found");
resources.add_account_ram_usage(
link->account,
-(int64_t)(sizeof(permission_link_object))
-(int64_t)(config::billable_size_v<permission_link_object>)
);
db.remove(*link);
......
......@@ -187,7 +187,7 @@ class apply_context {
context.require_write_lock( scope );
const auto& tab = context.find_or_create_table( context.receiver, scope, table );
const auto& tab = context.find_or_create_table( context.receiver, scope, table, payer );
const auto& obj = context.mutable_db.create<ObjectType>( [&]( auto& o ){
o.t_id = tab.id;
......@@ -200,7 +200,7 @@ class apply_context {
++t.count;
});
context.update_db_usage( payer, sizeof(ObjectType) + config::overhead_per_row_ram_bytes );
context.update_db_usage( payer, config::billable_size_v<ObjectType> );
itr_cache.cache_table( tab );
return itr_cache.add( obj );
......@@ -208,7 +208,7 @@ class apply_context {
void remove( int iterator ) {
const auto& obj = itr_cache.get( iterator );
context.update_db_usage( obj.payer, -( sizeof(ObjectType) + config::overhead_per_row_ram_bytes ) );
context.update_db_usage( obj.payer, -( config::billable_size_v<ObjectType> ) );
const auto& table_obj = itr_cache.get_table( obj.t_id );
context.require_write_lock( table_obj.scope );
......@@ -218,6 +218,10 @@ class apply_context {
});
context.mutable_db.remove( obj );
if (table_obj.count == 0) {
context.remove_table(table_obj);
}
itr_cache.remove( iterator );
}
......@@ -228,7 +232,7 @@ class apply_context {
if( payer == account_name() ) payer = obj.payer;
int64_t billing_size = sizeof(ObjectType) + config::overhead_per_row_ram_bytes;
int64_t billing_size = config::billable_size_v<ObjectType>;
if( obj.payer != payer ) {
context.update_db_usage( obj.payer, -(billing_size) );
......@@ -586,7 +590,8 @@ class apply_context {
using table_id_object = contracts::table_id_object;
const table_id_object* find_table( name code, name scope, name table );
const table_id_object& find_or_create_table( name code, name scope, name table );
const table_id_object& find_or_create_table( name code, name scope, name table, const account_name &payer );
void remove_table( const table_id_object& tid );
int db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size );
......
......@@ -5,6 +5,7 @@
#pragma once
#include <chainbase/chainbase.hpp>
#include <eosio/chain/transaction.hpp>
#include <eosio/chain/config.hpp>
#include <type_traits>
......@@ -21,6 +22,18 @@ struct key_weight {
weight_type weight;
};
namespace config {
template<>
struct billable_size<permission_level_weight> {
static const uint64_t value = 24; ///< over value of weight for safety
};
template<>
struct billable_size<key_weight> {
static const uint64_t value = 8; ///< over value of weight for safety, dynamically sizing key
};
}
struct authority {
authority( public_key_type k ):threshold(1),keys({{k,1}}){}
authority( uint32_t t, vector<key_weight> k, vector<permission_level_weight> p = {} )
......@@ -61,22 +74,14 @@ struct shared_authority {
}
size_t get_billable_size() const {
/**
* public_key_type contains a static_variant and so its size could change if we later wanted to add a new public key type of
* of larger size, thus increasing the returned value from shared_authority::get_billable_size() for old authorities that
* do not even use the new public key type.
*
* Although adding a new public key type is a hardforking change anyway, the current implementation means we would need to:
* - track historical sizes of public_key_type,
* - branch on hardfork versions within this function, and
* - calculate billable size of the authority based on the appropriate historical size of public_key_type,
* all in order to avoid retroactively changing the billable size of authorities.
* TODO: Better implementation of get_billable_size()?
* Perhaps it would require changes to how public_key_type is stored in shared_authority?
* For example: change keys (of type shared_vector<key_weight>) to packed_keys (of type shared_vector<char>)
* which store the packed data of vector<key_weight>, and then charge based on that packed size.
*/
return keys.size() * sizeof(key_weight) + accounts.size() * sizeof(permission_level_weight);
size_t accounts_size = accounts.size() * config::billable_size_v<permission_level_weight>;
size_t keys_size = 0;
for (const auto& k: keys) {
keys_size += config::billable_size_v<key_weight>;
keys_size += fc::raw::pack_size(k.key); ///< serialized size of the key
}
return accounts_size + keys_size;
}
};
......
......@@ -5,6 +5,7 @@
#pragma once
#include <eosio/chain/types.hpp>
#include <eosio/chain/config.hpp>
namespace eosio { namespace chain {
......@@ -30,6 +31,11 @@ struct chain_config {
uint32_t max_transaction_cpu_usage; ///< the maximum objectively measured cpu usage that the chain will allow regardless of account limits
uint32_t max_transaction_net_usage; ///< the maximum objectively measured net usage that the chain will allow regardless of account limits
uint64_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
uint64_t max_block_net_usage; ///< the maxiumum net usage in instructions for a block
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_lifetime;
uint32_t max_transaction_exec_time;
uint16_t max_authority_depth;
......@@ -51,7 +57,11 @@ struct chain_config {
<< "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 CPU Usage: " << c.max_transaction_cpu_usage << ", "
<< "Max Transaction Net Usage: " << c.max_transaction_net_usage << ", "
<< "Max Transaction Lifetime: " << c.max_transaction_lifetime << ", "
<< "Max Block CPU Usage: " << c.max_block_cpu_usage << ", "
<< "Target Block CPU Usage Percent: " << c.target_block_cpu_usage_pct << ", "
<< "Max Block NET Usage: " << c.max_block_net_usage << ", "
<< "Target Block NET Usage Percent: " << ((double)c.target_block_net_usage_pct / (double)config::percent_1) << "%, "
<< "Max Transaction Lifetime: " << ((double)c.max_transaction_lifetime / (double)config::percent_1) << "%, "
<< "Max Authority Depth: " << c.max_authority_depth << ", "
<< "Max Inline Depth: " << c.max_inline_depth << ", "
<< "Max Inline Action Size: " << c.max_inline_action_size << ", "
......@@ -73,6 +83,8 @@ FC_REFLECT(eosio::chain::chain_config,
(per_signature_cpu_usage)(per_lock_net_usage)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_cpu_usage)(max_transaction_net_usage)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_block_net_usage)(target_block_net_usage_pct)
(max_transaction_lifetime)(max_transaction_exec_time)(max_authority_depth)
(max_inline_depth)(max_inline_action_size)(max_generated_transaction_size)
(max_generated_transaction_count)
......
......@@ -44,11 +44,11 @@ static const uint32_t block_cpu_usage_average_window_ms = 60*1000l;
static const uint32_t block_size_average_window_ms = 60*1000l;
const static uint32_t default_max_block_size = 1024 * 1024; /// at 500ms blocks and 200byte trx, this enables ~10,000 TPS burst
const static uint32_t default_target_block_size = default_max_block_size / 10; /// we target 1000 TPS
const static uint32_t default_max_block_net_usage = 1024 * 1024; /// at 500ms blocks and 200byte trx, this enables ~10,000 TPS burst
const static int default_target_block_net_usage_pct = 10 * percent_1; /// we target 1000 TPS
const static uint32_t default_max_block_cpu_usage = 100 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst
const static uint32_t default_target_block_cpu_usage = default_max_block_cpu_usage / 10; /// target 1000 TPS
const static uint32_t default_max_block_cpu_usage = 100 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst
const static int default_target_block_cpu_usage_pct = 10 * percent_1; /// target 1000 TPS
const static uint64_t default_max_storage_size = 10 * 1024;
const static uint32_t default_max_trx_lifetime = 60*60;
......@@ -72,9 +72,9 @@ const static uint32_t default_per_lock_net_usage = 32;
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_max_transaction_cpu_usage = default_max_block_cpu_usage / 10;
const static uint32_t default_max_transaction_net_usage = default_max_block_size / 10;
const static uint32_t default_max_transaction_net_usage = default_max_block_net_usage / 10;
const static uint32_t overhead_per_row_ram_bytes = 512; ///< overhead accounts for basic tracking structures in a row
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
......@@ -98,6 +98,15 @@ const static uint32_t deferred_transactions_max_time_per_block_us = 20*1000; //2
const static int irreversible_threshold_percent= 70 * percent_1;
const static uint64_t billable_alignment = 16;
template<typename T>
struct billable_size;
template<typename T>
constexpr uint64_t billable_size_v = ((billable_size<T>::value + billable_alignment - 1) / billable_alignment) * billable_alignment;
} } } // namespace eosio::chain::config
template<typename Number>
......
......@@ -25,6 +25,7 @@ namespace eosio { namespace chain { namespace contracts {
account_name code;
scope_name scope;
table_name table;
account_name payer;
uint32_t count = 0; /// the number of elements in the table
};
......@@ -62,8 +63,8 @@ namespace eosio { namespace chain { namespace contracts {
id_type id;
table_id t_id;
uint64_t primary_key;
shared_string value;
account_name payer = 0;
shared_vector<char> value;
};
using key_value_index = chainbase::shared_multi_index_container<
......@@ -93,9 +94,8 @@ namespace eosio { namespace chain { namespace contracts {
typename chainbase::object<ObjectTypeId,index_object>::id_type id;
table_id t_id;
uint64_t primary_key;
SecondaryKey secondary_key;
account_name payer = 0;
uint16_t billed_overhead = 0;
SecondaryKey secondary_key;
};
......@@ -148,7 +148,48 @@ namespace eosio { namespace chain { namespace contracts {
typedef secondary_index<uint64_t,index_double_object_type,soft_double_less>::index_object index_double_object;
typedef secondary_index<uint64_t,index_double_object_type,soft_double_less>::index_index index_double_index;
} } } // namespace eosio::chain::contracts
} // ::contracts
namespace config {
template<>
struct billable_size<contracts::table_id_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 2; ///< overhead for 2x indices internal-key and code,scope,table
static const uint64_t value = 44 + overhead; ///< 36 bytes for constant size fields + overhead
};
template<>
struct billable_size<contracts::key_value_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 2; ///< overhead for potentially single-row table, 2x indices internal-key and primary key
static const uint64_t value = 32 + 8 + 4 + overhead; ///< 32 bytes for our constant size fields, 8 for pointer to vector data, 4 bytes for a size of vector + overhead
};
template<>
struct billable_size<contracts::index64_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 3; ///< overhead for potentially single-row table, 3x indices internal-key, primary key and primary+secondary key
static const uint64_t value = 24 + 8 + overhead; ///< 24 bytes for fixed fields + 8 bytes key + overhead
};
template<>
struct billable_size<contracts::index128_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 3; ///< overhead for potentially single-row table, 3x indices internal-key, primary key and primary+secondary key
static const uint64_t value = 24 + 16 + overhead; ///< 24 bytes for fixed fields + 16 bytes key + overhead
};
template<>
struct billable_size<contracts::index256_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 3; ///< overhead for potentially single-row table, 3x indices internal-key, primary key and primary+secondary key
static const uint64_t value = 24 + 32 + overhead; ///< 24 bytes for fixed fields + 32 bytes key + overhead
};
template<>
struct billable_size<contracts::index_double_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 3; ///< overhead for potentially single-row table, 3x indices internal-key, primary key and primary+secondary key
static const uint64_t value = 24 + 8 + overhead; ///< 24 bytes for fixed fields + 32 bytes key + overhead
};
}
} } // namespace eosio::chain
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::table_id_object, eosio::chain::contracts::table_id_multi_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key_value_object, eosio::chain::contracts::key_value_index)
......
......@@ -27,6 +27,10 @@ struct genesis_state_type {
.context_free_discount_cpu_usage_den = config::default_context_free_discount_cpu_usage_den,
.max_transaction_cpu_usage = config::default_max_transaction_cpu_usage,
.max_transaction_net_usage = config::default_max_transaction_net_usage,
.max_block_cpu_usage = config::default_max_block_cpu_usage,
.target_block_cpu_usage_pct = config::default_target_block_cpu_usage_pct,
.max_block_net_usage = config::default_max_block_net_usage,
.target_block_net_usage_pct = config::default_target_block_net_usage_pct,
.max_transaction_lifetime = config::default_max_trx_lifetime,
.max_transaction_exec_time = 0, // TODO: unused?
.max_authority_depth = config::default_max_auth_depth,
......
......@@ -70,6 +70,14 @@ namespace eosio { namespace chain {
>;
typedef chainbase::generic_index<generated_transaction_multi_index> generated_transaction_index;
namespace config {
template<>
struct billable_size<generated_transaction_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 5; ///< overhead for 5x indices internal-key, txid, expiration, delay, sender_id
static const uint64_t value = 96 + 4 + overhead; ///< 96 bytes for our constant size fields, 4 bytes for a varint for packed_trx size and 96 bytes of implementation overhead
};
}
} } // eosio::chain
CHAINBASE_SET_INDEX_TYPE(eosio::chain::generated_transaction_object, eosio::chain::generated_transaction_multi_index)
......
......@@ -67,6 +67,14 @@ namespace eosio { namespace chain {
>
>
>;
namespace config {
template<>
struct billable_size<permission_link_object> {
static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 3; ///< 3x indices id, action, permission
static const uint64_t value = 40 + overhead; ///< fixed field + overhead
};
}
} } // eosio::chain
CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_link_object, eosio::chain::permission_link_index)
......
......@@ -111,6 +111,13 @@ namespace eosio { namespace chain {
>
>;
namespace config {
template<>
struct billable_size<permission_object> {
static const uint64_t overhead = 6 * overhead_per_row_per_index_ram_bytes; ///< 6 indices 2x internal ID, parent, owner, name, name_usage
static const uint64_t value = 80 + overhead; ///< fixed field size + overhead
};
}
} } // eosio::chain
CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_object, eosio::chain::permission_index)
......
......@@ -4,6 +4,26 @@
#include <chainbase/chainbase.hpp>
namespace eosio { namespace chain { namespace resource_limits {
namespace impl {
template<typename T>
struct ratio {
T numerator;
T denominator;
};
}
using ratio = impl::ratio<uint64_t>;
struct elastic_limit_parameters {
uint64_t target; // the desired usage
uint64_t max; // the maximum usage
uint32_t periods; // the number of aggregation periods that contribute to the average usage
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
};
class resource_limits_manager {
public:
explicit resource_limits_manager(chainbase::database& db)
......@@ -14,6 +34,7 @@ namespace eosio { namespace chain { namespace resource_limits {
void initialize_database();
void initialize_chain();
void initialize_account( const account_name& account );
void set_block_parameters( const elastic_limit_parameters& cpu_limit_parameters, const elastic_limit_parameters& net_limit_parameters );
void add_transaction_usage( const vector<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t ordinal );
......
......@@ -8,12 +8,6 @@
namespace eosio { namespace chain { namespace resource_limits {
namespace impl {
template<typename T>
struct ratio {
T numerator;
T denominator;
};
template<typename T>
ratio<T> make_ratio(T n, T d) {
return ratio<T>{n, d};
......@@ -74,10 +68,10 @@ namespace eosio { namespace chain { namespace resource_limits {
value_ex += units * Precision / (uint64_t)window_size;
}
};
}
using usage_accumulator = impl::exponential_moving_average_accumulator<>;
using ratio = impl::ratio<uint64_t>;
/**
* Every account that authorizes a transaction is billed for the full size of that transaction. This object
......@@ -132,22 +126,12 @@ namespace eosio { namespace chain { namespace resource_limits {
>
>;
struct elastic_limit_parameters {
uint64_t target; // the desired usage
uint64_t max; // the maximum usage
uint32_t periods; // the number of aggregation periods that contribute to the average usage
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
};
class resource_limits_config_object : public chainbase::object<resource_limits_config_object_type, resource_limits_config_object> {
OBJECT_CTOR(resource_limits_config_object);
id_type id;
elastic_limit_parameters cpu_limit_parameters = {config::default_target_block_cpu_usage, 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 = {config::default_target_block_size, config::default_max_block_size, config::block_size_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}};
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}};
};
using resource_limits_config_index = chainbase::shared_multi_index_container<
......
......@@ -57,6 +57,14 @@ 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 ) {
const auto& config = _db.get<resource_limits_config_object>();
_db.modify(config, [&](resource_limits_config_object& c){
c.cpu_limit_parameters = cpu_limit_parameters;
c.net_limit_parameters = net_limit_parameters;
});
}
void resource_limits_manager::add_transaction_usage(const vector<account_name>& accounts, uint64_t cpu_usage, uint64_t net_usage, uint32_t time_slot ) {
const auto& state = _db.get<resource_limits_state_object>();
const auto& config = _db.get<resource_limits_config_object>();
......@@ -240,6 +248,7 @@ void resource_limits_manager::process_block_usage(uint32_t block_num) {
state.pending_net_usage = 0;
});
}
uint64_t resource_limits_manager::get_virtual_block_cpu_limit() const {
......
......@@ -128,9 +128,9 @@ class privileged_api : public context_aware_api {
* @param cpu_weight - the weight for determining share of compute capacity
*/
void set_resource_limits( account_name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight) {
EOS_ASSERT(ram_bytes >= -1 && ram_bytes <= INT64_MAX, wasm_execution_error, "invalid value for ram resource limit expected [-1,INT64_MAX]");
EOS_ASSERT(net_weight >= -1 && net_weight <= INT64_MAX, wasm_execution_error, "invalid value for net resource weight expected [-1,INT64_MAX]");
EOS_ASSERT(cpu_weight >= -1 && cpu_weight <= INT64_MAX, wasm_execution_error, "invalid value for cpu resource weight expected [-1,INT64_MAX]");
EOS_ASSERT(ram_bytes >= -1, wasm_execution_error, "invalid value for ram resource limit expected [-1,INT64_MAX]");
EOS_ASSERT(net_weight >= -1, wasm_execution_error, "invalid value for net resource weight expected [-1,INT64_MAX]");
EOS_ASSERT(cpu_weight >= -1, wasm_execution_error, "invalid value for cpu resource weight expected [-1,INT64_MAX]");
context.mutable_controller.get_mutable_resource_limits_manager().set_account_limits(account, ram_bytes, net_weight, cpu_weight);
}
......@@ -1049,14 +1049,6 @@ class transaction_api : public context_aware_api {
}
void send_deferred( const unsigned __int128& val, account_name payer, const fc::time_point_sec& execute_after, array_ptr<char> data, size_t data_len ) {
account_name actual_payer = payer;
if (actual_payer != account_name(0)) {
const auto* paying_account = context.db.find<account_object, by_name>(payer);
EOS_ASSERT(paying_account, tx_unknown_argument, "The account for the payer: ${a}, does not exist!", ("a", payer));
} else {
actual_payer = context.receiver;
}
try {
fc::uint128_t sender_id(val>>64, uint64_t(val) );
const auto& gpo = context.controller.get_global_properties();
......@@ -1069,7 +1061,7 @@ class transaction_api : public context_aware_api {
dtrx.execute_after = std::max( execute_after,
time_point_sec( (context.controller.head_block_time() + fc::seconds(dtrx.delay_sec))
+ fc::microseconds(999'999) ) /* rounds up to nearest second */ );
dtrx.payer = actual_payer;
dtrx.payer = payer;
context.execute_deferred(std::move(dtrx));
} FC_CAPTURE_AND_RETHROW((fc::to_hex(data, data_len)));
}
......
file(GLOB HEADERS "include/eos/chain_plugin/*.hpp")
file(GLOB HEADERS "include/eos/chain_api_plugin/*.hpp")
add_library( chain_api_plugin
chain_api_plugin.cpp
${HEADERS} )
......
......@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
// this is enough iterations for the average to reach/exceed the target (triggering congestion handling) and then the iterations to contract down to the min
// subtracting 1 for the iteration that pulls double duty as reaching/exceeding the target and starting congestion handling
const uint64_t expected_contract_iterations =
expected_exponential_average_iterations(0, config::default_target_block_cpu_usage, config::default_max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms ) +
expected_exponential_average_iterations(0, 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 ) +
expected_elastic_iterations( desired_virtual_limit, config::default_max_block_cpu_usage, 99, 100 ) - 1;
const account_name account(1);
......@@ -100,14 +100,14 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
* Test to make sure that the elastic limits for blocks relax and contract as expected
*/
BOOST_FIXTURE_TEST_CASE(elastic_net_relax_contract, resource_limits_fixture) try {
const uint64_t desired_virtual_limit = config::default_max_block_size * 1000ULL;
const uint64_t expected_relax_iterations = expected_elastic_iterations( config::default_max_block_size, desired_virtual_limit, 1000, 999 );
const uint64_t desired_virtual_limit = config::default_max_block_net_usage * 1000ULL;
const uint64_t expected_relax_iterations = expected_elastic_iterations( config::default_max_block_net_usage, desired_virtual_limit, 1000, 999 );
// this is enough iterations for the average to reach/exceed the target (triggering congestion handling) and then the iterations to contract down to the min
// subtracting 1 for the iteration that pulls double duty as reaching/exceeding the target and starting congestion handling
const uint64_t expected_contract_iterations =
expected_exponential_average_iterations(0, config::default_target_block_size, config::default_max_block_size, config::block_size_average_window_ms / config::block_interval_ms ) +
expected_elastic_iterations( desired_virtual_limit, config::default_max_block_size, 99, 100 ) - 1;
expected_exponential_average_iterations(0, 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 ) +
expected_elastic_iterations( desired_virtual_limit, config::default_max_block_net_usage, 99, 100 ) - 1;
const account_name account(1);
initialize_account(account);
......@@ -126,13 +126,13 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
// push maximum resources to go from idle back to congested as fast as possible
iterations = 0;
while (get_virtual_block_net_limit() > config::default_max_block_size && iterations <= expected_contract_iterations) {
add_transaction_usage({account},0, config::default_max_block_size, iterations);
while (get_virtual_block_net_limit() > config::default_max_block_net_usage && iterations <= expected_contract_iterations) {
add_transaction_usage({account},0, config::default_max_block_net_usage, iterations);
process_block_usage(iterations++);
}
BOOST_REQUIRE_EQUAL(iterations, expected_contract_iterations);
BOOST_REQUIRE_EQUAL(get_virtual_block_net_limit(), config::default_max_block_size);
BOOST_REQUIRE_EQUAL(get_virtual_block_net_limit(), config::default_max_block_net_usage);
} FC_LOG_AND_RETHROW();
/**
......@@ -174,7 +174,7 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
const vector<int64_t> weights = { 234, 511, 672, 800, 1213 };
const int64_t total = std::accumulate(std::begin(weights), std::end(weights), 0LL);
vector<int64_t> expected_limits;
std::transform(std::begin(weights), std::end(weights), std::back_inserter(expected_limits), [total](const auto& v){ return v * config::default_max_block_size / total; });
std::transform(std::begin(weights), std::end(weights), std::back_inserter(expected_limits), [total](const auto& v){ return v * config::default_max_block_net_usage / total; });
for (int64_t idx = 0; idx < weights.size(); idx++) {
const account_name account(idx + 100);
......@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_SUITE(resource_limits_test)
process_account_limit_updates();
const uint64_t increment = 1000;
const uint64_t expected_iterations = (config::default_max_block_size + increment - 1 ) / increment;
const uint64_t expected_iterations = (config::default_max_block_net_usage + increment - 1 ) / increment;
for (int idx = 0; idx < expected_iterations - 1; idx++) {
add_transaction_usage({account}, 0, increment, 0);
......
......@@ -417,13 +417,11 @@ BOOST_FIXTURE_TEST_CASE( dice_test, dice_tester ) try {
// No games in table
auto* game_tid = find_table(N(dice), N(dice), N(game));
BOOST_CHECK(game_tid != nullptr);
BOOST_CHECK(game_tid->count == 0);
BOOST_CHECK(game_tid == nullptr);
// No offers in table
auto* offer_tid = find_table(N(dice), N(dice), N(offer));
BOOST_CHECK(offer_tid != nullptr);
BOOST_CHECK(offer_tid->count == 0);
BOOST_CHECK(offer_tid == nullptr);
// 2 records in account table (Bob & Carol)
auto* account_tid = find_table(N(dice), N(dice), N(account));
......
......@@ -102,6 +102,10 @@ public:
("context_free_discount_cpu_usage_den", 100 + n )
("max_transaction_cpu_usage", 1000000 + n )
("max_transaction_net_usage", 1000000 + n )
("max_block_cpu_usage", 10000000 + n )
("target_block_cpu_usage_pct", 10 + n )
("max_block_net_usage", 10000000 + n )
("target_block_net_usage_pct", 10 + n )
("max_transaction_lifetime", 3600 + n)
("max_transaction_exec_time", 9900 + n)
("max_authority_depth", 6 + n)
......@@ -1381,6 +1385,10 @@ fc::mutable_variant_object config_to_variant( const eosio::chain::chain_config&
( "context_free_discount_cpu_usage_den", config.context_free_discount_cpu_usage_den )
( "max_transaction_cpu_usage", config.max_transaction_cpu_usage )
( "max_transaction_net_usage", config.max_transaction_net_usage )
( "max_block_cpu_usage", config.max_block_cpu_usage )
( "target_block_cpu_usage_pct", config.target_block_cpu_usage_pct )
( "max_block_net_usage", config.max_block_net_usage )
( "target_block_net_usage_pct", config.target_block_net_usage_pct )
( "max_transaction_lifetime", config.max_transaction_lifetime )
( "max_transaction_exec_time", config.max_transaction_exec_time )
( "max_authority_depth", config.max_authority_depth )
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册