diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 44a80cb217..f9cc22d270 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -59,6 +59,7 @@ namespace epee storage_entry load_storage_entry(); void read(section& sec); void read(std::string& str); + void read(array_entry &ae); private: struct recursuion_limitation_guard { @@ -114,6 +115,7 @@ namespace epee void throwable_buffer_reader::read(t_pod_type& pod_val) { RECURSION_LIMITATION(); + static_assert(std::is_pod::value, "POD type expected"); read(&pod_val, sizeof(pod_val)); } @@ -277,5 +279,11 @@ namespace epee m_ptr+=len; m_count -= len; } + inline + void throwable_buffer_reader::read(array_entry &ae) + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported"); + } } } diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h index 727f365525..5b2eafa9a1 100644 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -30,6 +30,8 @@ #include "parserse_base_utils.h" #include "file_io_utils.h" +#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100 + namespace epee { using namespace misc_utils::parse; @@ -44,8 +46,9 @@ namespace epee ASSERT_MES_AND_THROW("json parse error"); }*/ template - inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg) + inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg, unsigned int recursion) { + CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded"); std::string::const_iterator sub_element_start; std::string name; @@ -157,7 +160,7 @@ namespace epee //sub section here typename t_storage::hsection new_sec = stg.open_section(name, current_section, true); CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end)); - run_handler(new_sec, it, buf_end, stg); + run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_wonder_after_value; }else if(*it == '[') {//array of something @@ -186,7 +189,7 @@ namespace epee typename t_storage::hsection new_sec = nullptr; h_array = stg.insert_first_section(name, new_sec, current_section); CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section"); - run_handler(new_sec, it, buf_end, stg); + run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_array_after_value; array_md = array_mode_sections; }else if(*it == '"') @@ -260,7 +263,7 @@ namespace epee typename t_storage::hsection new_sec = NULL; bool res = stg.insert_next_section(h_array, new_sec); CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section"); - run_handler(new_sec, it, buf_end, stg); + run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_array_after_value; }else CHECK_ISSPACE(); break; @@ -362,7 +365,7 @@ namespace epee std::string::const_iterator sec_buf_begin = buff_json.begin(); try { - run_handler(nullptr, sec_buf_begin, buff_json.end(), stg); + run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0); return true; } catch(const std::exception& ex) diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 3cdc135f00..2a88975efe 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -165,7 +165,7 @@ int pop_blocks(cryptonote::core& core, int num_blocks) return num_blocks; } -int check_flush(cryptonote::core &core, std::list &blocks, bool force) +int check_flush(cryptonote::core &core, std::vector &blocks, bool force) { if (blocks.empty()) return 0; @@ -177,7 +177,7 @@ int check_flush(cryptonote::core &core, std::list &blocks, if (!force && new_height % HASH_OF_HASHES_STEP) return 0; - std::list hashes; + std::vector hashes; for (const auto &b: blocks) { cryptonote::block block; @@ -313,7 +313,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path MINFO("Reading blockchain from bootstrap file..."); std::cout << ENDL; - std::list blocks; + std::vector blocks; // Skip to start_height before we start adding. { @@ -438,7 +438,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { cryptonote::blobdata block; cryptonote::block_to_blob(bp.block, block); - std::list txs; + std::vector txs; for (const auto &tx: bp.txs) { txs.push_back(cryptonote::blobdata()); diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index de8e2a5b32..6ff39bf90b 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -5,6 +5,7 @@ #include #include #include +#include "common/int-util.h" #include "hash-ops.h" #include "keccak.h" @@ -105,7 +106,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { for (i = 0; i < rsizw; i++) - st[i] ^= ((uint64_t *) in)[i]; + st[i] ^= swap64le(((uint64_t *) in)[i]); keccakf(st, KECCAK_ROUNDS); } @@ -121,11 +122,15 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) temp[rsiz - 1] |= 0x80; for (i = 0; i < rsizw; i++) - st[i] ^= ((uint64_t *) temp)[i]; + st[i] ^= swap64le(((uint64_t *) temp)[i]); keccakf(st, KECCAK_ROUNDS); - memcpy(md, st, mdlen); + if (((size_t)mdlen % sizeof(uint64_t)) != 0) + { + local_abort("Bad keccak use"); + } + memcpy_swap64le(md, st, mdlen/sizeof(uint64_t)); } void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 5cd1709ab3..5e19174e66 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -52,7 +52,7 @@ namespace cryptonote }; state m_state; - std::list m_needed_objects; + std::vector m_needed_objects; std::unordered_set m_requested_objects; uint64_t m_remote_blockchain_height; uint64_t m_last_response_height; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index f88c4d12c8..6f0566c3a3 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -63,7 +63,7 @@ static_assert(STAKING_PORTIONS % 3 == 0, "Use a multiple of three, so that it di #define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 11 -#define UPTIME_PROOF_BUFFER_IN_SECONDS (5*60) +#define UPTIME_PROOF_BUFFER_IN_SECONDS (5*60) // The acceptable window of time to accept a peer's uptime proof from its reported timestamp #define UPTIME_PROOF_FREQUENCY_IN_SECONDS (60*60) #define UPTIME_PROOF_MAX_TIME_IN_SECONDS (UPTIME_PROOF_FREQUENCY_IN_SECONDS * 2 + UPTIME_PROOF_BUFFER_IN_SECONDS) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d7bc9c5940..9060988686 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -213,6 +213,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke MDEBUG("Additional outputs needed: " << absolute_offsets.size() - outputs.size()); std::vector < uint64_t > add_offsets; std::vector add_outputs; + add_outputs.reserve(absolute_offsets.size() - outputs.size()); for (size_t i = outputs.size(); i < absolute_offsets.size(); i++) add_offsets.push_back(absolute_offsets[i]); try @@ -835,6 +836,11 @@ difficulty_type Blockchain::get_difficulty_for_next_block() timestamps.clear(); difficulties.clear(); + if (height > offset) + { + timestamps.reserve(height - offset); + difficulties.reserve(height - offset); + } for (; offset < height; offset++) { timestamps.push_back(m_db->get_block_timestamp(offset)); @@ -1209,6 +1215,7 @@ void Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) m_db->block_txn_start(true); // add size of last blocks to vector (or less, if blockchain size < count) size_t start_offset = h - std::min(h, count); + sz.reserve(sz.size() + h - start_offset); for(size_t i = start_offset; i < h; i++) { sz.push_back(m_db->get_block_size(i)); @@ -1410,6 +1417,7 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height()); size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; + timestamps.reserve(timestamps.size() + start_top_height - stop_offset); while (start_top_height != stop_offset) { timestamps.push_back(m_db->get_block_timestamp(start_top_height)); @@ -1609,7 +1617,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id return true; } //------------------------------------------------------------------ -bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1623,7 +1631,7 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list missed_ids; + std::vector missed_ids; get_transactions_blobs(blk.second.tx_hashes, txs, missed_ids); CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain"); } @@ -1631,14 +1639,16 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_db->height()) + const uint64_t height = m_db->height(); + if(start_offset >= height) return false; - for(size_t i = start_offset; i < start_offset + count && i < m_db->height();i++) + blocks.reserve(blocks.size() + height - start_offset); + for(size_t i = start_offset; i < start_offset + count && i < height;i++) { blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(i), block())); if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) @@ -1663,13 +1673,13 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO CRITICAL_REGION_LOCAL(m_blockchain_lock); m_db->block_txn_start(true); rsp.current_blockchain_height = get_current_blockchain_height(); - std::list> blocks; + std::vector> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl: blocks) { - std::list missed_tx_ids; - std::list txs; + std::vector missed_tx_ids; + std::vector txs; // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. @@ -1685,8 +1695,8 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO // append missed transaction hashes to response missed_ids field, // as done below if any standalone transactions were requested // and missed. - rsp.missed_ids.splice(rsp.missed_ids.end(), missed_tx_ids); - m_db->block_txn_stop(); + rsp.missed_ids.insert(rsp.missed_ids.end(), missed_tx_ids.begin(), missed_tx_ids.end()); + m_db->block_txn_stop(); return false; } @@ -1699,7 +1709,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO e.txs.push_back(tx); } //get another transactions, if need - std::list txs; + std::vector txs; get_transactions_blobs(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx: txs) @@ -1709,11 +1719,12 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO return true; } //------------------------------------------------------------------ -bool Blockchain::get_alternative_blocks(std::list& blocks) const +bool Blockchain::get_alternative_blocks(std::vector& blocks) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + blocks.reserve(m_alternative_chains.size()); for (const auto& alt_bl: m_alternative_chains) { blocks.push_back(alt_bl.second.bl); @@ -2113,6 +2124,9 @@ uint64_t Blockchain::block_difficulty(uint64_t i) const return 0; } //------------------------------------------------------------------ +template void reserve_container(std::vector &v, size_t N) { v.reserve(N); } +template void reserve_container(std::list &v, size_t N) { } +//------------------------------------------------------------------ //TODO: return type should be void, throw on exception // alternatively, return true only if no blocks missed template @@ -2121,6 +2135,7 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + reserve_container(blocks, block_ids.size()); for (const auto& block_hash : block_ids) { try @@ -2155,6 +2170,7 @@ bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_con LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + reserve_container(txs, txs_ids.size()); for (const auto& tx_hash : txs_ids) { try @@ -2181,6 +2197,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + reserve_container(txs, txs_ids.size()); for (const auto& tx_hash : txs_ids) { try @@ -2209,7 +2226,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container // Find the split point between us and foreign blockchain and return // (by reference) the most recent common block hash along with up to // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. -bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, std::list& hashes, uint64_t& start_height, uint64_t& current_height) const +bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, std::vector& hashes, uint64_t& start_height, uint64_t& current_height) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2223,6 +2240,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc m_db->block_txn_start(true); current_height = get_current_blockchain_height(); size_t count = 0; + hashes.reserve(std::max((size_t)(current_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT)); for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { hashes.push_back(m_db->get_block_hash_from_height(i)); @@ -2247,7 +2265,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height ), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2273,13 +2291,14 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons m_db->block_txn_start(true); total_height = get_current_blockchain_height(); size_t count = 0, size = 0; + blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++) { blocks.resize(blocks.size()+1); blocks.back().first = m_db->get_block_blob_from_height(i); block b; CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block"); - std::list mis; + std::vector mis; get_transactions_blobs(b.tx_hashes, blocks.back().second, mis, pruned); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); size += blocks.back().first.size(); @@ -3040,7 +3059,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } - // Check the inputs (votes) of the transaction have not been already been + // Check the inputs (votes) of the transaction have not already been // submitted to the blockchain under another transaction using a different // combination of votes. tx_extra_service_node_deregister deregister; @@ -3094,8 +3113,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, const uint64_t height = deregister.block_height; const size_t num_blocks_to_check = loki::service_node_deregister::DEREGISTER_LIFETIME_BY_HEIGHT; - std::list> blocks; - std::list txs; + std::vector> blocks; + std::vector txs; if (get_blocks(height, num_blocks_to_check, blocks, txs)) { for (blobdata const &blob : txs) @@ -3129,6 +3148,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if (existing_deregister_quorum_state->nodes_to_test[existing_deregister.service_node_index] == quorum_state->nodes_to_test[deregister.service_node_index]) { + MERROR_VER("Already seen this deregister tx (aka double spend)"); tvc.m_double_spend = true; return false; } @@ -3146,6 +3166,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector &pubkeys, const std::vector& sig, uint64_t &result) { std::vector p_output_keys; + p_output_keys.reserve(pubkeys.size()); for (auto &key : pubkeys) { // rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy @@ -3242,6 +3263,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons const uint64_t min_block_size = get_min_block_size(version); std::vector sz; get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); + sz.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) sz.push_back(min_block_size); @@ -3382,6 +3404,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons // need most recent 60 blocks, get index of first of those size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + timestamps.reserve(h - offset); for(;offset < h; ++offset) { timestamps.push_back(m_db->get_block_timestamp(offset)); @@ -3408,7 +3431,7 @@ void Blockchain::return_tx_to_pool(std::vector &txs) } } //------------------------------------------------------------------ -bool Blockchain::flush_txes_from_pool(const std::list &txids) +bool Blockchain::flush_txes_from_pool(const std::vector &txids) { CRITICAL_REGION_LOCAL(m_tx_pool); @@ -3598,6 +3621,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // Iterate over the block's transaction hashes, grabbing each // from the tx_pool and validating them. Each is then added // to txs. Keys spent in each are added to by the double spend check. + txs.reserve(bl.tx_hashes.size()); for (const crypto::hash& tx_id : bl.tx_hashes) { transaction tx; @@ -4021,7 +4045,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector &hashes) +uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { // new: . . . . . X X X X X . . . . . . // pre: A A A A B B B B C C C C D D D D @@ -4124,7 +4148,7 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list &blocks_entry) +bool Blockchain::prepare_handle_incoming_blocks(const std::vector &blocks_entry) { MTRACE("Blockchain::" << __func__); TIME_MEASURE_START(prepare); @@ -4190,6 +4214,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list txs; + std::vector txs; m_tx_pool.get_transactions(txs); size_t blob_size; @@ -4736,6 +4761,6 @@ void Blockchain::hook_validate_miner_tx(Blockchain::ValidateMinerTxHook& validat } namespace cryptonote { -template bool Blockchain::get_transactions(const std::vector&, std::list&, std::list&) const; -template bool Blockchain::get_transactions_blobs(const std::vector&, std::list&, std::list&, bool) const; +template bool Blockchain::get_transactions(const std::vector&, std::vector&, std::vector&) const; +template bool Blockchain::get_transactions_blobs(const std::vector&, std::vector&, std::vector&, bool) const; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 344553e28f..9194c6c469 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -191,7 +191,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const; /** * @brief get blocks from blocks based on start height and count @@ -202,7 +202,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const; /** * @brief compiles a list of all blocks stored as alternative chains @@ -211,7 +211,7 @@ namespace cryptonote * * @return true */ - bool get_alternative_blocks(std::list& blocks) const; + bool get_alternative_blocks(std::vector& blocks) const; /** * @brief returns the number of alternative blocks stored @@ -247,7 +247,7 @@ namespace cryptonote * * @return false on erroneous blocks, else true */ - bool prepare_handle_incoming_blocks(const std::list &blocks); + bool prepare_handle_incoming_blocks(const std::vector &blocks); /** * @brief incoming blocks post-processing, cleanup, and disk sync @@ -407,7 +407,7 @@ namespace cryptonote * * @return true if a block found in common, else false */ - bool find_blockchain_supplement(const std::list& qblock_ids, std::list& hashes, uint64_t& start_height, uint64_t& current_height) const; + bool find_blockchain_supplement(const std::list& qblock_ids, std::vector& hashes, uint64_t& start_height, uint64_t& current_height) const; /** * @brief get recent block hashes for a foreign chain @@ -454,7 +454,7 @@ namespace cryptonote * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions @@ -862,7 +862,7 @@ namespace cryptonote * * @return false if any removals fail, otherwise true */ - bool flush_txes_from_pool(const std::list &txids); + bool flush_txes_from_pool(const std::vector &txids); /** * @brief return a histogram of outputs on the blockchain @@ -985,7 +985,7 @@ namespace cryptonote bool is_within_compiled_block_hash_area(uint64_t height) const; bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes); + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes); void lock(); void unlock(); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 298f342848..5572c1d9e9 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -339,19 +339,19 @@ namespace cryptonote top_id = m_blockchain_storage.get_tail_id(height); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::vector& blocks) const { - std::list> bs; + std::vector> bs; if (!m_blockchain_storage.get_blocks(start_offset, count, bs)) return false; for (const auto &b: bs) @@ -359,7 +359,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const + bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs); } @@ -370,12 +370,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const + bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list& blocks) const + bool core::get_alternative_blocks(std::vector& blocks) const { return m_blockchain_storage.get_alternative_blocks(blocks); } @@ -411,6 +411,7 @@ namespace cryptonote { r = init_service_node_key(); CHECK_AND_ASSERT_MES(r, false, "Failed to create or load service node key"); + m_service_node_list.set_my_service_node_keys(&m_service_node_pubkey); } boost::filesystem::path folder(m_config_folder); @@ -733,7 +734,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { TRY_ENTRY(); @@ -742,7 +743,7 @@ namespace cryptonote tvc.resize(tx_blobs.size()); tools::threadpool::waiter waiter; - std::list::const_iterator it = tx_blobs.begin(); + std::vector::const_iterator it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { m_threadpool.submit(&waiter, [&, i, it] { try @@ -811,7 +812,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { - std::list tx_blobs; + std::vector tx_blobs; tx_blobs.push_back(tx_blob); std::vector tvcv(1); bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay); @@ -983,8 +984,8 @@ namespace cryptonote const uint64_t end = start_offset + count - 1; m_blockchain_storage.for_blocks_range(start_offset, end, [this, &emission_amount, &total_fee_amount](uint64_t, const crypto::hash& hash, const block& b){ - std::list txs; - std::list missed_txs; + std::vector txs; + std::vector missed_txs; uint64_t coinbase_amount = get_outs_money_amount(b.miner_tx); this->get_transactions(b.tx_hashes, txs, missed_txs); uint64_t tx_fee_amount = 0; @@ -1080,7 +1081,7 @@ namespace cryptonote bool core::relay_txpool_transactions() { // we attempt to relay txes that should be relayed, but were not - std::list> txs; + std::vector> txs; if (m_mempool.get_relayable_transactions(txs) && !txs.empty()) { cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); @@ -1103,7 +1104,10 @@ namespace cryptonote cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); NOTIFY_UPTIME_PROOF::request r; m_quorum_cop.generate_uptime_proof_request(m_service_node_pubkey, m_service_node_key, r); - get_protocol()->relay_uptime_proof(r, fake_context); + bool relayed = get_protocol()->relay_uptime_proof(r, fake_context); + + if (relayed) + MGINFO("Submitted uptime-proof for service node (yours): " << m_service_node_pubkey); } return true; } @@ -1121,7 +1125,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob) { - std::list> txs; + std::vector> txs; cryptonote::transaction tx; crypto::hash tx_hash, tx_prefix_hash; if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash)) @@ -1162,7 +1166,7 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const { return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, max_count); } @@ -1219,7 +1223,7 @@ namespace cryptonote { block_verification_context bvc = boost::value_initialized(); m_miner.pause(); - std::list blocks; + std::vector blocks; try { blocks.push_back(get_block_complete_entry(b, m_mempool)); @@ -1243,8 +1247,8 @@ namespace cryptonote cryptonote_connection_context exclude_context = boost::value_initialized(); NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - std::list missed_txs; - std::list txs; + std::vector missed_txs; + std::vector txs; m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs); if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { @@ -1280,7 +1284,7 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------- - bool core::prepare_handle_incoming_blocks(const std::list &blocks) + bool core::prepare_handle_incoming_blocks(const std::vector &blocks) { m_incoming_tx_lock.lock(); m_blockchain_storage.prepare_handle_incoming_blocks(blocks); @@ -1373,7 +1377,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions(std::list& txs, bool include_sensitive_data) const + bool core::get_pool_transactions(std::vector& txs, bool include_sensitive_data) const { m_mempool.get_transactions(txs, include_sensitive_data); return true; @@ -1444,17 +1448,23 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- void core::do_uptime_proof_call() { - std::vector states = get_service_node_list_state({ m_service_node_pubkey }); - // wait one block before starting uptime proofs. + std::vector const states = get_service_node_list_state({ m_service_node_pubkey }); if (!states.empty() && states[0].info.registration_height + 1 < get_current_blockchain_height()) { - m_submit_uptime_proof_interval.do_call(boost::bind(&core::submit_uptime_proof, this)); + // Code snippet from Github @Jagerman + m_check_uptime_proof_interval.do_call([&states, this](){ + uint64_t last_uptime = m_quorum_cop.get_uptime_proof(states[0].pubkey); + if (last_uptime <= static_cast(time(nullptr) - UPTIME_PROOF_FREQUENCY_IN_SECONDS)) + this->submit_uptime_proof(); + + return true; + }); } else { - // reset the interval so that we're ready when we register. - m_submit_uptime_proof_interval = epee::math_helper::once_a_time_seconds(); + // reset the interval so that we're ready when we register, OR if we get deregistered this primes us up for re-registration in the same session + m_check_uptime_proof_interval = epee::math_helper::once_a_time_seconds(); } } //----------------------------------------------------------------------------------------------- @@ -1484,8 +1494,13 @@ namespace cryptonote m_deregisters_auto_relayer.do_call(boost::bind(&core::relay_deregister_votes, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); - if (m_service_node) + + time_t const lifetime = time(nullptr) - get_start_time(); + if (m_service_node && lifetime > DIFFICULTY_TARGET_V2) // Give us some time to connect to peers before sending uptimes + { do_uptime_proof_call(); + } + m_uptime_proof_pruner.do_call(boost::bind(&service_nodes::quorum_cop::prune_uptime_proof, &m_quorum_cop)); m_miner.on_idle(); @@ -1677,7 +1692,7 @@ namespace cryptonote return m_target_blockchain_height; } //----------------------------------------------------------------------------------------------- - uint64_t core::prevalidate_block_hashes(uint64_t height, const std::list &hashes) + uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return get_blockchain_storage().prevalidate_block_hashes(height, hashes); } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 4988d1f57c..020dfcb0b3 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -146,7 +146,7 @@ namespace cryptonote * * @return true if the transactions made it to the transaction pool, otherwise false */ - bool handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** * @brief handles an incoming block @@ -169,7 +169,7 @@ namespace cryptonote * * @note see Blockchain::prepare_handle_incoming_blocks */ - bool prepare_handle_incoming_blocks(const std::list &blocks); + bool prepare_handle_incoming_blocks(const std::vector &blocks); /** * @copydoc Blockchain::cleanup_handle_incoming_blocks @@ -325,25 +325,25 @@ namespace cryptonote void get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list>&, std::list&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&, std::vector&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list>&, std::list&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector>&, std::vector&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector& blocks) const; /** * @copydoc Blockchain::get_blocks(const t_ids_container&, t_blocks_container&, t_missed_container&) const @@ -368,14 +368,14 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; /** * @copydoc Blockchain::get_transactions * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; /** * @copydoc Blockchain::get_block_by_hash @@ -387,9 +387,9 @@ namespace cryptonote /** * @copydoc Blockchain::get_alternative_blocks * - * @note see Blockchain::get_alternative_blocks(std::list&) const + * @note see Blockchain::get_alternative_blocks(std::vector&) const */ - bool get_alternative_blocks(std::list& blocks) const; + bool get_alternative_blocks(std::vector& blocks) const; /** * @copydoc Blockchain::get_alternative_blocks_count @@ -446,7 +446,7 @@ namespace cryptonote * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transactions(std::list& txs, bool include_unrelayed_txes = true) const; + bool get_pool_transactions(std::vector& txs, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_txpool_backlog @@ -529,11 +529,11 @@ namespace cryptonote bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; /** - * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::list > >&, uint64_t&, uint64_t&, size_t) const + * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const * - * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::list > >&, uint64_t&, uint64_t&, size_t) const + * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; /** * @brief gets some stats about the daemon @@ -773,7 +773,7 @@ namespace cryptonote * * @return number of usable blocks */ - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes); + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes); /** * @brief get free disk space on the blockchain partition @@ -1056,7 +1056,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*2, false> m_deregisters_auto_relayer; //!< interval for checking re-relaying deregister votes epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space - epee::math_helper::once_a_time_seconds m_submit_uptime_proof_interval; //!< interval for submitting uptime proof + epee::math_helper::once_a_time_seconds m_check_uptime_proof_interval; //!< interval for checking our own uptime proof epee::math_helper::once_a_time_seconds<30, true> m_uptime_proof_pruner; std::atomic m_starter_message_showed; //!< has the "daemon will sync now" message been shown? diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 960b744c4d..30d1457784 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -298,7 +298,7 @@ namespace cryptonote case TESTNET: cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::TESTNET, ::config::testnet::GOVERNANCE_WALLET_ADDRESS); break; - case MAINNET: + case FAKECHAIN: case MAINNET: cryptonote::get_account_address_from_str(governance_wallet_address, cryptonote::MAINNET, ::config::GOVERNANCE_WALLET_ADDRESS); break; default: diff --git a/src/cryptonote_core/service_node_list.cpp b/src/cryptonote_core/service_node_list.cpp index b7aa35a3f0..5850a6103f 100644 --- a/src/cryptonote_core/service_node_list.cpp +++ b/src/cryptonote_core/service_node_list.cpp @@ -49,7 +49,7 @@ namespace service_nodes { service_node_list::service_node_list(cryptonote::Blockchain& blockchain) - : m_blockchain(blockchain), m_hooks_registered(false), m_height(0), m_db(nullptr) + : m_blockchain(blockchain), m_hooks_registered(false), m_height(0), m_db(nullptr), m_service_node_pubkey(nullptr) { } @@ -84,7 +84,7 @@ namespace service_nodes while (m_height < current_height) { - std::list> blocks; + std::vector> blocks; if (!m_blockchain.get_blocks(m_height, 1000, blocks)) { LOG_ERROR("Unable to initialize service nodes list"); @@ -94,8 +94,8 @@ namespace service_nodes for (const auto& block_pair : blocks) { const cryptonote::block& block = block_pair.second; - std::list txs; - std::list missed_txs; + std::vector txs; + std::vector missed_txs; if (!m_blockchain.get_transactions(block.tx_hashes, txs, missed_txs)) { LOG_ERROR("Unable to get transactions for block " << block.hash); @@ -280,12 +280,33 @@ namespace service_nodes if (iter == m_service_nodes_infos.end()) return; - LOG_PRINT_L1("Deregistration for service node: " << key); + if (m_service_node_pubkey && *m_service_node_pubkey == key) + { + MGINFO_RED("Deregistration for service node (yours): " << key); + } + else + { + LOG_PRINT_L1("Deregistration for service node: " << key); + } + m_rollback_events.push_back(std::unique_ptr(new rollback_change(block_height, key, iter->second))); m_service_nodes_infos.erase(iter); } + bool check_service_node_portions(const std::vector& portions) + { + uint64_t portions_left = STAKING_PORTIONS; + + for (const auto portion : portions) { + const uint64_t min_portions = std::min(portions_left, MIN_PORTIONS); + if (portion < min_portions || portion > portions_left) return false; + portions_left -= portion; + } + + return true; + } + bool service_node_list::is_registration_tx(const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, service_node_info& info) const { crypto::public_key tx_pub_key, service_node_key; @@ -302,15 +323,7 @@ namespace service_nodes return false; // check the portions - - uint64_t portions_left = STAKING_PORTIONS; - for (size_t i = 0; i < service_node_portions.size(); i++) - { - uint64_t min_portions = std::min(portions_left, MIN_PORTIONS); - if (service_node_portions[i] < min_portions || service_node_portions[i] > portions_left) - return false; - portions_left -= service_node_portions[i]; - } + if (!check_service_node_portions(service_node_portions)) return false; if (portions_for_operator > STAKING_PORTIONS) return false; @@ -381,7 +394,14 @@ namespace service_nodes if (iter != m_service_nodes_infos.end()) return; - LOG_PRINT_L1("New service node registered: " << key << " at block height: " << block_height); + if (m_service_node_pubkey && *m_service_node_pubkey == key) + { + MGINFO_GREEN("New service node registered (yours): " << key << " at block height: " << block_height); + } + else + { + LOG_PRINT_L1("New service node registered: " << key << " at block height: " << block_height); + } m_rollback_events.push_back(std::unique_ptr(new rollback_new(block_height, key))); m_service_nodes_infos[key] = info; @@ -587,7 +607,7 @@ namespace service_nodes const uint64_t expired_nodes_block_height = block_height - lock_blocks; - std::list> blocks; + std::vector> blocks; if (!m_blockchain.get_blocks(expired_nodes_block_height, 1, blocks)) { LOG_ERROR("Unable to get historical blocks"); @@ -595,8 +615,8 @@ namespace service_nodes } const cryptonote::block& block = blocks.begin()->second; - std::list txs; - std::list missed_txs; + std::vector txs; + std::vector missed_txs; if (!m_blockchain.get_transactions(block.tx_hashes, txs, missed_txs)) { LOG_ERROR("Unable to get transactions for block " << block.hash); @@ -727,7 +747,7 @@ namespace service_nodes return x / (secureMax / n); } - static void loki_shuffle(std::vector& a, uint64_t seed) + void loki_shuffle(std::vector& a, uint64_t seed) { if (a.size() <= 1) return; std::mt19937_64 mersenne_twister(seed); @@ -979,8 +999,8 @@ namespace service_nodes } } - LOG_PRINT_L0("Service node data loaded successfully, m_height: " << m_height); - LOG_PRINT_L0(m_service_nodes_infos.size() << " nodes and " << m_rollback_events.size() << " rollback events loaded."); + MGINFO("Service node data loaded successfully, m_height: " << m_height); + MGINFO(m_service_nodes_infos.size() << " nodes and " << m_rollback_events.size() << " rollback events loaded."); LOG_PRINT_L1("service_node_list::load() returning success"); return true; diff --git a/src/cryptonote_core/service_node_list.h b/src/cryptonote_core/service_node_list.h index 6c9ca65e9b..c83eec7f52 100644 --- a/src/cryptonote_core/service_node_list.h +++ b/src/cryptonote_core/service_node_list.h @@ -112,6 +112,8 @@ namespace service_nodes service_node_info info; }; + void loki_shuffle(std::vector& a, uint64_t seed); + class service_node_list : public cryptonote::Blockchain::BlockAddedHook, public cryptonote::Blockchain::BlockchainDetachedHook, @@ -136,6 +138,7 @@ namespace service_nodes std::vector get_service_node_list_state(const std::vector &service_node_pubkeys) const; void set_db_pointer(cryptonote::BlockchainDB* db) { m_db = db; } + void set_my_service_node_keys(crypto::public_key const *pub_key) { m_service_node_pubkey = pub_key; } bool store(); bool is_registration_tx(const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, service_node_info& info) const; @@ -273,6 +276,8 @@ namespace service_nodes bool m_hooks_registered; block_height m_height; + crypto::public_key const *m_service_node_pubkey; + cryptonote::BlockchainDB* m_db; std::map> m_quorum_states; @@ -282,6 +287,9 @@ namespace service_nodes bool make_registration_cmd(cryptonote::network_type nettype, const std::vector args, const crypto::public_key& service_node_pubkey, const crypto::secret_key service_node_key, std::string &cmd, bool make_friendly); + /// Check if portions are sufficiently large (except for the last) and add up to the required amount + bool check_service_node_portions(const std::vector& portions); + uint64_t get_staking_requirement_lock_blocks(cryptonote::network_type m_nettype); uint64_t get_staking_requirement(cryptonote::network_type nettype, uint64_t height); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 23e1c6af45..711fe8f87b 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -123,7 +123,7 @@ namespace cryptonote return false; } - std::list pool_txs; + std::vector pool_txs; get_transactions(pool_txs); for (const transaction& pool_tx : pool_txs) { @@ -648,11 +648,12 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_relayable_transactions(std::list> &txs) const + bool tx_memory_pool::get_relayable_transactions(std::vector> &txs) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); + txs.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){ if(!meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time)) { @@ -702,7 +703,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - void tx_memory_pool::set_relayed(const std::list> &txs) + void tx_memory_pool::set_relayed(const std::vector> &txs) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -735,10 +736,11 @@ namespace cryptonote return m_blockchain.get_txpool_tx_count(include_unrelayed_txes); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::list& txs, bool include_unrelayed_txes) const + void tx_memory_pool::get_transactions(std::vector& txs, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) @@ -756,6 +758,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ txs.push_back(txid); return true; @@ -767,6 +770,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); + backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now}); return true; @@ -855,6 +859,8 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + tx_infos.reserve(m_blockchain.get_txpool_tx_count()); + key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); @@ -925,6 +931,8 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + tx_infos.reserve(m_blockchain.get_txpool_tx_count()); + key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ cryptonote::rpc::tx_in_pool txi; txi.tx_hash = txid; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index e4acd65087..64e1442577 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -235,7 +235,7 @@ namespace cryptonote * @param include_unrelayed_txes include unrelayed txes in the result * */ - void get_transactions(std::list& txs, bool include_unrelayed_txes = true) const; + void get_transactions(std::vector& txs, bool include_unrelayed_txes = true) const; /** * @brief get a list of all transaction hashes in the pool @@ -322,14 +322,14 @@ namespace cryptonote * * @return true */ - bool get_relayable_transactions(std::list>& txs) const; + bool get_relayable_transactions(std::vector>& txs) const; /** * @brief tell the pool that certain transactions were just relayed * * @param txs the list of transactions (and their hashes) */ - void set_relayed(const std::list>& txs); + void set_relayed(const std::vector>& txs); /** * @brief get the total number of transactions in the pool diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 4054d46ce4..7c07ea9c81 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -51,10 +51,10 @@ namespace std { namespace cryptonote { -void block_queue::add_blocks(uint64_t height, std::list bcel, const boost::uuids::uuid &connection_id, float rate, size_t size) +void block_queue::add_blocks(uint64_t height, std::vector bcel, const boost::uuids::uuid &connection_id, float rate, size_t size) { boost::unique_lock lock(mutex); - std::list hashes; + std::vector hashes; bool has_hashes = remove_span(height, &hashes); blocks.insert(span(height, std::move(bcel), connection_id, rate, size)); if (has_hashes) @@ -98,7 +98,7 @@ void block_queue::flush_stale_spans(const std::set &live_con } } -bool block_queue::remove_span(uint64_t start_block_height, std::list *hashes) +bool block_queue::remove_span(uint64_t start_block_height, std::vector *hashes) { boost::unique_lock lock(mutex); for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i) @@ -173,7 +173,7 @@ bool block_queue::requested(const crypto::hash &hash) const return false; } -std::pair block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list &block_hashes, boost::posix_time::ptime time) +std::pair block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector &block_hashes, boost::posix_time::ptime time) { boost::unique_lock lock(mutex); @@ -184,14 +184,14 @@ std::pair block_queue::reserve_span(uint64_t first_block_hei } uint64_t span_start_height = last_block_height - block_hashes.size() + 1; - std::list::const_iterator i = block_hashes.begin(); + std::vector::const_iterator i = block_hashes.begin(); while (i != block_hashes.end() && requested(*i)) { ++i; ++span_start_height; } uint64_t span_length = 0; - std::list hashes; + std::vector hashes; while (i != block_hashes.end() && span_length < max_blocks) { hashes.push_back(*i); @@ -231,7 +231,7 @@ std::pair block_queue::get_start_gap_span() const return std::make_pair(current_height + 1, first_span_height - current_height - 1); } -std::pair block_queue::get_next_span_if_scheduled(std::list &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const +std::pair block_queue::get_next_span_if_scheduled(std::vector &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const { boost::unique_lock lock(mutex); if (blocks.empty()) @@ -249,7 +249,7 @@ std::pair block_queue::get_next_span_if_scheduled(std::list< return std::make_pair(i->start_block_height, i->nblocks); } -void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::list hashes) +void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector hashes) { boost::unique_lock lock(mutex); for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i) @@ -265,7 +265,7 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui } } -bool block_queue::get_next_span(uint64_t &height, std::list &bcel, boost::uuids::uuid &connection_id, bool filled) const +bool block_queue::get_next_span(uint64_t &height, std::vector &bcel, boost::uuids::uuid &connection_id, bool filled) const { boost::unique_lock lock(mutex); if (blocks.empty()) diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 8893f720b7..a9f961ac06 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -32,7 +32,7 @@ #pragma once #include -#include +#include #include #include #include @@ -50,15 +50,15 @@ namespace cryptonote struct span { uint64_t start_block_height; - std::list hashes; - std::list blocks; + std::vector hashes; + std::vector blocks; boost::uuids::uuid connection_id; uint64_t nblocks; float rate; size_t size; boost::posix_time::ptime time; - span(uint64_t start_block_height, std::list blocks, const boost::uuids::uuid &connection_id, float rate, size_t size): + span(uint64_t start_block_height, std::vector blocks, const boost::uuids::uuid &connection_id, float rate, size_t size): start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time() {} span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time): start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time) {} @@ -68,21 +68,21 @@ namespace cryptonote typedef std::set block_map; public: - void add_blocks(uint64_t height, std::list bcel, const boost::uuids::uuid &connection_id, float rate, size_t size); + void add_blocks(uint64_t height, std::vector bcel, const boost::uuids::uuid &connection_id, float rate, size_t size); void add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time = boost::date_time::min_date_time); void flush_spans(const boost::uuids::uuid &connection_id, bool all = false); void flush_stale_spans(const std::set &live_connections); - bool remove_span(uint64_t start_block_height, std::list *hashes = NULL); + bool remove_span(uint64_t start_block_height, std::vector *hashes = NULL); void remove_spans(const boost::uuids::uuid &connection_id, uint64_t start_block_height); uint64_t get_max_block_height() const; void print() const; std::string get_overview() const; - std::pair reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); + std::pair reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); bool is_blockchain_placeholder(const span &span) const; std::pair get_start_gap_span() const; - std::pair get_next_span_if_scheduled(std::list &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const; - void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::list hashes); - bool get_next_span(uint64_t &height, std::list &bcel, boost::uuids::uuid &connection_id, bool filled = true) const; + std::pair get_next_span_if_scheduled(std::vector &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const; + void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector hashes); + bool get_next_span(uint64_t &height, std::vector &bcel, boost::uuids::uuid &connection_id, bool filled = true) const; bool has_next_span(const boost::uuids::uuid &connection_id, bool &filled) const; size_t get_data_size() const; size_t get_num_filled_spans_prefix() const; diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 38c8de5594..dff6229a79 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -111,7 +111,7 @@ namespace cryptonote struct block_complete_entry { blobdata block; - std::list txs; + std::vector txs; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block) KV_SERIALIZE(txs) @@ -147,7 +147,7 @@ namespace cryptonote struct request { - std::list txs; + std::vector txs; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txs) @@ -163,8 +163,8 @@ namespace cryptonote struct request { - std::list txs; - std::list blocks; + std::vector txs; + std::vector blocks; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) @@ -179,9 +179,9 @@ namespace cryptonote struct request { - std::list txs; - std::list blocks; - std::list missed_ids; + std::vector txs; + std::vector blocks; + std::vector missed_ids; uint64_t current_blockchain_height; BEGIN_KV_SERIALIZE_MAP() @@ -232,7 +232,7 @@ namespace cryptonote uint64_t start_height; uint64_t total_height; uint64_t cumulative_difficulty; - std::list m_block_ids; + std::vector m_block_ids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(start_height) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 04d08d8d16..af5d170f2c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -350,7 +350,7 @@ namespace cryptonote return 1; } m_core.pause_mine(); - std::list blocks; + std::vector blocks; blocks.push_back(arg.b); m_core.prepare_handle_incoming_blocks(blocks); for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) @@ -437,7 +437,7 @@ namespace cryptonote } } - std::list have_tx; + std::vector have_tx; // Instead of requesting missing transactions by hash like BTC, // we do it by index (thanks to a suggestion from moneromooo) because @@ -577,8 +577,8 @@ namespace cryptonote else { std::vector tx_ids; - std::list txes; - std::list missing; + std::vector txes; + std::vector missing; tx_ids.push_back(tx_hash); if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty()) { @@ -625,7 +625,7 @@ namespace cryptonote b.block = arg.b.block; b.txs = have_tx; - std::list blocks; + std::vector blocks; blocks.push_back(b); m_core.prepare_handle_incoming_blocks(blocks); @@ -697,8 +697,8 @@ namespace cryptonote { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash); - std::list> local_blocks; - std::list local_txs; + std::vector> local_blocks; + std::vector local_txs; block b; if (!m_core.get_block_by_hash(arg.block_hash, b)) @@ -735,8 +735,8 @@ namespace cryptonote } } - std::list txs; - std::list missed; + std::vector txs; + std::vector missed; if (!m_core.get_transactions(txids, txs, missed)) { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " @@ -828,10 +828,12 @@ namespace cryptonote return 1; } - for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end();) + std::vector newtxs; + newtxs.reserve(arg.txs.size()); + for (size_t i = 0; i < arg.txs.size(); ++i) { cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(*tx_blob_it, tvc, false, true, false); + m_core.handle_incoming_tx(arg.txs[i], tvc, false, true, false); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); @@ -839,10 +841,9 @@ namespace cryptonote return 1; } if(tvc.m_should_be_relayed) - ++tx_blob_it; - else - arg.txs.erase(tx_blob_it++); + newtxs.push_back(std::move(arg.txs[i])); } + arg.txs = std::move(newtxs); if(arg.txs.size()) { @@ -1050,7 +1051,7 @@ skip: { const uint64_t previous_height = m_core.get_current_blockchain_height(); uint64_t start_height; - std::list blocks; + std::vector blocks; boost::uuids::uuid span_connection_id; if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id)) { @@ -1124,7 +1125,7 @@ skip: LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()"); return 1; } - std::list::const_iterator it = block_entry.txs.begin(); + std::vector::const_iterator it = block_entry.txs.begin(); for (size_t i = 0; i < tvc.size(); ++i, ++it) { if(tvc[i].m_verifivation_failed) @@ -1302,7 +1303,7 @@ skip: template bool t_cryptonote_protocol_handler::should_download_next_span(cryptonote_connection_context& context) const { - std::list hashes; + std::vector hashes; boost::uuids::uuid span_connection_id; boost::posix_time::ptime request_time; std::pair span; @@ -1321,7 +1322,7 @@ skip: // we might be in a weird case where there is a filled next span, // but it starts higher than the current height uint64_t height; - std::list bcel; + std::vector bcel; if (!m_block_queue.get_next_span(height, bcel, span_connection_id, true)) return false; if (height > m_core.get_current_blockchain_height()) @@ -1469,7 +1470,7 @@ skip: { if (span.second == 0) { - std::list hashes; + std::vector hashes; boost::uuids::uuid span_connection_id; boost::posix_time::ptime time; span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id, time); @@ -1495,14 +1496,18 @@ skip: goto skip; } // take out blocks we already have - while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front())) + size_t skip = 0; + while (skip < context.m_needed_objects.size() && m_core.have_block(context.m_needed_objects[skip])) { // if we're popping the last hash, record it so we can ask again from that hash, // this prevents never being able to progress on peers we get old hash lists from - if (context.m_needed_objects.size() == 1) - context.m_last_known_hash = context.m_needed_objects.front(); - context.m_needed_objects.pop_front(); + if (skip + 1 == context.m_needed_objects.size()) + context.m_last_known_hash = context.m_needed_objects[skip]; + ++skip; } + if (skip > 0) + context.m_needed_objects = std::vector(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); + const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_needed_objects); MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); @@ -1510,7 +1515,7 @@ skip: if (span.second == 0 && !force_next_span) { MDEBUG(context << " still no span reserved, we may be in the corner case of next span scheduled and everything else scheduled/filled"); - std::list hashes; + std::vector hashes; boost::uuids::uuid span_connection_id; boost::posix_time::ptime time; span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id, time); @@ -1541,23 +1546,21 @@ skip: MERROR("ERROR: skip " << skip << ", m_needed_objects " << context.m_needed_objects.size() << ", first_context_block_height" << first_context_block_height); return false; } - while (skip--) - context.m_needed_objects.pop_front(); + if (skip > 0) + context.m_needed_objects = std::vector(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); if (context.m_needed_objects.size() < span.second) { MERROR("ERROR: span " << span.first << "/" << span.second << ", m_needed_objects " << context.m_needed_objects.size()); return false; } - auto it = context.m_needed_objects.begin(); for (size_t n = 0; n < span.second; ++n) { - req.blocks.push_back(*it); + req.blocks.push_back(context.m_needed_objects[n]); ++count; - context.m_requested_objects.insert(*it); - auto j = it++; - context.m_needed_objects.erase(j); + context.m_requested_objects.insert(context.m_needed_objects[n]); } + context.m_needed_objects = std::vector(context.m_needed_objects.begin() + span.second, context.m_needed_objects.end()); } context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); @@ -1718,7 +1721,7 @@ skip: { NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg); fluffy_arg.current_blockchain_height = arg.current_blockchain_height; - std::list fluffy_txs; + std::vector fluffy_txs; fluffy_arg.b = arg.b; fluffy_arg.b.txs = fluffy_txs; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 08f167134a..1ee99914dd 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -217,7 +217,7 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getblocks.bin", req, res, r)) return r; - std::list > > bs; + std::vector > > bs; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -226,6 +226,8 @@ namespace cryptonote } size_t pruned_size = 0, unpruned_size = 0, ntxes = 0; + res.blocks.reserve(bs.size()); + res.output_indices.reserve(bs.size()); for(auto& bd: bs) { res.blocks.resize(res.blocks.size()+1); @@ -248,7 +250,9 @@ namespace cryptonote } size_t txidx = 0; ntxes += bd.second.size(); - for (std::list::iterator i = bd.second.begin(); i != bd.second.end(); ++i) + res.blocks.back().txs.reserve(bd.second.size()); + res.output_indices.back().indices.reserve(bd.second.size()); + for (std::vector::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { unpruned_size += i->size(); res.blocks.back().txs.push_back(std::move(*i)); @@ -277,7 +281,7 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r)) return r; - std::list blks; + std::vector blks; if(!m_core.get_alternative_blocks(blks)) { @@ -319,8 +323,8 @@ namespace cryptonote res.status = "Error retrieving block at height " + std::to_string(height); return true; } - std::list txs; - std::list missed_txs; + std::vector txs; + std::vector missed_txs; m_core.get_transactions(blk.tx_hashes, txs, missed_txs); res.blocks.resize(res.blocks.size() + 1); res.blocks.back().block = block_to_blob(blk); @@ -535,8 +539,8 @@ namespace cryptonote } vh.push_back(*reinterpret_cast(b.data())); } - std::list missed_txs; - std::list txs; + std::vector missed_txs; + std::vector txs; bool r = m_core.get_transactions(vh, txs, missed_txs); if(!r) { @@ -557,25 +561,26 @@ namespace cryptonote if(r) { // sort to match original request - std::list sorted_txs; + std::vector sorted_txs; std::vector::const_iterator i; + unsigned txs_processed = 0; for (const crypto::hash &h: vh) { if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end()) { - if (txs.empty()) + if (txs.size() == txs_processed) { res.status = "Failed: internal error - txs is empty"; return true; } // core returns the ones it finds in the right order - if (get_transaction_hash(txs.front()) != h) + if (get_transaction_hash(txs[txs_processed]) != h) { res.status = "Failed: tx hash mismatch"; return true; } - sorted_txs.push_back(std::move(txs.front())); - txs.pop_front(); + sorted_txs.push_back(std::move(txs[txs_processed])); + ++txs_processed; } else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end()) { @@ -586,7 +591,7 @@ namespace cryptonote return true; } sorted_txs.push_back(tx); - missed_txs.remove(h); + missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h)); pool_tx_hashes.insert(h); const std::string hash_string = epee::string_tools::pod_to_hex(h); for (const auto &ti: pool_tx_info) @@ -605,7 +610,7 @@ namespace cryptonote LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool"); } - std::list::const_iterator txhi = req.txs_hashes.begin(); + std::vector::const_iterator txhi = req.txs_hashes.begin(); std::vector::const_iterator vhi = vh.begin(); for(auto& tx: txs) { @@ -1649,10 +1654,10 @@ namespace cryptonote PERF_TIMER(on_flush_txpool); bool failed = false; - std::list txids; + std::vector txids; if (req.txids.empty()) { - std::list pool_txs; + std::vector pool_txs; bool r = m_core.get_pool_transactions(pool_txs); if (!r) { diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 2bd8e05e6d..cdd9fb1792 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -113,7 +113,7 @@ namespace cryptonote struct response { - std::list blocks; + std::vector blocks; uint64_t start_height; uint64_t current_height; std::string status; @@ -191,7 +191,7 @@ namespace cryptonote struct response { - std::list m_block_ids; + std::vector m_block_ids; uint64_t start_height; uint64_t current_height; std::string status; @@ -276,7 +276,7 @@ namespace cryptonote uint64_t total_received; uint64_t total_received_unlocked = 0; // OpenMonero only uint64_t scanned_height; - std::list transactions; + std::vector transactions; uint64_t blockchain_height; uint64_t scanned_block_height; std::string status; @@ -564,7 +564,7 @@ namespace cryptonote { struct request { - std::list txs_hashes; + std::vector txs_hashes; bool decode_as_json; bool prune; @@ -601,11 +601,11 @@ namespace cryptonote struct response { // older compatibility stuff - std::list txs_as_hex; //transactions blobs as hex (old compat) - std::list txs_as_json; //transactions decoded as json (old compat) + std::vector txs_as_hex; //transactions blobs as hex (old compat) + std::vector txs_as_json; //transactions decoded as json (old compat) // in both old and new - std::list missed_tx; //not found transactions + std::vector missed_tx; //not found transactions // new style std::vector txs; @@ -1948,7 +1948,7 @@ namespace cryptonote { struct request { - std::list txids; + std::vector txids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txids) @@ -2165,7 +2165,7 @@ namespace cryptonote { struct request { - std::list txids; + std::vector txids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txids) diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index a562f5b768..39c22bf8dd 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -50,7 +50,7 @@ namespace rpc void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res) { - std::list > > blocks; + std::vector > > blocks; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -62,9 +62,6 @@ namespace rpc res.blocks.resize(blocks.size()); res.output_indices.resize(blocks.size()); - //TODO: really need to switch uses of std::list to std::vector unless - // it's a huge performance concern - auto it = blocks.begin(); uint64_t block_count = 0; @@ -89,7 +86,7 @@ namespace rpc res.error_details = "incorrect number of transactions retrieved for block"; return; } - std::list txs; + std::vector txs; for (const auto& blob : it->second) { txs.resize(txs.size() + 1); @@ -163,10 +160,10 @@ namespace rpc void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res) { - std::list found_txs; - std::list missed_hashes; + std::vector found_txs_vec; + std::vector missed_vec; - bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes); + bool r = m_core.get_transactions(req.tx_hashes, found_txs_vec, missed_vec); // TODO: consider fixing core::get_transactions to not hide exceptions if (!r) @@ -176,20 +173,7 @@ namespace rpc return; } - size_t num_found = found_txs.size(); - - // std::list is annoying - std::vector found_txs_vec - { - std::make_move_iterator(std::begin(found_txs)), - std::make_move_iterator(std::end(found_txs)) - }; - - std::vector missed_vec - { - std::make_move_iterator(std::begin(missed_hashes)), - std::make_move_iterator(std::end(missed_hashes)) - }; + size_t num_found = found_txs_vec.size(); std::vector heights(num_found); std::vector in_pool(num_found, false); @@ -204,7 +188,7 @@ namespace rpc // if any missing from blockchain, check in tx pool if (!missed_vec.empty()) { - std::list pool_txs; + std::vector pool_txs; m_core.get_pool_transactions(pool_txs); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index 1495c845fc..8fff369df4 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -106,7 +106,7 @@ BEGIN_RPC_MESSAGE_CLASS(GetHashesFast); RPC_MESSAGE_MEMBER(uint64_t, start_height); END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(std::list, hashes); + RPC_MESSAGE_MEMBER(std::vector, hashes); RPC_MESSAGE_MEMBER(uint64_t, start_height); RPC_MESSAGE_MEMBER(uint64_t, current_height); END_RPC_MESSAGE_RESPONSE; diff --git a/src/version.cpp.in b/src/version.cpp.in index 1cdb3e67ea..a67a9d104d 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_LOKI_VERSION_TAG "@VERSIONTAG@" -#define DEF_LOKI_VERSION "1.0.1" +#define DEF_LOKI_VERSION "1.0.2" #define DEF_LOKI_RELEASE_NAME "Magic Mani" #define DEF_LOKI_VERSION_FULL DEF_LOKI_VERSION "-" DEF_LOKI_VERSION_TAG diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b15292dd6..cfd294a096 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1066,7 +1066,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & } } //---------------------------------------------------------------------------------------------------- -void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::list &tx_money_got_in_outs, std::vector &outs) const +void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, std::vector &tx_money_got_in_outs, std::vector &outs) const { THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); if (m_multisig) @@ -1088,18 +1088,20 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } - tx_money_got_in_outs.push_back(tx_money_got_in_out{ tx_scan_info.received->index, tx_scan_info.money_transfered, tx.get_unlock_time(i) }); - tx_scan_info.amount = tx_scan_info.money_transfered; - ++num_vouts_received; + + uint64_t unlock_time = tx.get_unlock_time(i); + tx_money_got_in_outs.push_back(tx_money_got_in_out{ tx_scan_info.received->index, tx_scan_info.money_transfered, unlock_time }); + tx_scan_info.amount = tx_scan_info.money_transfered; + tx_scan_info.unlock_time = unlock_time; } //---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) { //ensure device is let in NONE mode in any case - hw::device &hwdev = m_account.get_device(); - + hw::device &hwdev = m_account.get_device(); + boost::unique_lock hwdev_lock (hwdev); - hw::reset_mode rst(hwdev); + hw::reset_mode rst(hwdev); hwdev_lock.unlock(); // In this function, tx (probably) only contains the base information @@ -1107,7 +1109,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); std::vector outs; - std::list tx_money_got_in_outs; // per receiving subaddress index + + // NOTE: tx_scan_info contains the decoded amounts from the transaction destined for us + // tx_money_got_in_outs contains decoded amounts from the transaction, + // that removes amounts from our scanned outputs that got invalidated + // i.e. duplicated key images + std::vector tx_money_got_in_outs; + tx_money_got_in_outs.reserve(tx.vout.size()); crypto::public_key tx_pub_key = null_pkey; std::vector tx_extra_fields; @@ -1121,9 +1129,18 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote size_t pk_index = 0; std::vector tx_scan_info(tx.vout.size()); std::deque output_found(tx.vout.size(), false); + uint64_t total_received_1 = 0; + + // NOTE: This handles the case where you have multiple outputs in the same + // transaction with duplicated output keys. Unlock times is lost when it's + // stored into m_transfers so we cannot determine if the entry in m_transfers + // came from this transaction or a previous transaction. + using unlock_time_t = uint64_t; + std::unordered_map pk_to_unlock_times; while (!tx.vout.empty()) { // if tx.vout is not empty, we loop through all tx pubkeys + outs.clear(); tx_extra_pub_key pub_key_field; if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) @@ -1190,7 +1207,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (tx_scan_info[i].received) { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); + scan_output(tx, tx_pub_key, i, tx_scan_info[i], tx_money_got_in_outs, outs); } } hwdev_lock.unlock(); @@ -1212,7 +1229,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (tx_scan_info[i].received) { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); + scan_output(tx, tx_pub_key, i, tx_scan_info[i], tx_money_got_in_outs, outs); } } hwdev_lock.unlock(); @@ -1228,13 +1245,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote hwdev_lock.lock(); hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); + scan_output(tx, tx_pub_key, i, tx_scan_info[i], tx_money_got_in_outs, outs); hwdev_lock.unlock(); } } } - if(!outs.empty() && num_vouts_received > 0) + if(!outs.empty()) { //good news - got money! take care about it //usually we have only one transfer for user in transaction @@ -1255,10 +1272,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote error::wallet_internal_error, std::string("Unexpected transfer index from public key: ") + "got " + (kit == m_pub_keys.end() ? "" : boost::lexical_cast(kit->second)) + ", m_transfers.size() is " + boost::lexical_cast(m_transfers.size())); + if (kit == m_pub_keys.end()) { + uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; if (!pool) { + pk_to_unlock_times[tx_scan_info[o].in_ephemeral.pub] = tx_scan_info[o].unlock_time; + m_transfers.push_back(boost::value_initialized()); transfer_details& td = m_transfers.back(); td.m_block_height = height; @@ -1269,14 +1290,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_key_image = tx_scan_info[o].ki; td.m_key_image_known = !m_watch_only && !m_multisig; td.m_key_image_partial = m_multisig; - td.m_amount = tx.vout[o].amount; + td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; expand_subaddresses(tx_scan_info[o].received->index); - if (td.m_amount == 0) + if (tx.vout[o].amount == 0) { td.m_mask = tx_scan_info[o].mask; - td.m_amount = tx_scan_info[o].amount; td.m_rct = true; } else if (miner_tx && tx.version >= 2) @@ -1292,7 +1312,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote set_unspent(m_transfers.size()-1); if (!m_multisig && !m_watch_only) m_key_images[td.m_key_image] = m_transfers.size()-1; + m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1; + if (m_multisig) { THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info, @@ -1304,34 +1326,88 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } + total_received_1 += amount; } - else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount) + else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount) { LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first) << " from received " << print_money(tx.vout[o].amount) << " output already exists with " << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " " - << print_money(m_transfers[kit->second].amount()) << ", received output ignored"); + << print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored"); + + auto iter = std::find_if( + tx_money_got_in_outs.begin(), + tx_money_got_in_outs.end(), + [&tx_scan_info,&tx,&o](const tx_money_got_in_out& value) + { + return value.index == tx_scan_info[o].received->index && + value.amount == tx_scan_info[o].amount && + value.unlock_time == tx_scan_info[o].unlock_time; + } + ); + + THROW_WALLET_EXCEPTION_IF(iter == tx_money_got_in_outs.end(), error::wallet_internal_error, "Could not find the output we just added, this should never happen"); + tx_money_got_in_outs.erase(iter); } else { LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first) << " from received " << print_money(tx.vout[o].amount) << " output already exists with " << print_money(m_transfers[kit->second].amount()) << ", replacing with new output"); + // The new larger output replaced a previous smaller one + auto unlock_time_it = pk_to_unlock_times.find(kit->first); + if (unlock_time_it == pk_to_unlock_times.end()) + { + // NOTE: This output previously existed in m_transfers before any + // outputs in this transaction was processed, so we couldn't find. + // That's fine, we don't need to modify tx_money_got_in_outs. + // - 27/09/2018 Doyle + } + else + { + tx_money_got_in_out smaller_output = {}; + smaller_output.unlock_time = unlock_time_it->second; + smaller_output.amount = m_transfers[kit->second].amount(); + smaller_output.index = m_transfers[kit->second].m_subaddr_index; + + auto iter = std::find_if( + tx_money_got_in_outs.begin(), + tx_money_got_in_outs.end(), + [&smaller_output](const tx_money_got_in_out& value) + { + return value.index == smaller_output.index && + value.amount == smaller_output.amount && + value.unlock_time == smaller_output.unlock_time; + } + ); + + // Monero fix - 25/9/2018 rtharp, doyle, maxim + THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > iter->amount, error::wallet_internal_error, "Unexpected values of new and old outputs, new output is meant to be larger"); + THROW_WALLET_EXCEPTION_IF(iter != tx_money_got_in_outs.end(), error::wallet_internal_error, "Unexpected values of new and old outputs, new output is meant to be larger"); + tx_money_got_in_outs.erase(iter); + + } + auto iter = std::find_if( tx_money_got_in_outs.begin(), tx_money_got_in_outs.end(), - [&tx_scan_info,&tx,&o](const tx_money_got_in_out& value) + [&tx_scan_info, &o](const tx_money_got_in_out& value) { return value.index == tx_scan_info[o].received->index && - value.amount == tx.vout[o].amount; + value.amount == tx_scan_info[o].amount && + value.unlock_time == tx_scan_info[o].unlock_time; } ); - if (iter == tx_money_got_in_outs.end()) - LOG_ERROR("Could not find the output we just added, this should never happen"); - else + THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > iter->amount, error::wallet_internal_error, "Unexpected values of new and old outputs, new output is meant to be larger"); + THROW_WALLET_EXCEPTION_IF(iter == tx_money_got_in_outs.end(), error::wallet_internal_error, "Unexpected values of new and old outputs, new output is meant to be larger"); + iter->amount -= m_transfers[kit->second].amount(); + + if (iter->amount == 0) tx_money_got_in_outs.erase(iter); + uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; + uint64_t extra_amount = amount - m_transfers[kit->second].amount(); if (!pool) { transfer_details &td = m_transfers[kit->second]; @@ -1340,14 +1416,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; td.m_txid = txid; - td.m_amount = tx.vout[o].amount; + td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; expand_subaddresses(tx_scan_info[o].received->index); - if (td.m_amount == 0) + if (tx.vout[o].amount == 0) { td.m_mask = tx_scan_info[o].mask; - td.m_amount = tx_scan_info[o].amount; td.m_rct = true; } else if (miner_tx && tx.version >= 2) @@ -1374,6 +1449,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } + total_received_1 += extra_amount; } } } @@ -1497,6 +1573,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id); } + uint64_t total_received_2 = 0; + for (const auto& i : tx_money_got_in_outs) + total_received_2 += i.amount; + + if (total_received_1 != total_received_2) + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "**********************************************************************"); + MCLOG_RED(level, "global", "Consistency failure in amounts received"); + MCLOG_RED(level, "global", "Check transaction " << txid); + MCLOG_RED(level, "global", "**********************************************************************"); + exit(1); + return; + } + for (const auto& i : tx_money_got_in_outs) { payment_details payment; @@ -1661,7 +1752,7 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl bl_id = get_block_hash(bl); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices) +void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices) { cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); @@ -1711,7 +1802,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, o_indices = res.output_indices; } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &hashes) +void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &hashes) { cryptonote::COMMAND_RPC_GET_HASHES_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_HASHES_FAST::response res = AUTO_VAL_INIT(res); @@ -1729,7 +1820,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, hashes = res.m_block_ids; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added) +void wallet2::process_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &o_indices, uint64_t& blocks_added) { size_t current_index = start_height; blocks_added = 0; @@ -1746,13 +1837,13 @@ void wallet2::process_blocks(uint64_t start_height, const std::list round_blocks(threads); std::deque error(threads); size_t blocks_size = blocks.size(); - std::list::const_iterator blocki = blocks.begin(); + std::vector::const_iterator blocki = blocks.begin(); for (size_t b = 0; b < blocks_size; b += threads) { size_t round_size = std::min((size_t)threads, blocks_size - b); tools::threadpool::waiter waiter; - std::list::const_iterator tmpblocki = blocki; + std::vector::const_iterator tmpblocki = blocki; for (size_t i = 0; i < round_size; ++i) { tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block), @@ -1844,7 +1935,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched) refresh(start_height, blocks_fetched, received_money); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error) +void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &o_indices, bool &error) { error = false; @@ -1854,7 +1945,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei // prepend the last 3 blocks, should be enough to guard against a block or two's reorg cryptonote::block bl; - std::list::const_reverse_iterator i = prev_blocks.rbegin(); + std::vector::const_reverse_iterator i = prev_blocks.rbegin(); for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n) { bool ok = cryptonote::parse_and_validate_block_from_blob(i->block, bl); @@ -2121,7 +2212,7 @@ void wallet2::update_pool_state(bool refreshed) //---------------------------------------------------------------------------------------------------- void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history) { - std::list hashes; + std::vector hashes; const uint64_t checkpoint_height = m_checkpoints.get_max_height(); if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) @@ -2149,7 +2240,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, } if (hashes.size() + current_index < stop_height) { drop_from_short_history(short_chain_history, 3); - std::list::iterator right = hashes.end(); + std::vector::iterator right = hashes.end(); // prepend 3 more for (int i = 0; i<3; i++) { right--; @@ -2254,7 +2345,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; uint64_t blocks_start_height; - std::list blocks; + std::vector blocks; std::vector o_indices; bool refreshed = false; @@ -2287,7 +2378,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re { // pull the next set of blocks while we're processing the current one uint64_t next_blocks_start_height; - std::list next_blocks; + std::vector next_blocks; std::vector next_o_indices; bool error = false; if (blocks.empty()) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 5682cb3ebb..ca309b3848 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -213,9 +213,9 @@ namespace tools rct::key mask; uint64_t amount; uint64_t money_transfered; + uint64_t unlock_time; bool error; boost::optional received; - tx_scan_info_t(): money_transfered(0), error(true) {} }; @@ -1116,11 +1116,11 @@ namespace tools void get_short_chain_history(std::list& ids) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; bool clear(); - void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices); - void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &hashes); + void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); + void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history); - void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error); - void process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added); + void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &o_indices, bool &error); + void process_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &o_indices, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const; bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); @@ -1145,7 +1145,7 @@ namespace tools crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector &unused_transfers_indices, const std::vector &unused_dust_indices) const; std::vector get_only_rct(const std::vector &unused_dust_indices, const std::vector &unused_transfers_indices) const; - void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::list &tx_money_got_in_outs, std::vector &outs) const; + void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, std::vector &tx_money_got_in_outs, std::vector &outs) const; void trim_hashchain(); crypto::key_image get_multisig_composite_key_image(size_t n) const; rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set &used_L, std::unordered_set &new_used_L) const; diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index bac394c061..c692e4bde0 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -184,7 +184,7 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob, return true; } -bool tests::proxy_core::handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) +bool tests::proxy_core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { tvc.resize(tx_blobs.size()); size_t i = 0; diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index dfe11f151c..de3b6b327c 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -77,7 +77,7 @@ namespace tests bool have_block(const crypto::hash& id); void get_blockchain_top(uint64_t& height, crypto::hash& top_id); bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); bool handle_uptime_proof(uint64_t timestamp, const crypto::public_key& pubkey, const crypto::signature& sig); void pause_mine(){} @@ -88,7 +88,7 @@ namespace tests cryptonote::Blockchain &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); } bool get_test_drop_download() {return true;} bool get_test_drop_download_height() {return true;} - bool prepare_handle_incoming_blocks(const std::list &blocks) { return true; } + bool prepare_handle_incoming_blocks(const std::vector &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } @@ -96,15 +96,15 @@ namespace tests cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob) const { return false; } bool pool_has_tx(const crypto::hash &txid) const { return false; } - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const { return false; } - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const { return false; } + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return false; } bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version() const { return 0; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } // TODO(loki): Write tests virtual void set_deregister_votes_relayed(const std::vector& votes) {} diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 4840a147dc..ab52baa775 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -143,7 +143,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev m_recipient_account_3 = boost::get(events[3]); m_recipient_account_4 = boost::get(events[4]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(5 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); @@ -160,7 +160,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev CHECK_EQ(MK_COINS(14), get_balance(m_recipient_account_3, chain, mtx)); CHECK_EQ(MK_COINS(3), get_balance(m_recipient_account_4, chain, mtx)); - std::list tx_pool; + std::vector tx_pool; r = c.get_pool_transactions(tx_pool); CHECK_TEST_CONDITION(r); CHECK_EQ(1, tx_pool.size()); @@ -181,7 +181,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind { DEFINE_TESTS_ERROR_CONTEXT("gen_chain_switch_1::check_split_switched"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(6 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); @@ -190,7 +190,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind CHECK_TEST_CONDITION(std::equal(blocks.begin(), it, m_chain_1.begin())); CHECK_TEST_CONDITION(blocks.back() == boost::get(events[24 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_7 - std::list alt_blocks; + std::vector alt_blocks; r = c.get_alternative_blocks(alt_blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(2, c.get_alternative_blocks_count()); @@ -210,7 +210,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind CHECK_EQ(MK_COINS(14), get_balance(m_recipient_account_3, chain, mtx)); CHECK_EQ(MK_COINS(16), get_balance(m_recipient_account_4, chain, mtx)); - std::list tx_pool; + std::vector tx_pool; r = c.get_pool_transactions(tx_pool); CHECK_TEST_CONDITION(r); CHECK_EQ(1, tx_pool.size()); diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 5a035bf06f..989b6df11c 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -45,12 +45,12 @@ class gen_chain_switch_1 : public test_chain_unit_base bool check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector& events); private: - std::list m_chain_1; + std::vector m_chain_1; cryptonote::account_base m_recipient_account_1; cryptonote::account_base m_recipient_account_2; cryptonote::account_base m_recipient_account_3; cryptonote::account_base m_recipient_account_4; - std::list m_tx_pool; + std::vector m_tx_pool; }; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 749ba81005..7e85eb6cbb 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -262,6 +262,79 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size); } +cryptonote::transaction make_registration_tx(std::vector& events, + const cryptonote::account_base& account, + const cryptonote::keypair& service_node_keys, + uint64_t operator_cut, + const std::vector& addresses, + const std::vector& portions, + const cryptonote::block& head) +{ + const auto new_height = cryptonote::get_block_height(head) + 1; + const auto staking_requirement = service_nodes::get_staking_requirement(cryptonote::FAKECHAIN, new_height); + + uint64_t amount = service_nodes::portions_to_amount(portions[0], staking_requirement); + + cryptonote::transaction tx; + const auto unlock_time = new_height + service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN); + + std::vector extra; + add_service_node_pubkey_to_tx_extra(extra, service_node_keys.pub); + + const uint64_t exp_timestamp = time(nullptr) + STAKING_AUTHORIZATION_EXPIRATION_WINDOW; + + crypto::hash hash; + if (!cryptonote::get_registration_hash(addresses, operator_cut, portions, exp_timestamp, hash)) + { + MERROR("Could not make registration hash from addresses and portions"); + return {}; + } + + crypto::signature signature; + crypto::generate_signature(hash, service_node_keys.pub, service_node_keys.sec, signature); + + add_service_node_register_to_tx_extra(extra, addresses, operator_cut, portions, exp_timestamp, signature); + add_service_node_contributor_to_tx_extra(extra, addresses.at(0)); + + construct_tx_to_key( + events, tx, head, account, account, amount, TESTS_DEFAULT_FEE, 9, true /* staking */, extra, unlock_time); + events.push_back(tx); + return tx; +} + +cryptonote::transaction make_deregistration_tx(const std::vector& events, + const cryptonote::account_base& account, + const cryptonote::block& head, + const cryptonote::tx_extra_service_node_deregister& deregister, uint64_t fee) +{ + cryptonote::transaction tx; + + std::vector extra; + const bool full_tx_deregister_made = cryptonote::add_service_node_deregister_to_tx_extra(tx.extra, deregister); + + if (!full_tx_deregister_made) { + MERROR("Could not add deregister to extra"); + return {}; + } + + const uint64_t unlock_time = 0; + const uint64_t amount = 0; + + if (fee) construct_tx_to_key(events, tx, head, account, account, amount, fee, 9, false /* staking */, extra, unlock_time); + + tx.version = cryptonote::transaction::version_3_per_output_unlock_times; + tx.is_deregister = true; + + return tx; +} + +cryptonote::transaction make_default_registration_tx(std::vector& events, + const cryptonote::account_base& account, + const cryptonote::keypair& service_node_keys, + const cryptonote::block& head) +{ + return make_registration_tx(events, account, service_node_keys, 0, { account.get_keys().m_account_address }, { STAKING_PORTIONS }, head); +} struct output_index { const cryptonote::txout_target_v out; @@ -516,6 +589,9 @@ static bool fill_output_entries(const std::vector& out_indices, si bool fill_tx_sources(std::vector& sources, const std::vector& events, const block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix) { + /// Don't fill up sources if the amount is zero + if (amount == 0) return true; + output_index_vec outs; output_vec outs_mine; @@ -539,6 +615,8 @@ bool fill_tx_sources(std::vector& sources, const std::vector& sources, const std::vector& events, bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, const block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, - uint64_t fee, size_t nmix, bool stake, boost::optional reg_info, uint64_t unlock_time) + uint64_t fee, size_t nmix, bool stake, const std::vector& extra, uint64_t unlock_time) { vector sources; vector destinations; @@ -760,37 +838,6 @@ bool construct_tx_to_key(const std::vector& events, cryptonote fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations, &change_amount); tx_destination_entry change_addr{change_amount, from.get_keys().m_account_address, false /* is subaddr */ }; - - std::vector extra; - - if (stake) { - - if (!reg_info) { - LOG_ERROR("Stake tx has not registration info"); - return false; - } - - add_service_node_pubkey_to_tx_extra(extra, reg_info->service_node_keypair.pub); - - const uint64_t exp_timestamp = time(nullptr) + STAKING_AUTHORIZATION_EXPIRATION_WINDOW; - - crypto::hash hash; - bool hashed = cryptonote::get_registration_hash(reg_info->addresses, reg_info->operator_cut, reg_info->portions, exp_timestamp, hash); - if (!hashed) - { - MERROR("Could not make registration hash from addresses and portions"); - return false; - } - - crypto::signature signature; - crypto::generate_signature(hash, reg_info->service_node_keypair.pub, reg_info->service_node_keypair.sec, signature); - - add_service_node_register_to_tx_extra(extra, reg_info->addresses, reg_info->operator_cut, reg_info->portions, exp_timestamp, signature); - add_service_node_contributor_to_tx_extra(extra, reg_info->addresses.at(0)); - - } - - return cryptonote::construct_tx(from.get_keys(), sources, destinations, change_addr, extra, tx, unlock_time, stake, true); } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 519a997b1a..f8bdfd6af5 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -160,6 +160,7 @@ class test_chain_unit_base callbacks_map m_callbacks; }; +using sn_contributor_t = std::pair; class test_generator { @@ -191,8 +192,6 @@ class test_generator bf_hf_version= 1 << 8 }; - using sn_contributor_t = std::pair; - void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; void get_last_n_block_sizes(std::vector& block_sizes, const crypto::hash& head, size_t n) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; @@ -253,7 +252,7 @@ struct register_info { bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, - uint64_t amount, uint64_t fee, size_t nmix, bool stake=false, boost::optional reg_info = boost::none, uint64_t unlock_time=0); + uint64_t amount, uint64_t fee, size_t nmix, bool stake=false, const std::vector& extra = {}, uint64_t unlock_time=0); cryptonote::transaction construct_tx_with_fee(std::vector& events, const cryptonote::block& blk_head, const cryptonote::account_base& acc_from, const cryptonote::account_base& acc_to, @@ -515,7 +514,7 @@ inline bool do_replay_events(std::vector& events) MERROR("Failed to flush txpool"); return false; } - c.get_blockchain_storage().flush_txes_from_pool(std::list(pool_txs.begin(), pool_txs.end())); + c.get_blockchain_storage().flush_txes_from_pool(pool_txs); t_test_class validator; bool ret = replay_events_through_core(c, events, validator); @@ -670,28 +669,24 @@ inline bool do_replay_file(const std::string& filename) #define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) -inline cryptonote::transaction make_registration_tx( - std::vector& events, - const cryptonote::account_base& account, - const cryptonote::keypair& service_node_keys, - uint64_t operator_cut, - const std::vector& addresses, - const std::vector& portions, - const cryptonote::block& head) -{ +cryptonote::transaction make_registration_tx(std::vector& events, + const cryptonote::account_base& account, + const cryptonote::keypair& service_node_keys, + uint64_t operator_cut, + const std::vector& addresses, + const std::vector& portions, + const cryptonote::block& head); - const auto new_height = cryptonote::get_block_height(head) + 1; - const auto staking_requirement = service_nodes::get_staking_requirement(cryptonote::FAKECHAIN, new_height); +cryptonote::transaction make_default_registration_tx(std::vector& events, + const cryptonote::account_base& account, + const cryptonote::keypair& service_node_keys, + const cryptonote::block& head); - uint64_t amount = service_nodes::portions_to_amount(portions[0], staking_requirement); - cryptonote::transaction tx; - boost::optional reg_info = register_info{service_node_keys, portions, operator_cut, addresses}; - const auto unlock_time = new_height + service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN); - construct_tx_to_key(events, tx, head, account, account, amount, TESTS_DEFAULT_FEE, 9, true /* staking */, reg_info, unlock_time); - events.push_back(tx); - return tx; -} +cryptonote::transaction make_deregistration_tx(const std::vector& events, + const cryptonote::account_base& account, + const cryptonote::block& head, + const cryptonote::tx_extra_service_node_deregister& deregister, uint64_t fee = 0); #define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ cryptonote::transaction TX_NAME; \ diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index 405a3398a3..a4a7b1a65b 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -80,7 +80,7 @@ bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const eventV &eve //CHECK_TEST_CONDITION(get_block_reward(0) == get_balance(alice, events, chain, mtx)); // check height - std::list blocks; + std::vector blocks; std::list outs; bool r = c.get_blocks(0, 100, blocks); //c.get_outs(100, outs); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index ac88d20f92..13089f5815 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -92,6 +92,9 @@ int main(int argc, char* argv[]) else if (command_line::get_arg(vm, arg_generate_and_play_test_data)) { GENERATE_AND_PLAY(gen_service_nodes); + GENERATE_AND_PLAY(test_prefer_deregisters); + GENERATE_AND_PLAY(test_zero_fee_deregister); + GENERATE_AND_PLAY(test_deregister_safety_buffer); GENERATE_AND_PLAY(gen_simple_chain_001); GENERATE_AND_PLAY(gen_simple_chain_split_1); GENERATE_AND_PLAY(one_block); diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 7ed62cf6db..c60ea885e9 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -73,7 +73,7 @@ bool gen_double_spend_in_different_chains::check_double_spend(cryptonote::core& { DEFINE_TESTS_ERROR_CONTEXT("gen_double_spend_in_different_chains::check_double_spend"); - std::list block_list; + std::vector block_list; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); CHECK_TEST_CONDITION(r); diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 0c58fb0181..d021470657 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -64,7 +64,7 @@ bool gen_double_spend_base::check_block_verification_context(cons template bool gen_double_spend_base::mark_last_valid_block(cryptonote::core& c, size_t /*ev_index*/, const std::vector& /*events*/) { - std::list block_list; + std::vector block_list; bool r = c.get_blocks(c.get_current_blockchain_height() - 1, 1, block_list); CHECK_AND_ASSERT_MES(r, false, "core::get_blocks failed"); m_last_valid_block = block_list.back(); @@ -96,7 +96,7 @@ bool gen_double_spend_base::check_double_spend(cryptonote::core& } CHECK_NOT_EQ(invalid_index_value, m_invalid_block_index); - std::list block_list; + std::vector block_list; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); CHECK_TEST_CONDITION(r); CHECK_TEST_CONDITION(m_last_valid_block == block_list.back()); diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index b438a03896..a5477b1ed9 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -101,7 +101,7 @@ bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index m_bob_account = boost::get(events[3]); m_alice_account = boost::get(events[4]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -119,7 +119,7 @@ bool gen_ring_signature_1::check_balances_2(cryptonote::core& c, size_t ev_index { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_2"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -183,7 +183,7 @@ bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index m_bob_account = boost::get(events[1]); m_alice_account = boost::get(events[2]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -201,7 +201,7 @@ bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_2"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -293,7 +293,7 @@ bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_ind m_bob_account = boost::get(events[1]); m_alice_account = boost::get(events[1 + m_test_size]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -318,7 +318,7 @@ bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_ind { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_2"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); diff --git a/tests/core_tests/service_nodes.cpp b/tests/core_tests/service_nodes.cpp index 1e2af044b8..5e8d6f20e1 100644 --- a/tests/core_tests/service_nodes.cpp +++ b/tests/core_tests/service_nodes.cpp @@ -36,15 +36,497 @@ using namespace std; using namespace epee; using namespace cryptonote; +static const std::pair service_node_keys[] = { + { "1c09e237e7f451ee5df4ea6db970ba0b17597d788106df0c3b436a8ff8c94806", + "06d471cbb6d67cae5002f18dd1c46ae6307b474b6fad4d720bd639513438f219" }, + { "8336af81ddffa292122064b8ffa26977b76efdcfa441604b6013f43dd8e90101", + "fe979e53de1f82458f000d84bb8bfb6013153a7811a19a5b4d534794cb87c311" }, + { "b0169fced37d767a33a975ae3f8f7a12c3034e654024c62ce5de2a6e163b5b05", + "beca119a8a5581d0209a155f974e5331f08df48dbb3e11399f87867aae1973cc" }, + { "ee451af460163d7940a79f48890509ebc3efb5aad014c21964707dea8c604d0f", + "7897152e723644d325eb10e677197cfebd630c4e6be6908330ec1f47094e8b35" }, + { "d0704bb98564800a5554c400da220b0265b338c37b80a5cd8af4b010b57cb301", + "50483a668bf25d989a25d87f233835df3357c7e72000762529984d8c111a2c52" }, + { "878b8c4560499fdb929ebd5d9bd124603bcc7f3495c9581edcbdf7360fa51706", + "467ba9a442a9d2dfa8e4f9f07dd49e40554b299a9144f3fef392e8c5e1038cd7" }, + { "9dae6882b90ddc63b1832500fcccd7d027ea1081db9127f27316d16708b96c0a", + "151c6ccbfd05465d2752d6794dc2892381a284750597b8b321b712036cda3c4a" }, + { "91bab98d4c9bfbe39c84effb68cb7a68020f16583f0eca0e60c1cae1cf13920e", + "b16768d1a95d653310941c75e9ab9dcb6c79019195def04fd2279f67e32a4b7b" }, + { "a0a5daa0ddd972551147c48a223800c27a38eb156ff97d9c0e7595089bc04203", + "090788a7faeab266a3fe1d755df1e58a989e676ea5d94a2c748b5b94ad30ee30" }, + { "992f5da1cd19727a03becaacd21251485ddf4ffaa827b44d6c5ff9a2ec1d5402", + "f20624bc19ec1486ad49c0994faf4523b471fa53bf0bf856d142cdaf13b93165" }, + { "1a87c618ff444e324a0212e26e14af65670d566363b204300102afcaa085590c", + "f3bd64b0ba024ad8068b2e96213e6b7d354d13f468a2775e2fd22a85f352aa73" }, + { "8b079185e6e1df1b61d18a7b71fdea149e6555271fae055c28e3fe7174abc107", + "3b4d8725de3a16615358dad594114224a9fff21d5c6f4680528840b17e19e02b" }, + { "a01f14e058a0c414577e978f3e290cf662ae670fdee6699c57b14c9c4a452706", + "ff099e244736503eb32b6a869674c9bb1f2a29590026f2680968cbae9c466a41" }, + { "3c9d7ecac68bb506c0eb4b1de21839f14afd59ae292b55ce7c104322eca4e308", + "2a1d51e4f6f36ab79f1c642a9ab109eb34527fd1e4e9f130127662ca3c8a0fb9" }, + { "af7e04cf1f0816c66ee0b72fcad40e577bbffd10a391245c2b5b29c87a7a1304", + "0a25fae0eeb6418a291d0e93410510f2cb66b1cfa897398c9196737fbe19f35d" }, + { "b861b35dc2479652c37d5afcc4231b6363172ee73e4bf88b52a1a33e52490f0f", + "03cc9506ecf7acd5b13bbb5d3b59e4d4e3ff408f70622b9db131d0c120b841d0" }, + { "834b35e68c7fc5bcf56ed772bb5d72e08dcc38b987f16e52d79e9df2f9b7ec00", + "5f40d0c72036a85163ea97a23ef67b8c52c06f5eee59bfb03db1ff07b761604c" }, + { "441e071ba14153d0a931f9eaa7ee4dbae4306d2921865f2f3a7cc625b4fbc60b", + "836bbceec1b80f82e2edb43e3583cbc3a09925e16c5c66a08b94899de5fb59ce" }, + { "9c6e82db18c537c333b28b515cb25708ed42e22f3c9948511890389ea79e3d08", + "2d848c307f1e734af4dd1f09cc132038d6e0a5fc8223e4548094a3e354f43aec" }, + { "3f4472d29b95c742ad092ca603f2b888f7a699b000d9fdb7ca003ee86218cf02", + "356cf984c6a9efd2ca965bfdbb2d94d2906113e1a7acabf5dbda6a4911bd43fc" }, + { "78b784f007f7d1f80c6b6da2627b4f47c37f88ac96c7a0006e02727cd2b7770b", + "b1f24cf86553b1fc72ed5a9a09286200cdf62dabc52683ba61ee95b6d9b64679" }, + { "c8489fe5119e2ea00116a33ac38ec0636312dadeffe3a85967ae5243afd3a005", + "9d6bbe3c47d0994bd3383e88e84f1ab4daac4fd28843490ce5d101f1b52d4875" }, + { "7b9a1077d3ba10833b596d4c254ae2dc26c8b801807a3c61f78e8055aba19a04", + "b88af38e69c4fd44d95c3f41b551d9c41edb834331be2e85a3693f7a3eaa2004" }, + { "8376b213f06785788bde97027caab42384f2249d272c4eefc15ef9cc02efa302", + "5e2033fe1a5d4bf21952934b2a4b005d57075861baafc26d98c7e090d8ecbcac" }, + { "fad6778760c4f357559131f77ea3f2fb7e92cfb660bad72c0a37b29f8e37030c", + "30660777ff79db10e735be0c79732d0cbd15d5812a59e3467fb6b4a7bc71a601" } +}; + +static const auto SN_KEYS_COUNT = sizeof(service_node_keys) / sizeof(service_node_keys[0]); + +cryptonote::keypair get_static_keys(size_t idx) { + + if (idx >= SN_KEYS_COUNT) { MERROR("out of bounds"); throw std::exception(); } + + cryptonote::keypair keypair; + + epee::string_tools::hex_to_pod(service_node_keys[idx].first, keypair.sec); + epee::string_tools::hex_to_pod(service_node_keys[idx].second, keypair.pub); + + return keypair; +} + +struct last_reward_point { + uint64_t height; + uint64_t priority; +}; + +bool operator<(const last_reward_point& lhs, const last_reward_point& rhs) { + if (lhs.height != rhs.height) { + return lhs.height < rhs.height; + } + + return lhs.priority < rhs.priority; +} + +struct sn_registration { + uint64_t valid_until; /// block height + cryptonote::keypair keys; + sn_contributor_t contribution; + last_reward_point last_reward; +}; + +/// Service node and its index +struct sn_idx { + crypto::public_key sn_pk; + /// index in the sorted list of service nodes for a particular block + size_t idx_in_quorum; +}; + +struct QuorumState { + std::vector voters; + std::vector to_test; +}; + +class sn_list +{ + std::vector sn_owners_; + +public: + + const sn_registration& at(size_t idx) const { return sn_owners_.at(idx); } + + const boost::optional find_registration(const crypto::public_key& pk) const; + + void expire_old(uint64_t height); + + const boost::optional get_winner_pk(uint64_t height); + + size_t size() const { return sn_owners_.size(); } + + void add_registrations(const std::vector& regs); + + void remove_node(const crypto::public_key& pk); +}; + +inline void sn_list::remove_node(const crypto::public_key& pk) +{ + const auto it = + std::find_if(sn_owners_.begin(), sn_owners_.end(), [pk](const sn_registration& sn) { return sn.keys.pub == pk; }); + if (it != sn_owners_.end()) sn_owners_.erase(it); else abort(); +} + +inline void sn_list::add_registrations(const std::vector& regs) +{ + sn_owners_.insert(sn_owners_.begin(), regs.begin(), regs.end()); + + std::sort(sn_owners_.begin(), sn_owners_.end(), + [](const sn_registration &a, const sn_registration &b) { + return memcmp(reinterpret_cast(&a.keys.pub), reinterpret_cast(&b.keys.pub), + sizeof(a.keys.pub)) < 0; + }); +} + +inline void sn_list::expire_old(uint64_t height) +{ + /// remove_if is stable, no need for re-sorting + const auto new_end = std::remove_if( + sn_owners_.begin(), sn_owners_.end(), [height](const sn_registration& reg) { return reg.valid_until < height; }); + + sn_owners_.erase(new_end, sn_owners_.end()); +} + +inline const boost::optional sn_list::find_registration(const crypto::public_key& pk) const +{ + const auto it = + std::find_if(sn_owners_.begin(), sn_owners_.end(), [pk](const sn_registration& sn) { return sn.keys.pub == pk; }); + + if (it == sn_owners_.end()) return boost::none; + + return *it; +} + +inline const boost::optional sn_list::get_winner_pk(uint64_t height) +{ + if (sn_owners_.empty()) return boost::none; + + auto it = + std::min_element(sn_owners_.begin(), sn_owners_.end(), [](const sn_registration& lhs, const sn_registration& rhs) { + return lhs.last_reward < rhs.last_reward; + }); + + it->last_reward.height = height; + + return it->keys.pub; +} + +class dereg_tx_builder; + +class linear_chain_generator +{ + + private: + test_generator gen_; + std::vector& events_; + std::vector blocks_; + + sn_list sn_list_; + + /// keep new registrations here until the next block + std::vector registration_buffer_; + + cryptonote::account_base first_miner_; + + public: + linear_chain_generator(std::vector &events) + : gen_(), events_(events) + { } + + uint64_t height() const { return get_block_height(blocks_.back()); } + + cryptonote::account_base create_account(); + + void create_genesis_block(); + + void create_block(const std::vector& txs = {}); + + cryptonote::block create_block_on_fork(const cryptonote::block& prev, const std::vector& txs = {}); + + void rewind_until_v9(); + void rewind_blocks_n(int n); + void rewind_blocks(); + + cryptonote::transaction create_tx(const cryptonote::account_base& miner, + const cryptonote::account_base& acc, + uint64_t amount, + uint64_t fee = TESTS_DEFAULT_FEE); + + cryptonote::transaction create_registration_tx(const cryptonote::account_base& acc, const cryptonote::keypair& sn_keys); + + cryptonote::transaction create_registration_tx(); + + const cryptonote::account_base& first_miner() const { return first_miner_; } + + /// Note: should be carefull with returing a reference to vector elements + const cryptonote::block& chain_head() const { return blocks_.back(); } + + /// get a copy of the service node list + sn_list get_sn_list() const { return sn_list_; } + + void set_sn_list(const sn_list& list) { sn_list_ = list; } + + QuorumState get_quorum_idxs(uint64_t height) const; + + cryptonote::transaction create_deregister_tx(const crypto::public_key& pk, uint64_t height, const std::vector& voters, uint64_t fee = 0) const; + + dereg_tx_builder build_deregister(const crypto::public_key& pk); + + crypto::public_key get_test_pk(uint32_t idx) const; + + boost::optional get_idx_in_tested(const crypto::public_key& pk, uint64_t height) const; + + void deregister(const crypto::public_key& pk) { + sn_list_.remove_node(pk); + } + +}; + +class dereg_tx_builder { + + linear_chain_generator& gen_; + const crypto::public_key& pk_; + + /// the height at which `pk_` is to be found + boost::optional height_ = boost::none; + + boost::optional fee_ = boost::none; + + boost::optional&> voters_ = boost::none; + + public: + dereg_tx_builder(linear_chain_generator& gen, const crypto::public_key& pk) + : gen_(gen), pk_(pk) + {} + + dereg_tx_builder&& with_height(uint64_t height) { + height_ = height; + return std::move(*this); + } + + dereg_tx_builder&& with_fee(uint64_t fee) { + fee_ = fee; + return std::move(*this); + } + + dereg_tx_builder&& with_voters(const std::vector& voters) + { + voters_ = voters; + return std::move(*this); + } + + cryptonote::transaction build() + { + const auto height = height_ ? *height_ : gen_.height(); + const auto voters = voters_ ? *voters_ : gen_.get_quorum_idxs(height).voters; + return gen_.create_deregister_tx(pk_, height, voters, fee_.value_or(0)); + } + +}; + +dereg_tx_builder linear_chain_generator::build_deregister(const crypto::public_key& pk) +{ + return dereg_tx_builder(*this, pk); +} + +cryptonote::account_base linear_chain_generator::create_account() +{ + cryptonote::account_base account; + account.generate(); + events_.push_back(account); + return account; +} + +void linear_chain_generator::create_genesis_block() +{ + constexpr uint64_t ts_start = 1338224400; + first_miner_.generate(); + cryptonote::block gen_block; + gen_.construct_block(gen_block, first_miner_, ts_start); + events_.push_back(gen_block); + blocks_.push_back(gen_block); +} + +void linear_chain_generator::create_block(const std::vector& txs) +{ + const auto blk = create_block_on_fork(blocks_.back(), txs); + blocks_.push_back(blk); +} + +void linear_chain_generator::rewind_until_v9() +{ + gen_.set_hf_version(8); + create_block(); + gen_.set_hf_version(9); + create_block(); +} + +void linear_chain_generator::rewind_blocks_n(int n) +{ + for (auto i = 0; i < n; ++i) { + create_block(); + } +} + +void linear_chain_generator::rewind_blocks() +{ + rewind_blocks_n(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); +} + +cryptonote::block linear_chain_generator::create_block_on_fork(const cryptonote::block& prev, + const std::vector& txs) +{ + + const auto height = get_block_height(prev) + 1; + + const auto& winner_pk = sn_list_.get_winner_pk(height); + + const auto& sn_pk = winner_pk ? *winner_pk : crypto::null_pkey; + + std::vector contribs = { { { crypto::null_pkey, crypto::null_pkey }, STAKING_PORTIONS } }; + + if (winner_pk) { + const auto& reg = sn_list_.find_registration(*winner_pk); + if (reg) { + contribs = { reg->contribution }; + } + } + + cryptonote::block blk; + gen_.construct_block(blk, prev, first_miner_, { txs.begin(), txs.end() }, sn_pk, contribs); + events_.push_back(blk); + + /// now we can add sn from the buffer to be used in consequent nodes + sn_list_.add_registrations(registration_buffer_); + registration_buffer_.clear(); + sn_list_.expire_old(height); + + return blk; +} + +QuorumState linear_chain_generator::get_quorum_idxs(uint64_t height) const +{ + const auto block = blocks_.at(height); + + if (sn_list_.size() <= service_nodes::QUORUM_SIZE) { + std::cerr << "Not enough service nodes\n"; + return {}; + } + + std::vector pub_keys_indexes; + { + uint64_t seed = 0; + const crypto::hash block_hash = cryptonote::get_block_hash(block); + std::memcpy(&seed, block_hash.data, std::min(sizeof(seed), sizeof(block_hash.data))); + + pub_keys_indexes.resize(sn_list_.size()); + for (size_t i = 0; i < pub_keys_indexes.size(); i++) { + pub_keys_indexes[i] = i; + } + + service_nodes::loki_shuffle(pub_keys_indexes, seed); + } + + QuorumState quorum; + + for (auto i = 0u; i < service_nodes::QUORUM_SIZE; ++i) { + quorum.voters.push_back({ sn_list_.at(pub_keys_indexes[i]).keys.pub, i }); + } + + for (auto i = service_nodes::QUORUM_SIZE; i < pub_keys_indexes.size(); ++i) { + quorum.to_test.push_back({ sn_list_.at(pub_keys_indexes[i]).keys.pub, i }); + } + + return quorum; +} + +cryptonote::transaction linear_chain_generator::create_tx(const cryptonote::account_base& miner, + const cryptonote::account_base& acc, + uint64_t amount, + uint64_t fee) +{ + cryptonote::transaction t; + construct_tx_to_key(events_, t, blocks_.back(), miner, acc, amount, fee, 9); + events_.push_back(t); + return t; +} +cryptonote::transaction linear_chain_generator::create_registration_tx(const cryptonote::account_base& acc, + const cryptonote::keypair& sn_keys) +{ + const sn_contributor_t contr = { acc.get_keys().m_account_address, STAKING_PORTIONS }; + const uint32_t expires = height() + service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN); + + const auto reg_idx = registration_buffer_.size(); + registration_buffer_.push_back({ expires, sn_keys, contr, { height(), reg_idx } }); + return make_default_registration_tx(events_, acc, sn_keys, blocks_.back()); +} + +cryptonote::transaction linear_chain_generator::create_registration_tx() +{ + const auto sn_keys = keypair::generate(hw::get_device("default")); + + return create_registration_tx(first_miner_, sn_keys); +} + +cryptonote::transaction linear_chain_generator::create_deregister_tx(const crypto::public_key& pk, + uint64_t height, + const std::vector& voters, + uint64_t fee) const +{ + + cryptonote::tx_extra_service_node_deregister deregister; + deregister.block_height = height; + + const auto idx = get_idx_in_tested(pk, height); + + if (!idx) { MERROR("service node could not be found in the servcie node list"); throw std::exception(); } + + deregister.service_node_index = *idx; /// idx inside nodes to test + + /// need to create MIN_VOTES_TO_KICK_SERVICE_NODE (7) votes + for (const auto voter : voters) { + + const auto reg = sn_list_.find_registration(voter.sn_pk); + + if (!reg) return {}; + + const auto pk = reg->keys.pub; + const auto sk = reg->keys.sec; + const auto signature = + loki::service_node_deregister::sign_vote(deregister.block_height, deregister.service_node_index, pk, sk); + + deregister.votes.push_back({ signature, (uint32_t)voter.idx_in_quorum }); + } + + const auto deregister_tx = make_deregistration_tx(events_, first_miner_, blocks_.back(), deregister, fee); + + events_.push_back(deregister_tx); + + return deregister_tx; +} + +crypto::public_key linear_chain_generator::get_test_pk(uint32_t idx) const +{ + const auto& to_test = get_quorum_idxs(height()).to_test; + + return to_test.at(idx).sn_pk; +} + +boost::optional linear_chain_generator::get_idx_in_tested(const crypto::public_key& pk, uint64_t height) const +{ + const auto& to_test = get_quorum_idxs(height).to_test; + + for (const auto& sn : to_test) { + if (sn.sn_pk == pk) return sn.idx_in_quorum - service_nodes::QUORUM_SIZE; + } + + return boost::none; +} + +//----------------------------------------------------------------------------------------------------- +//---------------------------------- Generate Service Nodes ------------------------------------------- +//----------------------------------------------------------------------------------------------------- gen_service_nodes::gen_service_nodes() { /// NOTE: we don't generate random keys here, because the verification will call its own constructor - constexpr char pub_key_str[] = "cf6ae1d4e902f7a85af58d6069c29f09702e25fd07cf28d359e64401002db2a1"; - constexpr char sec_key_str[] = "ead4cc692c4237f62f9cefaf5e106995b2dda79a29002a546876f9ee7abcc203"; - - epee::string_tools::hex_to_pod(pub_key_str, m_alice_service_node_keys.pub); - epee::string_tools::hex_to_pod(sec_key_str, m_alice_service_node_keys.sec); + m_alice_service_node_keys = get_static_keys(0); REGISTER_CALLBACK("check_registered", gen_service_nodes::check_registered); REGISTER_CALLBACK("check_expired", gen_service_nodes::check_expired); @@ -52,37 +534,33 @@ gen_service_nodes::gen_service_nodes() //----------------------------------------------------------------------------------------------------- bool gen_service_nodes::generate(std::vector &events) const { - uint64_t ts_start = 1338224400; - GENERATE_ACCOUNT(miner); + linear_chain_generator gen(events); + gen.create_genesis_block(); // 1 - MAKE_GENESIS_BLOCK(events, blk_0, miner, ts_start); // 1 - MAKE_ACCOUNT(events, alice); + const auto miner = gen.first_miner(); + const auto alice = gen.create_account(); - generator.set_hf_version(8); - MAKE_NEXT_BLOCK(events, blk_a, blk_0, miner); // 2 + gen.rewind_until_v9(); // 3 + gen.rewind_blocks_n(10); // 13 - generator.set_hf_version(9); - MAKE_NEXT_BLOCK(events, blk_b, blk_a, miner); // 3 + gen.rewind_blocks(); // 13 + N + const auto tx0 = gen.create_tx(miner, alice, MK_COINS(101)); + gen.create_block({tx0}); // 14 + N - REWIND_BLOCKS_N(events, blk_c, blk_b, miner, 10); // 13 + gen.rewind_blocks(); // 14 + 2N - REWIND_BLOCKS(events, blk_1, blk_c, miner); // 13 + N + const auto reg_tx = gen.create_registration_tx(alice, m_alice_service_node_keys); - MAKE_TX(events, tx_0, miner, alice, MK_COINS(101), blk_1); - MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1, miner, tx_0); // 14 + N + gen.create_block({reg_tx}); // 15 + 2N - REWIND_BLOCKS(events, blk_3, blk_2, miner); // 14 + 2N - - cryptonote::transaction alice_registration = - make_registration_tx(events, alice, m_alice_service_node_keys, 0, { alice.get_keys().m_account_address }, { STAKING_PORTIONS }, blk_3); + DO_CALLBACK(events, "check_registered"); - MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner, alice_registration); // 15 + 2N + for (auto i = 0u; i < service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN); ++i) { + gen.create_block(); + } // 15 + 2N + M - DO_CALLBACK(events, "check_registered"); - std::vector sn_info = {{alice.get_keys().m_account_address, STAKING_PORTIONS}}; - REWIND_BLOCKS_N_V2(events, blk_5, blk_4, miner, service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN), m_alice_service_node_keys.pub, sn_info); // 15 + 2N + M DO_CALLBACK(events, "check_expired"); return true; @@ -94,7 +572,7 @@ bool gen_service_nodes::check_registered(cryptonote::core& c, size_t ev_index, c cryptonote::account_base alice = boost::get(events[1]); - std::list block_list; + std::vector block_list; bool r = c.get_blocks(0, 15 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); CHECK_TEST_CONDITION(r); std::vector chain; @@ -113,7 +591,7 @@ bool gen_service_nodes::check_registered(cryptonote::core& c, size_t ev_index, c return true; } - +//----------------------------------------------------------------------------------------------------- bool gen_service_nodes::check_expired(cryptonote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_service_nodes::check_expired"); @@ -122,7 +600,7 @@ bool gen_service_nodes::check_expired(cryptonote::core& c, size_t ev_index, cons const auto stake_lock_time = service_nodes::get_staking_requirement_lock_blocks(cryptonote::FAKECHAIN); - std::list block_list; + std::vector block_list; bool r = c.get_blocks(0, 15 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + stake_lock_time, block_list); CHECK_TEST_CONDITION(r); @@ -141,4 +619,233 @@ bool gen_service_nodes::check_expired(cryptonote::core& c, size_t ev_index, cons return true; +} +//----------------------------------------------------------------------------------------------------- +//------------------------------ Test Blocks Prefer Deregisters --------------------------------------- +//----------------------------------------------------------------------------------------------------- +test_prefer_deregisters::test_prefer_deregisters() { + REGISTER_CALLBACK("check_prefer_deregisters", test_prefer_deregisters::check_prefer_deregisters); +} +//----------------------------------------------------------------------------------------------------- +bool test_prefer_deregisters::generate(std::vector &events) +{ + linear_chain_generator gen(events); + + gen.create_genesis_block(); + + const auto miner = gen.first_miner(); + const auto alice = gen.create_account(); + + gen.rewind_until_v9(); + + /// give miner some outputs to spend and unlock them + gen.rewind_blocks_n(60); + gen.rewind_blocks(); + + /// register 12 random service nodes + std::vector reg_txs; + for (auto i = 0; i < 12; ++i) { + const auto tx = gen.create_registration_tx(); + reg_txs.push_back(tx); + } + + gen.create_block(reg_txs); + + /// generate transactions to fill up txpool entirely + for (auto i = 0u; i < 45; ++i) { + gen.create_tx(miner, alice, MK_COINS(1), TESTS_DEFAULT_FEE * 100); + } + + /// generate two deregisters + const auto pk1 = gen.get_test_pk(0); + const auto pk2 = gen.get_test_pk(1); + + gen.build_deregister(pk1).build(); + gen.build_deregister(pk2).build(); + + DO_CALLBACK(events, "check_prefer_deregisters"); + + return true; +} +//----------------------------------------------------------------------------------------------------- +bool test_prefer_deregisters::check_prefer_deregisters(cryptonote::core& c, size_t ev_index, const std::vector &events) +{ + + DEFINE_TESTS_ERROR_CONTEXT("test_prefer_deregisters::check_prefer_deregisters"); + + const auto tx_count = c.get_pool_transactions_count(); + + cryptonote::block full_blk; + { + difficulty_type diffic; + uint64_t height; + uint64_t expected_reward; + cryptonote::blobdata extra_nonce; + const auto miner = boost::get(events[1]); + c.get_block_template(full_blk, miner.get_keys().m_account_address, diffic, height, expected_reward, extra_nonce); + } + + map_hash2tx_t mtx; + { + std::vector chain; + CHECK_TEST_CONDITION(find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[0])))); + } + + const auto deregister_count = + std::count_if(full_blk.tx_hashes.begin(), full_blk.tx_hashes.end(), [&mtx](const crypto::hash& tx_hash) { + return mtx[tx_hash]->is_deregister; + }); + + /// test that there are more transactions in tx pool + CHECK_TEST_CONDITION(tx_count > full_blk.tx_hashes.size()); + /// test that all 2 deregister tx are in the block + CHECK_EQ(deregister_count, 2); + + return true; + +} +//----------------------------------------------------------------------------------------------------- +//------------------------------ Test Zero-Fee Deregisters Fail --------------------------------------- +//----------------------------------------------------------------------------------------------------- +test_zero_fee_deregister::test_zero_fee_deregister() { + // Test that deregister that are non-zero fee don't get accepted onto the blockchain or anywhere. + REGISTER_CALLBACK_METHOD(test_zero_fee_deregister, mark_invalid_tx); +} +//----------------------------------------------------------------------------------------------------- +bool test_zero_fee_deregister::generate(std::vector &events) +{ + + linear_chain_generator gen(events); + + gen.create_genesis_block(); + + gen.rewind_until_v9(); + + /// give miner some outputs to spend and unlock them + gen.rewind_blocks_n(20); + gen.rewind_blocks(); + + /// register 11 random service nodes + std::vector reg_txs; + for (auto i = 0; i < 11; ++i) { + const auto tx = gen.create_registration_tx(); + reg_txs.push_back(tx); + } + + gen.create_block(reg_txs); + /// create a deregister with a fee, should fail + DO_CALLBACK(events, "mark_invalid_tx"); + const auto pk = gen.get_test_pk(0); + gen.build_deregister(pk).with_fee(MK_COINS(1)).build(); + + return true; +} +//----------------------------------------------------------------------------------------------------- +//-------------------------------- Test Deregister Safety Buffer -------------------------------------- +//----------------------------------------------------------------------------------------------------- + +// Test if a person registers onto the network and they get included in the nodes to test (i.e. heights 0, 5, 10). If +// they get dereigstered in the nodes to test, height 5, and rejoin the network before height 10 (and are in the nodes +// to test), they don't get deregistered. This scenario will most likely happen with an autostaker. +test_deregister_safety_buffer::test_deregister_safety_buffer() { + REGISTER_CALLBACK_METHOD(test_deregister_safety_buffer, mark_invalid_tx); +} +//----------------------------------------------------------------------------------------------------- +bool test_deregister_safety_buffer::generate(std::vector &events) +{ + DEFINE_TESTS_ERROR_CONTEXT("test_deregister_safety_buffer::generate"); + + linear_chain_generator gen(events); + + gen.create_genesis_block(); + + const auto miner = gen.first_miner(); + + gen.rewind_until_v9(); + + /// give miner some outputs to spend and unlock them + gen.rewind_blocks_n(40); + gen.rewind_blocks(); + + /// save generated keys here + std::vector used_sn_keys; + + /// register 21 random service nodes + std::vector reg_txs; + + constexpr auto SERVICE_NODES_NEEDED = service_nodes::QUORUM_SIZE * 2 + 1; + static_assert(SN_KEYS_COUNT >= SERVICE_NODES_NEEDED, "not enough pre-computed service node keys"); + + for (auto i = 0u; i < SERVICE_NODES_NEEDED; ++i) + { + const auto sn_keys = get_static_keys(i); + used_sn_keys.push_back(sn_keys); + + const auto tx = gen.create_registration_tx(miner, sn_keys); + reg_txs.push_back(tx); + } + + gen.create_block({reg_txs}); + + const auto heightA = gen.height(); + + /// Note: would've used sets, but need `less` for public keys etc. + std::vector sn_set_A; + + for (const auto& sn : gen.get_quorum_idxs(heightA).to_test) { + sn_set_A.push_back(sn.sn_pk); + } + + /// create 5 blocks and find public key to be tested twice + for (auto i = 0; i < 5; ++i) { + gen.create_block(); + } + + const auto heightB = gen.height(); + + std::vector sn_set_B; + + for (const auto& sn : gen.get_quorum_idxs(heightB).to_test) { + sn_set_B.push_back(sn.sn_pk); + } + + /// Guaranteed to contain at least one element + std::vector tested_twice; + + for (const auto& sn : sn_set_A) { + if (std::find(sn_set_B.begin(), sn_set_B.end(), sn) != sn_set_B.end()) { + tested_twice.push_back(sn); + } + } + + /// Pick a node to deregister + const auto pk = tested_twice.at(0); + + /// Deregister for heightA + { + const auto dereg_tx = gen.build_deregister(pk).with_height(heightA).build(); + gen.create_block({dereg_tx}); + } + + /// Register the node again + { + // find secret key for pk: + auto it = std::find_if(used_sn_keys.begin(), used_sn_keys.end(), [&pk](const cryptonote::keypair& keys) { + return keys.pub == pk; + }); + + if (it == used_sn_keys.end()) { MERROR("SN pub key not found in generated keys"); return false; }; + + const auto tx = gen.create_registration_tx(miner, *it); + gen.create_block({tx}); + } + + /// Try to deregister the node again for heightB (should fail) + { + DO_CALLBACK(events, "mark_invalid_tx"); + const auto dereg_tx = gen.build_deregister(pk).with_height(heightB).build(); + } + + return true; + } \ No newline at end of file diff --git a/tests/core_tests/service_nodes.h b/tests/core_tests/service_nodes.h index c2c277323d..670b9fe96f 100644 --- a/tests/core_tests/service_nodes.h +++ b/tests/core_tests/service_nodes.h @@ -51,4 +51,87 @@ struct get_test_options { const cryptonote::test_options test_options = { hard_forks }; +}; + +class test_prefer_deregisters : public test_chain_unit_base +{ +public: + test_prefer_deregisters(); + bool generate(std::vector &events); + bool check_prefer_deregisters(cryptonote::core& c, size_t ev_index, const std::vector &events); +}; + +template<> +struct get_test_options { + const std::pair hard_forks[3] = {std::make_pair(7, 0), std::make_pair(8, 1), std::make_pair(9, 2)}; + const cryptonote::test_options test_options = { + hard_forks + }; +}; + +class test_zero_fee_deregister : public test_chain_unit_base +{ + size_t m_invalid_tx_index; + +public: + test_zero_fee_deregister(); + bool generate(std::vector &events); + + bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + { + m_invalid_tx_index = ev_index + 1; + return true; + } + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, + bool tx_added, + size_t event_idx, + const cryptonote::transaction& /*tx*/) + { + if (m_invalid_tx_index == event_idx) + return tvc.m_verifivation_failed; + else + return !tvc.m_verifivation_failed && tx_added; + } +}; + +template<> +struct get_test_options { + const std::pair hard_forks[3] = {std::make_pair(7, 0), std::make_pair(8, 1), std::make_pair(9, 2)}; + const cryptonote::test_options test_options = { + hard_forks + }; +}; + +class test_deregister_safety_buffer : public test_chain_unit_base +{ + size_t m_invalid_tx_index; + +public: + + test_deregister_safety_buffer(); + bool generate(std::vector& events); + + bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + { + m_invalid_tx_index = ev_index + 1; + return true; + } + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, + bool tx_added, + size_t event_idx, + const cryptonote::transaction& /*tx*/) + { + if (m_invalid_tx_index == event_idx) + return tvc.m_verifivation_failed; + else + return !tvc.m_verifivation_failed && tx_added; + } +}; + +template<> +struct get_test_options { + const std::pair hard_forks[3] = {std::make_pair(7, 0), std::make_pair(8, 1), std::make_pair(9, 2)}; + const cryptonote::test_options test_options = { + hard_forks + }; }; \ No newline at end of file diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index b7b18a3f25..b63c1f8a7a 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -55,7 +55,7 @@ class test_core bool have_block(const crypto::hash& id) const {return true;} void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } - bool handle_incoming_txs(const std::list& tx_blob, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } + bool handle_incoming_txs(const std::vector& tx_blob, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; } bool handle_uptime_proof(uint64_t timestamp, const crypto::public_key& pubkey, const crypto::signature& sig) { return false; } void pause_mine(){} @@ -66,7 +66,7 @@ class test_core cryptonote::blockchain_storage &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class test_core."); } bool get_test_drop_download() const {return true;} bool get_test_drop_download_height() const {return true;} - bool prepare_handle_incoming_blocks(const std::list &blocks) { return true; } + bool prepare_handle_incoming_blocks(const std::vector &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } @@ -74,15 +74,15 @@ class test_core cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob) const { return false; } bool pool_has_tx(const crypto::hash &txid) const { return false; } - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const { return false; } - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const { return false; } + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return false; } bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version() const { return 0; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } void stop() {} // TODO(loki): Write tests diff --git a/tests/unit_tests/service_nodes.cpp b/tests/unit_tests/service_nodes.cpp index bc31b1dd35..d088dbb96c 100644 --- a/tests/unit_tests/service_nodes.cpp +++ b/tests/unit_tests/service_nodes.cpp @@ -324,3 +324,74 @@ TEST(service_nodes, tx_extra_deregister_validation) ASSERT_FALSE(result); } } + +TEST(service_nodes, min_portions) +{ + // Test new contributors can *NOT* stake to a registration with under 25% of the total stake if there is more than 25% available. + { + ASSERT_FALSE(service_nodes::check_service_node_portions({0, STAKING_PORTIONS})); + } + + { + const auto small = MIN_PORTIONS - 1; + const auto rest = STAKING_PORTIONS - small; + ASSERT_FALSE(service_nodes::check_service_node_portions({small, rest})); + } + + { + const auto small = MIN_PORTIONS - 1; + const auto rest = STAKING_PORTIONS - small - STAKING_PORTIONS / 2; + ASSERT_FALSE(service_nodes::check_service_node_portions({STAKING_PORTIONS / 2, small, rest})); + } + + { + const auto small = MIN_PORTIONS - 1; + const auto rest = STAKING_PORTIONS - small - 2 * MIN_PORTIONS; + ASSERT_FALSE(service_nodes::check_service_node_portions({MIN_PORTIONS, MIN_PORTIONS, small, rest})); + } + + // Test new contributors *CAN* stake as the last person with under 25% if there is less than 25% available. + + // Two contributers + { + const auto large = 4 * (STAKING_PORTIONS / 5); + const auto rest = STAKING_PORTIONS - large; + bool result = service_nodes::check_service_node_portions({large, rest}); + ASSERT_TRUE(result); + } + + // Three contributers + { + const auto half = STAKING_PORTIONS / 2 - 1; + const auto rest = STAKING_PORTIONS - 2 * half; + bool result = service_nodes::check_service_node_portions({half, half, rest}); + ASSERT_TRUE(result); + } + + // Four contributers + { + const auto third = STAKING_PORTIONS / 3 - 1; + const auto rest = STAKING_PORTIONS - 3 * third; + bool result = service_nodes::check_service_node_portions({third, third, third, rest}); + ASSERT_TRUE(result); + } + +} + +// Test service node receive rewards proportionate to the amount they contributed. +TEST(service_nodes, service_node_rewards_proportional_to_portions) +{ + + { + const auto reward_a = cryptonote::get_portion_of_reward(MIN_PORTIONS, COIN); + const auto reward_b = cryptonote::get_portion_of_reward(3 * MIN_PORTIONS, COIN); + ASSERT_TRUE(3 * reward_a == reward_b); + } + + { + const auto reward_a = cryptonote::get_portion_of_reward(STAKING_PORTIONS/2, COIN); + const auto reward_b = cryptonote::get_portion_of_reward(STAKING_PORTIONS, COIN); + ASSERT_TRUE(2 * reward_a == reward_b); + } + +} \ No newline at end of file