diff --git a/contracts/enumivo.coin/enumivo.coin.cpp b/contracts/enumivo.coin/enumivo.coin.cpp index 92971f1106d..82b1406aeaf 100644 --- a/contracts/enumivo.coin/enumivo.coin.cpp +++ b/contracts/enumivo.coin/enumivo.coin.cpp @@ -52,7 +52,7 @@ void token::issue( account_name to, asset quantity, string memo ) s.supply += quantity; }); - add_balance( st.issuer, quantity, st, st.issuer ); + add_balance( st.issuer, quantity, st.issuer ); if( to != st.issuer ) { SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} ); @@ -80,11 +80,11 @@ void token::transfer( account_name from, enumivo_assert( memo.size() <= 256, "memo has more than 256 bytes" ); - sub_balance( from, quantity, st ); - add_balance( to, quantity, st, from ); + sub_balance( from, quantity ); + add_balance( to, quantity, from ); } -void token::sub_balance( account_name owner, asset value, const currency_stats& st ) { +void token::sub_balance( account_name owner, asset value ) { accounts from_acnts( _self, owner ); const auto& from = from_acnts.get( value.symbol.name(), "no balance object found" ); @@ -100,7 +100,7 @@ void token::sub_balance( account_name owner, asset value, const currency_stats& } } -void token::add_balance( account_name owner, asset value, const currency_stats& st, account_name ram_payer ) +void token::add_balance( account_name owner, asset value, account_name ram_payer ) { accounts to_acnts( _self, owner ); auto to = to_acnts.find( value.symbol.name() ); diff --git a/contracts/enumivo.coin/enumivo.coin.hpp b/contracts/enumivo.coin/enumivo.coin.hpp index e933ef2a5b6..0186dd8c790 100644 --- a/contracts/enumivo.coin/enumivo.coin.hpp +++ b/contracts/enumivo.coin/enumivo.coin.hpp @@ -57,9 +57,8 @@ namespace enumivo { typedef enumivo::multi_index accounts; typedef enumivo::multi_index stats; - void sub_balance( account_name owner, asset value, const currency_stats& st ); - void add_balance( account_name owner, asset value, const currency_stats& st, - account_name ram_payer ); + void sub_balance( account_name owner, asset value ); + void add_balance( account_name owner, asset value, account_name ram_payer ); public: struct transfer_args { diff --git a/contracts/enumivo.system/delegate_bandwidth.cpp b/contracts/enumivo.system/delegate_bandwidth.cpp index 1ec7c7d30b7..458ce127062 100644 --- a/contracts/enumivo.system/delegate_bandwidth.cpp +++ b/contracts/enumivo.system/delegate_bandwidth.cpp @@ -107,24 +107,32 @@ namespace enumivosystem { require_auth( payer ); enumivo_assert( quant.amount > 0, "must purchase a positive amount" ); + auto fee = quant; + fee.amount /= 200; /// .5% fee + auto quant_after_fee = quant; + quant_after_fee.amount -= fee.amount; + if( payer != N(enumivo) ) { INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {payer,N(active)}, - { payer, N(enumivo), quant, std::string("buy ram") } ); + { payer, N(enumivo.ram), quant_after_fee, std::string("buy ram") } ); } + if( fee.amount > 0 ) { + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {payer,N(active)}, + { payer, N(enumivo.rfee), fee, std::string("ram fee") } ); + } int64_t bytes_out; auto itr = _rammarket.find(S(4,RAMCORE)); _rammarket.modify( itr, 0, [&]( auto& es ) { - bytes_out = es.convert( quant, S(0,RAM) ).amount; + bytes_out = es.convert( quant_after_fee, S(0,RAM) ).amount; }); - enumivo_assert( bytes_out > 0, "must reserve a positive amount" ); _gstate.total_ram_bytes_reserved += uint64_t(bytes_out); - _gstate.total_ram_stake += quant.amount; + _gstate.total_ram_stake += quant_after_fee.amount; user_resources_table userres( _self, receiver ); auto res_itr = userres.find( receiver ); @@ -175,8 +183,13 @@ namespace enumivosystem { set_resource_limits( res_itr->owner, res_itr->ram_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount ); if( N(enumivo) != account ) { - INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo),N(active)}, - { N(enumivo), account, asset(tokens_out), std::string("sell ram") } ); + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo.ram),N(active)}, + { N(enumivo.ram), account, asset(tokens_out), std::string("sell ram") } ); + auto fee = tokens_out.amount / 200; + if( fee > 0 ) { + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {account,N(active)}, + { account, N(enumivo.rfee), asset(fee), std::string("sell ram fee") } ); + } } } @@ -252,7 +265,7 @@ namespace enumivosystem { } // tot_itr can be invalid, should go out of scope // create refund or update from existing refund - if ( N(enumivo) != source_stake_from ) { //for enumivo both transfer and refund make no sense + if ( N(enumivo.stk) != source_stake_from ) { //for enumivo both transfer and refund make no sense refunds_table refunds_tbl( _self, from ); auto req = refunds_tbl.find( from ); @@ -317,7 +330,7 @@ namespace enumivosystem { auto transfer_amount = net_balance + cpu_balance; if ( asset(0) < transfer_amount ) { INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {source_stake_from, N(active)}, - { source_stake_from, N(enumivo), asset(transfer_amount), std::string("stake bandwidth") } ); + { source_stake_from, N(enumivo.stk), asset(transfer_amount), std::string("stake bandwidth") } ); } } @@ -379,8 +392,8 @@ namespace enumivosystem { // allow people to get their tokens earlier than the 3 day delay if the unstake happened immediately after many // consecutive missed blocks. - INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo),N(active)}, - { N(enumivo), req->owner, req->net_amount + req->cpu_amount, std::string("unstake") } ); + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo.stk),N(active)}, + { N(enumivo.stk), req->owner, req->net_amount + req->cpu_amount, std::string("unstake") } ); refunds_tbl.erase( req ); } diff --git a/contracts/enumivo.system/enumivo.system.cpp b/contracts/enumivo.system/enumivo.system.cpp index 70af262e16b..9a5a6f017f7 100644 --- a/contracts/enumivo.system/enumivo.system.cpp +++ b/contracts/enumivo.system/enumivo.system.cpp @@ -54,6 +54,7 @@ namespace enumivosystem { void system_contract::setram( uint64_t max_ram_size ) { require_auth( _self ); + enumivo_assert( _gstate.max_ram_size < max_ram_size, "ram may only be increased" ); /// decreasing ram might result market maker issues enumivo_assert( max_ram_size < 1024ll*1024*1024*1024*1024, "ram size is unrealistic" ); enumivo_assert( max_ram_size > _gstate.total_ram_bytes_reserved, "attempt to set max below reserved" ); @@ -133,11 +134,12 @@ namespace enumivosystem { const authority& active*/ ) { if( creator != _self ) { - auto tmp = newact; + auto tmp = newact >> 4; bool has_dot = false; - for( uint32_t i = 0; i < 13; ++i ) { - has_dot |= (tmp >> 59); - tmp <<= 5; + + for( uint32_t i = 0; i < 12; ++i ) { + has_dot |= !(tmp & 0x1f); + tmp >>= 5; } auto suffix = enumivo::name_suffix(newact); if( has_dot ) { diff --git a/contracts/enumivo.system/producer_pay.cpp b/contracts/enumivo.system/producer_pay.cpp index 06db3c69eeb..7a46276a383 100644 --- a/contracts/enumivo.system/producer_pay.cpp +++ b/contracts/enumivo.system/producer_pay.cpp @@ -95,6 +95,15 @@ namespace enumivosystem { INLINE_ACTION_SENDER(enumivo::token, issue)( N(enumivo.coin), {{N(enumivo),N(active)}}, {N(enumivo), asset(new_tokens), std::string("issue tokens for producer pay and savings")} ); + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo),N(active)}, + { N(enumivo), N(enumivo.save), asset(to_savings), "unallocated inflation" } ); + + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo),N(active)}, + { N(enumivo), N(enumivo.bpay), asset(to_per_block_pay), "fund per-block bucket" } ); + + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo),N(active)}, + { N(enumivo), N(enumivo.vpay), asset(to_per_vote_pay), "fund per-vote bucket" } ); + _gstate.pervote_bucket += to_per_vote_pay; _gstate.perblock_bucket += to_per_block_pay; _gstate.savings += to_savings; @@ -113,8 +122,6 @@ namespace enumivosystem { if( producer_per_vote_pay < min_pervote_daily_pay ) { producer_per_vote_pay = 0; } - int64_t total_pay = producer_per_block_pay + producer_per_vote_pay; - _gstate.pervote_bucket -= producer_per_vote_pay; _gstate.perblock_bucket -= producer_per_block_pay; _gstate.total_unpaid_blocks -= prod.unpaid_blocks; @@ -124,9 +131,13 @@ namespace enumivosystem { p.unpaid_blocks = 0; }); - if( total_pay > 0 ) { - INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo),N(active)}, - { N(enumivo), owner, asset(total_pay), std::string("producer pay") } ); + if( producer_per_block_pay > 0 ) { + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo.bpay),N(active)}, + { N(enumivo.bpay), owner, asset(producer_per_block_pay), std::string("producer block pay") } ); + } + if( producer_per_vote_pay > 0 ) { + INLINE_ACTION_SENDER(enumivo::token, transfer)( N(enumivo.coin), {N(enumivo.vpay),N(active)}, + { N(enumivo.vpay), owner, asset(producer_per_vote_pay), std::string("producer vote pay") } ); } } diff --git a/contracts/enumivolib/print.hpp b/contracts/enumivolib/print.hpp index b449274fb65..1d0074c5c8b 100644 --- a/contracts/enumivolib/print.hpp +++ b/contracts/enumivolib/print.hpp @@ -26,6 +26,10 @@ namespace enumivo { prints_l( s.c_str(), s.size() ); } + inline void print( const char c ) { + prints_l( &c, 1 ); + } + /** * Prints signed integer * @brief Prints signed integer as a 64 bit signed integer diff --git a/contracts/test_api/CMakeLists.txt b/contracts/test_api/CMakeLists.txt index 1b0239d54d8..099f3f909ed 100644 --- a/contracts/test_api/CMakeLists.txt +++ b/contracts/test_api/CMakeLists.txt @@ -4,5 +4,4 @@ add_wast_executable(TARGET test_api INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}" LIBRARIES libc++ libc enumivolib DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR} - MAX_MEMORY 1048576 ) diff --git a/contracts/test_api/test_api.cpp b/contracts/test_api/test_api.cpp index a1bd083017c..85be4ec9e93 100644 --- a/contracts/test_api/test_api.cpp +++ b/contracts/test_api/test_api.cpp @@ -162,6 +162,14 @@ extern "C" { // test checktime WASM_TEST_HANDLER(test_checktime, checktime_pass); WASM_TEST_HANDLER(test_checktime, checktime_failure); + WASM_TEST_HANDLER(test_checktime, checktime_sha1_failure); + WASM_TEST_HANDLER(test_checktime, checktime_assert_sha1_failure); + WASM_TEST_HANDLER(test_checktime, checktime_sha256_failure); + WASM_TEST_HANDLER(test_checktime, checktime_assert_sha256_failure); + WASM_TEST_HANDLER(test_checktime, checktime_sha512_failure); + WASM_TEST_HANDLER(test_checktime, checktime_assert_sha512_failure); + WASM_TEST_HANDLER(test_checktime, checktime_ripemd160_failure); + WASM_TEST_HANDLER(test_checktime, checktime_assert_ripemd160_failure); // test datastream WASM_TEST_HANDLER(test_datastream, test_basic); diff --git a/contracts/test_api/test_api.hpp b/contracts/test_api/test_api.hpp index b522290362b..f302304c16e 100644 --- a/contracts/test_api/test_api.hpp +++ b/contracts/test_api/test_api.hpp @@ -244,6 +244,14 @@ struct test_memory { struct test_checktime { static void checktime_pass(); static void checktime_failure(); + static void checktime_sha1_failure(); + static void checktime_assert_sha1_failure(); + static void checktime_sha256_failure(); + static void checktime_assert_sha256_failure(); + static void checktime_sha512_failure(); + static void checktime_assert_sha512_failure(); + static void checktime_ripemd160_failure(); + static void checktime_assert_ripemd160_failure(); }; /* struct test_softfloat { diff --git a/contracts/test_api/test_checktime.cpp b/contracts/test_api/test_checktime.cpp index 0f2e04cd0f9..08f59a11f8c 100644 --- a/contracts/test_api/test_checktime.cpp +++ b/contracts/test_api/test_checktime.cpp @@ -4,8 +4,12 @@ */ #include +#include +#include #include "test_api.hpp" +#include + void test_checktime::checktime_pass() { int p = 0; for ( int i = 0; i < 10000; i++ ) @@ -23,3 +27,53 @@ void test_checktime::checktime_failure() { enumivo::print(p); } + +constexpr size_t size = 20000000; + +void test_checktime::checktime_sha1_failure() { + char* ptr = new char[size]; + checksum160 res; + sha1( ptr, size, &res ); +} + +void test_checktime::checktime_assert_sha1_failure() { + char* ptr = new char[size]; + checksum160 res; + assert_sha1( ptr, size, &res ); +} + +void test_checktime::checktime_sha256_failure() { + char* ptr = new char[size]; + checksum256 res; + sha256( ptr, size, &res ); +} + +void test_checktime::checktime_assert_sha256_failure() { + char* ptr = new char[size]; + checksum256 res; + assert_sha256( ptr, size, &res ); +} + +void test_checktime::checktime_sha512_failure() { + char* ptr = new char[size]; + checksum512 res; + sha512( ptr, size, &res ); +} + +void test_checktime::checktime_assert_sha512_failure() { + char* ptr = new char[size]; + checksum512 res; + assert_sha512( ptr, size, &res ); +} + +void test_checktime::checktime_ripemd160_failure() { + char* ptr = new char[size]; + checksum160 res; + ripemd160( ptr, size, &res ); +} + +void test_checktime::checktime_assert_ripemd160_failure() { + char* ptr = new char[size]; + checksum160 res; + assert_ripemd160( ptr, size, &res ); +} diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 31dd34808d2..e15b83d653c 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -16,18 +16,16 @@ using boost::container::flat_set; namespace enumivo { namespace chain { static inline void print_debug(account_name receiver, const action_trace& ar) { - if(fc::logger::get(DEFAULT_LOGGER).is_enabled(fc::log_level::debug)) { - if (!ar.console.empty()) { - auto prefix = fc::format_string( - "\n[(${a},${n})->${r}]", - fc::mutable_variant_object() - ("a", ar.act.account) - ("n", ar.act.name) - ("r", receiver)); - dlog(prefix + ": CONSOLE OUTPUT BEGIN =====================\n" - + ar.console - + prefix + ": CONSOLE OUTPUT END =====================" ); - } + if (!ar.console.empty()) { + auto prefix = fc::format_string( + "\n[(${a},${n})->${r}]", + fc::mutable_variant_object() + ("a", ar.act.account) + ("n", ar.act.name) + ("r", receiver)); + dlog(prefix + ": CONSOLE OUTPUT BEGIN =====================\n" + + ar.console + + prefix + ": CONSOLE OUTPUT END =====================" ); } } @@ -75,7 +73,9 @@ action_trace apply_context::exec_one() trx_context.executed.emplace_back( move(r) ); - print_debug(receiver, t); + if ( control.contracts_console() ) { + print_debug(receiver, t); + } reset_console(); diff --git a/libraries/chain/block_log.cpp b/libraries/chain/block_log.cpp index 673710e99c0..6bae15a93fc 100644 --- a/libraries/chain/block_log.cpp +++ b/libraries/chain/block_log.cpp @@ -262,7 +262,7 @@ namespace enumivo { namespace chain { } } // construct_index - void block_log::repair_log( const fc::path& data_dir ) { + fc::path block_log::repair_log( const fc::path& data_dir ) { ilog("Recovering Block Log..."); FC_ASSERT( fc::is_directory(data_dir) && fc::is_regular_file(data_dir / "blocks.log"), "Block log not found in '${blocks_dir}'", ("blocks_dir", data_dir) ); @@ -270,13 +270,12 @@ namespace enumivo { namespace chain { auto now = fc::time_point::now(); auto blocks_dir = fc::canonical( data_dir ); + if( blocks_dir.filename().generic_string() == "." ) { + blocks_dir = blocks_dir.parent_path(); + } auto backup_dir = blocks_dir.parent_path(); auto blocks_dir_name = blocks_dir.filename(); - if( blocks_dir_name.generic_string() == "." ) { - blocks_dir_name = backup_dir.filename(); - backup_dir = backup_dir.parent_path(); - FC_ASSERT( blocks_dir_name.generic_string() != ".", "Invalid path to blocks directory" ); - } + FC_ASSERT( blocks_dir_name.generic_string() != ".", "Invalid path to blocks directory" ); backup_dir = backup_dir / blocks_dir_name.generic_string().append("-").append( now ); FC_ASSERT( !fc::exists(backup_dir), @@ -369,6 +368,8 @@ namespace enumivo { namespace chain { } else { ilog( "Existing block log was undamaged. Recovered all irreversible blocks up to block number ${num}.", ("num", block_num) ); } + + return backup_dir; } } } /// enumivo::chain diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 6a65dd3fe2f..d86b46dcc2e 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -47,7 +47,7 @@ struct pending_state { struct controller_impl { controller& self; chainbase::database db; - chainbase::database unconfirmed_blocks; ///< a special database to persist blocks that have successfully been applied but are still reversible + chainbase::database reversible_blocks; ///< a special database to persist blocks that have successfully been applied but are still reversible block_log blog; optional pending; block_state_ptr head; @@ -73,9 +73,9 @@ struct controller_impl { auto prev = fork_db.get_block( head->header.previous ); FC_ASSERT( prev, "attempt to pop beyond last irreversible block" ); - if( const auto* b = unconfirmed_blocks.find(head->block_num) ) + if( const auto* b = reversible_blocks.find(head->block_num) ) { - unconfirmed_blocks.remove( *b ); + reversible_blocks.remove( *b ); } for( const auto& t : head->trxs ) @@ -92,14 +92,14 @@ struct controller_impl { controller_impl( const controller::config& cfg, controller& s ) :self(s), - db( cfg.shared_memory_dir, + db( cfg.state_dir, cfg.read_only ? database::read_only : database::read_write, - cfg.shared_memory_size ), - unconfirmed_blocks( cfg.block_log_dir/"unconfirmed", + cfg.state_size ), + reversible_blocks( cfg.blocks_dir/config::reversible_blocks_dir_name, cfg.read_only ? database::read_only : database::read_write, - cfg.unconfirmed_cache_size ), - blog( cfg.block_log_dir ), - fork_db( cfg.shared_memory_dir ), + cfg.reversible_cache_size ), + blog( cfg.blocks_dir ), + fork_db( cfg.state_dir ), wasmif( cfg.wasm_runtime ), resource_limits( db ), authorization( s, db ), @@ -174,10 +174,10 @@ struct controller_impl { emit( self.irreversible_block, s ); db.commit( s->block_num ); - const auto& ubi = unconfirmed_blocks.get_index(); + const auto& ubi = reversible_blocks.get_index(); auto objitr = ubi.begin(); while( objitr != ubi.end() && objitr->blocknum <= s->block_num ) { - unconfirmed_blocks.remove( *objitr ); + reversible_blocks.remove( *objitr ); objitr = ubi.begin(); } } @@ -209,16 +209,17 @@ struct controller_impl { replaying_irreversible = false; int unconf = 0; - while( auto obj = unconfirmed_blocks.find(head->block_num+1) ) { + while( auto obj = reversible_blocks.find(head->block_num+1) ) { ++unconf; self.push_block( obj->get_block(), true ); } std::cerr<< "\n"; - ilog( "${n} unconfirmed blocks replayed", ("n",unconf) ); + ilog( "${n} reversible blocks replayed", ("n",unconf) ); auto end = fc::time_point::now(); - ilog( "replayed blocks in ${n} seconds, ${spb} spb", - ("n", head->block_num)("spb", ((end-start).count()/1000000.0)/head->block_num) ); + ilog( "replayed ${n} blocks in seconds, ${mspb} ms/block", + ("n", head->block_num)("duration", (end-start).count()/1000000) + ("mspb", ((end-start).count()/1000.0)/head->block_num) ); std::cerr<< "\n"; replaying = false; @@ -227,16 +228,16 @@ struct controller_impl { } } - const auto& ubi = unconfirmed_blocks.get_index(); + const auto& ubi = reversible_blocks.get_index(); auto objitr = ubi.rbegin(); if( objitr != ubi.rend() ) { FC_ASSERT( objitr->blocknum == head->block_num, - "unconfirmed block database is inconsistent with fork database, replay blockchain", + "reversible block database is inconsistent with fork database, replay blockchain", ("head",head->block_num)("unconfimed", objitr->blocknum) ); } else { auto end = blog.read_head(); FC_ASSERT( end && end->block_num() == head->block_num, - "fork database exists but unconfirmed block database does not, replay blockchain", + "fork database exists but reversible block database does not, replay blockchain", ("blog_head",end->block_num())("head",head->block_num) ); } @@ -261,11 +262,11 @@ struct controller_impl { edump((db.revision())(head->block_num)(blog.read_head()->block_num())); db.flush(); - unconfirmed_blocks.flush(); + reversible_blocks.flush(); } void add_indices() { - unconfirmed_blocks.add_index(); + reversible_blocks.add_index(); db.add_index(); db.add_index(); @@ -410,7 +411,7 @@ struct controller_impl { emit( self.accepted_block, pending->_pending_block_state ); if( !replaying ) { - unconfirmed_blocks.create( [&]( auto& ubo ) { + reversible_blocks.create( [&]( auto& ubo ) { ubo.blocknum = pending->_pending_block_state->block_num; ubo.set_block( pending->_pending_block_state->block ); }); @@ -1282,6 +1283,10 @@ bool controller::skip_auth_check()const { return my->replaying_irreversible && !my->conf.force_all_checks; } +bool controller::contracts_console()const { + return my->conf.contracts_console; +} + const apply_handler* controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const { auto native_handler_scope = my->apply_handlers.find( receiver ); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index b1b39d700b0..7842e453617 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -55,7 +55,7 @@ namespace enumivo { namespace chain { if (!fc::is_directory(my->datadir)) fc::create_directories(my->datadir); - auto fork_db_dat = my->datadir / "forkdb.dat"; + auto fork_db_dat = my->datadir / config::forkdb_filename; if( fc::exists( fork_db_dat ) ) { string content; fc::read_file_contents( fork_db_dat, content ); @@ -71,6 +71,8 @@ namespace enumivo { namespace chain { fc::raw::unpack( ds, head_id ); my->head = get_block( head_id ); + + fc::remove( fork_db_dat ); } } @@ -84,7 +86,7 @@ namespace enumivo { namespace chain { states.push_back( *s ); } - auto fork_db_dat = my->datadir / "forkdb.dat"; + auto fork_db_dat = my->datadir / config::forkdb_filename; std::ofstream out( fork_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); fc::raw::pack( out, states ); if( my->head ) diff --git a/libraries/chain/include/enumivo/chain/block_log.hpp b/libraries/chain/include/enumivo/chain/block_log.hpp index d908a4442f0..dc6f82d8c9b 100644 --- a/libraries/chain/include/enumivo/chain/block_log.hpp +++ b/libraries/chain/include/enumivo/chain/block_log.hpp @@ -58,7 +58,7 @@ namespace enumivo { namespace chain { static const uint64_t npos = std::numeric_limits::max(); - static void repair_log( const fc::path& data_dir ); + static fc::path repair_log( const fc::path& data_dir ); private: void open(const fc::path& data_dir); diff --git a/libraries/chain/include/enumivo/chain/config.hpp b/libraries/chain/include/enumivo/chain/config.hpp index a59d8baf96d..b4f199e0ef9 100644 --- a/libraries/chain/include/enumivo/chain/config.hpp +++ b/libraries/chain/include/enumivo/chain/config.hpp @@ -12,10 +12,14 @@ namespace enumivo { namespace chain { namespace config { typedef __uint128_t uint128_t; -const static auto default_block_log_dir = "block_log"; -const static auto default_shared_memory_dir = "shared_mem"; -const static auto default_shared_memory_size = 1*1024*1024*1024ll; -const static auto default_unconfirmed_cache_size = 320*1024*1024ll;/// 1MB * 340 blocks based on 21 producer BFT delay +const static auto default_blocks_dir_name = "blocks"; +const static auto reversible_blocks_dir_name = "reversible"; +const static auto default_reversible_cache_size = 340*1024*1024ll;/// 1MB * 340 blocks based on 21 producer BFT delay + +const static auto default_state_dir_name = "state"; +const static auto forkdb_filename = "forkdb.dat"; +const static auto default_state_size = 1*1024*1024*1024ll; + const static uint64_t system_account_name = N(enumivo); const static uint64_t null_account_name = N(enumivo.null); diff --git a/libraries/chain/include/enumivo/chain/controller.hpp b/libraries/chain/include/enumivo/chain/controller.hpp index 309fe45b0de..01c90ea1020 100644 --- a/libraries/chain/include/enumivo/chain/controller.hpp +++ b/libraries/chain/include/enumivo/chain/controller.hpp @@ -36,12 +36,13 @@ namespace enumivo { namespace chain { class controller { public: struct config { - path block_log_dir = chain::config::default_block_log_dir; - uint64_t unconfirmed_cache_size = chain::config::default_unconfirmed_cache_size;; - path shared_memory_dir = chain::config::default_shared_memory_dir; - uint64_t shared_memory_size = chain::config::default_shared_memory_size; - bool read_only = false; - bool force_all_checks = false; + path blocks_dir = chain::config::default_blocks_dir_name; + path state_dir = chain::config::default_state_dir_name; + uint64_t state_size = chain::config::default_state_size; + uint64_t reversible_cache_size = chain::config::default_reversible_cache_size; + bool read_only = false; + bool force_all_checks = false; + bool contracts_console = false; genesis_state genesis; wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime; @@ -151,6 +152,7 @@ namespace enumivo { namespace chain { bool skip_auth_check()const; + bool contracts_console()const; signal accepted_block_header; signal accepted_block; @@ -202,10 +204,13 @@ namespace enumivo { namespace chain { } } /// enumivo::chain FC_REFLECT( enumivo::chain::controller::config, - (block_log_dir) - (unconfirmed_cache_size) - (shared_memory_dir)(shared_memory_size)(read_only) + (blocks_dir) + (state_dir) + (state_size) + (reversible_cache_size) + (read_only) (force_all_checks) + (contracts_console) (genesis) (wasm_runtime) ) diff --git a/libraries/chain/include/enumivo/chain/exceptions.hpp b/libraries/chain/include/enumivo/chain/exceptions.hpp index a386dd076f6..eb6bf8c7e30 100644 --- a/libraries/chain/include/enumivo/chain/exceptions.hpp +++ b/libraries/chain/include/enumivo/chain/exceptions.hpp @@ -244,7 +244,8 @@ namespace enumivo { namespace chain { 3100002, "unknown block" ) FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_exception, misc_exception, 3100003, "unknown transaction" ) - + FC_DECLARE_DERIVED_EXCEPTION( fixed_reversible_db_exception, misc_exception, + 3100004, "corrupted reversible block database was fixed" ) FC_DECLARE_DERIVED_EXCEPTION( missing_plugin_exception, chain_exception, 3110000, "missing plugin exception" ) diff --git a/libraries/chain/include/enumivo/chain/reversible_block_object.hpp b/libraries/chain/include/enumivo/chain/reversible_block_object.hpp new file mode 100644 index 00000000000..c91092c4041 --- /dev/null +++ b/libraries/chain/include/enumivo/chain/reversible_block_object.hpp @@ -0,0 +1,48 @@ + +/** + * @file + * @copyright defined in enumivo/LICENSE.txt + */ +#pragma once +#include +#include +#include +#include + +#include "multi_index_includes.hpp" + +namespace enumivo { namespace chain { + + class reversible_block_object : public chainbase::object { + OBJECT_CTOR(reversible_block_object,(packedblock) ) + + id_type id; + uint32_t blocknum = 0; + shared_string packedblock; + + void set_block( const signed_block_ptr& b ) { + packedblock.resize( fc::raw::pack_size( *b ) ); + fc::datastream ds( packedblock.data(), packedblock.size() ); + fc::raw::pack( ds, *b ); + } + + signed_block_ptr get_block()const { + fc::datastream ds( packedblock.data(), packedblock.size() ); + auto result = std::make_shared(); + fc::raw::unpack( ds, *result ); + return result; + } + }; + + struct by_num; + using reversible_block_index = chainbase::shared_multi_index_container< + reversible_block_object, + indexed_by< + ordered_unique, member>, + ordered_unique, member> + > + >; + +} } // enumivo::chain + +CHAINBASE_SET_INDEX_TYPE(enumivo::chain::reversible_block_object, enumivo::chain::reversible_block_index) diff --git a/libraries/chain/include/enumivo/chain/types.hpp b/libraries/chain/include/enumivo/chain/types.hpp index 7e310fcc9a2..ba0124ab312 100644 --- a/libraries/chain/include/enumivo/chain/types.hpp +++ b/libraries/chain/include/enumivo/chain/types.hpp @@ -150,7 +150,7 @@ namespace enumivo { namespace chain { resource_limits_config_object_type, account_history_object_type, action_history_object_type, - unconfirmed_block_object_type, + reversible_block_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -220,7 +220,7 @@ FC_REFLECT_ENUM(enumivo::chain::object_type, (resource_limits_config_object_type) (account_history_object_type) (action_history_object_type) - (unconfirmed_block_object_type) + (reversible_block_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT( enumivo::chain::void_t, ) diff --git a/libraries/chain/resource_limits.cpp b/libraries/chain/resource_limits.cpp index 59022e4d36d..742b8c0c96a 100644 --- a/libraries/chain/resource_limits.cpp +++ b/libraries/chain/resource_limits.cpp @@ -192,7 +192,7 @@ int64_t resource_limits_manager::get_account_ram_usage( const account_name& name bool resource_limits_manager::set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight) { - const auto& usage = _db.get( account ); + //const auto& usage = _db.get( account ); /* * Since we need to delay these until the next resource limiting boundary, these are created in a "pending" * state or adjusted in an existing "pending" state. The chain controller will collapse "pending" state into diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 7e7c7ffdba7..313d42d009f 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -977,68 +977,85 @@ class action_api : public context_aware_api { class console_api : public context_aware_api { public: console_api( apply_context& ctx ) - :context_aware_api(ctx,true){} + : context_aware_api(ctx,true) + , ignore(!ctx.control.contracts_console()) {} // Kept as intrinsic rather than implementing on WASM side (using prints_l and strlen) because strlen is faster on native side. void prints(null_terminated_ptr str) { - context.console_append(str); + if ( !ignore ) { + context.console_append(str); + } } void prints_l(array_ptr str, size_t str_len ) { - context.console_append(string(str, str_len)); + if ( !ignore ) { + context.console_append(string(str, str_len)); + } } void printi(int64_t val) { - context.console_append(val); + if ( !ignore ) { + context.console_append(val); + } } void printui(uint64_t val) { - context.console_append(val); + if ( !ignore ) { + context.console_append(val); + } } void printi128(const __int128& val) { - bool is_negative = (val < 0); - unsigned __int128 val_magnitude; + if ( !ignore ) { + bool is_negative = (val < 0); + unsigned __int128 val_magnitude; - if( is_negative ) - val_magnitude = static_cast(-val); // Works even if val is at the lowest possible value of a int128_t - else - val_magnitude = static_cast(val); + if( is_negative ) + val_magnitude = static_cast(-val); // Works even if val is at the lowest possible value of a int128_t + else + val_magnitude = static_cast(val); - fc::uint128_t v(val_magnitude>>64, static_cast(val_magnitude) ); + fc::uint128_t v(val_magnitude>>64, static_cast(val_magnitude) ); - if( is_negative ) { - context.console_append("-"); - } + if( is_negative ) { + context.console_append("-"); + } - context.console_append(fc::variant(v).get_string()); + context.console_append(fc::variant(v).get_string()); + } } void printui128(const unsigned __int128& val) { - fc::uint128_t v(val>>64, static_cast(val) ); - context.console_append(fc::variant(v).get_string()); + if ( !ignore ) { + fc::uint128_t v(val>>64, static_cast(val) ); + context.console_append(fc::variant(v).get_string()); + } } void printsf( float val ) { - // Assumes float representation on native side is the same as on the WASM side - auto& console = context.get_console_stream(); - auto orig_prec = console.precision(); + if ( !ignore ) { + // Assumes float representation on native side is the same as on the WASM side + auto& console = context.get_console_stream(); + auto orig_prec = console.precision(); - console.precision( std::numeric_limits::digits10 ); - context.console_append(val); + console.precision( std::numeric_limits::digits10 ); + context.console_append(val); - console.precision( orig_prec ); + console.precision( orig_prec ); + } } void printdf( double val ) { - // Assumes double representation on native side is the same as on the WASM side - auto& console = context.get_console_stream(); - auto orig_prec = console.precision(); + if ( !ignore ) { + // Assumes double representation on native side is the same as on the WASM side + auto& console = context.get_console_stream(); + auto orig_prec = console.precision(); - console.precision( std::numeric_limits::digits10 ); - context.console_append(val); + console.precision( std::numeric_limits::digits10 ); + context.console_append(val); - console.precision( orig_prec ); + console.precision( orig_prec ); + } } void printqf( const float128_t& val ) { @@ -1054,25 +1071,34 @@ class console_api : public context_aware_api { * having to deal with long doubles at all. */ - auto& console = context.get_console_stream(); - auto orig_prec = console.precision(); + if ( !ignore ) { + auto& console = context.get_console_stream(); + auto orig_prec = console.precision(); - console.precision( std::numeric_limits::digits10 ); + console.precision( std::numeric_limits::digits10 ); - extFloat80_t val_approx; - f128M_to_extF80M(&val, &val_approx); - context.console_append( *(long double*)(&val_approx) ); + extFloat80_t val_approx; + f128M_to_extF80M(&val, &val_approx); + context.console_append( *(long double*)(&val_approx) ); - console.precision( orig_prec ); + console.precision( orig_prec ); + } } void printn(const name& value) { - context.console_append(value.to_string()); + if ( !ignore ) { + context.console_append(value.to_string()); + } } void printhex(array_ptr data, size_t data_len ) { - context.console_append(fc::to_hex(data, data_len)); + if ( !ignore ) { + context.console_append(fc::to_hex(data, data_len)); + } } + + private: + bool ignore; }; #define DB_API_METHOD_WRAPPERS_SIMPLE_SECONDARY(IDX, TYPE)\ diff --git a/libraries/chainbase b/libraries/chainbase index 752f2646256..184464a6418 160000 --- a/libraries/chainbase +++ b/libraries/chainbase @@ -1 +1 @@ -Subproject commit 752f2646256eb2b8b1b2c1110179817c9d866381 +Subproject commit 184464a6418b258c61e273a228a20c416fbb3386 diff --git a/libraries/testing/include/enumivo/testing/tester.hpp b/libraries/testing/include/enumivo/testing/tester.hpp index 57d229e7d47..3c11b3ddf5a 100644 --- a/libraries/testing/include/enumivo/testing/tester.hpp +++ b/libraries/testing/include/enumivo/testing/tester.hpp @@ -310,9 +310,11 @@ namespace enumivo { namespace testing { controller::config vcfg; validating_tester() { - vcfg.block_log_dir = tempdir.path() / "vblocklog"; - vcfg.shared_memory_dir = tempdir.path() / "vshared"; - vcfg.shared_memory_size = 1024*1024*8; + vcfg.blocks_dir = tempdir.path() / std::string("v_").append(config::default_blocks_dir_name); + vcfg.state_dir = tempdir.path() / std::string("v_").append(config::default_state_dir_name); + vcfg.state_size = 1024*1024*8; + vcfg.reversible_cache_size = 1024*1024*8; + vcfg.contracts_console = false; vcfg.genesis.initial_timestamp = fc::time_point::from_iso_string("2020-01-01T00:00:00.000"); vcfg.genesis.initial_key = get_public_key( config::system_account_name, "active" ); diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 5adc351b185..2a2ac12de2f 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -37,10 +37,11 @@ namespace enumivo { namespace testing { } void base_tester::init(bool push_genesis) { - cfg.block_log_dir = tempdir.path() / "blocklog"; - cfg.shared_memory_dir = tempdir.path() / "shared"; - cfg.shared_memory_size = 1024*1024*8; - cfg.unconfirmed_cache_size = 1024*1024*8; + cfg.blocks_dir = tempdir.path() / config::default_blocks_dir_name; + cfg.state_dir = tempdir.path() / config::default_state_dir_name; + cfg.state_size = 1024*1024*8; + cfg.reversible_cache_size = 1024*1024*8; + cfg.contracts_console = true; cfg.genesis.initial_timestamp = fc::time_point::from_iso_string("2020-01-01T00:00:00.000"); cfg.genesis.initial_key = get_public_key( config::system_account_name, "active" ); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 5658703507a..ecd233ac7e8 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(http_plugin) add_subdirectory(chain_plugin) add_subdirectory(chain_api_plugin) add_subdirectory(producer_plugin) +add_subdirectory(producer_api_plugin) add_subdirectory(history_plugin) add_subdirectory(history_api_plugin) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index c807683d5b3..f4eb1abbf06 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -21,6 +22,8 @@ #include +#include + #include #include #include @@ -34,6 +37,8 @@ using namespace enumivo::chain::plugin_interface; using vm_type = wasm_interface::vm_type; using fc::flat_map; +using boost::signals2::scoped_connection; + //using txn_msg_rate_limits = controller::txn_msg_rate_limits; @@ -51,11 +56,10 @@ class chain_plugin_impl { ,incoming_transaction_sync_method(app().get_method()) {} - bfs::path block_log_dir; + bfs::path blocks_dir; bfs::path genesis_file; time_point genesis_timestamp; bool readonly = false; - uint64_t shared_memory_size; flat_map loaded_checkpoints; fc::optional fork_db; @@ -85,6 +89,15 @@ class chain_plugin_impl { methods::get_head_block_id::method_type::handle get_head_block_id_provider; methods::get_last_irreversible_block_number::method_type::handle get_last_irreversible_block_number_provider; + // scoped connections for chain controller + fc::optional accepted_block_header_connection; + fc::optional accepted_block_connection; + fc::optional irreversible_block_connection; + fc::optional accepted_transaction_connection; + fc::optional applied_transaction_connection; + fc::optional accepted_confirmation_connection; + + }; chain_plugin::chain_plugin() @@ -98,11 +111,13 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip cfg.add_options() ("genesis-json", bpo::value()->default_value("genesis.json"), "File to read Genesis State from") ("genesis-timestamp", bpo::value(), "override the initial timestamp in the Genesis State file") - ("block-log-dir", bpo::value()->default_value("blocks"), - "the location of the block log (absolute path or relative to application data dir)") - ("checkpoint,c", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.") + ("blocks-dir", bpo::value()->default_value("blocks"), + "the location of the blocks directory (absolute path or relative to application data dir)") + ("checkpoint", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.") ("wasm-runtime", bpo::value()->value_name("wavm/binaryen"), "Override default WASM runtime") - ("shared-memory-size-mb", bpo::value()->default_value(config::default_shared_memory_size / (1024 * 1024)), "Maximum size MB of database shared memory file") + ("chain-state-db-size-mb", bpo::value()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MB) of the chain state database") + ("reversible-blocks-db-size-mb", bpo::value()->default_value(config::default_reversible_cache_size / (1024 * 1024)), "Maximum size (in MB) of the reversible blocks database") + #warning TODO: rate limiting /*("per-authorized-account-transaction-msg-rate-limit-time-frame-sec", bpo::value()->default_value(default_per_auth_account_time_frame_seconds), @@ -115,14 +130,18 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "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() + ("fix-reversible-blocks", bpo::bool_switch()->default_value(false), + "recovers reversible block database if that database is in a bad state") ("force-all-checks", bpo::bool_switch()->default_value(false), "do not skip any checks that can be skipped while replaying irreversible blocks") ("replay-blockchain", bpo::bool_switch()->default_value(false), - "clear chain database and replay all blocks") + "clear chain state database and replay all blocks") ("hard-replay-blockchain", bpo::bool_switch()->default_value(false), - "clear chain database, recover as many blocks as possible from the block log, and then replay those blocks") - ("resync-blockchain", bpo::bool_switch()->default_value(false), - "clear chain database and block log") + "clear chain state database, recover as many blocks as possible from the block log, and then replay those blocks") + ("delete-all-blocks", bpo::bool_switch()->default_value(false), + "clear chain state database and block log") + ("contracts-console", bpo::bool_switch()->default_value(false), + "print contract's output to console") ; } @@ -152,28 +171,12 @@ void chain_plugin::plugin_initialize(const variables_map& options) { my->genesis_timestamp = time_point::from_iso_string (tstr); } } - if (options.count("block-log-dir")) { - auto bld = options.at("block-log-dir").as(); + if (options.count("blocks-dir")) { + auto bld = options.at("blocks-dir").as(); if(bld.is_relative()) - my->block_log_dir = app().data_dir() / bld; + my->blocks_dir = app().data_dir() / bld; else - my->block_log_dir = bld; - } - if (options.count("shared-memory-size-mb")) { - my->shared_memory_size = options.at("shared-memory-size-mb").as() * 1024 * 1024; - } - - if( options.at("resync-blockchain").as() ) { - ilog("Resync requested: wiping database and blocks"); - fc::remove_all(app().data_dir() / default_shared_memory_dir); - fc::remove_all(my->block_log_dir); - } else if( options.at("hard-replay-blockchain").as() ) { - ilog("Hard replay requested: wiping database"); - fc::remove_all(app().data_dir() / default_shared_memory_dir); - block_log::repair_log( my->block_log_dir ); - } else if( options.at("replay-blockchain").as() ) { - ilog("Replay requested: wiping database"); - fc::remove_all(app().data_dir() / default_shared_memory_dir); + my->blocks_dir = bld; } if(options.count("checkpoint")) @@ -190,26 +193,71 @@ void chain_plugin::plugin_initialize(const variables_map& options) { if(options.count("wasm-runtime")) my->wasm_runtime = options.at("wasm-runtime").as(); + my->chain_config->blocks_dir = my->blocks_dir; + my->chain_config->state_dir = app().data_dir() / config::default_state_dir_name; + my->chain_config->read_only = my->readonly; + + if (options.count("chain-state-db-size-mb")) + my->chain_config->state_size = options.at("chain-state-db-size-mb").as() * 1024 * 1024; + + if (options.count("reversible-blocks-db-size-mb")) + my->chain_config->reversible_cache_size = options.at("reversible-blocks-db-size-mb").as() * 1024 * 1024; + + if( my->wasm_runtime ) + my->chain_config->wasm_runtime = *my->wasm_runtime; + + my->chain_config->force_all_checks = options.at("force-all-checks").as(); + my->chain_config->contracts_console = options.at("contracts-console").as(); + + if( options.at("delete-all-blocks").as() ) { + ilog("Deleting state database and blocks"); + fc::remove_all( my->chain_config->state_dir ); + fc::remove_all(my->blocks_dir); + } else if( options.at("hard-replay-blockchain").as() ) { + ilog("Hard replay requested: deleting state database"); + fc::remove_all( my->chain_config->state_dir ); + auto backup_dir = block_log::repair_log( my->blocks_dir ); + if( fc::exists(backup_dir/config::reversible_blocks_dir_name) || options.at("fix-reversible-blocks").as() ) { + // Do not try to recover reversible blocks if the directory does not exist, unless the option was explicitly provided. + if( !recover_reversible_blocks( backup_dir/config::reversible_blocks_dir_name, + my->chain_config->reversible_cache_size, + my->chain_config->blocks_dir/config::reversible_blocks_dir_name ) ) { + ilog("Reversible blocks database was not corrupted. Copying from backup to blocks directory."); + fc::copy( backup_dir/config::reversible_blocks_dir_name, my->chain_config->blocks_dir/config::reversible_blocks_dir_name ); + fc::copy( backup_dir/"reversible/shared_memory.bin", my->chain_config->blocks_dir/"reversible/shared_memory.bin" ); + fc::copy( backup_dir/"reversible/shared_memory.meta", my->chain_config->blocks_dir/"reversible/shared_memory.meta" ); + } + } + } else if( options.at("replay-blockchain").as() ) { + ilog("Replay requested: deleting state database"); + fc::remove_all( my->chain_config->state_dir ); + if( options.at("fix-reversible-blocks").as() ) { + if( !recover_reversible_blocks( my->chain_config->blocks_dir/config::reversible_blocks_dir_name, + my->chain_config->reversible_cache_size ) ) { + ilog("Reversible blocks database was not corrupted."); + } + } + } else if( options.at("fix-reversible-blocks").as() ) { + if( !recover_reversible_blocks( my->chain_config->blocks_dir/config::reversible_blocks_dir_name, + my->chain_config->reversible_cache_size ) ) { + ilog("Reversible blocks database verified to not be corrupted. Now exiting..."); + } else { + ilog("Exiting after fixing reversible blocks database..."); + } + ENU_THROW( fixed_reversible_db_exception, "fixed corrupted reversible blocks database" ); + } + if( !fc::exists( my->genesis_file ) ) { wlog( "\n generating default genesis file ${f}", ("f", my->genesis_file.generic_string() ) ); genesis_state default_genesis; fc::json::save_to_file( default_genesis, my->genesis_file, true ); } - my->chain_config->block_log_dir = my->block_log_dir; - my->chain_config->shared_memory_dir = app().data_dir() / default_shared_memory_dir; - my->chain_config->read_only = my->readonly; - my->chain_config->shared_memory_size = my->shared_memory_size; + my->chain_config->genesis = fc::json::from_file(my->genesis_file).as(); if( my->genesis_timestamp.sec_since_epoch() > 0 ) { my->chain_config->genesis.initial_timestamp = my->genesis_timestamp; } - if( my->wasm_runtime ) - my->chain_config->wasm_runtime = *my->wasm_runtime; - - if( options.count("force-all-checks") ) - my->chain_config->force_all_checks = options.at("force-all-checks").as(); - my->chain.emplace(*my->chain_config); // set up method providers @@ -230,27 +278,27 @@ void chain_plugin::plugin_initialize(const variables_map& options) { }); // relay signals to channels - my->chain->accepted_block_header.connect([this](const block_state_ptr& blk) { + my->accepted_block_header_connection = my->chain->accepted_block_header.connect([this](const block_state_ptr& blk) { my->accepted_block_header_channel.publish(blk); }); - my->chain->accepted_block.connect([this](const block_state_ptr& blk) { + my->accepted_block_connection = my->chain->accepted_block.connect([this](const block_state_ptr& blk) { my->accepted_block_channel.publish(blk); }); - my->chain->irreversible_block.connect([this](const block_state_ptr& blk) { + my->irreversible_block_connection = my->chain->irreversible_block.connect([this](const block_state_ptr& blk) { my->irreversible_block_channel.publish(blk); }); - my->chain->accepted_transaction.connect([this](const transaction_metadata_ptr& meta){ + my->accepted_transaction_connection = my->chain->accepted_transaction.connect([this](const transaction_metadata_ptr& meta){ my->accepted_transaction_channel.publish(meta); }); - my->chain->applied_transaction.connect([this](const transaction_trace_ptr& trace){ + my->applied_transaction_connection = my->chain->applied_transaction.connect([this](const transaction_trace_ptr& trace){ my->applied_transaction_channel.publish(trace); }); - my->chain->accepted_confirmation.connect([this](const header_confirmation& conf){ + my->accepted_confirmation_connection = my->chain->accepted_confirmation.connect([this](const header_confirmation& conf){ my->accepted_confirmation_channel.publish(conf); }); @@ -272,6 +320,12 @@ void chain_plugin::plugin_startup() } FC_CAPTURE_LOG_AND_RETHROW( (my->genesis_file.generic_string()) ) } void chain_plugin::plugin_shutdown() { + my->accepted_block_header_connection.reset(); + my->accepted_block_connection.reset(); + my->irreversible_block_connection.reset(); + my->accepted_transaction_connection.reset(); + my->applied_transaction_connection.reset(); + my->accepted_confirmation_connection.reset(); my->chain.reset(); } @@ -292,6 +346,81 @@ bool chain_plugin::block_is_on_preferred_chain(const block_id_type& block_id) { return b && b->id() == block_id; } +bool chain_plugin::recover_reversible_blocks( const fc::path& db_dir, uint32_t cache_size, optional new_db_dir )const { + try { + chainbase::database reversible( db_dir, database::read_only); // Test if dirty + return false; // If it reaches here, then the reversible database is not dirty + } catch( const std::runtime_error& ) { + } catch( ... ) { + throw; + } + // Reversible block database is dirty. So back it up (unless already moved) and then create a new one. + + auto reversible_dir = fc::canonical( db_dir ); + if( reversible_dir.filename().generic_string() == "." ) { + reversible_dir = reversible_dir.parent_path(); + } + fc::path backup_dir; + + if( new_db_dir ) { + backup_dir = reversible_dir; + reversible_dir = *new_db_dir; + } else { + auto now = fc::time_point::now(); + + auto reversible_dir_name = reversible_dir.filename().generic_string(); + FC_ASSERT( reversible_dir_name != ".", "Invalid path to reversible directory" ); + backup_dir = reversible_dir.parent_path() / reversible_dir_name.append("-").append( now ); + + FC_ASSERT( !fc::exists(backup_dir), + "Cannot move existing reversible directory to already existing directory '${backup_dir}'", + ("backup_dir", backup_dir) ); + + fc::rename( reversible_dir, backup_dir ); + ilog( "Moved existing reversible directory to backup location: '${new_db_dir}'", ("new_db_dir", backup_dir) ); + } + + fc::create_directories( reversible_dir ); + + ilog( "Reconstructing '${reversible_dir}' from backed up reversible directory", ("reversible_dir", reversible_dir) ); + + chainbase::database old_reversible( backup_dir, database::read_only, 0, true ); + chainbase::database new_reversible( reversible_dir, database::read_write, cache_size ); + + uint32_t num = 0; + uint32_t start = 0; + uint32_t end = 0; + try { + old_reversible.add_index(); + new_reversible.add_index(); + const auto& ubi = old_reversible.get_index(); + auto itr = ubi.begin(); + if( itr != ubi.end() ) { + start = itr->blocknum; + end = start - 1; + } + for( ; itr != ubi.end(); ++itr ) { + FC_ASSERT( itr->blocknum == end + 1, "gap in reversible block database" ); + new_reversible.create( [&]( auto& ubo ) { + ubo.blocknum = itr->blocknum; + ubo.set_block( itr->get_block() ); // get_block and set_block rather than copying the packed data acts as additional validation + }); + end = itr->blocknum; + ++num; + } + } catch( ... ) {} + + if( num == 0 ) + ilog( "There were no recoverable blocks in the reversible block database" ); + else if( num == 1 ) + ilog( "Recovered 1 block from reversible block database: block ${start}", ("start", start) ); + else + ilog( "Recovered ${num} blocks from reversible block database: blocks ${start} to ${end}", + ("num", num)("start", start)("end", end) ); + + return true; +} + controller::config& chain_plugin::chain_config() { // will trigger optional assert if called before/after plugin_initialize() return *my->chain_config; diff --git a/plugins/chain_plugin/include/enumivo/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/enumivo/chain_plugin/chain_plugin.hpp index 3cdca5ea9ac..eb9ffe19011 100644 --- a/plugins/chain_plugin/include/enumivo/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/enumivo/chain_plugin/chain_plugin.hpp @@ -367,6 +367,8 @@ class chain_plugin : public plugin { bool block_is_on_preferred_chain(const chain::block_id_type& block_id); + bool recover_reversible_blocks( const fc::path& db_dir, uint32_t cache_size, optional new_db_dir = optional() )const; + // Only call this in plugin_initialize() to modify controller constructor configuration controller::config& chain_config(); // Only call this after plugin_startup()! diff --git a/plugins/history_plugin/history_plugin.cpp b/plugins/history_plugin/history_plugin.cpp index ae94c8f9adb..581beb720f0 100644 --- a/plugins/history_plugin/history_plugin.cpp +++ b/plugins/history_plugin/history_plugin.cpp @@ -8,9 +8,11 @@ #include #include +#include namespace enumivo { using namespace chain; + using boost::signals2::scoped_connection; static appbase::abstract_plugin& _history_plugin = app().register_plugin(); @@ -138,6 +140,7 @@ namespace enumivo { bool bypass_filter = false; std::set filter_on; chain_plugin* chain_plug = nullptr; + fc::optional applied_transaction_connection; bool filter( const action_trace& act ) { if( bypass_filter ) @@ -287,9 +290,9 @@ namespace enumivo { chain.db().add_index(); chain.db().add_index(); - chain.applied_transaction.connect( [&]( const transaction_trace_ptr& p ){ + my->applied_transaction_connection.emplace(chain.applied_transaction.connect( [&]( const transaction_trace_ptr& p ){ my->on_applied_transaction(p); - }); + })); } @@ -297,6 +300,7 @@ namespace enumivo { } void history_plugin::plugin_shutdown() { + my->applied_transaction_connection.reset(); } diff --git a/plugins/producer_api_plugin/CMakeLists.txt b/plugins/producer_api_plugin/CMakeLists.txt new file mode 100644 index 00000000000..b3913bcd98a --- /dev/null +++ b/plugins/producer_api_plugin/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB HEADERS "include/enumivo/producer_api_plugin/*.hpp") +add_library( producer_api_plugin + producer_api_plugin.cpp + ${HEADERS} ) + +target_link_libraries( producer_api_plugin producer_plugin http_plugin appbase ) +target_include_directories( producer_api_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/plugins/producer_api_plugin/include/enumivo/producer_api_plugin/producer_api_plugin.hpp b/plugins/producer_api_plugin/include/enumivo/producer_api_plugin/producer_api_plugin.hpp new file mode 100644 index 00000000000..cc5d5180b47 --- /dev/null +++ b/plugins/producer_api_plugin/include/enumivo/producer_api_plugin/producer_api_plugin.hpp @@ -0,0 +1,35 @@ +/** + * @file + * @copyright defined in enumivo/LICENSE.txt + */ +#pragma once + +#include +#include + +#include + +namespace enumivo { + +using namespace appbase; + +class producer_api_plugin : public plugin { + public: + APPBASE_PLUGIN_REQUIRES((producer_plugin) (http_plugin)) + + producer_api_plugin() = default; + producer_api_plugin(const producer_api_plugin&) = delete; + producer_api_plugin(producer_api_plugin&&) = delete; + producer_api_plugin& operator=(const producer_api_plugin&) = delete; + producer_api_plugin& operator=(producer_api_plugin&&) = delete; + virtual ~producer_api_plugin() override = default; + + virtual void set_program_options(options_description& cli, options_description& cfg) override {} + void plugin_initialize(const variables_map& vm); + void plugin_startup(); + void plugin_shutdown() {} + + private: +}; + +} diff --git a/plugins/producer_api_plugin/producer_api_plugin.cpp b/plugins/producer_api_plugin/producer_api_plugin.cpp new file mode 100644 index 00000000000..7037c671754 --- /dev/null +++ b/plugins/producer_api_plugin/producer_api_plugin.cpp @@ -0,0 +1,90 @@ +/** + * @file + * @copyright defined in enumivo/LICENSE.txt + */ +#include +#include + +#include +#include + +#include + +namespace enumivo { namespace detail { + struct producer_api_plugin_response { + std::string result; + }; +}} + +FC_REFLECT(enumivo::detail::producer_api_plugin_response, (result)); + +namespace enumivo { + +static appbase::abstract_plugin& _producer_api_plugin = app().register_plugin(); + +using namespace enumivo; + +#define CALL(api_name, api_handle, call_name, INVOKE, http_response_code) \ +{std::string("/v1/" #api_name "/" #call_name), \ + [&api_handle](string, string body, url_response_callback cb) mutable { \ + try { \ + if (body.empty()) body = "{}"; \ + INVOKE \ + cb(http_response_code, fc::json::to_string(result)); \ + } catch (...) { \ + http_plugin::handle_exception(#api_name, #call_name, body, cb); \ + } \ + }} + +#define INVOKE_R_R(api_handle, call_name, in_param) \ + auto result = api_handle.call_name(fc::json::from_string(body).as()); + +#define INVOKE_R_R_R_R(api_handle, call_name, in_param0, in_param1, in_param2) \ + const auto& vs = fc::json::json::from_string(body).as(); \ + auto result = api_handle.call_name(vs.at(0).as(), vs.at(1).as(), vs.at(2).as()); + +#define INVOKE_R_V(api_handle, call_name) \ + auto result = api_handle.call_name(); + +#define INVOKE_V_R(api_handle, call_name, in_param) \ + api_handle.call_name(fc::json::from_string(body).as()); \ + enumivo::detail::producer_api_plugin_response result{"ok"}; + +#define INVOKE_V_R_R(api_handle, call_name, in_param0, in_param1) \ + const auto& vs = fc::json::json::from_string(body).as(); \ + api_handle.call_name(vs.at(0).as(), vs.at(1).as()); \ + enumivo::detail::producer_api_plugin_response result{"ok"}; + +#define INVOKE_V_V(api_handle, call_name) \ + api_handle.call_name(); \ + enumivo::detail::producer_api_plugin_response result{"ok"}; + + +void producer_api_plugin::plugin_startup() { + ilog("starting producer_api_plugin"); + // lifetime of plugin is lifetime of application + auto& producer = app().get_plugin(); + + app().get_plugin().add_api({ + CALL(producer, producer, pause, + INVOKE_V_V(producer, pause), 201), + CALL(producer, producer, resume, + INVOKE_V_V(producer, resume), 201), + CALL(producer, producer, paused, + INVOKE_R_V(producer, paused), 201), + }); +} + +void producer_api_plugin::plugin_initialize(const variables_map& options) { +} + + +#undef INVOKE_R_R +#undef INVOKE_R_R_R_R +#undef INVOKE_R_V +#undef INVOKE_V_R +#undef INVOKE_V_R_R +#undef INVOKE_V_V +#undef CALL + +} diff --git a/plugins/producer_plugin/include/enumivo/producer_plugin/producer_plugin.hpp b/plugins/producer_plugin/include/enumivo/producer_plugin/producer_plugin.hpp index 8b2dde761b4..c61044a3bf3 100644 --- a/plugins/producer_plugin/include/enumivo/producer_plugin/producer_plugin.hpp +++ b/plugins/producer_plugin/include/enumivo/producer_plugin/producer_plugin.hpp @@ -32,9 +32,13 @@ class producer_plugin : public appbase::plugin { virtual void plugin_startup(); virtual void plugin_shutdown(); + void pause(); + void resume(); + bool paused() const; + signal confirmed_block; private: - std::unique_ptr my; + std::shared_ptr my; }; } //enumivo diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index edd9807d22d..74d8006b628 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace bmi = boost::multi_index; using bmi::indexed_by; @@ -34,6 +35,7 @@ using boost::multi_index_container; using std::string; using std::vector; +using boost::signals2::scoped_connection; // HACK TO EXPOSE LOGGER MAP @@ -84,7 +86,7 @@ enum class pending_block_mode { speculating }; -class producer_plugin_impl { +class producer_plugin_impl : public std::enable_shared_from_this { public: producer_plugin_impl(boost::asio::io_service& io) :_timer(io) @@ -99,6 +101,7 @@ class producer_plugin_impl { boost::program_options::variables_map _options; bool _production_enabled = false; + bool _pause_production = false; uint32_t _required_producer_participation = uint32_t(config::required_producer_participation); uint32_t _production_skip_flags = 0; //enumivo::chain::skip_nothing; @@ -111,7 +114,7 @@ class producer_plugin_impl { int32_t _max_transaction_time_ms; fc::microseconds _max_irreversible_block_age_us; - fc::microseconds _irreversible_block_age_us; + fc::time_point _irreversible_block_time; time_point _last_signed_block_time; time_point _start_time = fc::time_point::now(); @@ -129,6 +132,22 @@ class producer_plugin_impl { transaction_id_with_expiry_index _blacklisted_transactions; + fc::optional _accepted_block_connection; + fc::optional _irreversible_block_connection; + + /* + * HACK ALERT + * Boost timers can be in a state where a handler has not yet executed but is not abortable. + * As this method needs to mutate state handlers depend on for proper functioning to maintain + * invariants for other code (namely accepting incoming transactions in a nearly full block) + * the handlers capture a corelation ID at the time they are set. When they are executed + * they must check that correlation_id against the global ordinal. If it does not match that + * implies that this method has been called with the handler in the state where it should be + * cancelled but wasn't able to be. + */ + uint32_t _timer_corelation_id = 0; + + void on_block( const block_state_ptr& bsp ) { if( bsp->header.timestamp <= _last_signed_block_time ) return; if( bsp->header.timestamp <= _start_time ) return; @@ -166,8 +185,7 @@ class producer_plugin_impl { } void on_irreversible_block( const signed_block_ptr& lib ) { - chain::controller& chain = app().get_plugin().chain(); - _irreversible_block_age_us = chain.head_block_time() - lib->timestamp.to_time_point(); + _irreversible_block_time = lib->timestamp.to_time_point(); } template @@ -291,6 +309,19 @@ class producer_plugin_impl { }); } + fc::microseconds get_irreversible_block_age() { + auto now = fc::time_point::now(); + if (now < _irreversible_block_time) { + return fc::microseconds(0); + } else { + return now - _irreversible_block_time; + } + } + + bool production_disabled_by_policy() { + return !_production_enabled || _pause_production || get_irreversible_block_age() >= _max_irreversible_block_age_us; + } + enum class start_block_result { succeeded, failed, @@ -340,6 +371,7 @@ void producer_plugin::set_program_options( producer_options.add_options() ("enable-stale-production,e", boost::program_options::bool_switch()->notifier([this](bool e){my->_production_enabled = e;}), "Enable block production, even if the chain is stale.") + ("pause-on-startup,x", boost::program_options::bool_switch()->notifier([this](bool p){my->_pause_production = p;}), "Start this node in a state where production is paused") ("max-transaction-time", bpo::value()->default_value(30), "Limits the maximum time (in milliseconds) that is allowed a pushed transaction's code to execute before being considered invalid") ("max-irreversible-block-age", bpo::value()->default_value( 30 * 60 ), @@ -441,15 +473,15 @@ void producer_plugin::plugin_startup() ilog("producer plugin: plugin_startup() begin"); chain::controller& chain = app().get_plugin().chain(); - chain.accepted_block.connect( [this]( const auto& bsp ){ my->on_block( bsp ); } ); - chain.irreversible_block.connect( [this]( const auto& bsp ){ my->on_irreversible_block( bsp->block ); } ); + my->_accepted_block_connection.emplace(chain.accepted_block.connect( [this]( const auto& bsp ){ my->on_block( bsp ); } )); + my->_irreversible_block_connection.emplace(chain.irreversible_block.connect( [this]( const auto& bsp ){ my->on_irreversible_block( bsp->block ); } )); const auto lib_num = chain.last_irreversible_block_num(); const auto lib = chain.fetch_block_by_number(lib_num); if (lib) { my->on_irreversible_block(lib); } else { - my->_irreversible_block_age_us = fc::seconds(0); + my->_irreversible_block_time = fc::time_point::maximum(); } if (!my->_producers.empty()) { @@ -474,13 +506,37 @@ void producer_plugin::plugin_shutdown() { } catch(fc::exception& e) { edump((e.to_detail_string())); } + + my->_accepted_block_connection.reset(); + my->_irreversible_block_connection.reset(); +} + +void producer_plugin::pause() { + my->_pause_production = true; } +void producer_plugin::resume() { + my->_pause_production = false; + // it is possible that we are only speculating because of this policy which we have now changed + // re-evaluate that now + // + if (my->_pending_block_mode == pending_block_mode::speculating) { + chain::controller& chain = app().get_plugin().chain(); + chain.abort_block(); + my->schedule_production_loop(); + } +} + +bool producer_plugin::paused() const { + return my->_pause_production; +} + + optional producer_plugin_impl::calculate_next_block_time(const account_name& producer_name) const { chain::controller& chain = app().get_plugin().chain(); - const auto& hbs = chain.head_block_state(); - const auto& active_schedule = hbs->active_schedule.producers; - const auto& hbt = hbs->header.timestamp; + const auto& pbs = chain.pending_block_state(); + const auto& active_schedule = pbs->active_schedule.producers; + const auto& hbt = pbs->header.timestamp; // determine if this producer is in the active schedule and if so, where auto itr = std::find_if(active_schedule.begin(), active_schedule.end(), [&](const auto& asp){ return asp.producer_name == producer_name; }); @@ -500,9 +556,9 @@ optional producer_plugin_impl::calculate_next_block_time(const a auto current_watermark_itr = _producer_watermarks.find(producer_name); if (current_watermark_itr != _producer_watermarks.end()) { auto watermark = current_watermark_itr->second; - if (watermark > hbs->block_num) { + if (watermark > pbs->block_num) { // if I have a watermark then I need to wait until after that watermark - minimum_offset = watermark - hbs->block_num + 1; + minimum_offset = watermark - pbs->block_num + 1; } } @@ -553,6 +609,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { const auto& scheduled_producer = hbs->get_scheduled_producer(block_time); auto currrent_watermark_itr = _producer_watermarks.find(scheduled_producer.producer_name); auto private_key_itr = _private_keys.find(scheduled_producer.block_signing_key); + auto irreversible_block_age = get_irreversible_block_age(); // If the next block production opportunity is in the present or future, we're synced. if( !_production_enabled ) { @@ -562,8 +619,11 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { } else if (private_key_itr == _private_keys.end()) { elog("Not producing block because I don't have the private key for ${scheduled_key}", ("scheduled_key", scheduled_producer.block_signing_key)); _pending_block_mode = pending_block_mode::speculating; - } else if ( _irreversible_block_age_us >= _max_irreversible_block_age_us ) { - elog("Not producing block because the irreversible block is too old [age:${age}s, max:${max}s]", ("age", _irreversible_block_age_us.count() / 1'000'000)( "max", _max_irreversible_block_age_us.count() / 1'000'000 )); + } else if ( _pause_production ) { + elog("Not producing block because production is explicitly paused"); + _pending_block_mode = pending_block_mode::speculating; + } else if ( irreversible_block_age >= _max_irreversible_block_age_us ) { + elog("Not producing block because the irreversible block is too old [age:${age}s, max:${max}s]", ("age", irreversible_block_age.count() / 1'000'000)( "max", _max_irreversible_block_age_us.count() / 1'000'000 )); _pending_block_mode = pending_block_mode::speculating; } @@ -726,29 +786,19 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { void producer_plugin_impl::schedule_production_loop() { chain::controller& chain = app().get_plugin().chain(); _timer.cancel(); + std::weak_ptr weak_this = shared_from_this(); auto result = start_block(); - /* - * HACK ALERT - * Boost timers can be in a state where a handler has not yet executed but is not abortable. - * As this method needs to mutate state handlers depend on for proper functioning to maintain - * invariants for other code (namely accepting incoming transactions in a nearly full block) - * the handlers capture a coorelation ID at the time they are set. When they are executed - * they must check that correlation_id against the global ordinal. If it does not match that - * implies that this method has been called with the handler in the state where it should be - * cancelled but wasn't able to be. - */ - static uint32_t _coorelation_id = 0; - if (result == start_block_result::failed) { elog("Failed to start a pending block, will try again later"); _timer.expires_from_now( boost::posix_time::microseconds( config::block_interval_us / 10 )); // we failed to start a block, so try again later? - _timer.async_wait([&,cid=++_coorelation_id](const boost::system::error_code& ec) { - if (ec != boost::asio::error::operation_aborted && cid == _coorelation_id) { - schedule_production_loop(); + _timer.async_wait([weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) { + auto self = weak_this.lock(); + if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) { + self->schedule_production_loop(); } }); } else if (_pending_block_mode == pending_block_mode::producing) { @@ -765,13 +815,14 @@ void producer_plugin_impl::schedule_production_loop() { fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} immediately", ("num", chain.pending_block_state()->block_num)); } - _timer.async_wait([&,cid=++_coorelation_id](const boost::system::error_code& ec) { - if (ec != boost::asio::error::operation_aborted && cid == _coorelation_id) { - auto res = maybe_produce_block(); + _timer.async_wait([&chain,weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) { + auto self = weak_this.lock(); + if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) { + auto res = self->maybe_produce_block(); fc_dlog(_log, "Producing Block #${num} returned: ${res}", ("num", chain.pending_block_state()->block_num)("res", res) ); } }); - } else if (_pending_block_mode == pending_block_mode::speculating && !_producers.empty() && _irreversible_block_age_us < _max_irreversible_block_age_us){ + } else if (_pending_block_mode == pending_block_mode::speculating && !_producers.empty() && !production_disabled_by_policy()){ // if we have any producers then we should at least set a timer for our next available slot optional wake_up_time; for (const auto&p: _producers) { @@ -791,9 +842,10 @@ void producer_plugin_impl::schedule_production_loop() { fc_dlog(_log, "Specualtive Block Created; Scheduling Speculative/Production Change at ${time}", ("time", wake_up_time)); static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); _timer.expires_at(epoch + boost::posix_time::microseconds(wake_up_time->time_since_epoch().count())); - _timer.async_wait([&,cid=++_coorelation_id](const boost::system::error_code& ec) { - if (ec != boost::asio::error::operation_aborted && cid == _coorelation_id) { - schedule_production_loop(); + _timer.async_wait([weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) { + auto self = weak_this.lock(); + if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) { + self->schedule_production_loop(); } }); } else { diff --git a/plugins/sql_db_plugin/include/enumivo/sql_db_plugin/sql_db_plugin.hpp b/plugins/sql_db_plugin/include/enumivo/sql_db_plugin/sql_db_plugin.hpp index a2db641d2a6..581fea93152 100644 --- a/plugins/sql_db_plugin/include/enumivo/sql_db_plugin/sql_db_plugin.hpp +++ b/plugins/sql_db_plugin/include/enumivo/sql_db_plugin/sql_db_plugin.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "consumer.h" @@ -46,6 +47,7 @@ class sql_db_plugin final : public plugin { private: std::unique_ptr> m_irreversible_block_consumer; + fc::optional m_irreversible_block_connection; consumer m_block_consumer; }; diff --git a/plugins/sql_db_plugin/sql_db_plugin.cpp b/plugins/sql_db_plugin/sql_db_plugin.cpp index 1157d2f4877..cca58adacf5 100644 --- a/plugins/sql_db_plugin/sql_db_plugin.cpp +++ b/plugins/sql_db_plugin/sql_db_plugin.cpp @@ -72,7 +72,7 @@ void sql_db_plugin::plugin_initialize(const variables_map& options) m_irreversible_block_consumer = std::make_unique>(std::make_unique(db)); // chain.accepted_block.connect([=](const chain::block_state_ptr& b) {m_block_consumer.push(b);}); - chain.irreversible_block.connect([=](const chain::block_state_ptr& b) {m_irreversible_block_consumer->push(b);}); + m_irreversible_block_connection.emplace(chain.irreversible_block.connect([=](const chain::block_state_ptr& b) {m_irreversible_block_consumer->push(b);})); } void sql_db_plugin::plugin_startup() @@ -83,6 +83,7 @@ void sql_db_plugin::plugin_startup() void sql_db_plugin::plugin_shutdown() { ilog("shutdown"); + m_irreversible_block_connection.reset(); } } // namespace enumivo diff --git a/programs/enucli/httpc.cpp b/programs/enucli/httpc.cpp index f70abd24bfe..6c5a4068c23 100644 --- a/programs/enucli/httpc.cpp +++ b/programs/enucli/httpc.cpp @@ -100,8 +100,7 @@ namespace enumivo { namespace client { namespace http { return res; } - fc::variant do_http_call( const std::string& server_url, - const std::string& path, + fc::variant do_http_call( const connection_param& cp, const fc::variant& postdata ) { std::string postjson; if( !postdata.is_null() ) @@ -109,6 +108,9 @@ namespace enumivo { namespace client { namespace http { boost::asio::io_service io_service; + const string& server_url = cp.url; + const string& path = cp.path; + auto url = parse_url( server_url ); boost::asio::streambuf request; @@ -117,7 +119,13 @@ namespace enumivo { namespace client { namespace http { request_stream << "Host: " << url.server << "\r\n"; request_stream << "content-length: " << postjson.size() << "\r\n"; request_stream << "Accept: */*\r\n"; - request_stream << "Connection: close\r\n\r\n"; + request_stream << "Connection: close\r\n"; + // append more customized headers + std::vector::iterator itr; + for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) { + request_stream << *itr << "\r\n"; + } + request_stream << "\r\n"; request_stream << postjson; unsigned int status_code; @@ -140,7 +148,8 @@ namespace enumivo { namespace client { namespace http { #endif boost::asio::ssl::stream socket(io_service, ssl_context); - socket.set_verify_mode(boost::asio::ssl::verify_peer); + if(cp.verify_cert) + socket.set_verify_mode(boost::asio::ssl::verify_peer); do_connect(socket.next_layer(), url.server, url.port); socket.handshake(boost::asio::ssl::stream_base::client); diff --git a/programs/enucli/httpc.hpp b/programs/enucli/httpc.hpp index 9bf59bce4a4..0b55e95ed5e 100644 --- a/programs/enucli/httpc.hpp +++ b/programs/enucli/httpc.hpp @@ -6,6 +6,20 @@ namespace enumivo { namespace client { namespace http { + struct connection_param { + string& url; + string& path; + bool verify_cert; + std::vector& headers; + + connection_param( std::string& u, + std::string& p, + bool verify, + std::vector& h) : url(u), path(p), headers(h) { + verify_cert = verify; + } + }; + struct parsed_url { string scheme; string server; @@ -15,8 +29,7 @@ namespace enumivo { namespace client { namespace http { parsed_url parse_url( const string& server_url ); - fc::variant do_http_call( const std::string& server_url, - const std::string& path, + fc::variant do_http_call( const connection_param& cp, const fc::variant& postdata = fc::variant() ); const string chain_func_base = "/v1/chain"; diff --git a/programs/enucli/main.cpp b/programs/enucli/main.cpp index 158935af78e..e57c9946ed8 100644 --- a/programs/enucli/main.cpp +++ b/programs/enucli/main.cpp @@ -26,6 +26,8 @@ Usage: programs/enucli/enucli [OPTIONS] SUBCOMMAND the http/https URL where enunode is running --wallet-url TEXT=http://localhost:8888/ the http/https URL where enuwallet is running + -r,--header pass specific HTTP header, repeat this option to pass multiple headers + -n,--no-verify don't verify peer certificate when using HTTPS -v,--verbose output verbose actions on error Subcommands: @@ -150,6 +152,8 @@ FC_DECLARE_EXCEPTION( localized_exception, 10000000, "an error occured" ); string url = "http://localhost:8888/"; string wallet_url = "http://localhost:8900/"; int64_t wallet_unlock_timeout = 0; +bool no_verify = false; +vector headers; auto tx_expiration = fc::seconds(30); string tx_ref_block_num_or_id; @@ -207,7 +211,10 @@ fc::variant call( const std::string& url, const std::string& path, const T& v ) { try { - return enumivo::client::http::do_http_call( url, path, fc::variant(v) ); + enumivo::client::http::connection_param *cp = new enumivo::client::http::connection_param((std::string&)url, (std::string&)path, + no_verify ? false : true, headers); + + return enumivo::client::http::do_http_call( *cp, fc::variant(v) ); } catch(boost::system::system_error& e) { if(url == ::url) @@ -1432,6 +1439,16 @@ void get_account( const string& accountName, bool json_format ) { } } +CLI::callback_t header_opt_callback = [](CLI::results_t res) { + vector::iterator itr; + + for (itr = res.begin(); itr != res.end(); itr++) { + headers.push_back(*itr); + } + + return true; +}; + int main( int argc, char** argv ) { setlocale(LC_ALL, ""); bindtextdomain(locale_domain, locale_path); @@ -1447,6 +1464,8 @@ int main( int argc, char** argv ) { app.add_option( "-u,--url", url, localized("the http/https URL where enunode is running"), true ); app.add_option( "--wallet-url", wallet_url, localized("the http/https URL where enuwallet is running"), true ); + app.add_option( "-r,--header", header_opt_callback, localized("pass specific HTTP header; repeat this option to pass multiple headers")); + app.add_flag( "-n,--no-verify", no_verify, localized("don't verify peer certificate when using HTTPS")); app.set_callback([] { ensure_enuwallet_running();}); bool verbose_errors = false; diff --git a/programs/enunode/CMakeLists.txt b/programs/enunode/CMakeLists.txt index 8d2a20ab5a4..d4d520e2c44 100644 --- a/programs/enunode/CMakeLists.txt +++ b/programs/enunode/CMakeLists.txt @@ -54,6 +54,7 @@ target_link_libraries( enunode # PRIVATE -Wl,${whole_archive_flag} faucet_testnet_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${whole_archive_flag} txn_test_gen_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${whole_archive_flag} db_size_api_plugin -Wl,${no_whole_archive_flag} + PRIVATE -Wl,${whole_archive_flag} producer_api_plugin -Wl,${no_whole_archive_flag} PRIVATE chain_plugin http_plugin producer_plugin PRIVATE enumivo_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/programs/enunode/main.cpp b/programs/enunode/main.cpp index 0896548628f..c553648a8de 100644 --- a/programs/enunode/main.cpp +++ b/programs/enunode/main.cpp @@ -80,10 +80,11 @@ void initialize_logging() } enum return_codes { + OTHER_FAIL = -2, INITIALIZE_FAIL = -1, SUCCESS = 0, BAD_ALLOC = 1, - OTHER_FAIL = 2 + FIXED_REVERSIBLE = 2 }; int main(int argc, char** argv) @@ -102,19 +103,29 @@ int main(int argc, char** argv) ilog("enumivo root is ${root}", ("root", root.string())); app().startup(); app().exec(); - } catch (const fc::exception& e) { + } catch( const fixed_reversible_db_exception& e ) { + return FIXED_REVERSIBLE; + } catch( const fc::exception& e ) { elog("${e}", ("e",e.to_detail_string())); return OTHER_FAIL; - } catch (const boost::interprocess::bad_alloc& e) { + } catch( const boost::interprocess::bad_alloc& e ) { elog("bad alloc"); return BAD_ALLOC; - } catch (const boost::exception& e) { + } catch( const boost::exception& e ) { elog("${e}", ("e",boost::diagnostic_information(e))); return OTHER_FAIL; - } catch (const std::exception& e) { + } catch( const std::runtime_error& e ) { + if( std::string(e.what()) == "database dirty flag set" ) { + elog( "database dirty flag set (likely due to unclean shutdown): replay required" ); + } else if( std::string(e.what()) == "database metadata dirty flag set" ) { + elog( "database metadata dirty flag set (likely due to unclean shutdown): replay required" ); + } else { + elog( "${e}", ("e",e.what())); + } + } catch( const std::exception& e ) { elog("${e}", ("e",e.what())); return OTHER_FAIL; - } catch (...) { + } catch( ... ) { elog("unknown exception"); return OTHER_FAIL; } diff --git a/tests/chain_tests/block_tests.cpp b/tests/chain_tests/block_tests.cpp index bbd5a3fbbe2..996ba557c8c 100644 --- a/tests/chain_tests/block_tests.cpp +++ b/tests/chain_tests/block_tests.cpp @@ -869,8 +869,8 @@ BOOST_FIXTURE_TEST_CASE(reindex, validating_tester) // Create shared configuration, so the new chain can be recreated from existing block log chain_controller::controller_config cfg; fc::temp_directory tempdir; - cfg.block_log_dir = tempdir.path() / "blocklog"; - cfg.shared_memory_dir = tempdir.path() / "shared"; + cfg.blocks_dir = tempdir.path() / config::default_blocks_dir_name; + cfg.shared_memory_dir = tempdir.path() / config::default_state_dir_name; cfg.genesis.initial_timestamp = fc::time_point::from_iso_string("2020-01-01T00:00:00.000"); cfg.genesis.initial_key = get_public_key( config::system_account_name, "active" ); diff --git a/tests/testUtils.py b/tests/testUtils.py index a0fbe962001..9f3a6aadb07 100755 --- a/tests/testUtils.py +++ b/tests/testUtils.py @@ -1975,6 +1975,27 @@ def bootstrap(totalNodes, prodCount, biosHost, biosPort, dontKill=False, onlyBio if trans is None: Utils.Print("ERROR: Failed to create account %s" % (enumivoTokenAccount.name)) return False + + enumivoRamAccount=copy.deepcopy(enumivoAccount) + enumivoRamAccount.name="enumivo.ram" + trans=biosNode.createAccount(enumivoRamAccount, enumivoAccount, 0) + if trans is None: + Utils.Print("ERROR: Failed to create account %s" % (enumivoRamAccount.name)) + return False + + enumivoRamfeeAccount=copy.deepcopy(enumivoAccount) + enumivoRamfeeAccount.name="enumivo.rfee" + trans=biosNode.createAccount(enumivoRamfeeAccount, enumivoAccount, 0) + if trans is None: + Utils.Print("ERROR: Failed to create account %s" % (enumivoRamfeeAccount.name)) + return False + + enumivoStakeAccount=copy.deepcopy(enumivoAccount) + enumivoStakeAccount.name="enumivo.stk" + trans=biosNode.createAccount(enumivoStakeAccount, enumivoAccount, 0) + if trans is None: + Utils.Print("ERROR: Failed to create account %s" % (enumivoStakeAccount.name)) + return False Node.validateTransaction(trans) transId=Node.getTransId(trans) diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index b5af70cc474..6caf457f6d5 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -27,7 +27,7 @@ target_link_libraries( unit_test enumivo_chain chainbase enumivo_testing enu_uti target_include_directories( unit_test PUBLIC ${CMAKE_BINARY_DIR}/contracts ${CMAKE_CURRENT_BINARY_DIR}/tests/contracts ) target_include_directories( unit_test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/wasm_tests ) target_include_directories( unit_test PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include ) -add_dependencies(unit_test asserter test_api test_api_mem test_api_db test_api_multi_index exchange enumivo.coin proxy identity identity_test stltest infinite enumivo.system enumivo.coin enumivo.bios test.inline multi_index_test noop dice enumivo.msig payloadless tic_tac_toe) +add_dependencies(unit_test asserter test_api test_api_mem test_api_db test_ram_limit test_api_multi_index exchange enumivo.coin proxy identity identity_test stltest infinite enumivo.system enumivo.coin enumivo.bios test.inline multi_index_test noop dice enumivo.msig payloadless tic_tac_toe) #Manually run unit_test for all supported runtimes #To run unit_test with all log from blockchain displayed, put --verbose after --, i.e. unit_test -- --verbose diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 441a9f23c34..92d654fb7c8 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -689,6 +689,23 @@ BOOST_FIXTURE_TEST_CASE(checktime_pass_tests, TESTER) { try { BOOST_REQUIRE_EQUAL( validate(), true ); } FC_LOG_AND_RETHROW() } +template +void call_test(TESTER& test, T ac, uint32_t billed_cpu_time_us , uint32_t max_cpu_usage_ms = 200 ) { + signed_transaction trx; + + auto pl = vector{{N(testapi), config::active_name}}; + action act(pl, ac); + + trx.actions.push_back(act); + test.set_transaction_headers(trx); + //trx.max_cpu_usage_ms = max_cpu_usage_ms; + auto sigs = trx.sign(test.get_private_key(N(testapi), "active"), chain_id_type()); + trx.get_signature_keys(chain_id_type() ); + auto res = test.push_transaction( trx, fc::time_point::now() + fc::milliseconds(max_cpu_usage_ms), billed_cpu_time_us ); + BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); + test.produce_block(); +}; + BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try { TESTER t; t.produce_blocks(2); @@ -704,25 +721,6 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try { t.control->get_resource_limits_manager().get_account_limits( N(testapi), x, net, cpu ); wdump((net)(cpu)); - auto call_test = [](TESTER& test, auto ac, uint32_t billed_cpu_time_us /*, uint8_t max_cpu_usage_ms */ ) { - signed_transaction trx; - - auto pl = vector{{N(testapi), config::active_name}}; - action act(pl, ac); - - ilog( "call test" ); - - trx.actions.push_back(act); - test.set_transaction_headers(trx); - //trx.max_cpu_usage_ms = max_cpu_usage_ms; - auto sigs = trx.sign(test.get_private_key(N(testapi), "active"), chain_id_type()); - trx.get_signature_keys(chain_id_type() ); - auto res = test.push_transaction( trx, fc::time_point::now() + fc::milliseconds(200), billed_cpu_time_us ); - BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); - test.produce_block(); - }; - - BOOST_CHECK_EXCEPTION( call_test( t, test_api_action{}, 5000 ), deadline_exception, is_deadline_exception ); @@ -745,6 +743,55 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try { BOOST_REQUIRE_EQUAL( t.validate(), true ); } FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE(checktime_hashing_fail, TESTER) { try { + produce_blocks(2); + create_account( N(testapi) ); + produce_blocks(10); + set_code( N(testapi), test_api_wast ); + produce_blocks(1); + + //hit deadline exception, but cache the contract + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + //the contract should be cached, now we should get deadline_exception because of calls to checktime() from hashing function + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, + 5000, 10 ), + deadline_exception, is_deadline_exception ); + + BOOST_REQUIRE_EQUAL( validate(), true ); +} FC_LOG_AND_RETHROW() } + /************************************************************************************* * compiler_builtins_tests test case *************************************************************************************/ diff --git a/unittests/bootseq_tests.cpp b/unittests/bootseq_tests.cpp index c89ae22141b..e14b158eada 100644 --- a/unittests/bootseq_tests.cpp +++ b/unittests/bootseq_tests.cpp @@ -92,7 +92,7 @@ class bootseq_tester : public TESTER { auto delegate_bandwidth( name from, name receiver, asset net, asset cpu, uint8_t transfer = 1) { auto r = base_tester::push_action(N(enumivo), N(delegatebw), from, mvo() - ("from", "enumivo" ) + ("from", from ) ("receiver", receiver) ("stake_net_quantity", net) ("stake_cpu_quantity", cpu) @@ -182,7 +182,7 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) { try { // Create enumivo.msig and enumivo.coin - create_accounts({N(enumivo.msig), N(enumivo.coin)}); + create_accounts({N(enumivo.msig), N(enumivo.coin), N(enumivo.ram), N(enumivo.rfee), N(enumivo.stk), N(enumivo.vpay), N(enumivo.bpay), N(enumivo.save) }); // Set code for the following accounts: // - enumivo (code: enumivo.bios) (already set by tester constructor) @@ -230,7 +230,7 @@ BOOST_FIXTURE_TEST_CASE( bootseq_test, bootseq_tester ) { auto r = buyram(N(enumivo), a.aname, asset(ram)); BOOST_REQUIRE( !r->except_ptr ); - r = delegate_bandwidth(N(enumivo), a.aname, asset(net), asset(cpu)); + r = delegate_bandwidth(N(enumivo.stk), a.aname, asset(net), asset(cpu)); BOOST_REQUIRE( !r->except_ptr ); } diff --git a/unittests/enumivo.system_tests.cpp b/unittests/enumivo.system_tests.cpp index 38dedec1e58..7bf81171249 100644 --- a/unittests/enumivo.system_tests.cpp +++ b/unittests/enumivo.system_tests.cpp @@ -14,7 +14,7 @@ BOOST_AUTO_TEST_SUITE(enumivo_system_tests) BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) + get_balance( "enumivo.rfee" ) + get_balance( "enumivo.stk" ) ); BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111" ) ); transfer( "enumivo", "alice1111111", core_from_string("1000.0000"), "enumivo" ); @@ -34,13 +34,19 @@ BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { BOOST_REQUIRE_EQUAL( true, 0 < bought_bytes ); BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111", bought_bytes ) ); - BOOST_REQUIRE_EQUAL( core_from_string("999.9999"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("998.0050"), get_balance( "alice1111111" ) ); total = get_total_stake( "alice1111111" ); BOOST_REQUIRE_EQUAL( true, total["ram_bytes"].as_uint64() == init_bytes ); transfer( "enumivo", "alice1111111", core_from_string("100000000.0000"), "enumivo" ); - BOOST_REQUIRE_EQUAL( core_from_string("100000999.9999"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("100000998.0050"), get_balance( "alice1111111" ) ); + // alice buys ram for 10000000.0000, 0.5% = 50000.0000 got to ramfee + // after fee 9950000.0000 got to bought bytes + // when selling back bought bytes, pay 0.5% fee and get back 99.5% of 9950000.0000 = 9900250.0000 + // expected account after that is 90000998.0050 + 9900250.0000 = 99901248.0050 with a difference + // of order 0.0001 due to rounding errors BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("90000998.0050"), get_balance( "alice1111111" ) ); total = get_total_stake( "alice1111111" ); bytes = total["ram_bytes"].as_uint64(); @@ -55,7 +61,7 @@ BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { wdump((init_bytes)(bought_bytes)(bytes) ); BOOST_REQUIRE_EQUAL( true, total["ram_bytes"].as_uint64() == init_bytes ); - BOOST_REQUIRE_EQUAL( core_from_string("100000999.9993"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("99901248.0044"), get_balance( "alice1111111" ) ); BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("100.0000") ) ); @@ -67,7 +73,7 @@ BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("10.0000") ) ); BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("10.0000") ) ); BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("30.0000") ) ); - BOOST_REQUIRE_EQUAL( core_from_string("100000439.9993"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("99900688.0044"), get_balance( "alice1111111" ) ); auto newtotal = get_total_stake( "alice1111111" ); @@ -76,7 +82,7 @@ BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { wdump((newbytes)(bytes)(bought_bytes) ); BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111", bought_bytes ) ); - BOOST_REQUIRE_EQUAL( core_from_string("100000999.9991"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("99901242.4183"), get_balance( "alice1111111" ) ); newtotal = get_total_stake( "alice1111111" ); @@ -91,7 +97,7 @@ BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("100000.0000") ) ); BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("100000.0000") ) ); BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111", "alice1111111", core_from_string("300000.0000") ) ); - BOOST_REQUIRE_EQUAL( core_from_string("49400999.9991"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("49301242.4183"), get_balance( "alice1111111" ) ); auto finaltotal = get_total_stake( "alice1111111" ); auto endbytes = finaltotal["ram_bytes"].as_uint64(); @@ -101,17 +107,16 @@ BOOST_FIXTURE_TEST_CASE( buysell, enumivo_system_tester ) try { BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111", bought_bytes ) ); - BOOST_REQUIRE_EQUAL( core_from_string("100000999.9943"), get_balance( "alice1111111" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("99396507.4147"), get_balance( "alice1111111" ) ); } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE( stake_unstake, enumivo_system_tester ) try { - //issue( "enumivo", core_from_string("1000.0000"), config::system_account_name ); - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) + get_balance( "enumivo.rfee" ) + get_balance( "enumivo.stk" ) ); BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111" ) ); transfer( "enumivo", "alice1111111", core_from_string("1000.0000"), "enumivo" ); - BOOST_REQUIRE_EQUAL( core_from_string("999999000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance( "alice1111111" ) ); BOOST_REQUIRE_EQUAL( success(), stake( "enumivo", "alice1111111", core_from_string("200.0000"), core_from_string("100.0000") ) ); @@ -165,11 +170,12 @@ BOOST_FIXTURE_TEST_CASE( stake_unstake, enumivo_system_tester ) try { } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE( stake_unstake_with_transfer, enumivo_system_tester ) try { - //issue( "enumivo", core_from_string("1000.0000"), config::system_account_name ); - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + issue( "enumivo", core_from_string("1000.0000"), config::system_account_name ); + issue( "enumivo.stk", core_from_string("1000.0000"), config::system_account_name ); BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111" ) ); //enumivo stakes for alice with transfer flag + transfer( "enumivo", "bob111111111", core_from_string("1000.0000"), "enumivo" ); BOOST_REQUIRE_EQUAL( success(), stake_with_transfer( "bob111111111", "alice1111111", core_from_string("200.0000"), core_from_string("100.0000") ) ); @@ -179,7 +185,6 @@ BOOST_FIXTURE_TEST_CASE( stake_unstake_with_transfer, enumivo_system_tester ) tr BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); REQUIRE_MATCHING_OBJECT( voter( "alice1111111", core_from_string("300.0000")), get_voter_info( "alice1111111" ) ); - //BOOST_REQUIRE_EQUAL( core_from_string("999999700.0000"), get_balance( "enumivo" ) ); BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111" ) ); //alice stakes for herself @@ -196,12 +201,16 @@ BOOST_FIXTURE_TEST_CASE( stake_unstake_with_transfer, enumivo_system_tester ) tr BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111", "alice1111111", core_from_string("400.0000"), core_from_string("200.0000") ) ); BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111" ) ); + edump((get_balance( "enumivo.stk" ))); + produce_block( fc::hours(3*24-1) ); produce_blocks(1); BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111" ) ); //after 3 days funds should be released + produce_block( fc::hours(1) ); produce_blocks(1); + BOOST_REQUIRE_EQUAL( core_from_string("1300.0000"), get_balance( "alice1111111" ) ); //stake should be equal to what was staked in constructor, votring power should be 0 @@ -1763,6 +1772,8 @@ BOOST_FIXTURE_TEST_CASE(producer_onblock_check, enumivo_system_tester) try { create_account_with_resources( N(producvoterb), config::system_account_name, core_from_string("1.0000"), false, large_asset, large_asset ); create_account_with_resources( N(producvoterc), config::system_account_name, core_from_string("1.0000"), false, large_asset, large_asset ); + + // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers std::vector producer_names; producer_names.reserve('z' - 'a' + 1); @@ -2262,6 +2273,7 @@ BOOST_FIXTURE_TEST_CASE( buyname, enumivo_system_tester ) try { //wlog( "verify dan cannot create test.fail" ); BOOST_REQUIRE_THROW( create_accounts_with_resources( { N(test.fail) }, N(dan) ), fc::exception ); // dan shouldn't be able to do this + create_accounts_with_resources( { N(goodgoodgood) }, N(dan) ); /// 12 char names should succeed } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE( multiple_namebids, enumivo_system_tester ) try { diff --git a/unittests/enumivo_system_tester.hpp b/unittests/enumivo_system_tester.hpp index 9681ca1a050..95faa4ed40c 100644 --- a/unittests/enumivo_system_tester.hpp +++ b/unittests/enumivo_system_tester.hpp @@ -46,7 +46,8 @@ class enumivo_system_tester : public TESTER { produce_blocks( 2 ); - create_accounts( { N(enumivo.coin) } ); + create_accounts({ N(enumivo.coin), N(enumivo.ram), N(enumivo.rfee), N(enumivo.stk), + N(enumivo.bpay), N(enumivo.vpay), N(enumivo.save) }); produce_blocks( 100 ); @@ -81,7 +82,7 @@ class enumivo_system_tester : public TESTER { create_account_with_resources( N(carol1111111), config::system_account_name, core_from_string("1.0000"), false ); - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance("enumivo") + get_balance("enumivo.rfee") + get_balance("enumivo.stk")); } @@ -322,7 +323,6 @@ class enumivo_system_tester : public TESTER { } asset get_balance( const account_name& act ) { - vector data = get_row_by_account( N(enumivo.coin), act, N(accounts), symbol(CORE_SYMBOL).to_symbol_code().value ); return data.empty() ? asset(0, symbol(CORE_SYMBOL)) : token_abi_ser.binary_to_variant("account", data)["balance"].as(); } diff --git a/unittests/misc_tests.cpp b/unittests/misc_tests.cpp index 7f79e180455..4070b2a7922 100644 --- a/unittests/misc_tests.cpp +++ b/unittests/misc_tests.cpp @@ -532,51 +532,6 @@ BOOST_AUTO_TEST_CASE(alphabetic_sort) } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( suffix_test ) try { - uint64_t com = name( "com" ); - uint64_t order = com; - - uint64_t name_com = name("name.com"); - uint64_t name_com_au = name("name.com.au"); - - //std::string str(13,'.'); - - uint64_t tmp = com; - auto print = []( uint64_t tmp ) { - static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz"; - - uint64_t suffix = 0; - bool endsuffix = false; - uint32_t offset = 0; - for( uint32_t i = 0; i <= 12; ++i, ++offset ) { - auto p = tmp >> 59; - char c = charmap[p]; - - if( !p ) { - endsuffix = true; - } else { - if( !endsuffix ) { - suffix |= uint64_t(p) << (59-(5*offset)); - } - } - if( endsuffix && p ) { - endsuffix = false; - offset = 0; - suffix = uint64_t(p) << (59-(5*offset)); - } - std::cerr << c; - // str[12-i] = c; - tmp <<= 5; - } - // std::cerr << " suffix: " << name(suffix) <<"\n"; - }; - - print( com ); - //std::cerr <<"\n"; - print( name_com ); - print( name_com_au ); - -} FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_CASE(transaction_test) { try { diff --git a/unittests/multisig_tests.cpp b/unittests/multisig_tests.cpp index f62322b4685..f8a23178d34 100644 --- a/unittests/multisig_tests.cpp +++ b/unittests/multisig_tests.cpp @@ -33,7 +33,7 @@ class enumivo_msig_tester : public tester { public: enumivo_msig_tester() { - create_accounts( { N(enumivo.msig), N(alice), N(bob), N(carol) } ); + create_accounts( { N(enumivo.msig), N(enumivo.stk), N(enumivo.ram), N(enumivo.rfee), N(alice), N(bob), N(carol) } ); produce_block(); auto trace = base_tester::push_action(config::system_account_name, N(setpriv), @@ -420,7 +420,7 @@ BOOST_FIXTURE_TEST_CASE( update_system_contract_all_approve, enumivo_msig_tester create_currency( N(enumivo.coin), config::system_account_name, core_from_string("10000000000.0000") ); issue(config::system_account_name, core_from_string("1000000000.0000")); - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance("enumivo") + get_balance("enumivo.rfee") + get_balance("enumivo.stk") ); set_code( config::system_account_name, enumivo_system_wast ); set_abi( config::system_account_name, enumivo_system_abi ); @@ -431,7 +431,7 @@ BOOST_FIXTURE_TEST_CASE( update_system_contract_all_approve, enumivo_msig_tester create_account_with_resources( N(bob111111111), N(enumivo), core_from_string("0.4500"), false ); create_account_with_resources( N(carol1111111), N(enumivo), core_from_string("1.0000"), false ); - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance("enumivo") + get_balance("enumivo.rfee") + get_balance("enumivo.stk") ); vector perm = { { N(alice), config::active_name }, { N(bob), config::active_name }, {N(carol), config::active_name} }; @@ -541,7 +541,7 @@ BOOST_FIXTURE_TEST_CASE( update_system_contract_major_approve, enumivo_msig_test create_account_with_resources( N(bob111111111), N(enumivo), core_from_string("0.4500"), false ); create_account_with_resources( N(carol1111111), N(enumivo), core_from_string("1.0000"), false ); - BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( "enumivo" ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance("enumivo") + get_balance("enumivo.rfee") + get_balance("enumivo.stk") ); vector perm = { { N(alice), config::active_name }, { N(bob), config::active_name }, {N(carol), config::active_name}, {N(apple), config::active_name}}; diff --git a/unittests/ram_tests.cpp b/unittests/ram_tests.cpp index 9624b4e8ca8..d22ea8de65d 100644 --- a/unittests/ram_tests.cpp +++ b/unittests/ram_tests.cpp @@ -42,7 +42,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try create_account_with_resources(N(testram11111),N(enumivo), init_request_bytes); create_account_with_resources(N(testram22222),N(enumivo), init_request_bytes); produce_blocks(10); - BOOST_REQUIRE_EQUAL( success(), stake( "enumivo", "testram11111", core_from_string("10.0000"), core_from_string("5.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), stake( "enumivo.stk", "testram11111", core_from_string("10.0000"), core_from_string("5.0000") ) ); produce_blocks(10); for (auto i = 0; i < 10; ++i) { @@ -91,7 +91,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 1) ("to", 10) - ("size", 1900));//("size", 1910)); + ("size", 1780 /*1910*/)); produce_blocks(1); auto ram_usage = rlm.get_account_ram_usage(N(testram11111)); @@ -106,7 +106,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 1) ("to", 10) - ("size", 1910)),//("size", 1920)), + ("size", 1790 /*1920*/)), ram_usage_exceeded, fc_exception_message_starts_with("account testram11111 has insufficient ram")); wlog("ram_tests 2 %%%%%%"); @@ -118,7 +118,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 1) ("to", 10) - ("size", 1800));//("size", 1810)); + ("size", 1680/*1810*/)); produce_blocks(1); BOOST_REQUIRE_EQUAL(ram_usage - 1000, rlm.get_account_ram_usage(N(testram11111))); @@ -128,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 1) ("to", 11) - ("size", 1810)),//("size", 1810)), + ("size", 1680/*1810*/)), ram_usage_exceeded, fc_exception_message_starts_with("account testram11111 has insufficient ram")); produce_blocks(1); @@ -151,21 +151,21 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 1) ("to", 11) - ("size", 1720)); + ("size", 1600/*1720*/)); produce_blocks(1); tester->push_action( N(testram11111), N(rmentry), N(testram11111), mvo() ("from", 3) ("to", 3)); produce_blocks(1); - + // verify that the new entry will exceed the allocation bytes limit BOOST_REQUIRE_EXCEPTION( tester->push_action( N(testram11111), N(setentry), N(testram11111), mvo() ("payer", "testram11111") ("from", 12) ("to", 12) - ("size", 1900)), + ("size", 1780)), ram_usage_exceeded, fc_exception_message_starts_with("account testram11111 has insufficient ram")); produce_blocks(1); @@ -175,7 +175,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 12) ("to", 12) - ("size", 1720)); + ("size", 1620/*1720*/)); produce_blocks(1); // verify that anoth new entry will exceed the allocation bytes limit, to setup testing of new payer @@ -184,11 +184,12 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("payer", "testram11111") ("from", 13) ("to", 13) - ("size", 1720)), + ("size", 1660)), ram_usage_exceeded, fc_exception_message_starts_with("account testram11111 has insufficient ram")); produce_blocks(1); +#if 0 // verify that the new entry is under the allocation bytes limit tester->push_action( N(testram11111), N(setentry), {N(testram11111),N(testram22222)}, mvo() ("payer", "testram22222") @@ -259,6 +260,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, enumivo_system::enumivo_system_tester) { try ("to", 22) ("size", 1910)); produce_blocks(1); +#endif } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index 2f08e0b3c25..587a48b9273 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -900,7 +900,7 @@ BOOST_FIXTURE_TEST_CASE( lotso_globals, TESTER ) try { //1028 should fail BOOST_CHECK_THROW(set_code(N(globals), string(ss.str() + "(global $z (mut i64) (i64.const -12)))") - .c_str()), enumivo::chain::wasm_execution_error);; + .c_str()), enumivo::chain::wasm_execution_error); } FC_LOG_AND_RETHROW()