提交 9dfa4ede 编写于 作者: B Brian Johnson

STAT-81 (GH-607) Added logic for tracking db usage and limiting.

上级 b89b918b
...@@ -33,10 +33,10 @@ class wasm_interface { ...@@ -33,10 +33,10 @@ class wasm_interface {
}; };
typedef map<name, key_type> TableMap; typedef map<name, key_type> TableMap;
struct ModuleState { struct ModuleState {
Runtime::ModuleInstance* instance = nullptr; Runtime::ModuleInstance* instance = nullptr;
IR::Module* module = nullptr; IR::Module* module = nullptr;
int mem_start = 0; int mem_start = 0;
int mem_end = 1<<16; int mem_end = 1<<16;
vector<char> init_memory; vector<char> init_memory;
fc::sha256 code_version; fc::sha256 code_version;
TableMap table_key_types; TableMap table_key_types;
...@@ -55,19 +55,23 @@ class wasm_interface { ...@@ -55,19 +55,23 @@ class wasm_interface {
static key_type to_key_type(const types::type_name& type_name); static key_type to_key_type(const types::type_name& type_name);
static std::string to_type_name(key_type key_type); static std::string to_type_name(key_type key_type);
apply_context* current_apply_context = nullptr; apply_context* current_apply_context = nullptr;
apply_context* current_validate_context = nullptr; apply_context* current_validate_context = nullptr;
apply_context* current_precondition_context = nullptr; apply_context* current_precondition_context = nullptr;
Runtime::MemoryInstance* current_memory = nullptr; Runtime::MemoryInstance* current_memory = nullptr;
Runtime::ModuleInstance* current_module = nullptr; Runtime::ModuleInstance* current_module = nullptr;
ModuleState* current_state = nullptr; ModuleState* current_state = nullptr;
wasm_memory* current_memory_management = nullptr; wasm_memory* current_memory_management = nullptr;
TableMap* table_key_types = nullptr; TableMap* table_key_types = nullptr;
bool tables_fixed = false; bool tables_fixed = false;
int64_t table_storage_delta = 0;
uint32_t checktime_limit = 0; uint32_t checktime_limit = 0;
int32_t per_code_account_max_db_limit_mbytes = config::default_per_code_account_max_db_limit_mbytes;
uint32_t row_overhead_db_limit_bytes = config::default_row_overhead_db_limit_bytes;
private: private:
void load( const account_name& name, const chainbase::database& db ); void load( const account_name& name, const chainbase::database& db );
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <boost/multiprecision/cpp_bin_float.hpp> #include <boost/multiprecision/cpp_bin_float.hpp>
#include <eos/chain/wasm_interface.hpp> #include <eos/chain/wasm_interface.hpp>
#include <eos/chain/chain_controller.hpp> #include <eos/chain/chain_controller.hpp>
#include <eos/chain/rate_limiting_object.hpp>
#include "Platform/Platform.h" #include "Platform/Platform.h"
#include "WAST/WAST.h" #include "WAST/WAST.h"
#include "Runtime/Runtime.h" #include "Runtime/Runtime.h"
...@@ -25,6 +26,7 @@ namespace eosio { namespace chain { ...@@ -25,6 +26,7 @@ namespace eosio { namespace chain {
using namespace IR; using namespace IR;
using namespace Runtime; using namespace Runtime;
typedef boost::multiprecision::cpp_bin_float_50 DOUBLE; typedef boost::multiprecision::cpp_bin_float_50 DOUBLE;
const uint32_t bytes_per_mbyte = 1024 * 1024;
class wasm_memory class wasm_memory
{ {
...@@ -129,6 +131,14 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) { ...@@ -129,6 +131,14 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) {
return func(wasm.current_apply_context, &keys, value, valuelen); return func(wasm.current_apply_context, &keys, value, valuelen);
} }
int64_t round_to_byte_boundary(int64_t bytes)
{
const unsigned int byte_boundary = sizeof(uint64_t);
int64_t remainder = bytes % byte_boundary;
if (remainder > 0)
bytes += byte_boundary - remainder;
return bytes;
}
#define VERIFY_TABLE(TYPE) \ #define VERIFY_TABLE(TYPE) \
const auto table_name = name(table); \ const auto table_name = name(table); \
...@@ -159,20 +169,63 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) { ...@@ -159,20 +169,63 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) {
auto lambda = [&](apply_context* ctx, INDEX::value_type::key_type* keys, char *data, uint32_t datalen) -> int32_t { \ auto lambda = [&](apply_context* ctx, INDEX::value_type::key_type* keys, char *data, uint32_t datalen) -> int32_t { \
return ctx->UPDATEFUNC<INDEX::value_type>( name(scope), name(ctx->code.value), table_name, keys, data, datalen); \ return ctx->UPDATEFUNC<INDEX::value_type>( name(scope), name(ctx->code.value), table_name, keys, data, datalen); \
}; \ }; \
return validate<decltype(lambda), INDEX::value_type::key_type, INDEX::value_type::number_of_keys>(valueptr, DATASIZE, lambda); const int32_t ret = validate<decltype(lambda), INDEX::value_type::key_type, INDEX::value_type::number_of_keys>(valueptr, DATASIZE, lambda);
char* key_str(uint64_t key)
{
const char nums[] = "0123456789";
char* ret = new char[512];
char* ret2 = new char[512];
const uint64_t rem = 10;
uint32_t index = 0;
for (; key > 0; ++index)
{
ret[index] = nums[(uint32_t)(key % rem)];
key /= rem;
}
for (uint32_t i = 0; i < index; ++i)
{
ret2[i] = ret[index - 1 - i];
}
ret2[index] = '\0';
return ret2;
}
#define DEFINE_RECORD_UPDATE_FUNCTIONS(OBJTYPE, INDEX) \ #define DEFINE_RECORD_UPDATE_FUNCTIONS(OBJTYPE, INDEX, KEY_SIZE) \
DEFINE_INTRINSIC_FUNCTION4(env,store_##OBJTYPE,store_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \ DEFINE_INTRINSIC_FUNCTION4(env,store_##OBJTYPE,store_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \
VERIFY_TABLE(OBJTYPE) \ VERIFY_TABLE(OBJTYPE) \
UPDATE_RECORD(store_record, INDEX, valuelen); \ UPDATE_RECORD(store_record, INDEX, valuelen); \
/* ret is -1 if record created, or else it is the length of the data portion of the originally stored */ \
/* structure (it does not include the key portion */ \
const bool created = (ret == -1); \
int64_t& delta = wasm_interface::get().table_storage_delta; \
if (created) \
delta += round_to_byte_boundary(valuelen) + wasm_interface::get().row_overhead_db_limit_bytes; \
else \
/* need to calculate the difference between the original rounded byte size and the new rounded byte size */ \
delta += round_to_byte_boundary(valuelen) - round_to_byte_boundary(KEY_SIZE + ret); \
return created ? 1 : 0; \
} \ } \
DEFINE_INTRINSIC_FUNCTION4(env,update_##OBJTYPE,update_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \ DEFINE_INTRINSIC_FUNCTION4(env,update_##OBJTYPE,update_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \
VERIFY_TABLE(OBJTYPE) \ VERIFY_TABLE(OBJTYPE) \
UPDATE_RECORD(update_record, INDEX, valuelen); \ UPDATE_RECORD(update_record, INDEX, valuelen); \
/* ret is -1 if record created, or else it is the length of the data portion of the originally stored */ \
/* structure (it does not include the key portion */ \
if (ret == -1) return 0; \
int64_t& delta = wasm_interface::get().table_storage_delta; \
/* need to calculate the difference between the original rounded byte size and the new rounded byte size */ \
delta += round_to_byte_boundary(valuelen) - round_to_byte_boundary(KEY_SIZE + ret); \
return 1; \
} \ } \
DEFINE_INTRINSIC_FUNCTION3(env,remove_##OBJTYPE,remove_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr) { \ DEFINE_INTRINSIC_FUNCTION3(env,remove_##OBJTYPE,remove_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr) { \
VERIFY_TABLE(OBJTYPE) \ VERIFY_TABLE(OBJTYPE) \
UPDATE_RECORD(remove_record, INDEX, sizeof(typename INDEX::value_type::key_type)*INDEX::value_type::number_of_keys); \ UPDATE_RECORD(remove_record, INDEX, sizeof(typename INDEX::value_type::key_type)*INDEX::value_type::number_of_keys); \
/* ret is -1 if record created, or else it is the length of the data portion of the originally stored */ \
/* structure (it does not include the key portion */ \
if (ret == -1) return 0; \
int64_t& delta = wasm_interface::get().table_storage_delta; \
delta -= round_to_byte_boundary(KEY_SIZE + ret) + wasm_interface::get().row_overhead_db_limit_bytes; \
return 1; \
} }
#define DEFINE_RECORD_READ_FUNCTION(OBJTYPE, ACTION, FUNCPREFIX, INDEX, SCOPE) \ #define DEFINE_RECORD_READ_FUNCTION(OBJTYPE, ACTION, FUNCPREFIX, INDEX, SCOPE) \
...@@ -190,14 +243,14 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) { ...@@ -190,14 +243,14 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) {
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, lower_bound, FUNCPREFIX, INDEX, SCOPE) \ DEFINE_RECORD_READ_FUNCTION(OBJTYPE, lower_bound, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, upper_bound, FUNCPREFIX, INDEX, SCOPE) DEFINE_RECORD_READ_FUNCTION(OBJTYPE, upper_bound, FUNCPREFIX, INDEX, SCOPE)
DEFINE_RECORD_UPDATE_FUNCTIONS(i64, key_value_index); DEFINE_RECORD_UPDATE_FUNCTIONS(i64, key_value_index, 8);
DEFINE_RECORD_READ_FUNCTIONS(i64,,key_value_index, by_scope_primary); DEFINE_RECORD_READ_FUNCTIONS(i64,,key_value_index, by_scope_primary);
DEFINE_RECORD_UPDATE_FUNCTIONS(i128i128, key128x128_value_index); DEFINE_RECORD_UPDATE_FUNCTIONS(i128i128, key128x128_value_index, 32);
DEFINE_RECORD_READ_FUNCTIONS(i128i128, primary_, key128x128_value_index, by_scope_primary); DEFINE_RECORD_READ_FUNCTIONS(i128i128, primary_, key128x128_value_index, by_scope_primary);
DEFINE_RECORD_READ_FUNCTIONS(i128i128, secondary_, key128x128_value_index, by_scope_secondary); DEFINE_RECORD_READ_FUNCTIONS(i128i128, secondary_, key128x128_value_index, by_scope_secondary);
DEFINE_RECORD_UPDATE_FUNCTIONS(i64i64i64, key64x64x64_value_index); DEFINE_RECORD_UPDATE_FUNCTIONS(i64i64i64, key64x64x64_value_index, 24);
DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, primary_, key64x64x64_value_index, by_scope_primary); DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, primary_, key64x64x64_value_index, by_scope_primary);
DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, secondary_, key64x64x64_value_index, by_scope_secondary); DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, secondary_, key64x64x64_value_index, by_scope_secondary);
DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_scope_tertiary); DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_scope_tertiary);
...@@ -208,7 +261,7 @@ DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_ ...@@ -208,7 +261,7 @@ DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_
auto lambda = [&](apply_context* ctx, std::string* keys, char *data, uint32_t datalen) -> int32_t { \ auto lambda = [&](apply_context* ctx, std::string* keys, char *data, uint32_t datalen) -> int32_t { \
return ctx->FUNCTION<keystr_value_object>( name(scope), name(ctx->code.value), table_name, keys, data, datalen); \ return ctx->FUNCTION<keystr_value_object>( name(scope), name(ctx->code.value), table_name, keys, data, datalen); \
}; \ }; \
return validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda); const int32_t ret = validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda);
#define READ_RECORD_STR(FUNCTION) \ #define READ_RECORD_STR(FUNCTION) \
VERIFY_TABLE(str) \ VERIFY_TABLE(str) \
...@@ -219,14 +272,34 @@ DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_ ...@@ -219,14 +272,34 @@ DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_
return validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda); return validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda);
DEFINE_INTRINSIC_FUNCTION6(env,store_str,store_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) { DEFINE_INTRINSIC_FUNCTION6(env,store_str,store_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
UPDATE_RECORD_STR(store_record) UPDATE_RECORD_STR(store_record)
const bool created = (ret == -1);
auto& delta = wasm_interface::get().table_storage_delta;
if (created)
{
delta += round_to_byte_boundary(keylen + valuelen) + wasm_interface::get().row_overhead_db_limit_bytes;
}
else
// need to calculate the difference between the original rounded byte size and the new rounded byte size
delta += round_to_byte_boundary(keylen + valuelen) - round_to_byte_boundary(keylen + ret);
return created ? 1 : 0;
} }
DEFINE_INTRINSIC_FUNCTION6(env,update_str,update_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) { DEFINE_INTRINSIC_FUNCTION6(env,update_str,update_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
UPDATE_RECORD_STR(update_record) UPDATE_RECORD_STR(update_record)
if (ret == -1) return 0;
auto& delta = wasm_interface::get().table_storage_delta;
// need to calculate the difference between the original rounded byte size and the new rounded byte size
delta += round_to_byte_boundary(keylen + valuelen) - round_to_byte_boundary(keylen + ret);
return 1;
} }
DEFINE_INTRINSIC_FUNCTION4(env,remove_str,remove_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen) { DEFINE_INTRINSIC_FUNCTION4(env,remove_str,remove_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen) {
int32_t valueptr=0, valuelen=0; int32_t valueptr=0, valuelen=0;
UPDATE_RECORD_STR(remove_record) UPDATE_RECORD_STR(remove_record)
if (ret == -1) return 0;
auto& delta = wasm_interface::get().table_storage_delta;
delta -= round_to_byte_boundary(keylen + ret) + wasm_interface::get().row_overhead_db_limit_bytes;
return 1;
} }
DEFINE_INTRINSIC_FUNCTION7(env,load_str,load_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) { DEFINE_INTRINSIC_FUNCTION7(env,load_str,load_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
...@@ -765,6 +838,27 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { ...@@ -765,6 +838,27 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
vm_apply(); vm_apply();
if (table_storage_delta != 0)
{
auto rate_limiting = c.db.find<rate_limiting_object, by_name>(c.code);
if (rate_limiting == nullptr)
{
EOS_ASSERT(table_storage_delta <= per_code_account_max_db_limit_mbytes * bytes_per_mbyte, tx_code_db_limit_exceeded, "Database limit exceeded for account=${name}",("name", c.code));
c.mutable_db.create<rate_limiting_object>([delta=this->table_storage_delta,code=c.code](rate_limiting_object& rlo) {
rlo.name = code;
rlo.per_code_account_db_bytes = delta;
});
}
else
{
EOS_ASSERT(table_storage_delta <= (per_code_account_max_db_limit_mbytes * bytes_per_mbyte) - rate_limiting->per_code_account_db_bytes, tx_code_db_limit_exceeded, "Database limit exceeded for account=${name}",("name", c.code));
c.mutable_db.modify(*rate_limiting, [delta=this->table_storage_delta] (rate_limiting_object& rlo) {
rlo.per_code_account_db_bytes += delta;
});
}
}
} FC_CAPTURE_AND_RETHROW() } } FC_CAPTURE_AND_RETHROW() }
void wasm_interface::init( apply_context& c ) { void wasm_interface::init( apply_context& c ) {
...@@ -864,11 +958,12 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { ...@@ -864,11 +958,12 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
throw; throw;
} }
} }
current_module = state.instance; current_module = state.instance;
current_memory = getDefaultMemory( current_module ); current_memory = getDefaultMemory( current_module );
current_state = &state; current_state = &state;
table_key_types = &state.table_key_types; table_key_types = &state.table_key_types;
tables_fixed = state.tables_fixed; tables_fixed = state.tables_fixed;
table_storage_delta = 0;
} }
wasm_memory::wasm_memory(wasm_interface& interface) wasm_memory::wasm_memory(wasm_interface& interface)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册