diff --git a/libraries/chain/backing_store/db_key_value_format.cpp b/libraries/chain/backing_store/db_key_value_format.cpp index 0298f0ded8a..0b0ec974d4c 100644 --- a/libraries/chain/backing_store/db_key_value_format.cpp +++ b/libraries/chain/backing_store/db_key_value_format.cpp @@ -290,4 +290,31 @@ namespace eosio { namespace chain { namespace backing_store { namespace db_key_v bytes composite_key = kt ? create_prefix_type_key(scope, table, *kt) : create_prefix_key(scope, table); return create_full_key(composite_key, code); } + + eosio::session::shared_bytes create_full_primary_key(const eosio::session::shared_bytes& prefix, uint64_t primary_key) { + const auto prefix_size = detail::prefix_size(); + b1::chain_kv::bytes composite_key; + constexpr static auto type_size = sizeof(key_type); + static_assert( type_size == 1, "" ); // this changing will break check below of prefix.back() + if (prefix.size() == prefix_size + type_size) { + const key_type kt = static_cast(prefix[prefix.size() - 1]); + EOS_ASSERT(kt == key_type::primary, bad_composite_key_exception, + "DB intrinsic create_full_primary_key should only be called with a prefix for a primary key, this has a prefix for type: ${type}", + ("type", static_cast(kt))); + composite_key.reserve(detail::key_size(key_type::primary)); + } + else { + EOS_ASSERT(prefix.size() == prefix_size, bad_composite_key_exception, + "DB intrinsic create_full_primary_key should only be called with a prefix of code, scope, and table and optionally a primary type indicator"); + composite_key.reserve(type_size + detail::key_size(key_type::primary)); + composite_key.push_back(static_cast(key_type::primary)); + } + + b1::chain_kv::append_key(composite_key, primary_key); + return eosio::session::make_shared_bytes({std::string_view{prefix.data(), + prefix.size()}, + std::string_view{composite_key.data(), + composite_key.size()}}); + + } }}}} // namespace eosio::chain::backing_store::db_key_value_format diff --git a/libraries/chain/include/eosio/chain/backing_store/db_key_value_format.hpp b/libraries/chain/include/eosio/chain/backing_store/db_key_value_format.hpp index 0edcbc143c7..474461713ba 100644 --- a/libraries/chain/include/eosio/chain/backing_store/db_key_value_format.hpp +++ b/libraries/chain/include/eosio/chain/backing_store/db_key_value_format.hpp @@ -565,6 +565,7 @@ namespace chain { namespace backing_store { namespace db_key_value_format { eosio::session::shared_bytes create_full_primary_key(name code, name scope, name table, uint64_t primary_key); eosio::session::shared_bytes create_full_prefix_key(name code, name scope, name table, std::optional kt = std::optional{}); + eosio::session::shared_bytes create_full_primary_key(const eosio::session::shared_bytes& prefix, uint64_t primary_key); template eosio::session::shared_bytes create_full_secondary_key(name code, name scope, name table, const Key& sec_key, uint64_t primary_key) { diff --git a/libraries/chain/include/eosio/chain/combined_database.hpp b/libraries/chain/include/eosio/chain/combined_database.hpp index 38647d30b25..99ebe6fbf10 100644 --- a/libraries/chain/include/eosio/chain/combined_database.hpp +++ b/libraries/chain/include/eosio/chain/combined_database.hpp @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -134,6 +136,36 @@ namespace eosio { namespace chain { auto &get_kv_undo_stack(void) const { return kv_undo_stack; } backing_store_type get_backing_store() const { return backing_store; } + template + bool get_primary_key_data(name code, name scope, name table, uint64_t primary_key, Lambda&& process_data) const { + if (backing_store == backing_store_type::CHAINBASE) { + const auto* t_id = db.find( boost::make_tuple( code, scope, table ) ); + if ( !t_id ) { + return false; + } + + const auto* obj = db.find(boost::make_tuple(t_id->id, primary_key) ); + + if (obj) { + return process_data(obj->payer, obj->value.data(), obj->value.size()); + } + } + else { + using namespace eosio::chain; + EOS_ASSERT(backing_store == backing_store_type::ROCKSDB, + chain::contract_table_query_exception, + "Support for configured backing_store has not been added"); + auto full_primary_key = chain::backing_store::db_key_value_format::create_full_primary_key(code, scope, table, primary_key); + auto session = get_kv_undo_stack()->top(); + auto value = session.read(full_primary_key); + if (value) { + backing_store::payer_payload pp{value->data(), value->size()}; + return process_data(pp.payer, pp.value, pp.value_size); + } + } + return false; + } + private: void add_contract_tables_to_snapshot(const snapshot_writer_ptr& snapshot) const; void read_contract_tables_from_snapshot(const snapshot_reader_ptr& snapshot); diff --git a/unittests/eosio_system_tester.hpp b/libraries/testing/include/eosio/testing/eosio_system_tester.hpp similarity index 68% rename from unittests/eosio_system_tester.hpp rename to libraries/testing/include/eosio/testing/eosio_system_tester.hpp index 17425fdb198..279910d1247 100644 --- a/unittests/eosio_system_tester.hpp +++ b/libraries/testing/include/eosio/testing/eosio_system_tester.hpp @@ -23,100 +23,96 @@ using mvo = fc::mutable_variant_object; namespace eosio_system { -class eosio_system_tester : public TESTER { +template +class eosio_system_tester : public Impl { public: + using action_result = eosio::testing::base_tester::action_result; - eosio_system_tester() - : eosio_system_tester([](TESTER& ) {}){} + eosio_system_tester() { + this->produce_blocks( 2 ); - template - eosio_system_tester(Lambda setup) { - setup(*this); - - produce_blocks( 2 ); - - create_accounts({ "eosio.token"_n, "eosio.ram"_n, "eosio.ramfee"_n, "eosio.stake"_n, + this->create_accounts({ "eosio.token"_n, "eosio.ram"_n, "eosio.ramfee"_n, "eosio.stake"_n, "eosio.bpay"_n, "eosio.vpay"_n, "eosio.saving"_n, "eosio.names"_n }); - produce_blocks( 100 ); + this->produce_blocks( 100 ); - set_code( "eosio.token"_n, contracts::eosio_token_wasm() ); - set_abi( "eosio.token"_n, contracts::eosio_token_abi().data() ); + this->set_code( "eosio.token"_n, contracts::eosio_token_wasm() ); + this->set_abi( "eosio.token"_n, contracts::eosio_token_abi().data() ); { - const auto& accnt = control->db().get( "eosio.token"_n ); + const auto& accnt = this->control->db().template get( "eosio.token"_n ); abi_def abi; BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - token_abi_ser.set_abi(abi, abi_serializer::create_yield_function( abi_serializer_max_time )); + token_abi_ser.set_abi(abi, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time )); } - create_currency( "eosio.token"_n, config::system_account_name, core_from_string("10000000000.0000") ); - issue(config::system_account_name, core_from_string("1000000000.0000")); + this->create_currency( "eosio.token"_n, config::system_account_name, core_from_string("10000000000.0000") ); + this->issue(config::system_account_name, core_from_string("1000000000.0000")); BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), get_balance( name("eosio") ) ); - set_code( config::system_account_name, contracts::eosio_system_wasm() ); - set_abi( config::system_account_name, contracts::eosio_system_abi().data() ); + this->set_code( config::system_account_name, contracts::eosio_system_wasm() ); + this->set_abi( config::system_account_name, contracts::eosio_system_abi().data() ); - base_tester::push_action(config::system_account_name, "init"_n, + Impl::push_action(config::system_account_name, "init"_n, config::system_account_name, mutable_variant_object() ("version", 0) ("core", CORE_SYM_STR)); { - const auto& accnt = control->db().get( config::system_account_name ); + const auto& accnt = this->control->db().template get( config::system_account_name ); abi_def abi; BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - abi_ser.set_abi(abi, abi_serializer::create_yield_function( abi_serializer_max_time )); + abi_ser.set_abi(abi, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time )); } - produce_blocks(); + this->produce_blocks(); - create_account_with_resources( "alice1111111"_n, config::system_account_name, core_from_string("1.0000"), false ); - create_account_with_resources( "bob111111111"_n, config::system_account_name, core_from_string("0.4500"), false ); - create_account_with_resources( "carol1111111"_n, config::system_account_name, core_from_string("1.0000"), false ); + this->create_account_with_resources( "alice1111111"_n, config::system_account_name, core_from_string("1.0000"), false ); + this->create_account_with_resources( "bob111111111"_n, config::system_account_name, core_from_string("0.4500"), false ); + this->create_account_with_resources( "carol1111111"_n, config::system_account_name, core_from_string("1.0000"), false ); BOOST_REQUIRE_EQUAL( core_from_string("1000000000.0000"), - get_balance(name("eosio")) + get_balance(name("eosio.ramfee")) + get_balance(name("eosio.stake")) + get_balance(name("eosio.ram")) ); + this->get_balance(name("eosio")) + this->get_balance(name("eosio.ramfee")) + this->get_balance(name("eosio.stake")) + this->get_balance(name("eosio.ram")) ); } action_result open( account_name owner, const string& symbolname, account_name ram_payer ) { - return push_action( ram_payer, "open"_n, mvo() - ( "owner", owner ) - ( "symbol", symbolname ) - ( "ram_payer", ram_payer ) + return this->push_action( ram_payer, "open"_n, mvo() + ( "owner", owner ) + ( "symbol", symbolname ) + ( "ram_payer", ram_payer ) ); } void create_accounts_with_resources( vector accounts, account_name creator = config::system_account_name ) { for( auto a : accounts ) { - create_account_with_resources( a, creator ); + Impl::create_account_with_resources( a, creator ); } } transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, uint32_t ram_bytes = 8000 ) { signed_transaction trx; - set_transaction_headers(trx); + this->set_transaction_headers(trx); authority owner_auth; - owner_auth = authority( get_public_key( a, "owner" ) ); + owner_auth = authority( this->get_public_key( a, "owner" ) ); trx.actions.emplace_back( vector{{creator,config::active_name}}, newaccount{ .creator = creator, .name = a, .owner = owner_auth, - .active = authority( get_public_key( a, "active" ) ) + .active = authority( this->get_public_key( a, "active" ) ) }); - trx.actions.emplace_back( get_action( config::system_account_name, "buyrambytes"_n, vector{{creator,config::active_name}}, + trx.actions.emplace_back( this->get_action( config::system_account_name, "buyrambytes"_n, vector{{creator,config::active_name}}, mvo() ("payer", creator) ("receiver", a) ("bytes", ram_bytes) ) ); - trx.actions.emplace_back( get_action( config::system_account_name, "delegatebw"_n, vector{{creator,config::active_name}}, + trx.actions.emplace_back( this->get_action( config::system_account_name, "delegatebw"_n, vector{{creator,config::active_name}}, mvo() ("from", creator) ("receiver", a) @@ -126,22 +122,22 @@ class eosio_system_tester : public TESTER { ) ); - set_transaction_headers(trx); - trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); - return push_transaction( trx ); + this->set_transaction_headers(trx); + trx.sign( this->get_private_key( creator, "active" ), this->control->get_chain_id() ); + return this->push_transaction( trx ); } transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, asset ramfunds, bool multisig, asset net = core_from_string("10.0000"), asset cpu = core_from_string("10.0000") ) { signed_transaction trx; - set_transaction_headers(trx); + this->set_transaction_headers(trx); authority owner_auth; if (multisig) { // multisig between account's owner key and creators active permission - owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}}); + owner_auth = authority(2, {key_weight{this->get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}}); } else { - owner_auth = authority( get_public_key( a, "owner" ) ); + owner_auth = authority( this->get_public_key( a, "owner" ) ); } trx.actions.emplace_back( vector{{creator,config::active_name}}, @@ -149,17 +145,17 @@ class eosio_system_tester : public TESTER { .creator = creator, .name = a, .owner = owner_auth, - .active = authority( get_public_key( a, "active" ) ) + .active = authority( this->get_public_key( a, "active" ) ) }); - trx.actions.emplace_back( get_action( config::system_account_name, "buyram"_n, vector{{creator,config::active_name}}, + trx.actions.emplace_back( this->get_action( config::system_account_name, "buyram"_n, vector{{creator,config::active_name}}, mvo() ("payer", creator) ("receiver", a) ("quant", ramfunds) ) ); - trx.actions.emplace_back( get_action( config::system_account_name, "delegatebw"_n, vector{{creator,config::active_name}}, + trx.actions.emplace_back( this->get_action( config::system_account_name, "delegatebw"_n, vector{{creator,config::active_name}}, mvo() ("from", creator) ("receiver", a) @@ -169,37 +165,37 @@ class eosio_system_tester : public TESTER { ) ); - set_transaction_headers(trx); - trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); - return push_transaction( trx ); + this->set_transaction_headers(trx); + trx.sign( this->get_private_key( creator, "active" ), this->control->get_chain_id() ); + return this->push_transaction( trx ); } transaction_trace_ptr setup_producer_accounts( const std::vector& accounts ) { account_name creator(config::system_account_name); signed_transaction trx; - set_transaction_headers(trx); + this->set_transaction_headers(trx); asset cpu = core_from_string("80.0000"); asset net = core_from_string("80.0000"); asset ram = core_from_string("1.0000"); for (const auto& a: accounts) { - authority owner_auth( get_public_key( a, "owner" ) ); + authority owner_auth( this->get_public_key( a, "owner" ) ); trx.actions.emplace_back( vector{{creator,config::active_name}}, newaccount{ .creator = creator, .name = a, .owner = owner_auth, - .active = authority( get_public_key( a, "active" ) ) + .active = authority( this->get_public_key( a, "active" ) ) }); - trx.actions.emplace_back( get_action( config::system_account_name, "buyram"_n, vector{ {creator, config::active_name} }, + trx.actions.emplace_back( this->get_action( config::system_account_name, "buyram"_n, vector{ {creator, config::active_name} }, mvo() ("payer", creator) ("receiver", a) ("quant", ram) ) ); - trx.actions.emplace_back( get_action( config::system_account_name, "delegatebw"_n, vector{ {creator, config::active_name} }, + trx.actions.emplace_back( this->get_action( config::system_account_name, "delegatebw"_n, vector{ {creator, config::active_name} }, mvo() ("from", creator) ("receiver", a) @@ -210,9 +206,9 @@ class eosio_system_tester : public TESTER { ); } - set_transaction_headers(trx); - trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); - return push_transaction( trx ); + this->set_transaction_headers(trx); + trx.sign( this->get_private_key( creator, "active" ), this->control->get_chain_id() ); + return this->push_transaction( trx ); } action_result buyram( const account_name& payer, account_name receiver, const asset& eosin ) { @@ -232,10 +228,10 @@ class eosio_system_tester : public TESTER { action act; act.account = config::system_account_name; act.name = name; - act.data = abi_ser.variant_to_binary( action_type_name, data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + act.data = abi_ser.variant_to_binary( action_type_name, data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); - return base_tester::push_action( std::move(act), auth ? signer.to_uint64_t() : - signer == "bob111111111"_n ? "alice1111111"_n.to_uint64_t() : "bob111111111"_n.to_uint64_t() ); + return Impl::push_action( std::move(act), auth ? signer.to_uint64_t() : + signer == "bob111111111"_n ? "alice1111111"_n.to_uint64_t() : "bob111111111"_n.to_uint64_t() ); } action_result stake( const account_name& from, const account_name& to, const asset& net, const asset& cpu ) { @@ -313,11 +309,11 @@ class eosio_system_tester : public TESTER { action_result regproducer( const account_name& acnt, int params_fixture = 1 ) { action_result r = push_action( acnt, "regproducer"_n, mvo() ("producer", acnt ) - ("producer_key", get_public_key( acnt, "active" ) ) + ("producer_key", this->get_public_key( acnt, "active" ) ) ("url", "" ) ("location", 0 ) ); - BOOST_REQUIRE_EQUAL( success(), r); + BOOST_REQUIRE_EQUAL( this->success(), r); return r; } @@ -329,27 +325,28 @@ class eosio_system_tester : public TESTER { } uint32_t last_block_time() const { - return time_point_sec( control->head_block_time() ).sec_since_epoch(); + return time_point_sec( this->control->head_block_time() ).sec_since_epoch(); } asset get_balance( const account_name& act ) { - vector data = get_row_by_account( "eosio.token"_n, act, "accounts"_n, name(symbol(CORE_SYMBOL).to_symbol_code().value) ); - return data.empty() ? asset(0, symbol(CORE_SYMBOL)) : token_abi_ser.binary_to_variant("account", data, abi_serializer::create_yield_function( abi_serializer_max_time ))["balance"].as(); + vector data = this->get_row_by_account( "eosio.token"_n, act, "accounts"_n, name(symbol(CORE_SYMBOL).to_symbol_code().value) ); + const auto ret = data.empty() ? asset(0, symbol(CORE_SYMBOL)) : token_abi_ser.binary_to_variant("account", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ))["balance"].template as(); + return ret; } fc::variant get_total_stake( const account_name& act ) { - vector data = get_row_by_account( config::system_account_name, act, "userres"_n, act ); - return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "user_resources", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + vector data = this->get_row_by_account( config::system_account_name, act, "userres"_n, act ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "user_resources", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); } fc::variant get_voter_info( const account_name& act ) { - vector data = get_row_by_account( config::system_account_name, config::system_account_name, "voters"_n, act ); - return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "voter_info", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + vector data = this->get_row_by_account( config::system_account_name, config::system_account_name, "voters"_n, act ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "voter_info", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); } fc::variant get_producer_info( const account_name& act ) { - vector data = get_row_by_account( config::system_account_name, config::system_account_name, "producers"_n, act ); - return abi_ser.binary_to_variant( "producer_info", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + vector data = this->get_row_by_account( config::system_account_name, config::system_account_name, "producers"_n, act ); + return abi_ser.binary_to_variant( "producer_info", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); } void create_currency( name contract, name manager, asset maxsupply ) { @@ -377,7 +374,7 @@ class eosio_system_tester : public TESTER { } double stake2votes( asset stake ) { - auto now = control->pending_block_time().time_since_epoch().count() / 1000000; + auto now = this->control->pending_block_time().time_since_epoch().count() / 1000000; return stake.get_amount() * pow(2, int64_t((now - (config::block_timestamp_epoch / 1000)) / (86400 * 7))/ double(52) ); // 52 week periods (i.e. ~years) } @@ -388,32 +385,32 @@ class eosio_system_tester : public TESTER { fc::variant get_stats( const string& symbolname ) { auto symb = eosio::chain::symbol::from_string(symbolname); auto symbol_code = symb.to_symbol_code().value; - vector data = get_row_by_account( "eosio.token"_n, name(symbol_code), "stat"_n, name(symbol_code) ); - return data.empty() ? fc::variant() : token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + vector data = this->get_row_by_account( "eosio.token"_n, name(symbol_code), "stat"_n, name(symbol_code) ); + return data.empty() ? fc::variant() : token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); } asset get_token_supply() { - return get_stats("4," CORE_SYMBOL_NAME)["supply"].as(); + return get_stats("4," CORE_SYMBOL_NAME)["supply"].template as(); } fc::variant get_global_state() { - vector data = get_row_by_account( config::system_account_name, config::system_account_name, "global"_n, "global"_n ); + vector data = this->get_row_by_account( config::system_account_name, config::system_account_name, "global"_n, "global"_n ); if (data.empty()) std::cout << "\nData is empty\n" << std::endl; - return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); } fc::variant get_refund_request( name account ) { - vector data = get_row_by_account( config::system_account_name, account, "refunds"_n, account ); - return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "refund_request", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + vector data = this->get_row_by_account( config::system_account_name, account, "refunds"_n, account ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "refund_request", data, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time ) ); } abi_serializer initialize_multisig() { abi_serializer msig_abi_ser; { - create_account_with_resources( "eosio.msig"_n, config::system_account_name ); - BOOST_REQUIRE_EQUAL( success(), buyram( name("eosio"), name("eosio.msig"), core_from_string("5000.0000") ) ); - produce_block(); + this->create_account_with_resources( "eosio.msig"_n, config::system_account_name ); + BOOST_REQUIRE_EQUAL( this->success(), buyram( name("eosio"), name("eosio.msig"), core_from_string("5000.0000") ) ); + this->produce_block(); auto trace = base_tester::push_action(config::system_account_name, "setpriv"_n, config::system_account_name, mutable_variant_object() @@ -421,14 +418,14 @@ class eosio_system_tester : public TESTER { ("is_priv", 1) ); - set_code( "eosio.msig"_n, contracts::eosio_msig_wasm() ); - set_abi( "eosio.msig"_n, contracts::eosio_msig_abi().data() ); + this->set_code( "eosio.msig"_n, contracts::eosio_msig_wasm() ); + this->set_abi( "eosio.msig"_n, contracts::eosio_msig_abi().data() ); - produce_blocks(); - const auto& accnt = control->db().get( "eosio.msig"_n ); + this->produce_blocks(); + const auto& accnt = this->control->db().template get( "eosio.msig"_n ); abi_def msig_abi; BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, msig_abi), true); - msig_abi_ser.set_abi(msig_abi, abi_serializer::create_yield_function( abi_serializer_max_time )); + msig_abi_ser.set_abi(msig_abi, abi_serializer::create_yield_function( eosio::testing::base_tester::abi_serializer_max_time )); } return msig_abi_ser; } @@ -436,7 +433,7 @@ class eosio_system_tester : public TESTER { vector active_and_vote_producers() { //stake more than 15% of total EOS supply to activate chain transfer( name("eosio"), name("alice1111111"), core_from_string("650000000.0000"), name("eosio") ); - BOOST_REQUIRE_EQUAL( success(), stake( name("alice1111111"), name("alice1111111"), core_from_string("300000000.0000"), core_from_string("300000000.0000") ) ); + BOOST_REQUIRE_EQUAL( this->success(), stake( name("alice1111111"), name("alice1111111"), core_from_string("300000000.0000"), core_from_string("300000000.0000") ) ); // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers std::vector producer_names; @@ -449,16 +446,16 @@ class eosio_system_tester : public TESTER { setup_producer_accounts(producer_names); for (const auto& p: producer_names) { - BOOST_REQUIRE_EQUAL( success(), regproducer(p) ); + BOOST_REQUIRE_EQUAL( this->success(), regproducer(p) ); } } - produce_blocks( 250); + this->produce_blocks( 250); - auto trace_auth = TESTER::push_action(config::system_account_name, updateauth::get_name(), config::system_account_name, mvo() + auto trace_auth = Impl::push_action(config::system_account_name, updateauth::get_name(), config::system_account_name, mvo() ("account", name(config::system_account_name).to_string()) ("permission", name(config::active_name).to_string()) ("parent", name(config::owner_name).to_string()) - ("auth", authority(1, {key_weight{get_public_key( config::system_account_name, "active" ), 1}}, { + ("auth", authority(1, {key_weight{this->get_public_key( config::system_account_name, "active" ), 1}}, { permission_level_weight{{config::system_account_name, config::eosio_code_name}, 1}, permission_level_weight{{config::producers_account_name, config::active_name}, 1} } @@ -469,18 +466,18 @@ class eosio_system_tester : public TESTER { //vote for producers { transfer( config::system_account_name, name("alice1111111"), core_from_string("100000000.0000"), config::system_account_name ); - BOOST_REQUIRE_EQUAL(success(), stake( name("alice1111111"), core_from_string("30000000.0000"), core_from_string("30000000.0000") ) ); - BOOST_REQUIRE_EQUAL(success(), buyram( name("alice1111111"), name("alice1111111"), core_from_string("30000000.0000") ) ); - BOOST_REQUIRE_EQUAL(success(), push_action("alice1111111"_n, "voteproducer"_n, mvo() + BOOST_REQUIRE_EQUAL(this->success(), stake( name("alice1111111"), core_from_string("30000000.0000"), core_from_string("30000000.0000") ) ); + BOOST_REQUIRE_EQUAL(this->success(), buyram( name("alice1111111"), name("alice1111111"), core_from_string("30000000.0000") ) ); + BOOST_REQUIRE_EQUAL(this->success(), push_action("alice1111111"_n, "voteproducer"_n, mvo() ("voter", "alice1111111") ("proxy", name(0).to_string()) ("producers", vector(producer_names.begin(), producer_names.begin()+21)) ) ); } - produce_blocks( 250 ); + this->produce_blocks( 250 ); - auto producer_keys = control->head_block_state()->active_schedule.producers; + auto producer_keys = this->control->head_block_state()->active_schedule.producers; BOOST_REQUIRE_EQUAL( 21, producer_keys.size() ); BOOST_REQUIRE_EQUAL( name("defproducera"), producer_keys[0].producer_name ); @@ -492,9 +489,9 @@ class eosio_system_tester : public TESTER { regproducer("producer1111"_n); { signed_transaction trx; - set_transaction_headers(trx); + this->set_transaction_headers(trx); - trx.actions.emplace_back( get_action( config::system_account_name, "delegatebw"_n, + trx.actions.emplace_back( this->get_action( config::system_account_name, "delegatebw"_n, vector{{config::system_account_name, config::active_name}}, mvo() ("from", name{config::system_account_name}) @@ -504,7 +501,7 @@ class eosio_system_tester : public TESTER { ("transfer", 1 ) ) ); - trx.actions.emplace_back( get_action( config::system_account_name, "voteproducer"_n, + trx.actions.emplace_back( this->get_action( config::system_account_name, "voteproducer"_n, vector{{"producer1111"_n, config::active_name}}, mvo() ("voter", "producer1111") @@ -512,7 +509,7 @@ class eosio_system_tester : public TESTER { ("producers", vector(1, "producer1111"_n)) ) ); - trx.actions.emplace_back( get_action( config::system_account_name, "undelegatebw"_n, + trx.actions.emplace_back( this->get_action( config::system_account_name, "undelegatebw"_n, vector{{"producer1111"_n, config::active_name}}, mvo() ("from", "producer1111") @@ -522,10 +519,10 @@ class eosio_system_tester : public TESTER { ) ); - set_transaction_headers(trx); - trx.sign( get_private_key( config::system_account_name, "active" ), control->get_chain_id() ); - trx.sign( get_private_key( "producer1111"_n, "active" ), control->get_chain_id() ); - push_transaction( trx ); + this->set_transaction_headers(trx); + trx.sign( this->get_private_key( config::system_account_name, "active" ), this->control->get_chain_id() ); + trx.sign( this->get_private_key( "producer1111"_n, "active" ), this->control->get_chain_id() ); + this->push_transaction( trx ); } } diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 7056ce976fe..c8e01ea10fc 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -354,31 +354,6 @@ namespace eosio { namespace testing { void sync_with(base_tester& other); - const table_id_object* find_table( name code, name scope, name table ); - - // method treats key as a name type, if this is not appropriate in your case, pass require == false and report the correct behavior - template - bool get_table_entry(Object& obj, account_name code, account_name scope, account_name table, uint64_t key, bool require = true) { - auto* maybe_tid = find_table(code, scope, table); - if( maybe_tid == nullptr ) { - BOOST_FAIL( "table for code=\"" + code.to_string() - + "\" scope=\"" + scope.to_string() - + "\" table=\"" + table.to_string() - + "\" does not exist" ); - } - - auto* o = control->db().find(boost::make_tuple(maybe_tid->id, key)); - if( o == nullptr ) { - if( require ) - BOOST_FAIL("object does not exist for primary_key=\"" + name(key).to_string() + "\""); - - return false; - } - - fc::raw::unpack(o->value.data(), o->value.size(), obj); - return true; - } - const controller::config& get_config() const { return cfg; } diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index d008a2cbb77..3641ebdf9d1 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -969,41 +970,28 @@ namespace eosio { namespace testing { asset base_tester::get_currency_balance( const account_name& code, const symbol& asset_symbol, const account_name& account ) const { - const auto& db = control->db(); - const auto* tbl = db.template find(boost::make_tuple(code, account, "accounts"_n)); share_type result = 0; - - // the balance is implied to be 0 if either the table or row does not exist - if (tbl) { - const auto *obj = db.template find(boost::make_tuple(tbl->id, asset_symbol.to_symbol_code().value)); - if (obj) { - //balance is the first field in the serialization - fc::datastream ds(obj->value.data(), obj->value.size()); - fc::raw::unpack(ds, result); - } - } + auto get_balance = [&result](name , const char* value, std::size_t size) { + //balance is the first field in the serialization + fc::datastream ds(value, size); + fc::raw::unpack(ds, result); + return true; + }; + const auto& kv_database = control->kv_db(); + kv_database.get_primary_key_data(code, account, "accounts"_n, asset_symbol.to_symbol_code().value, get_balance); return asset(result, asset_symbol); } vector base_tester::get_row_by_account( name code, name scope, name table, const account_name& act ) const { vector data; - const auto& db = control->db(); - const auto* t_id = db.find( boost::make_tuple( code, scope, table ) ); - if ( !t_id ) { - return data; - } - //FC_ASSERT( t_id != 0, "object not found" ); - - const auto& idx = db.get_index(); - - auto itr = idx.lower_bound( boost::make_tuple( t_id->id, act.to_uint64_t() ) ); - if ( itr == idx.end() || itr->t_id != t_id->id || act.to_uint64_t() != itr->primary_key ) { - return data; - } - - data.resize( itr->value.size() ); - memcpy( data.data(), itr->value.data(), data.size() ); + auto copy_data = [&data](name , const char* value, std::size_t value_size) { + data.resize( value_size ); + memcpy( data.data(), value, value_size ); + return true; + }; + const auto& kv_database = control->kv_db(); + kv_database.get_primary_key_data(code, scope, table, act.to_uint64_t(), copy_data); return data; } @@ -1125,11 +1113,6 @@ namespace eosio { namespace testing { } - const table_id_object* base_tester::find_table( name code, name scope, name table ) { - auto tid = control->db().find(boost::make_tuple(code, scope, table)); - return tid; - } - void base_tester::schedule_protocol_features_wo_preactivation(const vector feature_digests) { protocol_features_to_be_activated_wo_preactivation.insert( protocol_features_to_be_activated_wo_preactivation.end(), diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index f35c5fdcb36..79ae6b1abff 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -2649,23 +2649,20 @@ read_only::get_producers_result read_only::get_producers( const read_only::get_p } auto session = kv_database.get_kv_undo_stack()->top(); - auto retrieve_primary_key = [&code,&scope,&producers_table,&session](uint64_t primary_key) { - auto full_primary_key = backing_store::db_key_value_format::create_full_primary_key(code, scope, producers_table, primary_key); - auto value = session.read(full_primary_key); - return *value; - }; + auto prefix_primary_key = + backing_store::db_key_value_format::create_full_prefix_key(code, scope, producers_table, backing_store::db_key_value_format::key_type::primary); using done_func = decltype(done); using add_val_func = decltype(add_val); - using retrieve_prim_func = decltype(retrieve_primary_key); + using session_type = decltype(session); struct f64_secondary_key_receiver : backing_store::single_type_error_receiver, contract_table_query_exception> { f64_secondary_key_receiver(read_only::get_producers_result& result, done_func&& done, - add_val_func&& add_val, retrieve_prim_func&& retrieve_prim) - : result_(result), done_(done), add_val_(add_val), retrieve_primary_key_(retrieve_prim) {} + add_val_func&& add_val, eosio::session::shared_bytes&& prefix_primary_key, session_type& session) + : result_(result), done_(done), add_val_(add_val), prefix_primary_key_(std::move(prefix_primary_key)), session_(session) {} void add_only_row(const backing_store::secondary_index_view& row) { // needs to allow a second pass after limit is reached or time has passed, to allow "more" processing @@ -2673,8 +2670,12 @@ read_only::get_producers_result read_only::get_producers( const read_only::get_p finished_ = true; } else { - auto value = retrieve_primary_key_(row.primary_key); - add_val_(backing_store::primary_index_view::create(row.primary_key, value.data(), value.size())); + auto full_primary_key = backing_store::db_key_value_format::create_full_primary_key(prefix_primary_key_, row.primary_key); + auto value = session_.read(full_primary_key); + EOS_ASSERT(value, + contract_table_query_exception, + "Secondary key lookup identified primary key: ${pk}, but primary key not found in database", ("pk",row.primary_key)); + add_val_(backing_store::primary_index_view::create(row.primary_key, value->data(), value->size())); } } @@ -2691,10 +2692,11 @@ read_only::get_producers_result read_only::get_producers( const read_only::get_p read_only::get_producers_result& result_; done_func done_; add_val_func add_val_; - retrieve_prim_func retrieve_primary_key_; + eosio::session::shared_bytes prefix_primary_key_; + session_type& session_; bool finished_ = false; }; - f64_secondary_key_receiver receiver(result, std::move(done), std::move(add_val), std::move(retrieve_primary_key)); + f64_secondary_key_receiver receiver(result, std::move(done), std::move(add_val), std::move(prefix_primary_key), session); auto kpe = receiver.keep_processing_entries(); backing_store::rocksdb_contract_db_table_writer> writer(receiver, backing_store::key_context::standalone, kpe); diff --git a/tests/get_producers_tests.cpp b/tests/get_producers_tests.cpp new file mode 100644 index 00000000000..c5bcc81c365 --- /dev/null +++ b/tests/get_producers_tests.cpp @@ -0,0 +1,91 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + + +BOOST_AUTO_TEST_SUITE(get_producers_tests) +using namespace eosio::testing; +using backing_store_ts = boost::mpl::list< TESTER, ROCKSDB_TESTER >; + +// this test verifies the exception case of get_producer, where it is populated by the active schedule of producers +BOOST_AUTO_TEST_CASE_TEMPLATE( get_producers, TESTER_T, backing_store_ts) { try { + TESTER_T chain; + + eosio::chain_apis::read_only plugin(*(chain.control), {}, fc::microseconds::maximum()); + eosio::chain_apis::read_only::get_producers_params params = { .json = true, .lower_bound = "", .limit = 21 }; + + auto results = plugin.get_producers(params); + BOOST_REQUIRE_EQUAL(results.more, ""); + BOOST_REQUIRE_EQUAL(results.rows.size(), 1); + const auto& row = results.rows[0].get_object(); + BOOST_REQUIRE(row.contains("owner")); + BOOST_REQUIRE_EQUAL(row["owner"].as_string(), "eosio"); + // check for producer_authority, since it is only set when the producer schedule is used + BOOST_REQUIRE(row.contains("producer_authority")); + + + chain.produce_blocks(2); + + chain.create_accounts( {"dan"_n,"sam"_n,"pam"_n} ); + chain.produce_block(); + chain.set_producers( {"dan"_n,"sam"_n,"pam"_n} ); + chain.produce_blocks(30); + + results = plugin.get_producers(params); + BOOST_REQUIRE_EQUAL(results.rows.size(), 3); + auto owners = std::vector{"dan", "sam", "pam"}; + auto it = owners.begin(); + for (const auto& elem : results.rows) { + BOOST_REQUIRE_EQUAL(elem["owner"].as_string(), *it++); + } +} FC_LOG_AND_RETHROW() } + +using backing_store_system_ts = boost::mpl::list< eosio_system::eosio_system_tester, eosio_system::eosio_system_tester >; + +// this test verifies the normal case of get_producer, where the contents of the system contract's producers table is used +BOOST_AUTO_TEST_CASE_TEMPLATE( get_producers_from_table, TESTER_T, backing_store_system_ts) { try { + TESTER_T chain; + + // ensure that enough voting is occurring so that producer1111 is elected as the producer + chain.cross_15_percent_threshold(); + + eosio::chain_apis::read_only plugin(*(chain.control), {}, fc::microseconds::maximum()); + eosio::chain_apis::read_only::get_producers_params params = { .json = true, .lower_bound = "", .limit = 21 }; + + auto results = plugin.get_producers(params); + BOOST_REQUIRE_EQUAL(results.more, ""); + BOOST_REQUIRE_EQUAL(results.rows.size(), 1); + const auto& row = results.rows[0].get_object(); + BOOST_REQUIRE(row.contains("owner")); + BOOST_REQUIRE_EQUAL(row["owner"].as_string(), "producer1111"); + // check for producer_authority not present, since it is only set when the producer schedule is used, this verifies producers table was used + BOOST_REQUIRE(!row.contains("producer_authority")); + +} FC_LOG_AND_RETHROW() } + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/ram_tests.cpp b/unittests/ram_tests.cpp index 5deb8d89ffc..dd4ee207931 100644 --- a/unittests/ram_tests.cpp +++ b/unittests/ram_tests.cpp @@ -12,7 +12,7 @@ #include -#include "eosio_system_tester.hpp" +#include /* * register test suite `ram_tests` @@ -22,7 +22,7 @@ BOOST_AUTO_TEST_SUITE(ram_tests) /************************************************************************************* * ram_tests test case *************************************************************************************/ -BOOST_FIXTURE_TEST_CASE(ram_tests, eosio_system::eosio_system_tester) { try { +BOOST_FIXTURE_TEST_CASE(ram_tests, eosio_system::eosio_system_tester) { try { auto init_request_bytes = 80000 + 7110; // `7110' is for table token row const auto increment_contract_bytes = 10000; const auto table_allocation_bytes = 12000;