diff --git a/contracts/eosiolib/eosiolib.cpp b/contracts/eosiolib/eosiolib.cpp index d72f79e30a3..7a7f7b1b672 100644 --- a/contracts/eosiolib/eosiolib.cpp +++ b/contracts/eosiolib/eosiolib.cpp @@ -202,7 +202,6 @@ namespace eosio { { _heap_size = size; _heap = mem_heap; - memset(_heap, 0, _heap_size); } uint32_t is_init() const diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 785a3c1da61..bf695dff451 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -55,8 +55,12 @@ chain_controller::chain_controller( const chain_controller::controller_config& c { _initialize_indexes(); + for (auto& f : cfg.applied_block_callbacks) + applied_block.connect(f); for (auto& f : cfg.applied_irreversible_block_callbacks) applied_irreversible_block.connect(f); + for (auto& f : cfg.on_pending_transaction_callbacks) + on_pending_transaction.connect(f); contracts::chain_initializer starter(cfg.genesis); starter.register_types(*this, _db); diff --git a/libraries/chain/include/eosio/chain/authority_checker.hpp b/libraries/chain/include/eosio/chain/authority_checker.hpp index cdff9b484a2..ca4a73dcea8 100644 --- a/libraries/chain/include/eosio/chain/authority_checker.hpp +++ b/libraries/chain/include/eosio/chain/authority_checker.hpp @@ -32,7 +32,7 @@ namespace detail { bool operator()(const meta_permission& a, const meta_permission& b) const { get_weight_visitor scale; if (a.visit(scale) > b.visit(scale)) return true; - return a.contains() && !b.contains(); + return a.contains() && b.contains(); } }; diff --git a/libraries/chain/include/eosio/chain/chain_config.hpp b/libraries/chain/include/eosio/chain/chain_config.hpp index 7e8af7ad463..fd5ad2fd306 100644 --- a/libraries/chain/include/eosio/chain/chain_config.hpp +++ b/libraries/chain/include/eosio/chain/chain_config.hpp @@ -37,6 +37,24 @@ struct chain_config { uint32_t max_generated_transaction_size; static chain_config get_median_values( vector votes ); + + template + friend Stream& operator << ( Stream& out, const chain_config& c ) { + return out << "Target Block Size: " << c.target_block_size << ", " + << "Max Block Size: " << c.max_block_size << ", " + << "Target Block Acts Per Scope: " << c.target_block_acts_per_scope << ", " + << "Max Block Acts Per Scope: " << c.max_block_acts_per_scope << ", " + << "Target Block Acts: " << c.target_block_acts << ", " + << "Max Block Acts: " << c.max_block_acts << ", " + << "Real Threads: " << c.real_threads << ", " + << "Max Storage Size: " << c.max_storage_size << ", " + << "Max Transaction Lifetime: " << c.max_transaction_lifetime << ", " + << "Max Authority Depth: " << c.max_authority_depth << ", " + << "Max Transaction Exec Time: " << c.max_transaction_exec_time << ", " + << "Max Inline Depth: " << c.max_inline_depth << ", " + << "Max Inline Action Size: " << c.max_inline_action_size << ", " + << "Max Generated Transaction Size: " << c.max_generated_transaction_size << "\n"; + } }; bool operator==(const chain_config& a, const chain_config& b); diff --git a/libraries/chain/include/eosio/chain/chain_controller.hpp b/libraries/chain/include/eosio/chain/chain_controller.hpp index c7e125dec10..b3f6a4b8820 100644 --- a/libraries/chain/include/eosio/chain/chain_controller.hpp +++ b/libraries/chain/include/eosio/chain/chain_controller.hpp @@ -72,12 +72,14 @@ namespace eosio { namespace chain { path shared_memory_dir = config::default_shared_memory_dir; uint64_t shared_memory_size = config::default_shared_memory_size; bool read_only = false; + std::vector::slot_type> applied_block_callbacks; std::vector::slot_type> applied_irreversible_block_callbacks; + std::vector::slot_type> on_pending_transaction_callbacks; contracts::genesis_state_type genesis; runtime_limits limits; }; - chain_controller( const controller_config& cfg ); + explicit chain_controller( const controller_config& cfg ); ~chain_controller(); diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index ee6e1c65f19..465dc60eb39 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -24,8 +24,6 @@ const static uint64_t eosio_all_scope = N(eosio.all); const static uint64_t active_name = N(active); const static uint64_t owner_name = N(owner); -const static share_type initial_token_supply = asset::from_string("1000000000.0000 EOS").amount; - const static int block_interval_ms = 500; const static int block_interval_us = block_interval_ms*1000; const static uint64_t block_timestamp_epoch = 946684800000ll; // epoch is year 2000. diff --git a/libraries/chain/include/eosio/chain/types.hpp b/libraries/chain/include/eosio/chain/types.hpp index 0b435394f27..bfe6c8757e9 100644 --- a/libraries/chain/include/eosio/chain/types.hpp +++ b/libraries/chain/include/eosio/chain/types.hpp @@ -58,7 +58,6 @@ namespace eosio { namespace chain { using std::make_pair; using std::enable_shared_from_this; using std::tie; - using std::make_pair; using std::move; using std::forward; using std::to_string; diff --git a/plugins/mongo_db_plugin/mongo_db_plugin.cpp b/plugins/mongo_db_plugin/mongo_db_plugin.cpp index 989530fb35f..e6f13afb6bd 100644 --- a/plugins/mongo_db_plugin/mongo_db_plugin.cpp +++ b/plugins/mongo_db_plugin/mongo_db_plugin.cpp @@ -34,8 +34,10 @@ using chain::account_name; using chain::action_name; using chain::block_id_type; using chain::permission_name; +using chain::transaction; using chain::signed_transaction; using chain::signed_block; +using chain::block_trace; using chain::transaction_id_type; static appbase::abstract_plugin& _mongo_db_plugin = app().register_plugin(); @@ -45,7 +47,10 @@ class mongo_db_plugin_impl { mongo_db_plugin_impl(); ~mongo_db_plugin_impl(); + void applied_block(const block_trace&); void applied_irreversible_block(const signed_block&); + void process_block(const block_trace&, const signed_block&); + void _process_block(const block_trace&, const signed_block&); void process_irreversible_block(const signed_block&); void _process_irreversible_block(const signed_block&); @@ -64,42 +69,41 @@ class mongo_db_plugin_impl { size_t queue_size = 0; size_t processed = 0; - std::queue queue; + std::deque signed_block_queue; + std::deque signed_block_process_queue; + std::deque> block_trace_queue; + std::deque> block_trace_process_queue; + // transaction.id -> actions + std::map> reversible_actions; boost::mutex mtx; boost::condition_variable condtion; - boost::thread consum_thread; + boost::thread consume_thread; boost::atomic done{false}; boost::atomic startup{true}; - void consum_blocks(); + void consume_blocks(); void update_account(const chain::action& msg); static const account_name newaccount; static const account_name transfer; - static const account_name lock; - static const account_name unlock; - static const account_name claim; - static const account_name setcode; static const account_name setabi; static const std::string blocks_col; static const std::string trans_col; static const std::string actions_col; + static const std::string action_traces_col; static const std::string accounts_col; }; const account_name mongo_db_plugin_impl::newaccount = "newaccount"; const account_name mongo_db_plugin_impl::transfer = "transfer"; -const account_name mongo_db_plugin_impl::lock = "lock"; -const account_name mongo_db_plugin_impl::unlock = "unlock"; -const account_name mongo_db_plugin_impl::claim = "claim"; -const account_name mongo_db_plugin_impl::setcode = "setcode"; const account_name mongo_db_plugin_impl::setabi = "setabi"; const std::string mongo_db_plugin_impl::blocks_col = "Blocks"; const std::string mongo_db_plugin_impl::trans_col = "Transactions"; const std::string mongo_db_plugin_impl::actions_col = "Actions"; +const std::string mongo_db_plugin_impl::action_traces_col = "ActionTraces"; const std::string mongo_db_plugin_impl::accounts_col = "Accounts"; @@ -110,7 +114,7 @@ void mongo_db_plugin_impl::applied_irreversible_block(const signed_block& block) process_irreversible_block(block); } else { boost::mutex::scoped_lock lock(mtx); - queue.push(block); + signed_block_queue.push_back(block); lock.unlock(); condtion.notify_one(); } @@ -123,33 +127,71 @@ void mongo_db_plugin_impl::applied_irreversible_block(const signed_block& block) } } -void mongo_db_plugin_impl::consum_blocks() { +void mongo_db_plugin_impl::applied_block(const block_trace& bt) { + try { + if (startup) { + // on startup we don't want to queue, instead push back on caller + process_block(bt, bt.block); + } else { + boost::mutex::scoped_lock lock(mtx); + block_trace_queue.emplace_back(std::make_pair(bt, bt.block)); + lock.unlock(); + condtion.notify_one(); + } + } catch (fc::exception& e) { + elog("FC Exception while applied_block ${e}", ("e", e.to_string())); + } catch (std::exception& e) { + elog("STD Exception while applied_block ${e}", ("e", e.what())); + } catch (...) { + elog("Unknown exception while applied_block"); + } +} + +void mongo_db_plugin_impl::consume_blocks() { try { - signed_block block; - size_t size = 0; while (true) { boost::mutex::scoped_lock lock(mtx); - while (queue.empty() && !done) { + while (signed_block_queue.empty() && block_trace_queue.empty() && !done) { condtion.wait(lock); } - size = queue.size(); - if (size > 0) { - block = queue.front(); - queue.pop(); - lock.unlock(); - // warn if queue size greater than 75% - if (size > (queue_size * 0.75)) { - wlog("queue size: ${q}", ("q", size + 1)); - } else if (done) { - ilog("draining queue, size: ${q}", ("q", size + 1)); - } - process_irreversible_block(block); - continue; + // capture blocks for processing + size_t block_trace_size = block_trace_queue.size(); + if (block_trace_size > 0) { + block_trace_process_queue = move(block_trace_queue); + block_trace_queue.clear(); + } + size_t signed_block_size = signed_block_queue.size(); + if (signed_block_size > 0) { + signed_block_process_queue = move(signed_block_queue); + signed_block_queue.clear(); + } + + lock.unlock(); + + // warn if queue size greater than 75% + if (signed_block_size > (queue_size * 0.75) || block_trace_size > (queue_size * 0.75)) { + wlog("queue size: ${q}", ("q", signed_block_size + block_trace_size + 1)); } else if (done) { - break; + ilog("draining queue, size: ${q}", ("q", signed_block_size + block_trace_size + 1)); + } + + // process block traces + while (!block_trace_process_queue.empty()) { + const auto& bt_pair = block_trace_process_queue.front(); + process_block(bt_pair.first, bt_pair.second); + block_trace_process_queue.pop_front(); } + + // process blocks + while (!signed_block_process_queue.empty()) { + const signed_block& block = signed_block_process_queue.front(); + process_irreversible_block(block); + signed_block_process_queue.pop_front(); + } + + if (signed_block_size == 0 && block_trace_size == 0 && done) break; } - ilog("mongo_db_plugin consum thread shutdown gracefully"); + ilog("mongo_db_plugin consume thread shutdown gracefully"); } catch (fc::exception& e) { elog("FC Exception while consuming block ${e}", ("e", e.to_string())); } catch (std::exception& e) { @@ -172,6 +214,28 @@ namespace { return *account; } + auto find_transaction(mongocxx::collection& transactions, const string& id) { + using bsoncxx::builder::stream::document; + document find_trans{}; + find_trans << "transaction_id" << id; + auto transaction = transactions.find_one(find_trans.view()); + if (!transaction) { + FC_THROW("Unable to find transaction ${id}", ("id", id)); + } + return *transaction; + } + + auto find_block(mongocxx::collection& blocks, const string& id) { + using bsoncxx::builder::stream::document; + document find_block{}; + find_block << "block_id" << id; + auto block = blocks.find_one(find_block.view()); + if (!block) { + FC_THROW("Unable to find block ${id}", ("id", id)); + } + return *block; + } + void add_data(bsoncxx::builder::basic::document& msg_doc, mongocxx::collection& accounts, const chain::action& msg) @@ -179,7 +243,10 @@ namespace { using bsoncxx::builder::basic::kvp; try { auto from_account = find_account(accounts, msg.account); - auto abi = fc::json::from_string(bsoncxx::to_json(from_account.view()["abi"].get_document())).as(); + abi_def abi; + if (from_account.view().find("abi") != from_account.view().end()) { + abi = fc::json::from_string(bsoncxx::to_json(from_account.view()["abi"].get_document())).as(); + } abi_serializer abis; if (msg.account == chain::config::system_account_name) { abi = chain::contracts::chain_initializer::eos_contract_abi(abi); @@ -238,13 +305,24 @@ void mongo_db_plugin_impl::process_irreversible_block(const signed_block& block) } } -void mongo_db_plugin_impl::_process_irreversible_block(const signed_block& block) -{ +void mongo_db_plugin_impl::process_block(const block_trace& bt, const signed_block& block) { + try { + _process_block(bt, block); + } catch (fc::exception& e) { + elog("FC Exception while processing block trace ${e}", ("e", e.to_string())); + } catch (std::exception& e) { + elog("STD Exception while processing block trace ${e}", ("e", e.what())); + } catch (...) { + elog("Unknown exception while processing trace block"); + } +} + +void mongo_db_plugin_impl::_process_block(const block_trace& bt, const signed_block& block) { + // note bt.block is invalid at this point since it is a reference to internal chainbase block using namespace bsoncxx::types; using namespace bsoncxx::builder; using bsoncxx::builder::basic::kvp; - bool transactions_in_block = false; mongocxx::options::bulk_write bulk_opts; bulk_opts.ordered(false); mongocxx::bulk_write bulk_trans{bulk_opts}; @@ -252,8 +330,9 @@ void mongo_db_plugin_impl::_process_irreversible_block(const signed_block& block auto blocks = mongo_conn[db_name][blocks_col]; // Blocks auto trans = mongo_conn[db_name][trans_col]; // Transactions auto msgs = mongo_conn[db_name][actions_col]; // Actions + auto action_traces = mongo_conn[db_name][action_traces_col]; // ActionTraces - stream::document block_doc{}; + auto block_doc = bsoncxx::builder::basic::document{}; const auto block_id = block.id(); const auto block_id_str = block_id.str(); const auto prev_block_id_str = block.previous.str(); @@ -268,107 +347,193 @@ void mongo_db_plugin_impl::_process_irreversible_block(const signed_block& block // verify on restart we have previous block verify_last_block(blocks, prev_block_id_str); } - } auto now = std::chrono::duration_cast( std::chrono::microseconds{fc::time_point::now().time_since_epoch().count()}); - block_doc << "block_num" << b_int32{static_cast(block_num)} - << "block_id" << block_id_str - << "prev_block_id" << prev_block_id_str - << "timestamp" << b_date{std::chrono::milliseconds{std::chrono::seconds{block.timestamp.operator fc::time_point().sec_since_epoch()}}} - << "transaction_merkle_root" << block.transaction_mroot.str() - << "producer_account_id" << block.producer.to_string(); - auto blk_doc = block_doc << "transactions" << stream::open_array; - - int32_t trx_num = -1; - for (const auto& packed_trx : block.input_transactions) { - const signed_transaction& trx = packed_trx.get_signed_transaction(); - ++trx_num; + block_doc.append(kvp("block_num", b_int32{static_cast(block_num)}), + kvp("block_id", block_id_str), + kvp("prev_block_id", prev_block_id_str), + kvp("timestamp", b_date{std::chrono::milliseconds{ + std::chrono::seconds{block.timestamp.operator fc::time_point().sec_since_epoch()}}}), + kvp("transaction_merkle_root", block.transaction_mroot.str()), + kvp("producer_account_id", block.producer.to_string()), + kvp("pending", b_bool{true})); + block_doc.append(kvp("createdAt", b_date{now})); + + if (!blocks.insert_one(block_doc.view())) { + elog("Failed to insert block ${bid}", ("bid", block_id)); + } - auto txn_oid = bsoncxx::oid{}; - blk_doc = blk_doc << txn_oid; // add to transaction.actions array - stream::document doc{}; - const auto trans_id_str = trx.id().str(); - auto trx_doc = doc - << "_id" << txn_oid - << "transaction_id" << trans_id_str - << "sequence_num" << b_int32{trx_num} - << "block_id" << block_id_str - << "ref_block_num" << b_int32{static_cast(trx.ref_block_num)} - << "ref_block_prefix" << b_int32{static_cast(trx.ref_block_prefix)} - << "expiration" << b_date{std::chrono::milliseconds{std::chrono::seconds{trx.expiration.sec_since_epoch()}}} - << "signatures" << stream::open_array; - for (const auto& sig : trx.signatures) { - trx_doc = trx_doc << fc::variant(sig).as_string(); - } - trx_doc = trx_doc - << stream::close_array - << "actions" << stream::open_array; - - mongocxx::bulk_write bulk_msgs{bulk_opts}; - int32_t i = 0; - for (const auto& msg : trx.actions) { - auto msg_oid = bsoncxx::oid{}; - trx_doc = trx_doc << msg_oid; // add to transaction.actions array - - auto msg_doc = bsoncxx::builder::basic::document{}; - msg_doc.append(kvp("_id", b_oid{msg_oid}), - kvp("action_id", b_int32{i}), - kvp("transaction_id", trans_id_str)); - msg_doc.append(kvp("authorization", [&msg](bsoncxx::builder::basic::sub_array subarr) { - for (const auto& auth : msg.authorization) { - subarr.append([&auth](bsoncxx::builder::basic::sub_document subdoc) { - subdoc.append(kvp("actor", auth.actor.to_string()), - kvp("permission", auth.permission.to_string())); - }); - } - })); - msg_doc.append(kvp("handler_account_name", msg.account.to_string())); - msg_doc.append(kvp("name", msg.name.to_string())); - add_data(msg_doc, accounts, msg); - msg_doc.append(kvp("createdAt", b_date{now})); - mongocxx::model::insert_one insert_msg{msg_doc.view()}; - bulk_msgs.append(insert_msg); - - // eos account update - if (msg.account == chain::config::system_account_name) { - try { - update_account(msg); - } catch (fc::exception& e) { - elog("Unable to update account ${e}", ("e", e.to_string())); - } + int32_t msg_num = -1; + bool actions_to_write = false; + auto process_action = [&](const std::string& trans_id_str, mongocxx::bulk_write& bulk_msgs, const chain::action& msg) -> auto { + auto msg_oid = bsoncxx::oid{}; + auto msg_doc = bsoncxx::builder::basic::document{}; + msg_doc.append(kvp("_id", b_oid{msg_oid}), + kvp("action_id", b_int32{msg_num}), + kvp("transaction_id", trans_id_str)); + msg_doc.append(kvp("authorization", [&msg](bsoncxx::builder::basic::sub_array subarr) { + for (const auto& auth : msg.authorization) { + subarr.append([&auth](bsoncxx::builder::basic::sub_document subdoc) { + subdoc.append(kvp("actor", auth.actor.to_string()), + kvp("permission", auth.permission.to_string())); + }); } + })); + msg_doc.append(kvp("handler_account_name", msg.account.to_string())); + msg_doc.append(kvp("name", msg.name.to_string())); + add_data(msg_doc, accounts, msg); + msg_doc.append(kvp("createdAt", b_date{now})); + mongocxx::model::insert_one insert_msg{msg_doc.view()}; + bulk_msgs.append(insert_msg); + actions_to_write = true; + ++msg_num; + return msg_oid; + }; + + bool action_traces_to_write = false; + auto process_action_trace = [&](const std::string& trans_id_str, + mongocxx::bulk_write& bulk_acts, + const chain::action_trace& act, + const auto& msg_oid) + { + auto act_oid = bsoncxx::oid{}; + auto act_doc = bsoncxx::builder::basic::document{}; + act_doc.append(kvp("_id", b_oid{act_oid}), + kvp("transaction_id", trans_id_str), + kvp("receiver", act.receiver.to_string()), + kvp("action", b_oid{msg_oid}), + kvp("console", act.console), + kvp("region_id", b_int64{act.region_id}), + kvp("cycle_index", b_int64{act.cycle_index})); + act_doc.append(kvp("data_access", [&act](bsoncxx::builder::basic::sub_array subarr) { + for (const auto& data : act.data_access) { + subarr.append([&data](bsoncxx::builder::basic::sub_document subdoc) { + subdoc.append(kvp("type", data.type == chain::data_access_info::read ? "read" : "write"), + kvp("code", data.code.to_string()), + kvp("scope", data.scope.to_string()), + kvp("sequence", b_int64{static_cast(data.sequence)})); + }); + } + })); + act_doc.append(kvp("createdAt", b_date{now})); + mongocxx::model::insert_one insert_act{act_doc.view()}; + bulk_acts.append(insert_act); + action_traces_to_write = true; + }; + + int32_t trx_num = 0; + std::map trx_status_map; + bool transactions_in_block = false; - ++i; - } + auto process_trx = [&](const chain::transaction& trx) -> auto { + auto txn_oid = bsoncxx::oid{}; + auto doc = bsoncxx::builder::basic::document{}; + auto trx_id = trx.id(); + const auto trans_id_str = trx_id.str(); + doc.append(kvp("_id", txn_oid), + kvp("transaction_id", trans_id_str), + kvp("sequence_num", b_int32{trx_num}), + kvp("block_id", block_id_str), + kvp("ref_block_num", b_int32{static_cast(trx.ref_block_num)}), + kvp("ref_block_prefix", b_int32{static_cast(trx.ref_block_prefix)}), + kvp("status", trx_status_map[trx_id]), + kvp("expiration", + b_date{std::chrono::milliseconds{std::chrono::seconds{trx.expiration.sec_since_epoch()}}}), + kvp("pending", b_bool{true}) + ); + doc.append(kvp("createdAt", b_date{now})); if (!trx.actions.empty()) { + mongocxx::bulk_write bulk_msgs{bulk_opts}; + msg_num = 0; + for (const auto& msg : trx.actions) { + process_action(trans_id_str, bulk_msgs, msg); + } auto result = msgs.bulk_write(bulk_msgs); if (!result) { elog("Bulk action insert failed for block: ${bid}, transaction: ${trx}", - ("bid", block_id)("trx", trx.id())); + ("bid", block_id)("trx", trx_id)); } } - - auto complete_doc = trx_doc << stream::close_array - << "createdAt" << b_date{now} - << stream::finalize; - mongocxx::model::insert_one insert_op{complete_doc.view()}; transactions_in_block = true; - bulk_trans.append(insert_op); - + return doc; + }; + + mongocxx::bulk_write bulk_msgs{bulk_opts}; + mongocxx::bulk_write bulk_acts{bulk_opts}; + trx_num = 1000000; + for (const auto& rt: bt.region_traces) { + for (const auto& ct: rt.cycle_traces) { + for (const auto& st: ct.shard_traces) { + for (const auto& trx_trace: st.transaction_traces) { + std::string trx_status = (trx_trace.status == chain::transaction_receipt::executed) ? "executed" : + (trx_trace.status == chain::transaction_receipt::soft_fail) ? "soft_fail" : + (trx_trace.status == chain::transaction_receipt::hard_fail) ? "hard_fail" : + "unknown"; + trx_status_map[trx_trace.id] = trx_status; + + for (const auto& trx : trx_trace.deferred_transactions) { + auto doc = process_trx(trx); + doc.append(kvp("type", "deferred"), + kvp("sender_id", b_int64{trx.sender_id}), + kvp("sender", trx.sender.to_string()), + kvp("execute_after", b_date{std::chrono::milliseconds{ + std::chrono::seconds{trx.execute_after.sec_since_epoch()}}})); + mongocxx::model::insert_one insert_op{doc.view()}; + bulk_trans.append(insert_op); + ++trx_num; + } + if (!trx_trace.action_traces.empty()) { + actions_to_write = true; + msg_num = 1000000; + for (const auto& act_trace : trx_trace.action_traces) { + const auto& msg = act_trace.act; + auto msg_oid = process_action(trx_trace.id.str(), bulk_msgs, msg); + if (trx_trace.status == chain::transaction_receipt::executed) { + if (act_trace.receiver == chain::config::system_account_name) { + reversible_actions[trx_trace.id.str()].emplace_back(msg); + } + } + process_action_trace(trx_trace.id.str(), bulk_acts, act_trace, msg_oid); + } + } + + // TODO: handle canceled_deferred + } + } + } } - auto blk_complete = blk_doc << stream::close_array - << "createdAt" << b_date{now} - << stream::finalize; - - if (!blocks.insert_one(blk_complete.view())) { - elog("Failed to insert block ${bid}", ("bid", block_id)); + trx_num = 0; + for (const auto& packed_trx : block.input_transactions) { + const signed_transaction& trx = packed_trx.get_signed_transaction(); + auto doc = process_trx(trx); + doc.append(kvp("type", "input")); + doc.append(kvp("signatures", [&trx](bsoncxx::builder::basic::sub_array subarr) { + for (const auto& sig : trx.signatures) { + subarr.append(fc::variant(sig).as_string()); + } + })); + mongocxx::model::insert_one insert_op{doc.view()}; + bulk_trans.append(insert_op); + ++trx_num; } + if (actions_to_write) { + auto result = msgs.bulk_write(bulk_msgs); + if (!result) { + elog("Bulk actions insert failed for block: ${bid}", ("bid", block_id)); + } + } + if (action_traces_to_write) { + auto result = action_traces.bulk_write(bulk_acts); + if (!result) { + elog("Bulk action traces insert failed for block: ${bid}", ("bid", block_id)); + } + } if (transactions_in_block) { auto result = trans.bulk_write(bulk_trans); if (!result) { @@ -379,6 +544,64 @@ void mongo_db_plugin_impl::_process_irreversible_block(const signed_block& block ++processed; } +void mongo_db_plugin_impl::_process_irreversible_block(const signed_block& block) +{ + using namespace bsoncxx::types; + using namespace bsoncxx::builder; + using bsoncxx::builder::basic::kvp; + using bsoncxx::builder::stream::document; + using bsoncxx::builder::stream::open_document; + using bsoncxx::builder::stream::close_document; + using bsoncxx::builder::stream::finalize; + + + auto blocks = mongo_conn[db_name][blocks_col]; // Blocks + auto trans = mongo_conn[db_name][trans_col]; // Transactions + + const auto block_id = block.id(); + const auto block_id_str = block_id.str(); + + auto now = std::chrono::duration_cast( + std::chrono::microseconds{fc::time_point::now().time_since_epoch().count()}); + + auto ir_block = find_block(blocks, block_id_str); + + document update_block{}; + update_block << "$set" << open_document << "pending" << b_bool{false} + << "updatedAt" << b_date{now} + << close_document; + + blocks.update_one(document{} << "_id" << ir_block.view()["_id"].get_oid() << finalize, update_block.view()); + + for (const auto& r: block.regions) { + for (const auto& cs: r.cycles_summary) { + for (const auto& ss: cs) { + for (const auto& trx_receipt: ss.transactions) { + const auto trans_id_str = trx_receipt.id.str(); + auto ir_trans = find_transaction(trans, trans_id_str); + + document update_trans{}; + update_trans << "$set" << open_document << "pending" << b_bool{false} + << "updatedAt" << b_date{now} + << close_document; + + trans.update_one(document{} << "_id" << ir_trans.view()["_id"].get_oid() << finalize, + update_trans.view()); + + // actions are irreversible, so update account document + if (ir_trans.view()["status"].get_utf8().value.to_string() == "executed") { + for (const auto& msg : reversible_actions[trans_id_str]) { + update_account(msg); + } + } + reversible_actions.erase(trans_id_str); + } + } + } + } + +} + // For now providing some simple account processing to maintain eos_balance void mongo_db_plugin_impl::update_account(const chain::action& msg) { using bsoncxx::builder::basic::kvp; @@ -408,6 +631,7 @@ void mongo_db_plugin_impl::update_account(const chain::action& msg) { asset from_balance = asset::from_string(from_account.view()["eos_balance"].get_utf8().value.to_string()); asset to_balance = asset::from_string(to_account.view()["eos_balance"].get_utf8().value.to_string()); auto asset_quantity = transfer["quantity"].as(); + edump((from_balance)(to_balance)(asset_quantity)); from_balance -= asset_quantity; to_balance += asset_quantity; @@ -467,9 +691,9 @@ mongo_db_plugin_impl::~mongo_db_plugin_impl() { done = true; condtion.notify_one(); - consum_thread.join(); + consume_thread.join(); } catch (std::exception& e) { - elog("Exception on mongo_db_plugin shutdown of consum thread: ${e}", ("e", e.what())); + elog("Exception on mongo_db_plugin shutdown of consume thread: ${e}", ("e", e.what())); } } @@ -477,14 +701,16 @@ void mongo_db_plugin_impl::wipe_database() { ilog("mongo db wipe_database"); accounts = mongo_conn[db_name][accounts_col]; // Accounts + auto blocks = mongo_conn[db_name][blocks_col]; // Blocks auto trans = mongo_conn[db_name][trans_col]; // Transactions auto msgs = mongo_conn[db_name][actions_col]; // Actions - auto blocks = mongo_conn[db_name][blocks_col]; // Blocks + auto action_traces = mongo_conn[db_name][action_traces_col]; // ActionTraces - accounts.drop(); + blocks.drop(); trans.drop(); + accounts.drop(); msgs.drop(); - blocks.drop(); + action_traces.drop(); } void mongo_db_plugin_impl::init() { @@ -498,7 +724,7 @@ void mongo_db_plugin_impl::init() { auto now = std::chrono::duration_cast( std::chrono::microseconds{fc::time_point::now().time_since_epoch().count()}); doc << "name" << name(chain::config::system_account_name).to_string() - << "eos_balance" << asset(chain::config::initial_token_supply).to_string() + << "eos_balance" << asset().to_string() << "staked_balance" << asset().to_string() << "unstaking_balance" << asset().to_string() << "createdAt" << b_date{now} @@ -508,6 +734,11 @@ void mongo_db_plugin_impl::init() { elog("Failed to insert account ${n}", ("n", name(chain::config::system_account_name).to_string())); } + // Blocks indexes + auto blocks = mongo_conn[db_name][blocks_col]; // Blocks + blocks.create_index(bsoncxx::from_json(R"xxx({ "block_num" : 1 })xxx")); + blocks.create_index(bsoncxx::from_json(R"xxx({ "block_id" : 1 })xxx")); + // Accounts indexes accounts.create_index(bsoncxx::from_json(R"xxx({ "name" : 1 })xxx")); @@ -515,15 +746,14 @@ void mongo_db_plugin_impl::init() { auto trans = mongo_conn[db_name][trans_col]; // Transactions trans.create_index(bsoncxx::from_json(R"xxx({ "transaction_id" : 1 })xxx")); - // Messages indexes + // Action indexes auto msgs = mongo_conn[db_name][actions_col]; // Messages - msgs.create_index(bsoncxx::from_json(R"xxx({ "message_id" : 1 })xxx")); + msgs.create_index(bsoncxx::from_json(R"xxx({ "action_id" : 1 })xxx")); msgs.create_index(bsoncxx::from_json(R"xxx({ "transaction_id" : 1 })xxx")); - // Blocks indexes - auto blocks = mongo_conn[db_name][blocks_col]; // Blocks - blocks.create_index(bsoncxx::from_json(R"xxx({ "block_num" : 1 })xxx")); - blocks.create_index(bsoncxx::from_json(R"xxx({ "block_id" : 1 })xxx")); + // ActionTraces indexes + auto action_traces = mongo_conn[db_name][action_traces_col]; // ActionTraces + action_traces.create_index(bsoncxx::from_json(R"xxx({ "transaction_id" : 1 })xxx")); } } @@ -582,6 +812,8 @@ void mongo_db_plugin::plugin_initialize(const variables_map& options) // add callback to chain_controller config chain_plugin* chain_plug = app().find_plugin(); FC_ASSERT(chain_plug); + chain_plug->chain_config().applied_block_callbacks.emplace_back( + [my = my](const chain::block_trace& bt) { my->applied_block(bt); }); chain_plug->chain_config().applied_irreversible_block_callbacks.emplace_back( [my = my](const chain::signed_block& b) { my->applied_irreversible_block(b); }); @@ -600,7 +832,7 @@ void mongo_db_plugin::plugin_startup() if (my->configured) { ilog("starting db plugin"); - my->consum_thread = boost::thread([this] { my->consum_blocks(); }); + my->consume_thread = boost::thread([this] { my->consume_blocks(); }); // chain_controller is created and has resynced or replayed if needed my->startup = false; diff --git a/programs/eosioc/main.cpp b/programs/eosioc/main.cpp index 83760e9539c..16a14ea1442 100644 --- a/programs/eosioc/main.cpp +++ b/programs/eosioc/main.cpp @@ -978,9 +978,9 @@ int main( int argc, char** argv ) { trxsSubcommand->set_callback([&] { fc::variant trx_var; try { - trx_var = fc::json::from_string(trxJson); + trx_var = fc::json::from_string(trxsJson); } EOS_CAPTURE_AND_RETHROW(transaction_type_exception, "Fail to parse transaction JSON") - auto trxs_result = call(push_txn_func, trx_var); + auto trxs_result = call(push_txns_func, trx_var); std::cout << fc::json::to_pretty_string(trxs_result) << std::endl; }); diff --git a/tests/testUtils.py b/tests/testUtils.py index 45917fa2c3b..3130cd78f74 100755 --- a/tests/testUtils.py +++ b/tests/testUtils.py @@ -331,7 +331,7 @@ def getTransaction(self, transId, retry=True, silentErrors=False): else: for i in range(2): cmd="%s %s" % (Utils.MongoPath, self.mongoEndpointArgs) - subcommand='db.Transactions.findOne( { "transaction_id": "%s" } )' % (transId) + subcommand='db.Transactions.findOne( { $and : [ { "transaction_id": "%s" }, {"pending":false} ] } )' % (transId) Utils.Debug and Utils.Print("cmd: echo '%s' | %s" % (subcommand, cmd)) try: trans=Node.runMongoCmdReturnJson(cmd.split(), subcommand) @@ -452,6 +452,12 @@ def createInitAccounts(self): transId=Node.getTransId(trans[1]) self.waitForTransIdOnNode(transId) + expectedAmount=10000000000000 + Utils.Print("Verify eosio issue, Expected: %d" % (expectedAmount)) + actualAmount=self.getAccountBalance(eosio.name) + if expectedAmount != actualAmount: + Utils.errorExit("Issue verification failed. Excepted %d, actual: %d" % (expectedAmount, actualAmount)) + initx = copy.copy(Cluster.initaAccount) self.createAccount(Cluster.initaAccount, eosio, 0) for i in range(2, 21): diff --git a/tests/tests/misc_tests.cpp b/tests/tests/misc_tests.cpp index 87134b95089..22199c65857 100644 --- a/tests/tests/misc_tests.cpp +++ b/tests/tests/misc_tests.cpp @@ -2,8 +2,12 @@ * @file * @copyright defined in eos/LICENSE.txt */ +#include #include #include +#include +#include +#include #include #include @@ -13,9 +17,8 @@ #include using namespace eosio::chain; - - -namespace eosio { +namespace eosio +{ using namespace chain; using namespace std; @@ -41,36 +44,37 @@ BOOST_AUTO_TEST_CASE(json_from_string_test) BOOST_CHECK_EQUAL(exc_found, true); } -/* Disabling test. It may no longer be relevant /// Test calculation of median values of blockchain operation properties BOOST_AUTO_TEST_CASE(median_properties_test) -{ try { - vector votes{ - {1024 , 512 , 4096 , asset(5000 ).amount, asset(4000 ).amount, asset(100 ).amount, 512 , 6, 1000 , 3, 4096, 65536 }, - {10000 , 100 , 4096 , asset(3333 ).amount, asset(27109 ).amount, asset(10 ).amount, 100 , 6, 3000 , 2, 4096, 65536 }, - {2048 , 1500 , 1000 , asset(5432 ).amount, asset(2000 ).amount, asset(50 ).amount, 1500 , 6, 5000 , 9, 4096, 65536 }, - {100 , 25 , 1024 , asset(90000 ).amount, asset(0 ).amount, asset(433 ).amount, 25 , 6, 10000 , 4, 4096, 65536 }, - {1024 , 1000 , 100 , asset(10 ).amount, asset(50 ).amount, asset(200 ).amount, 1000 , 6, 4000 , 1, 4096, 65536 }, - }; - blockchain_configuration medians{ - 1024, 512, 1024, asset(5000).amount, asset(2000).amount, asset(100).amount, 512, 6, 4000, 3, 4096, 65536 - }; - - BOOST_CHECK_EQUAL(blockchain_configuration::get_median_values(votes), medians); - - votes.emplace_back(blockchain_configuration{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); - votes.emplace_back(blockchain_configuration{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); - medians = blockchain_configuration {1024, 100, 1000, asset(3333).amount, asset(50).amount, asset(50).amount, 100, 6, 3000, 2, 4096, 65536 }; - - BOOST_CHECK_EQUAL(blockchain_configuration::get_median_values(votes), medians); - BOOST_CHECK_EQUAL(blockchain_configuration::get_median_values({medians}), medians); +{ + try + { + vector votes{ + {512 , 1024 , 256, 512 , 512 , 1024 , 1000, 4096, 512 , 6, 1000 , 3, 4096, 65536}, + {100 , 10000, 50 , 5000, 100 , 10000, 500 , 4096, 100 , 6, 3000 , 2, 4096, 65536}, + {1500, 2048 , 750, 1024, 1500, 2048 , 300 , 1000, 1500, 6, 5000 , 9, 4096, 65536}, + {25 , 100 , 12 , 50 , 25 , 100 , 1200, 1024, 25 , 6, 10000, 4, 4096, 65536}, + {1000, 1024 , 500, 512 , 10 , 1024 , 1500, 100 , 1000, 6, 4000 , 1, 4096, 65536}}; + + chain_config medians{ + 512, 1024, 256, 512, 100, 1024, 1000, 1024, 512, 6, 4000, 3, 4096, 65536}; + + BOOST_TEST(chain_config::get_median_values(votes) == medians); + + votes.emplace_back(chain_config{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); + votes.emplace_back(chain_config{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); + medians = chain_config{100, 1024, 50, 512, 25, 1024, 500, 1000, 100, 6, 3000, 2, 4096, 65536}; + + BOOST_TEST(chain_config::get_median_values(votes) == medians); + BOOST_TEST(chain_config::get_median_values({medians}) == medians); votes.erase(votes.begin() + 2); votes.erase(votes.end() - 1); - medians = blockchain_configuration {1024, 100, 1024, asset(3333).amount, asset(50).amount, asset(100).amount, 100, 6, 3000, 2, 4096, 65536 }; - BOOST_CHECK_EQUAL(blockchain_configuration::get_median_values(votes), medians); -} FC_LOG_AND_RETHROW() } -*/ + medians = chain_config{100, 1024, 50, 512, 25, 1024, 1000, 1024, 100, 6, 3000, 2, 4096, 65536}; + BOOST_TEST(chain_config::get_median_values(votes) == medians); + } + FC_LOG_AND_RETHROW() +} /// Test that our deterministic random shuffle algorithm gives the same results in all environments BOOST_AUTO_TEST_CASE(deterministic_randomness) @@ -78,10 +82,10 @@ BOOST_AUTO_TEST_CASE(deterministic_randomness) utilities::rand::random rng(123454321); vector words = {"infamy", "invests", "estimated", "potters", "memorizes", "hal9000"}; rng.shuffle(words); - BOOST_CHECK_EQUAL(fc::json::to_string(words), + BOOST_TEST(fc::json::to_string(words) == fc::json::to_string(vector{"hal9000","infamy","invests","estimated","memorizes","potters"})); rng.shuffle(words); - BOOST_CHECK_EQUAL(fc::json::to_string(words), + BOOST_TEST(fc::json::to_string(words) == fc::json::to_string(vector{"memorizes","infamy","hal9000","potters","estimated","invests"})); } FC_LOG_AND_RETHROW() } @@ -89,21 +93,203 @@ BOOST_AUTO_TEST_CASE(deterministic_distributions) { try { utilities::rand::random rng(123454321); - BOOST_CHECK_EQUAL(rng.next(), UINT64_C(13636622732572118961)); - BOOST_CHECK_EQUAL(rng.next(), UINT64_C(8049736256506128729)); - BOOST_CHECK_EQUAL(rng.next(), UINT64_C(1224405983932261174)); + BOOST_TEST(rng.next() == UINT64_C(13636622732572118961)); + BOOST_TEST(rng.next() == UINT64_C(8049736256506128729)); + BOOST_TEST(rng.next() == UINT64_C(1224405983932261174)); std::vector nums = {0, 1, 2}; rng.shuffle(nums); std::vector a{2, 0, 1}; - BOOST_CHECK(std::equal(nums.begin(), nums.end(), a.begin())); + BOOST_TEST(std::equal(nums.begin(), nums.end(), a.begin())); rng.shuffle(nums); std::vector b{0, 2, 1}; - BOOST_CHECK(std::equal(nums.begin(), nums.end(), b.begin())); + BOOST_TEST(std::equal(nums.begin(), nums.end(), b.begin())); rng.shuffle(nums); std::vector c{1, 0, 2}; - BOOST_CHECK(std::equal(nums.begin(), nums.end(), c.begin())); + BOOST_TEST(std::equal(nums.begin(), nums.end(), c.begin())); +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(authority_checker) +{ try { + testing::tester test; + auto a = test.get_public_key("a", "active"); + auto b = test.get_public_key("b", "active"); + auto c = test.get_public_key("c", "active"); + + auto GetNullAuthority = [](auto){abort(); return authority();}; + + auto A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}); + { + auto checker = make_auth_checker(GetNullAuthority, 2, {a, b}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 2); + BOOST_TEST(checker.unused_keys().size() == 0); + } + { + auto checker = make_auth_checker(GetNullAuthority, 2, {a, c}); + BOOST_TEST(!checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 0); + BOOST_TEST(checker.unused_keys().size() == 2); + } + { + auto checker = make_auth_checker(GetNullAuthority, 2, {a, b, c}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 2); + BOOST_TEST(checker.used_keys().count(a) == 1); + BOOST_TEST(checker.used_keys().count(b) == 1); + BOOST_TEST(checker.unused_keys().size() == 1); + BOOST_TEST(checker.unused_keys().count(c) == 1); + } + { + auto checker = make_auth_checker(GetNullAuthority, 2, {b, c}); + BOOST_TEST(!checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 0); + } + + A = authority(3, {key_weight{a, 1}, key_weight{b, 1}, key_weight{c, 1}}); + BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {c, b, a}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, b}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {a, c}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {b, c}).satisfied(A)); + + A = authority(1, {key_weight{a, 1}, key_weight{b, 1}}); + BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {a}).satisfied(A)); + BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A)); + + A = authority(1, {key_weight{a, 2}, key_weight{b, 1}}); + BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {a}).satisfied(A)); + BOOST_TEST(make_auth_checker(GetNullAuthority, 2, {b}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetNullAuthority, 2, {c}).satisfied(A)); + + auto GetCAuthority = [c](auto){ + return authority(1, {key_weight{c, 1}}); + }; + + A = authority(2, {key_weight{a, 2}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 1}}); + { + auto checker = make_auth_checker(GetCAuthority, 2, {a}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(checker.all_keys_used()); + } + { + auto checker = make_auth_checker(GetCAuthority, 2, {b}); + BOOST_TEST(!checker.satisfied(A)); + BOOST_TEST(checker.used_keys().size() == 0); + BOOST_TEST(checker.unused_keys().size() == 1); + BOOST_TEST(checker.unused_keys().count(b) == 1); + } + { + auto checker = make_auth_checker(GetCAuthority, 2, {c}); + BOOST_TEST(!checker.satisfied(A)); + BOOST_TEST(checker.used_keys().size() == 0); + BOOST_TEST(checker.unused_keys().size() == 1); + BOOST_TEST(checker.unused_keys().count(c) == 1); + } + { + auto checker = make_auth_checker(GetCAuthority, 2, {b, c}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 2); + BOOST_TEST(checker.unused_keys().size() == 0); + BOOST_TEST(checker.used_keys().count(b) == 1); + BOOST_TEST(checker.used_keys().count(c) == 1); + } + { + auto checker = make_auth_checker(GetCAuthority, 2, {b, c, a}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 1); + BOOST_TEST(checker.used_keys().count(a) == 1); + BOOST_TEST(checker.unused_keys().size() == 2); + BOOST_TEST(checker.unused_keys().count(b) == 1); + BOOST_TEST(checker.unused_keys().count(c) == 1); + } + + A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 1}}); + BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {c}).satisfied(A)); + BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A)); + BOOST_TEST(make_auth_checker(GetCAuthority, 2, {b, c}).satisfied(A)); + BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, c}).satisfied(A)); + { + auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 2); + BOOST_TEST(checker.unused_keys().size() == 1); + BOOST_TEST(checker.unused_keys().count(c) == 1); + } + + A = authority(2, {key_weight{a, 1}, key_weight{b, 1}}, {permission_level_weight{{"hello", "world"}, 2}}); + BOOST_TEST(make_auth_checker(GetCAuthority, 2, {a, b}).satisfied(A)); + BOOST_TEST(make_auth_checker(GetCAuthority, 2, {c}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {a}).satisfied(A)); + BOOST_TEST(!make_auth_checker(GetCAuthority, 2, {b}).satisfied(A)); + { + auto checker = make_auth_checker(GetCAuthority, 2, {a, b, c}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 1); + BOOST_TEST(checker.unused_keys().size() == 2); + BOOST_TEST(checker.used_keys().count(c) == 1); + } + + auto d = test.get_public_key("d", "active"); + auto e = test.get_public_key("e", "active"); + + auto GetAuthority = [d, e] (const permission_level& perm) { + if (perm.actor == "top") + return authority(2, {key_weight{d, 1}}, {permission_level_weight{{"bottom", "bottom"}, 1}}); + return authority{1, {{e, 1}}, {}}; + }; + + A = authority(5, {key_weight{a, 2}, key_weight{b, 2}, key_weight{c, 2}}, {permission_level_weight{{"top", "top"}, 5}}); + { + auto checker = make_auth_checker(GetAuthority, 2, {d, e}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(checker.all_keys_used()); + } + { + auto checker = make_auth_checker(GetAuthority, 2, {a, b, c, d, e}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 2); + BOOST_TEST(checker.unused_keys().size() == 3); + BOOST_TEST(checker.used_keys().count(d) == 1); + BOOST_TEST(checker.used_keys().count(e) == 1); + } + { + auto checker = make_auth_checker(GetAuthority, 2, {a, b, c, e}); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.used_keys().size() == 3); + BOOST_TEST(checker.unused_keys().size() == 1); + BOOST_TEST(checker.used_keys().count(a) == 1); + BOOST_TEST(checker.used_keys().count(b) == 1); + BOOST_TEST(checker.used_keys().count(c) == 1); + } + BOOST_TEST(make_auth_checker(GetAuthority, 1, {a, b, c}).satisfied(A)); + // Fails due to short recursion depth limit + BOOST_TEST(!make_auth_checker(GetAuthority, 1, {d, e}).satisfied(A)); + + A = authority(2, {key_weight{c, 1}, key_weight{b, 1}, key_weight{a, 1}}); + auto B = authority(1, {key_weight{c, 1}, key_weight{b, 1}}); + { + auto checker = make_auth_checker(GetNullAuthority, 2, {a, b, c}); + BOOST_TEST(validate(A)); + BOOST_TEST(validate(B)); + BOOST_TEST(checker.satisfied(A)); + BOOST_TEST(checker.satisfied(B)); + BOOST_TEST(!checker.all_keys_used()); + BOOST_TEST(checker.unused_keys().count(a) == 1); + } } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE(alphabetic_sort) @@ -132,7 +318,7 @@ BOOST_AUTO_TEST_CASE(alphabetic_sort) "...g", "lale.....333", }; - + std::sort(words.begin(), words.end(), std::less()); vector uwords; @@ -150,7 +336,7 @@ BOOST_AUTO_TEST_CASE(alphabetic_sort) } for(int i = 0; i < words.size(); ++i ) { - BOOST_CHECK_EQUAL(tmp[i], words[i]); + BOOST_TEST(tmp[i] == words[i]); } } FC_LOG_AND_RETHROW() }