diff options
-rw-r--r-- | external/db_drivers/CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/common/dns_utils.cpp | 34 | ||||
-rw-r--r-- | src/common/dns_utils.h | 5 | ||||
-rw-r--r-- | src/crypto/slow-hash.c | 2 | ||||
-rw-r--r-- | src/cryptonote_core/checkpoints_create.cpp | 74 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 9 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 3 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 35 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 2 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 63 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 4 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 15 |
12 files changed, 208 insertions, 47 deletions
diff --git a/external/db_drivers/CMakeLists.txt b/external/db_drivers/CMakeLists.txt index 99b3a20bf..cd7479724 100644 --- a/external/db_drivers/CMakeLists.txt +++ b/external/db_drivers/CMakeLists.txt @@ -37,13 +37,8 @@ set(LMDB_LIBRARY "lmdb" CACHE STRING "LMDB Library name") if (NOT STATIC) find_package(BerkeleyDB) - if(NOT BERKELEY_DB_LIBRARIES OR STATIC) - add_subdirectory(libdb) - message(STATUS "BerkeleyDB not found, building from src tree") - - set(BDB_STATIC true CACHE BOOL "BDB Static flag") - set(BDB_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/libdb" CACHE STRING "BDB include path") - set(BDB_LIBRARY "db" CACHE STRING "BDB library name") + if(NOT BERKELEY_DB_LIBRARIES) + die("BerkeleyDB not found. At this time it should be installed in your system for a non-static build.") else() message(STATUS "Found BerkeleyDB include (db.h) in ${BERKELEY_DB_INCLUDE_DIR}") if(BERKELEY_DB_LIBRARIES) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index ea7f1078b..347cf758a 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -205,13 +205,15 @@ std::vector<std::string> DNSResolver::get_ipv4(const std::string& url, bool& dns dnssec_valid = false; char urlC[1000]; // waaaay too big, but just in case... - strncpy(urlC, url.c_str(), 999); - urlC[999] = '\0'; - if (!check_address_syntax(urlC)) + std::string url_copy{url}; + if (!check_address_syntax(url_copy)) { return addresses; } + strncpy(urlC, url_copy.c_str(), 999); + urlC[999] = '\0'; + // destructor takes care of cleanup ub_result_ptr result; @@ -239,14 +241,15 @@ std::vector<std::string> DNSResolver::get_ipv6(const std::string& url, bool& dns dnssec_valid = false; char urlC[1000]; // waaaay too big, but just in case... - strncpy(urlC, url.c_str(), 999); - urlC[999] = '\0'; - - if (!check_address_syntax(urlC)) + std::string url_copy{url}; + if (!check_address_syntax(url_copy)) { return addresses; } + strncpy(urlC, url_copy.c_str(), 999); + urlC[999] = '\0'; + ub_result_ptr result; // call DNS resolver, blocking. if return value not zero, something went wrong @@ -273,14 +276,15 @@ std::vector<std::string> DNSResolver::get_txt_record(const std::string& url, boo dnssec_valid = false; char urlC[1000]; // waaaay too big, but just in case... - strncpy(urlC, url.c_str(), 999); - urlC[999] = '\0'; - - if (!check_address_syntax(urlC)) + std::string url_copy{url}; + if (!check_address_syntax(url_copy)) { return records; } + strncpy(urlC, url_copy.c_str(), 999); + urlC[999] = '\0'; + ub_result_ptr result; // call DNS resolver, blocking. if return value not zero, something went wrong @@ -314,13 +318,17 @@ DNSResolver& DNSResolver::instance() return *staticInstance; } -bool DNSResolver::check_address_syntax(const std::string& addr) +bool DNSResolver::check_address_syntax(std::string& addr) { // if string doesn't contain a dot, we won't consider it a url for now. - if (addr.find(".") == std::string::npos) + auto first_dot = addr.find("."); + if (first_dot == std::string::npos) { return false; } + + // allow name@domain.tld to work + addr.replace(first_dot, 1, "@"); return true; } diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index a16c7eff7..4e48acb09 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -112,11 +112,14 @@ private: /** * @brief Checks a string to see if it looks like a URL * + * If the address looks good, but contains one @ symbol, replace that with a . + * e.g. donate@getmonero.org becomes donate.getmonero.org + * * @param addr the string to be checked * * @return true if it looks enough like a URL, false if not */ - bool check_address_syntax(const std::string& addr); + bool check_address_syntax(std::string& addr); DNSResolverData *m_data; }; // class DNSResolver diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 131c216a8..425737984 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -37,7 +37,7 @@ #include "hash-ops.h" #include "oaes_lib.h" -#if defined(__x86_64__) || defined(__i386) +#if defined(__x86_64__) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Fall back to more portable code is down at the bottom diff --git a/src/cryptonote_core/checkpoints_create.cpp b/src/cryptonote_core/checkpoints_create.cpp index 43f926682..6f22e596d 100644 --- a/src/cryptonote_core/checkpoints_create.cpp +++ b/src/cryptonote_core/checkpoints_create.cpp @@ -35,6 +35,30 @@ #include <random> #include "storages/portable_storage_template_helper.h" // epee json include +namespace +{ + bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b) + { + if (a.size() != b.size()) return false; + + for (const auto& record_in_a : a) + { + bool ok = false; + for (const auto& record_in_b : b) + { + if (record_in_a == record_in_b) + { + ok = true; + break; + } + } + if (!ok) return false; + } + + return true; + } +} // anonymous namespace + namespace cryptonote { @@ -127,14 +151,16 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testne , "testpoints.moneropulse.net" , "testpoints.moneropulse.co" }; - bool avail, valid; - std::vector<std::string> records; + + std::vector<std::vector<std::string> > records; + records.resize(dns_urls.size()); std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1); size_t first_index = dis(gen); + bool avail, valid; size_t cur_index = first_index; do { @@ -148,7 +174,7 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testne url = dns_urls[cur_index]; } - records = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); if (!avail) { LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); @@ -158,26 +184,58 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testne LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); } - if (records.size() == 0 || !avail || !valid) + if (records[cur_index].size() == 0 || !avail || !valid) { cur_index++; if (cur_index == dns_urls.size()) { cur_index = 0; } - records.clear(); + records[cur_index].clear(); continue; } break; } while (cur_index != first_index); - if (records.size() == 0) + size_t num_valid_records = 0; + + for( const auto& record_set : records) + { + if (record_set.size() != 0) + { + num_valid_records++; + } + } + + if (num_valid_records < 2) + { + LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); + return true; + } + + int good_records_index = -1; + for (size_t i = 0; i < records.size() - 1; ++i) + { + if (records[i].size() == 0) continue; + + for (size_t j = i + 1; j < records.size(); ++j) + { + if (dns_records_match(records[i], records[j])) + { + good_records_index = i; + break; + } + } + if (good_records_index >= 0) break; + } + + if (good_records_index < 0) { - LOG_PRINT_L0("WARNING: All MoneroPulse checkpoint URLs failed DNSSEC validation and/or returned no records"); + LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); return true; } - for (auto& record : records) + for (auto& record : records[good_records_index]) { auto pos = record.find(":"); if (pos != std::string::npos) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index f6eaff2b1..a5517e011 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -100,6 +100,8 @@ namespace cryptonote { if (m_testnet) return true; + if (m_checkpoints_updating.test_and_set()) return true; + bool res = true; if (time(NULL) - m_last_dns_checkpoints_update >= 3600) { @@ -113,6 +115,8 @@ namespace cryptonote m_last_json_checkpoints_update = time(NULL); } + m_checkpoints_updating.clear(); + // if anything fishy happened getting new checkpoints, bring down the house if (!res) { @@ -654,6 +658,11 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const + { + return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos); + } + //----------------------------------------------------------------------------------------------- bool core::get_short_chain_history(std::list<crypto::hash>& ids) { return m_blockchain_storage.get_short_chain_history(ids); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 973f01272..ff187b4ce 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -108,6 +108,7 @@ namespace cryptonote void set_enforce_dns_checkpoints(bool enforce_dns); bool get_pool_transactions(std::list<transaction>& txs); + bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const; size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); //bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys); @@ -189,6 +190,8 @@ namespace cryptonote std::string m_checkpoints_path; time_t m_last_dns_checkpoints_update; time_t m_last_json_checkpoints_update; + + std::atomic_flag m_checkpoints_updating; }; } diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b26c3cc8d..781d1af5c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -264,6 +264,7 @@ namespace cryptonote (tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && it->second.kept_by_block) ) { LOG_PRINT_L1("Tx " << it->first << " removed from tx pool due to outdated, age: " << tx_age ); + remove_transaction_keyimages(it->second.tx); auto sorted_it = m_txs_by_fee.find(it->first); m_transactions.erase(it++); m_txs_by_fee.erase(sorted_it); @@ -285,6 +286,40 @@ namespace cryptonote BOOST_FOREACH(const auto& tx_vt, m_transactions) txs.push_back(tx_vt.second.tx); } + //------------------------------------------------------------------ + bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + for (const auto& tx_vt : m_transactions) + { + tx_info txi; + const tx_details& txd = tx_vt.second; + txi.id_hash = epee::string_tools::pod_to_hex(tx_vt.first); + txi.tx_json = obj_to_json_str(*const_cast<transaction*>(&txd.tx)); + txi.blob_size = txd.blob_size; + txi.fee = txd.fee; + txi.kept_by_block = txd.kept_by_block; + txi.max_used_block_height = txd.max_used_block_height; + txi.max_used_block_id_hash = epee::string_tools::pod_to_hex(txd.max_used_block_id); + txi.last_failed_height = txd.last_failed_height; + txi.last_failed_id_hash = epee::string_tools::pod_to_hex(txd.last_failed_id); + txi.receive_time = txd.receive_time; + tx_infos.push_back(txi); + } + + for (const key_images_container::value_type& kee : m_spent_key_images) { + const crypto::key_image& k_image = kee.first; + const std::unordered_set<crypto::hash>& kei_image_set = kee.second; + spent_key_image_info ki; + ki.id_hash = epee::string_tools::pod_to_hex(k_image); + for (const crypto::hash& tx_id_hash : kei_image_set) + { + ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); + } + key_image_infos.push_back(ki); + } + return true; + } //--------------------------------------------------------------------------------- bool tx_memory_pool::get_transaction(const crypto::hash& id, transaction& tx) const { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 9eb7f7e7c..7c8f4a449 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -44,6 +44,7 @@ #include "cryptonote_basic_impl.h" #include "verification_context.h" #include "crypto/hash.h" +#include "rpc/core_rpc_server_commands_defs.h" namespace cryptonote { @@ -82,6 +83,7 @@ namespace cryptonote bool deinit(); bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); void get_transactions(std::list<transaction>& txs) const; + bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const; bool get_transaction(const crypto::hash& h, transaction& tx) const; size_t get_transactions_count() const; std::string print_pool(bool short_format) const; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 5d73c7e8c..8ef91600c 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -523,20 +523,58 @@ bool t_rpc_command_executor::print_transaction_pool_long() { } } - if (res.transactions.empty()) + if (res.transactions.empty() && res.spent_key_images.empty()) { tools::msg_writer() << "Pool is empty" << std::endl; } - for (auto & tx_info : res.transactions) + if (! res.transactions.empty()) { - tools::msg_writer() << "id: " << tx_info.id_hash << std::endl - << "blob_size: " << tx_info.blob_size << std::endl - << "fee: " << tx_info.fee << std::endl - << "kept_by_block: " << tx_info.kept_by_block << std::endl - << "max_used_block_height: " << tx_info.max_used_block_height << std::endl - << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl - << "last_failed_height: " << tx_info.last_failed_height << std::endl - << "last_failed_id: " << tx_info.last_failed_id_hash << std::endl; + tools::msg_writer() << "Transactions: "; + for (auto & tx_info : res.transactions) + { + tools::msg_writer() << "id: " << tx_info.id_hash << std::endl + << tx_info.tx_json << std::endl + << "blob_size: " << tx_info.blob_size << std::endl + << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl + << "max_used_block_height: " << tx_info.max_used_block_height << std::endl + << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl + << "last_failed_height: " << tx_info.last_failed_height << std::endl + << "last_failed_id: " << tx_info.last_failed_id_hash << std::endl; + } + if (res.spent_key_images.empty()) + { + tools::msg_writer() << "WARNING: Inconsistent pool state - no spent key images"; + } + } + if (! res.spent_key_images.empty()) + { + tools::msg_writer() << ""; // one newline + tools::msg_writer() << "Spent key images: "; + for (const cryptonote::spent_key_image_info& kinfo : res.spent_key_images) + { + tools::msg_writer() << "key image: " << kinfo.id_hash; + if (kinfo.txs_hashes.size() == 1) + { + tools::msg_writer() << " tx: " << kinfo.txs_hashes[0]; + } + else if (kinfo.txs_hashes.size() == 0) + { + tools::msg_writer() << " WARNING: spent key image has no txs associated"; + } + else + { + tools::msg_writer() << " NOTE: key image for multiple txs: " << kinfo.txs_hashes.size(); + for (const std::string& tx_id : kinfo.txs_hashes) + { + tools::msg_writer() << " tx: " << tx_id; + } + } + } + if (res.transactions.empty()) + { + tools::msg_writer() << "WARNING: Inconsistent pool state - no transactions"; + } } return true; @@ -571,10 +609,9 @@ bool t_rpc_command_executor::print_transaction_pool_short() { for (auto & tx_info : res.transactions) { tools::msg_writer() << "id: " << tx_info.id_hash << std::endl - << tx_info.tx_json << std::endl << "blob_size: " << tx_info.blob_size << std::endl - << "fee: " << tx_info.fee << std::endl - << "kept_by_block: " << tx_info.kept_by_block << std::endl + << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl << "max_used_block_height: " << tx_info.max_used_block_height << std::endl << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl << "last_failed_height: " << tx_info.last_failed_height << std::endl diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 8eeac489d..561161950 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -401,10 +401,8 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res) { - /* CHECK_CORE_BUSY(); - res.transactions = m_core.transaction_pool_info(); - */ + m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index e54dec2c9..b6a2edd0b 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -619,7 +619,18 @@ namespace cryptonote KV_SERIALIZE(receive_time) END_KV_SERIALIZE_MAP() }; - + + struct spent_key_image_info + { + std::string id_hash; + std::vector<std::string> txs_hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id_hash) + KV_SERIALIZE(txs_hashes) + END_KV_SERIALIZE_MAP() + }; + struct COMMAND_RPC_GET_TRANSACTION_POOL { struct request @@ -632,10 +643,12 @@ namespace cryptonote { std::string status; std::vector<tx_info> transactions; + std::vector<spent_key_image_info> spent_key_images; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(transactions) + KV_SERIALIZE(spent_key_images) END_KV_SERIALIZE_MAP() }; }; |