aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--external/db_drivers/CMakeLists.txt9
-rw-r--r--src/common/dns_utils.cpp34
-rw-r--r--src/common/dns_utils.h5
-rw-r--r--src/crypto/slow-hash.c2
-rw-r--r--src/cryptonote_core/checkpoints_create.cpp74
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp9
-rw-r--r--src/cryptonote_core/cryptonote_core.h3
-rw-r--r--src/cryptonote_core/tx_pool.cpp35
-rw-r--r--src/cryptonote_core/tx_pool.h2
-rw-r--r--src/daemon/rpc_command_executor.cpp63
-rw-r--r--src/rpc/core_rpc_server.cpp4
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h15
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()
};
};