aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--ZMQ.md2
-rw-r--r--contrib/depends/packages/packages.mk9
-rw-r--r--contrib/epee/include/net/http_protocol_handler.inl2
-rw-r--r--contrib/gitian/gitian-android.yml7
-rw-r--r--contrib/gitian/gitian-freebsd.yml7
-rw-r--r--contrib/gitian/gitian-linux.yml7
-rw-r--r--contrib/gitian/gitian-osx.yml1
-rw-r--r--contrib/gitian/gitian-win.yml2
-rw-r--r--external/easylogging++/easylogging++.cc7
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h1
-rw-r--r--src/cryptonote_basic/events.h2
-rw-r--r--src/cryptonote_basic/fwd.h1
-rw-r--r--src/cryptonote_core/blockchain.cpp69
-rw-r--r--src/cryptonote_core/blockchain.h39
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp10
-rw-r--r--src/cryptonote_core/cryptonote_core.h7
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h9
-rw-r--r--src/cryptonote_core/tx_pool.cpp39
-rw-r--r--src/cryptonote_core/tx_pool.h10
-rw-r--r--src/cryptonote_protocol/levin_notify.cpp54
-rw-r--r--src/cryptonote_protocol/levin_notify.h3
-rw-r--r--src/daemon/daemon.cpp1
-rw-r--r--src/net/socks.h7
-rw-r--r--src/p2p/net_node.cpp5
-rw-r--r--src/p2p/net_node.h6
-rw-r--r--src/p2p/net_node.inl6
-rw-r--r--src/rpc/core_rpc_server.cpp53
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h52
-rw-r--r--src/rpc/zmq_pub.cpp99
-rw-r--r--src/rpc/zmq_pub.h14
-rw-r--r--src/serialization/json_object.cpp22
-rw-r--r--src/serialization/json_object.h3
-rw-r--r--src/simplewallet/simplewallet.cpp4
-rw-r--r--tests/unit_tests/levin.cpp113
-rw-r--r--tests/unit_tests/node_server.cpp259
-rw-r--r--utils/python-rpc/framework/daemon.py8
38 files changed, 837 insertions, 107 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ec6847cd6..b27d65766 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,7 +10,7 @@ jobs:
with:
submodules: recursive
- name: update brew and install dependencies
- run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf
+ run: brew update && brew install boost hidapi openssl zmq libpgm miniupnpc ldns expat libunwind-headers protobuf
- name: build
run: make -j3
diff --git a/ZMQ.md b/ZMQ.md
index 9128ff2ad..a7006f486 100644
--- a/ZMQ.md
+++ b/ZMQ.md
@@ -25,6 +25,8 @@ allows for filtering on: (1) format, (2) context, and (3) event.
Includes previously unseen transactions in a block but _not_ the
`miner_tx`. Does not "re-publish" after a reorg. Includes `do_not_relay`
transactions.
+ * `miner_data` - provides the necessary data to create a custom block template
+ Available only in the `full` context.
The subscription topics are formatted as `format-context-event`, with prefix
matching supported by both Monero and ZMQ. The `format`, `context` and `event`
diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk
index 95b23a37e..978e056b4 100644
--- a/contrib/depends/packages/packages.mk
+++ b/contrib/depends/packages/packages.mk
@@ -1,6 +1,9 @@
packages:=boost openssl zeromq libiconv
+# ccache is useless in gitian builds
+ifneq ($(GITIAN),1)
native_packages := native_ccache
+endif
hardware_packages := hidapi protobuf libusb
hardware_native_packages := native_protobuf
@@ -8,8 +11,8 @@ hardware_native_packages := native_protobuf
android_native_packages = android_ndk
android_packages = ncurses readline sodium
-darwin_native_packages = native_biplist native_ds_store native_mac_alias $(hardware_native_packages)
-darwin_packages = sodium ncurses readline $(hardware_packages)
+darwin_native_packages = $(hardware_native_packages)
+darwin_packages = ncurses readline sodium $(hardware_packages)
# not really native...
freebsd_native_packages = freebsd_base
@@ -31,6 +34,6 @@ mingw32_packages = icu4c sodium $(hardware_packages)
mingw32_native_packages = $(hardware_native_packages)
ifneq ($(build_os),darwin)
-darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus
+darwin_native_packages += native_cctools
endif
diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl
index 19bdf4ff0..0f4a28c99 100644
--- a/contrib/epee/include/net/http_protocol_handler.inl
+++ b/contrib/epee/include/net/http_protocol_handler.inl
@@ -668,7 +668,7 @@ namespace net_utils
// Cross-origin resource sharing
if(m_query_info.m_header_info.m_origin.size())
{
- if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin))
+ if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), "*") || std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin))
{
buf += "Access-Control-Allow-Origin: ";
buf += m_query_info.m_header_info.m_origin;
diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml
index b8eaa8af9..d988bd4c8 100644
--- a/contrib/gitian/gitian-android.yml
+++ b/contrib/gitian/gitian-android.yml
@@ -24,12 +24,6 @@ packages:
- "ca-certificates"
- "python"
- "cmake"
-- "ccache"
-- "protobuf-compiler"
-- "libdbus-1-dev"
-- "libharfbuzz-dev"
-- "libprotobuf-dev"
-- "python3-zmq"
- "unzip"
remotes:
- "url": "https://github.com/monero-project/monero.git"
@@ -52,6 +46,7 @@ script: |
if test -n "$GBUILD_CACHE_ENABLED"; then
export SOURCES_PATH=${GBUILD_COMMON_CACHE}
export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ export GITIAN=1
mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
fi
diff --git a/contrib/gitian/gitian-freebsd.yml b/contrib/gitian/gitian-freebsd.yml
index 36b81c641..bf23a05ff 100644
--- a/contrib/gitian/gitian-freebsd.yml
+++ b/contrib/gitian/gitian-freebsd.yml
@@ -25,12 +25,6 @@ packages:
- "ca-certificates"
- "python"
- "cmake"
-- "ccache"
-- "protobuf-compiler"
-- "libdbus-1-dev"
-- "libharfbuzz-dev"
-- "libprotobuf-dev"
-- "python3-zmq"
remotes:
- "url": "https://github.com/monero-project/monero.git"
"dir": "monero"
@@ -52,6 +46,7 @@ script: |
if test -n "$GBUILD_CACHE_ENABLED"; then
export SOURCES_PATH=${GBUILD_COMMON_CACHE}
export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ export GITIAN=1
mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
fi
diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml
index 0aac983cc..7e75e489f 100644
--- a/contrib/gitian/gitian-linux.yml
+++ b/contrib/gitian/gitian-linux.yml
@@ -36,12 +36,6 @@ packages:
- "ca-certificates"
- "python"
- "cmake"
-- "ccache"
-- "protobuf-compiler"
-- "libdbus-1-dev"
-- "libharfbuzz-dev"
-- "libprotobuf-dev"
-- "python3-zmq"
remotes:
- "url": "https://github.com/monero-project/monero.git"
"dir": "monero"
@@ -63,6 +57,7 @@ script: |
if test -n "$GBUILD_CACHE_ENABLED"; then
export SOURCES_PATH=${GBUILD_COMMON_CACHE}
export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ export GITIAN=1
mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
fi
diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml
index 9889ca45f..fdfe5bd22 100644
--- a/contrib/gitian/gitian-osx.yml
+++ b/contrib/gitian/gitian-osx.yml
@@ -41,6 +41,7 @@ script: |
if test -n "$GBUILD_CACHE_ENABLED"; then
export SOURCES_PATH=${GBUILD_COMMON_CACHE}
export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ export GITIAN=1
mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
fi
diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml
index c53086144..ee7920b6c 100644
--- a/contrib/gitian/gitian-win.yml
+++ b/contrib/gitian/gitian-win.yml
@@ -20,7 +20,6 @@ packages:
- "zip"
- "ca-certificates"
- "python"
-- "rename"
- "cmake"
alternatives:
-
@@ -54,6 +53,7 @@ script: |
if test -n "$GBUILD_CACHE_ENABLED"; then
export SOURCES_PATH=${GBUILD_COMMON_CACHE}
export BASE_CACHE=${GBUILD_PACKAGE_CACHE}
+ export GITIAN=1
mkdir -p ${BASE_CACHE} ${SOURCES_PATH}
fi
diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc
index bf877c018..cffc63a23 100644
--- a/external/easylogging++/easylogging++.cc
+++ b/external/easylogging++/easylogging++.cc
@@ -2968,8 +2968,8 @@ void Writer::initializeLogger(Logger *logger, bool needLock) {
}
void Writer::processDispatch() {
- static std::atomic_flag in_dispatch;
- if (in_dispatch.test_and_set())
+ static __thread bool in_dispatch = false;
+ if (in_dispatch)
{
if (m_proceed && m_logger != NULL)
{
@@ -2978,6 +2978,7 @@ void Writer::processDispatch() {
}
return;
}
+ in_dispatch = true;
#if ELPP_LOGGING_ENABLED
if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
bool firstDispatched = false;
@@ -3016,7 +3017,7 @@ void Writer::processDispatch() {
m_logger->releaseLock();
}
#endif // ELPP_LOGGING_ENABLED
- in_dispatch.clear();
+ in_dispatch = false;
}
void Writer::triggerDispatch(void) {
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index c70ae1df1..cdc130c81 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -49,6 +49,7 @@
#include "misc_language.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
+#include "cryptonote_basic/fwd.h"
namespace cryptonote
{
diff --git a/src/cryptonote_basic/events.h b/src/cryptonote_basic/events.h
index 6c6742215..3417ece8c 100644
--- a/src/cryptonote_basic/events.h
+++ b/src/cryptonote_basic/events.h
@@ -41,6 +41,8 @@ namespace cryptonote
{
cryptonote::transaction tx;
crypto::hash hash;
+ uint64_t blob_size;
+ uint64_t weight;
bool res; //!< Listeners must ignore `tx` when this is false.
};
}
diff --git a/src/cryptonote_basic/fwd.h b/src/cryptonote_basic/fwd.h
index d54223461..901ad151b 100644
--- a/src/cryptonote_basic/fwd.h
+++ b/src/cryptonote_basic/fwd.h
@@ -33,4 +33,5 @@ namespace cryptonote
struct block;
class transaction;
struct txpool_event;
+ struct tx_block_template_backlog_entry;
}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 06e7cd87b..18d5e5dac 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1234,6 +1234,12 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(),
"%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL);
+ crypto::hash prev_id;
+ if (!get_block_hash(alt_chain.back().bl, prev_id))
+ MERROR("Failed to get block hash of an alternative chain's tip");
+ else
+ send_miner_notifications(prev_id, alt_chain.back().already_generated_coins);
+
for (const auto& notifier : m_block_notifiers)
{
std::size_t notify_height = split_height;
@@ -1780,6 +1786,30 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
}
//------------------------------------------------------------------
+bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
+{
+ prev_id = m_db->top_block_hash(&height);
+ ++height;
+
+ major_version = m_hardfork->get_ideal_version(height);
+
+ seed_hash = crypto::null_hash;
+ if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
+ {
+ uint64_t seed_height, next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+ seed_hash = get_block_id_by_height(seed_height);
+ }
+
+ difficulty = get_difficulty_for_next_block();
+ median_weight = m_current_block_cumul_weight_median;
+ already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+
+ m_tx_pool.get_block_template_backlog(tx_backlog);
+
+ return true;
+}
+//------------------------------------------------------------------
// for an alternate chain, get the timestamps from the main chain to complete
// the needed number of timestamps for the BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW.
bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
@@ -4378,6 +4408,7 @@ leave:
get_difficulty_for_next_block(); // just to cache it
invalidate_block_template_cache();
+ send_miner_notifications(id, already_generated_coins);
for (const auto& notifier: m_block_notifiers)
notifier(new_height - 1, {std::addressof(bl), 1});
@@ -5286,7 +5317,7 @@ void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint
m_max_prepare_blocks_threads = maxthreads;
}
-void Blockchain::add_block_notify(boost::function<void(std::uint64_t, epee::span<const block>)>&& notify)
+void Blockchain::add_block_notify(BlockNotifyCallback&& notify)
{
if (notify)
{
@@ -5295,6 +5326,15 @@ void Blockchain::add_block_notify(boost::function<void(std::uint64_t, epee::span
}
}
+void Blockchain::add_miner_notify(MinerNotifyCallback&& notify)
+{
+ if (notify)
+ {
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ m_miner_notifiers.push_back(std::move(notify));
+ }
+}
+
void Blockchain::safesyncmode(const bool onoff)
{
/* all of this is no-op'd if the user set a specific
@@ -5547,6 +5587,33 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
m_btc_valid = true;
}
+void Blockchain::send_miner_notifications(const crypto::hash &prev_id, uint64_t already_generated_coins)
+{
+ if (m_miner_notifiers.empty())
+ return;
+
+ const uint64_t height = m_db->height();
+ const uint8_t major_version = m_hardfork->get_ideal_version(height);
+ const difficulty_type diff = get_difficulty_for_next_block();
+ const uint64_t median_weight = m_current_block_cumul_weight_median;
+
+ crypto::hash seed_hash = crypto::null_hash;
+ if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
+ {
+ uint64_t seed_height, next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+ seed_hash = get_block_id_by_height(seed_height);
+ }
+
+ std::vector<tx_block_template_backlog_entry> tx_backlog;
+ m_tx_pool.get_block_template_backlog(tx_backlog);
+
+ for (const auto& notifier : m_miner_notifiers)
+ {
+ notifier(major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog);
+ }
+}
+
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const;
template bool Blockchain::get_split_transactions_blobs(const std::vector<crypto::hash>&, std::vector<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>>&, std::vector<crypto::hash>&) const;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 3529255e1..acbc361ef 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -89,6 +89,9 @@ namespace cryptonote
*/
typedef std::function<const epee::span<const unsigned char>(cryptonote::network_type network)> GetCheckpointsCallback;
+ typedef boost::function<void(uint64_t /* height */, epee::span<const block> /* blocks */)> BlockNotifyCallback;
+ typedef boost::function<void(uint8_t /* major_version */, uint64_t /* height */, const crypto::hash& /* prev_id */, const crypto::hash& /* seed_hash */, difficulty_type /* diff */, uint64_t /* median_weight */, uint64_t /* already_generated_coins */, const std::vector<tx_block_template_backlog_entry>& /* tx_backlog */)> MinerNotifyCallback;
+
/************************************************************************/
/* */
/************************************************************************/
@@ -370,6 +373,22 @@ namespace cryptonote
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
/**
+ * @brief gets data required to create a block template and start mining on it
+ *
+ * @param major_version current hardfork version
+ * @param height current blockchain height
+ * @param prev_id hash of the top block
+ * @param seed_hash seed hash used for RandomX initialization
+ * @param difficulty current mining difficulty
+ * @param median_weight current median block weight
+ * @param already_generated_coins current emission
+ * @param tx_backlog transactions in mempool ready to be mined
+ *
+ * @return true if block template filled in successfully, else false
+ */
+ bool get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog);
+
+ /**
* @brief checks if a block is known about with a given hash
*
* This function checks the main chain, alternate chains, and invalid blocks
@@ -771,7 +790,14 @@ namespace cryptonote
*
* @param notify the notify object to call at every new block
*/
- void add_block_notify(boost::function<void(std::uint64_t, epee::span<const block>)> &&notify);
+ void add_block_notify(BlockNotifyCallback&& notify);
+
+ /**
+ * @brief sets a miner notify object to call for every new block
+ *
+ * @param notify the notify object to call at every new block
+ */
+ void add_miner_notify(MinerNotifyCallback&& notify);
/**
* @brief sets a reorg notify object to call for every reorg
@@ -1153,7 +1179,8 @@ namespace cryptonote
the callable object has a single `std::shared_ptr` or `std::weap_ptr`
internally. Whereas, the libstdc++ `std::function` will allocate. */
- std::vector<boost::function<void(std::uint64_t, epee::span<const block>)>> m_block_notifiers;
+ std::vector<BlockNotifyCallback> m_block_notifiers;
+ std::vector<MinerNotifyCallback> m_miner_notifiers;
std::shared_ptr<tools::Notify> m_reorg_notify;
// for prepare_handle_incoming_blocks
@@ -1533,5 +1560,13 @@ namespace cryptonote
* At some point, may be used to push an update to miners
*/
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
+
+ /**
+ * @brief sends new block notifications to ZMQ `miner_data` subscribers
+ *
+ * @param prev_id hash of new blockchain tip
+ * @param already_generated_coins total coins mined by the network so far
+ */
+ void send_miner_notifications(const crypto::hash &prev_id, uint64_t already_generated_coins);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 12125fb9d..17dca7dba 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1061,8 +1061,9 @@ namespace cryptonote
if (already_have[i])
continue;
- const uint64_t weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, it->blob.size());
- ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], tx_relay, relayed);
+ results[i].blob_size = it->blob.size();
+ results[i].weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, it->blob.size());
+ ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, results[i].weight, tvc[i], tx_relay, relayed);
if(tvc[i].m_verifivation_failed)
{MERROR_VER("Transaction verification failed: " << results[i].hash);}
@@ -1401,6 +1402,11 @@ namespace cryptonote
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
+ {
+ return m_blockchain_storage.get_miner_data(major_version, height, prev_id, seed_hash, difficulty, median_weight, already_generated_coins, tx_backlog);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index a6cce8617..82abfe918 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -237,6 +237,13 @@ namespace cryptonote
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
/**
+ * @copydoc Blockchain::get_miner_data
+ *
+ * @note see Blockchain::get_miner_data
+ */
+ bool get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog);
+
+ /**
* @brief called when a transaction is relayed.
* @note Should only be invoked from `levin_notify`.
*/
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 73cdd31cd..06412d6bf 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -108,6 +108,15 @@ namespace cryptonote
};
//---------------------------------------------------------------
+
+ struct tx_block_template_backlog_entry
+ {
+ crypto::hash id;
+ uint64_t weight;
+ uint64_t fee;
+ };
+
+ //---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index d059ab78f..42d0b9cf6 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -914,6 +914,32 @@ namespace cryptonote
}, false, category);
}
//------------------------------------------------------------------
+ void tx_memory_pool::get_block_template_backlog(std::vector<tx_block_template_backlog_entry>& backlog, bool include_sensitive) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+ const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
+ backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive));
+ txpool_tx_meta_t tmp_meta;
+ m_blockchain.for_all_txpool_txes([this, &backlog, &tmp_meta](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
+ transaction tx;
+ if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx)))
+ {
+ MERROR("Failed to parse tx from txpool");
+ // continue
+ return true;
+ }
+ tx.set_hash(txid);
+
+ tmp_meta = meta;
+
+ if (is_transaction_ready_to_go(tmp_meta, txid, *bd, tx))
+ backlog.push_back({txid, meta.weight, meta.fee});
+
+ return true;
+ }, true, category);
+ }
+ //------------------------------------------------------------------
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -1224,11 +1250,11 @@ namespace cryptonote
return ret;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction &tx) const
+ bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata_ref& txblob, transaction &tx) const
{
- struct transction_parser
+ struct transaction_parser
{
- transction_parser(const cryptonote::blobdata &txblob, const crypto::hash &txid, transaction &tx): txblob(txblob), txid(txid), tx(tx), parsed(false) {}
+ transaction_parser(const cryptonote::blobdata_ref &txblob, const crypto::hash &txid, transaction &tx): txblob(txblob), txid(txid), tx(tx), parsed(false) {}
cryptonote::transaction &operator()()
{
if (!parsed)
@@ -1240,7 +1266,7 @@ namespace cryptonote
}
return tx;
}
- const cryptonote::blobdata &txblob;
+ const cryptonote::blobdata_ref &txblob;
const crypto::hash &txid;
transaction &tx;
bool parsed;
@@ -1291,6 +1317,11 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata& txblob, transaction &tx) const
+ {
+ return is_transaction_ready_to_go(txd, txid, cryptonote::blobdata_ref{txblob.data(), txblob.size()}, tx);
+ }
+ //---------------------------------------------------------------------------------
bool tx_memory_pool::have_key_images(const std::unordered_set<crypto::key_image>& k_images, const transaction_prefix& tx)
{
for(size_t i = 0; i!= tx.vin.size(); i++)
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index ab2a57ea2..80b38c51d 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -266,6 +266,15 @@ namespace cryptonote
void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive = false) const;
/**
+ * @brief get (hash, weight, fee) for all transactions in the pool - the minimum required information to create a block template
+ *
+ * @param backlog return-by-reference that data
+ * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
+ *
+ */
+ void get_block_template_backlog(std::vector<tx_block_template_backlog_entry>& backlog, bool include_sensitive = false) const;
+
+ /**
* @brief get a summary statistics of all transaction hashes in the pool
*
* @param stats return-by-reference the pool statistics
@@ -540,6 +549,7 @@ namespace cryptonote
*
* @return true if the transaction is good to go, otherwise false
*/
+ bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata_ref &txblob, transaction&tx) const;
bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction&tx) const;
/**
diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index ab4eeeb82..8b36ca58c 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -298,6 +298,12 @@ namespace levin
boost::asio::steady_timer next_epoch;
boost::asio::steady_timer flush_txs;
boost::asio::io_service::strand strand;
+ struct context_t {
+ std::vector<cryptonote::blobdata> fluff_txs;
+ std::chrono::steady_clock::time_point flush_time;
+ bool m_is_income;
+ };
+ boost::unordered_map<boost::uuids::uuid, context_t> contexts;
net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems
std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand`
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
@@ -374,14 +380,16 @@ namespace levin
const auto now = std::chrono::steady_clock::now();
auto next_flush = std::chrono::steady_clock::time_point::max();
std::vector<std::pair<std::vector<blobdata>, boost::uuids::uuid>> connections{};
- zone_->p2p->foreach_connection([timer_error, now, &next_flush, &connections] (detail::p2p_context& context)
+ for (auto &e: zone_->contexts)
{
+ auto &id = e.first;
+ auto &context = e.second;
if (!context.fluff_txs.empty())
{
if (context.flush_time <= now || timer_error) // flush on canceled timer
{
context.flush_time = std::chrono::steady_clock::time_point::max();
- connections.emplace_back(std::move(context.fluff_txs), context.m_connection_id);
+ connections.emplace_back(std::move(context.fluff_txs), id);
context.fluff_txs.clear();
}
else // not flushing yet
@@ -389,8 +397,7 @@ namespace levin
}
else // nothing to flush
context.flush_time = std::chrono::steady_clock::time_point::max();
- return true;
- });
+ }
/* Always send with `fluff` flag, even over i2p/tor. The hidden service
will disable the forwarding delay and immediately fluff. The i2p/tor
@@ -438,22 +445,21 @@ namespace levin
MDEBUG("Queueing " << txs.size() << " transaction(s) for Dandelion++ fluffing");
-
- zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
+ for (auto &e: zone->contexts)
{
+ auto &id = e.first;
+ auto &context = e.second;
// When i2p/tor, only fluff to outbound connections
- if (context.handshake_complete() && source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income))
+ if (source != id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income))
{
if (context.fluff_txs.empty())
context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
next_flush = std::min(next_flush, context.flush_time);
context.fluff_txs.reserve(context.fluff_txs.size() + txs.size());
- for (const blobdata& tx : txs)
- context.fluff_txs.push_back(tx); // must copy instead of move (multiple conns)
+ context.fluff_txs.insert(context.fluff_txs.end(), txs.begin(), txs.end());
}
- return true;
- });
+ }
if (next_flush == std::chrono::steady_clock::time_point::max())
MWARNING("Unable to send transaction(s), no available connections");
@@ -764,6 +770,32 @@ namespace levin
);
}
+ void notify::on_handshake_complete(const boost::uuids::uuid &id, bool is_income)
+ {
+ if (!zone_)
+ return;
+
+ auto& zone = zone_;
+ zone_->strand.dispatch([zone, id, is_income]{
+ zone->contexts[id] = {
+ .fluff_txs = {},
+ .flush_time = std::chrono::steady_clock::time_point::max(),
+ .m_is_income = is_income,
+ };
+ });
+ }
+
+ void notify::on_connection_close(const boost::uuids::uuid &id)
+ {
+ if (!zone_)
+ return;
+
+ auto& zone = zone_;
+ zone_->strand.dispatch([zone, id]{
+ zone->contexts.erase(id);
+ });
+ }
+
void notify::run_epoch()
{
if (!zone_)
diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h
index abbf9d461..12704746a 100644
--- a/src/cryptonote_protocol/levin_notify.h
+++ b/src/cryptonote_protocol/levin_notify.h
@@ -101,6 +101,9 @@ namespace levin
//! Probe for new outbound connection - skips if not needed.
void new_out_connection();
+ void on_handshake_complete(const boost::uuids::uuid &id, bool is_income);
+ void on_connection_close(const boost::uuids::uuid &id);
+
//! Run the logic for the next epoch immediately. Only use in testing.
void run_epoch();
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 99430b2b0..3f1885423 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -120,6 +120,7 @@ public:
if (shared)
{
core.get().get_blockchain_storage().add_block_notify(cryptonote::listener::zmq_pub::chain_main{shared});
+ core.get().get_blockchain_storage().add_miner_notify(cryptonote::listener::zmq_pub::miner_data{shared});
core.get().set_txpool_listener(cryptonote::listener::zmq_pub::txpool_add{shared});
}
}
diff --git a/src/net/socks.h b/src/net/socks.h
index 739c972ab..506b53195 100644
--- a/src/net/socks.h
+++ b/src/net/socks.h
@@ -201,6 +201,13 @@ namespace socks
std::shared_ptr<client> self_;
void operator()(boost::system::error_code error = boost::system::error_code{});
};
+
+ //! Calls `async_close` on `self` at destruction. NOP if `nullptr`.
+ struct close_on_exit
+ {
+ std::shared_ptr<client> self;
+ ~close_on_exit() { async_close{std::move(self)}(); }
+ };
};
template<typename Handler>
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index 8dd551d1e..36977346d 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -338,6 +338,7 @@ namespace nodetool
}
};
+ net::socks::client::close_on_exit close_client{};
boost::unique_future<client_result> socks_result{};
{
boost::promise<client_result> socks_promise{};
@@ -346,6 +347,7 @@ namespace nodetool
auto client = net::socks::make_connect_client(
boost::asio::ip::tcp::socket{service}, net::socks::version::v4a, notify{std::move(socks_promise)}
);
+ close_client.self = client;
if (!start_socks(std::move(client), proxy, remote))
return boost::none;
}
@@ -367,7 +369,10 @@ namespace nodetool
{
auto result = socks_result.get();
if (!result.first)
+ {
+ close_client.self.reset();
return {std::move(result.second)};
+ }
MERROR("Failed to make socks connection to " << remote.str() << " (via " << proxy << "): " << result.first.message());
}
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 59a6e5091..3660d2edb 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -111,15 +111,11 @@ namespace nodetool
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
{
p2p_connection_context_t()
- : fluff_txs(),
- flush_time(std::chrono::steady_clock::time_point::max()),
- peer_id(0),
+ : peer_id(0),
support_flags(0),
m_in_timedsync(false)
{}
- std::vector<cryptonote::blobdata> fluff_txs;
- std::chrono::steady_clock::time_point flush_time;
peerid_type peer_id;
uint32_t support_flags;
bool m_in_timedsync;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index b8cf2d124..438b8ca11 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -1432,6 +1432,7 @@ namespace nodetool
ape.first_seen = first_seen_stamp ? first_seen_stamp : time(nullptr);
zone.m_peerlist.append_with_peer_anchor(ape);
+ zone.m_notifier.on_handshake_complete(con->m_connection_id, con->m_is_income);
zone.m_notifier.new_out_connection();
LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK.");
@@ -2559,6 +2560,8 @@ namespace nodetool
return 1;
}
+ zone.m_notifier.on_handshake_complete(context.m_connection_id, context.m_is_income);
+
if(has_too_many_connections(context.m_remote_address))
{
LOG_PRINT_CCONTEXT_L1("CONNECTION FROM " << context.m_remote_address.host_str() << " REFUSED, too many connections from the same address");
@@ -2683,6 +2686,9 @@ namespace nodetool
zone.m_peerlist.remove_from_peer_anchor(na);
}
+ if (!zone.m_net_server.is_stop_signal_sent()) {
+ zone.m_notifier.on_connection_close(context.m_connection_id);
+ }
m_payload_handler.on_connection_close(context);
MINFO("["<< epee::net_utils::print_connection_context(context) << "] CLOSE CONNECTION");
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 664f058e2..5bfb3fea6 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -939,14 +939,26 @@ namespace cryptonote
LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool");
}
- std::vector<std::string>::const_iterator txhi = req.txs_hashes.begin();
- std::vector<crypto::hash>::const_iterator vhi = vh.begin();
+ CHECK_AND_ASSERT_MES(txs.size() + missed_txs.size() == vh.size(), false, "mismatched number of txs");
+
+ auto txhi = req.txs_hashes.cbegin();
+ auto vhi = vh.cbegin();
+ auto missedi = missed_txs.cbegin();
+
for(auto& tx: txs)
{
res.txs.push_back(COMMAND_RPC_GET_TRANSACTIONS::entry());
COMMAND_RPC_GET_TRANSACTIONS::entry &e = res.txs.back();
+ while (missedi != missed_txs.end() && *missedi == *vhi)
+ {
+ ++vhi;
+ ++txhi;
+ ++missedi;
+ }
+
crypto::hash tx_hash = *vhi++;
+ CHECK_AND_ASSERT_MES(tx_hash == std::get<0>(tx), false, "mismatched tx hash");
e.tx_hash = *txhi++;
e.prunable_hash = epee::string_tools::pod_to_hex(std::get<2>(tx));
if (req.split || req.prune || std::get<3>(tx).empty())
@@ -1828,6 +1840,43 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_getminerdata(const COMMAND_RPC_GETMINERDATA::request& req, COMMAND_RPC_GETMINERDATA::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ if(!check_core_ready())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY;
+ error_resp.message = "Core is busy";
+ return false;
+ }
+
+ crypto::hash prev_id, seed_hash;
+ difficulty_type difficulty;
+
+ std::vector<tx_block_template_backlog_entry> tx_backlog;
+ if (!m_core.get_miner_data(res.major_version, res.height, prev_id, seed_hash, difficulty, res.median_weight, res.already_generated_coins, tx_backlog))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to get miner data";
+ LOG_ERROR("Failed to get miner data");
+ return false;
+ }
+
+ res.tx_backlog.clear();
+ res.tx_backlog.reserve(tx_backlog.size());
+
+ for (const auto& entry : tx_backlog)
+ {
+ res.tx_backlog.emplace_back(COMMAND_RPC_GETMINERDATA::response::tx_backlog_entry{string_tools::pod_to_hex(entry.id), entry.weight, entry.fee});
+ }
+
+ res.prev_id = string_tools::pod_to_hex(prev_id);
+ res.seed_hash = string_tools::pod_to_hex(seed_hash);
+ res.difficulty = cryptonote::hex(difficulty);
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
RPC_TRACKER(submitblock);
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index dcf6b4e4b..68dbeed7d 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -146,6 +146,7 @@ namespace cryptonote
MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
MAP_JON_RPC_WE("get_block_template", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
+ MAP_JON_RPC_WE("get_miner_data", on_getminerdata, COMMAND_RPC_GETMINERDATA)
MAP_JON_RPC_WE("submit_block", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE_IF("generateblocks", on_generateblocks, COMMAND_RPC_GENERATEBLOCKS, !m_restricted)
@@ -226,6 +227,7 @@ namespace cryptonote
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, const connection_context *ctx = NULL);
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_getminerdata(const COMMAND_RPC_GETMINERDATA::request& req, COMMAND_RPC_GETMINERDATA::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index bbcb27f1c..0a6af0404 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -88,7 +88,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 3
-#define CORE_RPC_VERSION_MINOR 5
+#define CORE_RPC_VERSION_MINOR 8
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -938,6 +938,56 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_GETMINERDATA
+ {
+ struct request_t: public rpc_request_base
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t: public rpc_response_base
+ {
+ uint8_t major_version;
+ uint64_t height;
+ std::string prev_id;
+ std::string seed_hash;
+ std::string difficulty;
+ uint64_t median_weight;
+ uint64_t already_generated_coins;
+
+ struct tx_backlog_entry
+ {
+ std::string id;
+ uint64_t weight;
+ uint64_t fee;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(id)
+ KV_SERIALIZE(weight)
+ KV_SERIALIZE(fee)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ std::vector<tx_backlog_entry> tx_backlog;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(major_version)
+ KV_SERIALIZE(height)
+ KV_SERIALIZE(prev_id)
+ KV_SERIALIZE(seed_hash)
+ KV_SERIALIZE(difficulty)
+ KV_SERIALIZE(median_weight)
+ KV_SERIALIZE(already_generated_coins)
+ KV_SERIALIZE(tx_backlog)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_SUBMITBLOCK
{
typedef std::vector<std::string> request;
diff --git a/src/rpc/zmq_pub.cpp b/src/rpc/zmq_pub.cpp
index 0dffffac6..f45f70e04 100644
--- a/src/rpc/zmq_pub.cpp
+++ b/src/rpc/zmq_pub.cpp
@@ -48,6 +48,8 @@
#include "cryptonote_basic/events.h"
#include "misc_log_ex.h"
#include "serialization/json_object.h"
+#include "ringct/rctTypes.h"
+#include "cryptonote_core/cryptonote_tx_utils.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.zmq"
@@ -57,6 +59,7 @@ namespace
constexpr const char txpool_signal[] = "tx_signal";
using chain_writer = void(epee::byte_stream&, std::uint64_t, epee::span<const cryptonote::block>);
+ using miner_writer = void(epee::byte_stream&, uint8_t, uint64_t, const crypto::hash&, const crypto::hash&, cryptonote::difficulty_type, uint64_t, uint64_t, const std::vector<cryptonote::tx_block_template_backlog_entry>&);
using txpool_writer = void(epee::byte_stream&, epee::span<const cryptonote::txpool_event>);
template<typename F>
@@ -116,13 +119,30 @@ namespace
const epee::span<const cryptonote::block> blocks;
};
+ //! Object for miner data serialization
+ struct miner_data
+ {
+ uint8_t major_version;
+ uint64_t height;
+ const crypto::hash& prev_id;
+ const crypto::hash& seed_hash;
+ cryptonote::difficulty_type diff;
+ uint64_t median_weight;
+ uint64_t already_generated_coins;
+ const std::vector<cryptonote::tx_block_template_backlog_entry>& tx_backlog;
+ };
+
//! Object for "minimal" tx serialization
struct minimal_txpool
{
const cryptonote::transaction& tx;
+ crypto::hash hash;
+ uint64_t blob_size;
+ uint64_t weight;
+ uint64_t fee;
};
- void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_chain self)
+ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_chain& self)
{
namespace adapt = boost::adaptors;
@@ -143,19 +163,27 @@ namespace
dest.EndObject();
}
- void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_txpool self)
+ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const miner_data& self)
{
- crypto::hash id{};
- std::size_t blob_size = 0;
- if (!get_transaction_hash(self.tx, id, blob_size))
- {
- MERROR("ZMQ/Pub failure: get_transaction_hash");
- return;
- }
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, major_version, self.major_version);
+ INSERT_INTO_JSON_OBJECT(dest, height, self.height);
+ INSERT_INTO_JSON_OBJECT(dest, prev_id, self.prev_id);
+ INSERT_INTO_JSON_OBJECT(dest, seed_hash, self.seed_hash);
+ INSERT_INTO_JSON_OBJECT(dest, difficulty, cryptonote::hex(self.diff));
+ INSERT_INTO_JSON_OBJECT(dest, median_weight, self.median_weight);
+ INSERT_INTO_JSON_OBJECT(dest, already_generated_coins, self.already_generated_coins);
+ INSERT_INTO_JSON_OBJECT(dest, tx_backlog, self.tx_backlog);
+ dest.EndObject();
+ }
+ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_txpool& self)
+ {
dest.StartObject();
- INSERT_INTO_JSON_OBJECT(dest, id, id);
- INSERT_INTO_JSON_OBJECT(dest, blob_size, blob_size);
+ INSERT_INTO_JSON_OBJECT(dest, id, self.hash);
+ INSERT_INTO_JSON_OBJECT(dest, blob_size, self.blob_size);
+ INSERT_INTO_JSON_OBJECT(dest, weight, self.weight);
+ INSERT_INTO_JSON_OBJECT(dest, fee, self.fee);
dest.EndObject();
}
@@ -169,6 +197,11 @@ namespace
json_pub(buf, minimal_chain{height, blocks});
}
+ void json_miner_data(epee::byte_stream& buf, uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, cryptonote::difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<cryptonote::tx_block_template_backlog_entry>& tx_backlog)
+ {
+ json_pub(buf, miner_data{major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog});
+ }
+
// boost::adaptors are in place "views" - no copy/move takes place
// moving transactions (via sort, etc.), is expensive!
@@ -187,7 +220,7 @@ namespace
namespace adapt = boost::adaptors;
const auto to_minimal_tx = [](const cryptonote::txpool_event& event)
{
- return minimal_txpool{event.tx};
+ return minimal_txpool{event.tx, event.hash, event.blob_size, event.weight, cryptonote::get_tx_fee(event.tx)};
};
json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_minimal_tx)));
}
@@ -198,6 +231,11 @@ namespace
{u8"json-minimal-chain_main", json_minimal_chain}
}};
+ constexpr const std::array<context<miner_writer>, 1> miner_contexts =
+ {{
+ {u8"json-full-miner_data", json_miner_data},
+ }};
+
constexpr const std::array<context<txpool_writer>, 2> txpool_contexts =
{{
{u8"json-full-txpool_add", json_full_txpool},
@@ -321,6 +359,7 @@ namespace cryptonote { namespace listener
zmq_pub::zmq_pub(void* context)
: relay_(),
chain_subs_{{0}},
+ miner_subs_{{0}},
txpool_subs_{{0}},
sync_()
{
@@ -328,6 +367,7 @@ zmq_pub::zmq_pub(void* context)
throw std::logic_error{"ZMQ context cannot be NULL"};
verify_sorted(chain_contexts, "chain_contexts");
+ verify_sorted(miner_contexts, "miner_contexts");
verify_sorted(txpool_contexts, "txpool_contexts");
relay_.reset(zmq_socket(context, ZMQ_PAIR));
@@ -348,22 +388,25 @@ bool zmq_pub::sub_request(boost::string_ref message)
message.remove_prefix(1);
const auto chain_range = get_range(chain_contexts, message);
+ const auto miner_range = get_range(miner_contexts, message);
const auto txpool_range = get_range(txpool_contexts, message);
- if (!chain_range.empty() || !txpool_range.empty())
+ if (!chain_range.empty() || !miner_range.empty() || !txpool_range.empty())
{
MDEBUG("Client " << (tag ? "subscribed" : "unsubscribed") << " to " <<
- chain_range.size() << " chain topic(s) and " << txpool_range.size() << " txpool topic(s)");
+ chain_range.size() << " chain topic(s), " << miner_range.size() << " miner topic(s) and " << txpool_range.size() << " txpool topic(s)");
const boost::lock_guard<boost::mutex> lock{sync_};
switch (tag)
{
case 0:
remove_subscriptions(chain_subs_, chain_range, chain_contexts.begin());
+ remove_subscriptions(miner_subs_, miner_range, miner_contexts.begin());
remove_subscriptions(txpool_subs_, txpool_range, txpool_contexts.begin());
return true;
case 1:
add_subscriptions(chain_subs_, chain_range, chain_contexts.begin());
+ add_subscriptions(miner_subs_, miner_range, miner_contexts.begin());
add_subscriptions(txpool_subs_, txpool_range, txpool_contexts.begin());
return true;
default:
@@ -436,6 +479,25 @@ std::size_t zmq_pub::send_chain_main(const std::uint64_t height, const epee::spa
return 0;
}
+std::size_t zmq_pub::send_miner_data(uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<tx_block_template_backlog_entry>& tx_backlog)
+{
+ boost::unique_lock<boost::mutex> guard{sync_};
+
+ const auto subs_copy = miner_subs_;
+ guard.unlock();
+
+ for (const std::size_t sub : subs_copy)
+ {
+ if (sub)
+ {
+ auto messages = make_pubs(subs_copy, miner_contexts, major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog);
+ guard.lock();
+ return send_messages(relay_.get(), messages);
+ }
+ }
+ return 0;
+}
+
std::size_t zmq_pub::send_txpool_add(std::vector<txpool_event> txes)
{
if (txes.empty())
@@ -466,6 +528,15 @@ void zmq_pub::chain_main::operator()(const std::uint64_t height, epee::span<cons
MERROR("Unable to send ZMQ/Pub - ZMQ server destroyed");
}
+void zmq_pub::miner_data::operator()(uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<tx_block_template_backlog_entry>& tx_backlog) const
+{
+ const std::shared_ptr<zmq_pub> self = self_.lock();
+ if (self)
+ self->send_miner_data(major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog);
+ else
+ MERROR("Unable to send ZMQ/Pub - ZMQ server destroyed");
+}
+
void zmq_pub::txpool_add::operator()(std::vector<cryptonote::txpool_event> txes) const
{
const std::shared_ptr<zmq_pub> self = self_.lock();
diff --git a/src/rpc/zmq_pub.h b/src/rpc/zmq_pub.h
index 02e6b8103..c636e1d7b 100644
--- a/src/rpc/zmq_pub.h
+++ b/src/rpc/zmq_pub.h
@@ -39,6 +39,7 @@
#include "cryptonote_basic/fwd.h"
#include "net/zmq.h"
#include "span.h"
+#include "cryptonote_basic/difficulty.h"
namespace cryptonote { namespace listener
{
@@ -59,6 +60,7 @@ class zmq_pub
net::zmq::socket relay_;
std::deque<std::vector<txpool_event>> txes_;
std::array<std::size_t, 2> chain_subs_;
+ std::array<std::size_t, 1> miner_subs_;
std::array<std::size_t, 2> txpool_subs_;
boost::mutex sync_; //!< Synchronizes counts in `*_subs_` arrays.
@@ -88,6 +90,11 @@ class zmq_pub
\return Number of ZMQ messages sent to relay. */
std::size_t send_chain_main(std::uint64_t height, epee::span<const cryptonote::block> blocks);
+ /*! Send a `ZMQ_PUB` notification for a new miner data.
+ Thread-safe.
+ \return Number of ZMQ messages sent to relay. */
+ std::size_t send_miner_data(uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<tx_block_template_backlog_entry>& tx_backlog);
+
/*! Send a `ZMQ_PUB` notification for new tx(es) being added to the local
pool. Thread-safe.
\return Number of ZMQ messages sent to relay. */
@@ -100,6 +107,13 @@ class zmq_pub
void operator()(std::uint64_t height, epee::span<const cryptonote::block> blocks) const;
};
+ //! Callable for `send_miner_data` with weak ownership to `zmq_pub` object.
+ struct miner_data
+ {
+ std::weak_ptr<zmq_pub> self_;
+ void operator()(uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<tx_block_template_backlog_entry>& tx_backlog) const;
+ };
+
//! Callable for `send_txpool_add` with weak ownership to `zmq_pub` object.
struct txpool_add
{
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index 28e207ff2..b03da1edc 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -34,6 +34,7 @@
#include <type_traits>
#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_core/cryptonote_tx_utils.h"
// drop macro from windows.h
#ifdef GetObject
@@ -1411,6 +1412,27 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribu
GET_FROM_JSON_OBJECT(val, dist.data.base, base);
}
+void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_block_template_backlog_entry& entry)
+{
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, id, entry.id);
+ INSERT_INTO_JSON_OBJECT(dest, weight, entry.weight);
+ INSERT_INTO_JSON_OBJECT(dest, fee, entry.fee);
+ dest.EndObject();
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_block_template_backlog_entry& entry)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, entry.id, id);
+ GET_FROM_JSON_OBJECT(val, entry.weight, weight);
+ GET_FROM_JSON_OBJECT(val, entry.fee, fee);
+}
+
} // namespace json
} // namespace cryptonote
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index de14c8911..15459163b 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -304,6 +304,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_distribution& dist);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist);
+void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_block_template_backlog_entry& entry);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_block_template_backlog_entry& entry);
+
template <typename Map>
typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Map& map);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 81c18352f..a18152e68 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -8817,12 +8817,12 @@ bool simple_wallet::export_transfers(const std::vector<std::string>& args_)
// header
file <<
- boost::format("%8.8s,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s") %
+ boost::format("%8.8s,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%106.106s,%20.20s,%s,%s") %
tr("block") % tr("direction") % tr("unlocked") % tr("timestamp") % tr("amount") % tr("running balance") % tr("hash") % tr("payment ID") % tr("fee") % tr("destination") % tr("amount") % tr("index") % tr("note")
<< std::endl;
uint64_t running_balance = 0;
- auto formatter = boost::format("%8.8llu,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s");
+ auto formatter = boost::format("%8.8llu,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%106.106s,%20.20s,\"%s\",%s");
for (const auto& transfer : all_transfers)
{
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index 4d8c5225e..3e7a32557 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -265,11 +265,17 @@ namespace
virtual void callback(cryptonote::levin::detail::p2p_context& context) override final
{}
- virtual void on_connection_new(cryptonote::levin::detail::p2p_context&) override final
- {}
+ virtual void on_connection_new(cryptonote::levin::detail::p2p_context& context) override final
+ {
+ if (notifier)
+ notifier->on_handshake_complete(context.m_connection_id, context.m_is_income);
+ }
- virtual void on_connection_close(cryptonote::levin::detail::p2p_context&) override final
- {}
+ virtual void on_connection_close(cryptonote::levin::detail::p2p_context& context) override final
+ {
+ if (notifier)
+ notifier->on_connection_close(context.m_connection_id);
+ }
public:
test_receiver()
@@ -306,6 +312,8 @@ namespace
{
return get_raw_message(notified_);
}
+
+ std::shared_ptr<cryptonote::levin::notify> notifier{};
};
class levin_notify : public ::testing::Test
@@ -343,13 +351,16 @@ namespace
EXPECT_EQ(connection_ids_.size(), connections_->get_connections_count());
}
- cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public, bool pad_txs)
+ std::shared_ptr<cryptonote::levin::notify> make_notifier(const std::size_t noise_size, bool is_public, bool pad_txs)
{
epee::byte_slice noise = nullptr;
if (noise_size)
noise = epee::levin::make_noise_notify(noise_size);
epee::net_utils::zone zone = is_public ? epee::net_utils::zone::public_ : epee::net_utils::zone::i2p;
- return cryptonote::levin::notify{io_service_, connections_, std::move(noise), zone, pad_txs, events_};
+ receiver_.notifier.reset(
+ new cryptonote::levin::notify{io_service_, connections_, std::move(noise), zone, pad_txs, events_}
+ );
+ return receiver_.notifier;
}
boost::uuids::random_generator random_generator_;
@@ -517,7 +528,8 @@ TEST_F(levin_notify, defaulted)
TEST_F(levin_notify, fluff_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -563,7 +575,8 @@ TEST_F(levin_notify, fluff_without_padding)
TEST_F(levin_notify, stem_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -635,7 +648,8 @@ TEST_F(levin_notify, stem_without_padding)
TEST_F(levin_notify, stem_no_outs_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(true);
@@ -690,7 +704,8 @@ TEST_F(levin_notify, stem_no_outs_without_padding)
TEST_F(levin_notify, local_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -762,7 +777,8 @@ TEST_F(levin_notify, local_without_padding)
TEST_F(levin_notify, forward_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -834,7 +850,8 @@ TEST_F(levin_notify, forward_without_padding)
TEST_F(levin_notify, block_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -863,7 +880,8 @@ TEST_F(levin_notify, block_without_padding)
TEST_F(levin_notify, none_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -892,7 +910,8 @@ TEST_F(levin_notify, none_without_padding)
TEST_F(levin_notify, fluff_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -938,7 +957,8 @@ TEST_F(levin_notify, fluff_with_padding)
TEST_F(levin_notify, stem_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1005,7 +1025,8 @@ TEST_F(levin_notify, stem_with_padding)
TEST_F(levin_notify, stem_no_outs_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(true);
@@ -1059,7 +1080,8 @@ TEST_F(levin_notify, stem_no_outs_with_padding)
TEST_F(levin_notify, local_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1126,7 +1148,8 @@ TEST_F(levin_notify, local_with_padding)
TEST_F(levin_notify, forward_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1193,7 +1216,8 @@ TEST_F(levin_notify, forward_with_padding)
TEST_F(levin_notify, block_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1222,7 +1246,8 @@ TEST_F(levin_notify, block_with_padding)
TEST_F(levin_notify, none_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1251,7 +1276,8 @@ TEST_F(levin_notify, none_with_padding)
TEST_F(levin_notify, private_fluff_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1302,7 +1328,8 @@ TEST_F(levin_notify, private_fluff_without_padding)
TEST_F(levin_notify, private_stem_without_padding)
{
// private mode always uses fluff but marked as stem
- cryptonote::levin::notify notifier = make_notifier(0, false, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1353,7 +1380,8 @@ TEST_F(levin_notify, private_stem_without_padding)
TEST_F(levin_notify, private_local_without_padding)
{
// private mode always uses fluff but marked as stem
- cryptonote::levin::notify notifier = make_notifier(0, false, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1404,7 +1432,8 @@ TEST_F(levin_notify, private_local_without_padding)
TEST_F(levin_notify, private_forward_without_padding)
{
// private mode always uses fluff but marked as stem
- cryptonote::levin::notify notifier = make_notifier(0, false, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1455,7 +1484,8 @@ TEST_F(levin_notify, private_forward_without_padding)
TEST_F(levin_notify, private_block_without_padding)
{
// private mode always uses fluff but marked as stem
- cryptonote::levin::notify notifier = make_notifier(0, false, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1485,7 +1515,8 @@ TEST_F(levin_notify, private_block_without_padding)
TEST_F(levin_notify, private_none_without_padding)
{
// private mode always uses fluff but marked as stem
- cryptonote::levin::notify notifier = make_notifier(0, false, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1514,7 +1545,8 @@ TEST_F(levin_notify, private_none_without_padding)
TEST_F(levin_notify, private_fluff_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1564,7 +1596,8 @@ TEST_F(levin_notify, private_fluff_with_padding)
TEST_F(levin_notify, private_stem_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1614,7 +1647,8 @@ TEST_F(levin_notify, private_stem_with_padding)
TEST_F(levin_notify, private_local_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1664,7 +1698,8 @@ TEST_F(levin_notify, private_local_with_padding)
TEST_F(levin_notify, private_forward_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1714,7 +1749,8 @@ TEST_F(levin_notify, private_forward_with_padding)
TEST_F(levin_notify, private_block_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1743,7 +1779,8 @@ TEST_F(levin_notify, private_block_with_padding)
TEST_F(levin_notify, private_none_with_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, false, true);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -1774,7 +1811,8 @@ TEST_F(levin_notify, stem_mappings)
{
static constexpr const unsigned test_connections_count = (CRYPTONOTE_DANDELIONPP_STEMS + 1) * 2;
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < test_connections_count; ++count)
add_connection(count % 2 == 0);
@@ -1897,7 +1935,8 @@ TEST_F(levin_notify, fluff_multiple)
{
static constexpr const unsigned test_connections_count = (CRYPTONOTE_DANDELIONPP_STEMS + 1) * 2;
- cryptonote::levin::notify notifier = make_notifier(0, true, false);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+ auto &notifier = *notifier_ptr;
for (unsigned count = 0; count < test_connections_count; ++count)
add_connection(count % 2 == 0);
@@ -2015,7 +2054,8 @@ TEST_F(levin_notify, noise)
txs[0].resize(1900, 'h');
const boost::uuids::uuid incoming_id = random_generator_();
- cryptonote::levin::notify notifier = make_notifier(2048, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(2048, false, true);
+ auto &notifier = *notifier_ptr;
{
const auto status = notifier.get_status();
@@ -2106,7 +2146,8 @@ TEST_F(levin_notify, noise_stem)
txs[0].resize(1900, 'h');
const boost::uuids::uuid incoming_id = random_generator_();
- cryptonote::levin::notify notifier = make_notifier(2048, false, true);
+ std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(2048, false, true);
+ auto &notifier = *notifier_ptr;
{
const auto status = notifier.get_status();
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index f91e2e78b..b2da2eb60 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -35,6 +35,7 @@
#include "cryptonote_core/i_core_events.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.inl"
+#include <condition_variable>
#define MAKE_IPV4_ADDRESS(a,b,c,d) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),0}
#define MAKE_IPV4_ADDRESS_PORT(a,b,c,d,e) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),e}
@@ -914,5 +915,263 @@ TEST(cryptonote_protocol_handler, race_condition)
remove_tree(dir);
}
+TEST(node_server, race_condition)
+{
+ struct contexts {
+ using cryptonote = cryptonote::cryptonote_connection_context;
+ using p2p = nodetool::p2p_connection_context_t<cryptonote>;
+ };
+ using context_t = contexts::cryptonote;
+ using options_t = boost::program_options::variables_map;
+ using options_description_t = boost::program_options::options_description;
+ using worker_t = std::thread;
+ struct protocol_t {
+ private:
+ using p2p_endpoint_t = nodetool::i_p2p_endpoint<context_t>;
+ using lock_t = std::mutex;
+ using condition_t = std::condition_variable_any;
+ using unique_lock_t = std::unique_lock<lock_t>;
+ p2p_endpoint_t *p2p_endpoint;
+ lock_t lock;
+ condition_t condition;
+ bool started{};
+ size_t counter{};
+ public:
+ using payload_t = cryptonote::CORE_SYNC_DATA;
+ using blob_t = cryptonote::blobdata;
+ using connection_context = context_t;
+ using payload_type = payload_t;
+ using relay_t = cryptonote::relay_method;
+ using string_t = std::string;
+ using span_t = epee::span<const uint8_t>;
+ using blobs_t = epee::span<const cryptonote::blobdata>;
+ using connections_t = std::list<cryptonote::connection_info>;
+ using block_queue_t = cryptonote::block_queue;
+ using stripes_t = std::pair<uint32_t, uint32_t>;
+ struct core_events_t: cryptonote::i_core_events {
+ uint64_t get_current_blockchain_height() const override { return {}; }
+ bool is_synchronized() const override { return {}; }
+ void on_transactions_relayed(blobs_t blobs, relay_t relay) override {}
+ };
+ int handle_invoke_map(bool is_notify, int command, const span_t in, string_t &out, context_t &context, bool &handled) {
+ return {};
+ }
+ bool on_idle() {
+ if (not p2p_endpoint)
+ return {};
+ {
+ unique_lock_t guard(lock);
+ if (not started)
+ started = true;
+ else
+ return {};
+ }
+ std::vector<blob_t> txs(128 / 64 * 1024 * 1024, blob_t(1, 'x'));
+ worker_t worker([this]{
+ p2p_endpoint->for_each_connection(
+ [this](context_t &, uint64_t, uint32_t){
+ {
+ unique_lock_t guard(lock);
+ ++counter;
+ condition.notify_all();
+ condition.wait(guard, [this]{ return counter >= 3; });
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(8));
+ return false;
+ }
+ );
+ });
+ {
+ unique_lock_t guard(lock);
+ ++counter;
+ condition.notify_all();
+ condition.wait(guard, [this]{ return counter >= 3; });
+ ++counter;
+ condition.notify_all();
+ condition.wait(guard, [this]{ return counter >= 5; });
+ }
+ p2p_endpoint->send_txs(
+ std::move(txs),
+ epee::net_utils::zone::public_,
+ {},
+ relay_t::fluff
+ );
+ worker.join();
+ return {};
+ }
+ bool init(const options_t &options) { return {}; }
+ bool deinit() { return {}; }
+ void set_p2p_endpoint(p2p_endpoint_t *p2p_endpoint) {
+ this->p2p_endpoint = p2p_endpoint;
+ }
+ bool process_payload_sync_data(const payload_t &payload, contexts::p2p &context, bool is_inital) {
+ context.m_state = context_t::state_normal;
+ context.m_needed_objects.resize(512 * 1024);
+ {
+ unique_lock_t guard(lock);
+ ++counter;
+ condition.notify_all();
+ condition.wait(guard, [this]{ return counter >= 3; });
+ ++counter;
+ condition.notify_all();
+ condition.wait(guard, [this]{ return counter >= 5; });
+ }
+ return true;
+ }
+ bool get_payload_sync_data(blob_t &blob) { return {}; }
+ bool get_payload_sync_data(payload_t &payload) { return {}; }
+ bool on_callback(context_t &context) { return {}; }
+ core_events_t &get_core(){ static core_events_t core_events; return core_events;}
+ void log_connections() {}
+ connections_t get_connections() { return {}; }
+ const block_queue_t &get_block_queue() const {
+ static block_queue_t block_queue;
+ return block_queue;
+ }
+ void stop() {}
+ void on_connection_close(context_t &context) {}
+ void set_max_out_peers(unsigned int max) {}
+ bool no_sync() const { return {}; }
+ void set_no_sync(bool value) {}
+ string_t get_peers_overview() const { return {}; }
+ stripes_t get_next_needed_pruning_stripe() const { return {}; }
+ bool needs_new_sync_connections() const { return {}; }
+ bool is_busy_syncing() { return {}; }
+ };
+ using node_server_t = nodetool::node_server<protocol_t>;
+ auto conduct_test = [](protocol_t &protocol){
+ struct messages {
+ struct core {
+ using sync = cryptonote::CORE_SYNC_DATA;
+ };
+ using handshake = nodetool::COMMAND_HANDSHAKE_T<core::sync>;
+ };
+ using handler_t = epee::levin::async_protocol_handler<context_t>;
+ using connection_t = epee::net_utils::connection<handler_t>;
+ using connection_ptr = boost::shared_ptr<connection_t>;
+ using shared_state_t = typename connection_t::shared_state;
+ using shared_state_ptr = std::shared_ptr<shared_state_t>;
+ using io_context_t = boost::asio::io_service;
+ using work_t = boost::asio::io_service::work;
+ using work_ptr = std::shared_ptr<work_t>;
+ using workers_t = std::vector<std::thread>;
+ using endpoint_t = boost::asio::ip::tcp::endpoint;
+ using event_t = epee::simple_event;
+ struct command_handler_t: epee::levin::levin_commands_handler<context_t> {
+ using span_t = epee::span<const uint8_t>;
+ using string_t = std::string;
+ int invoke(int, const span_t, string_t &, context_t &) override { return {}; }
+ int notify(int, const span_t, context_t &) override { return {}; }
+ void callback(context_t &) override {}
+ void on_connection_new(context_t &) override {}
+ void on_connection_close(context_t &) override {}
+ ~command_handler_t() override {}
+ static void destroy(epee::levin::levin_commands_handler<context_t>* ptr) { delete ptr; }
+ };
+ io_context_t io_context;
+ work_ptr work = std::make_shared<work_t>(io_context);
+ workers_t workers;
+ while (workers.size() < 4) {
+ workers.emplace_back([&io_context]{
+ io_context.run();
+ });
+ }
+ io_context.post([&]{
+ protocol.on_idle();
+ });
+ io_context.post([&]{
+ protocol.on_idle();
+ });
+ shared_state_ptr shared_state = std::make_shared<shared_state_t>();
+ shared_state->set_handler(new command_handler_t, &command_handler_t::destroy);
+ connection_ptr conn{new connection_t(io_context, shared_state, {}, {})};
+ endpoint_t endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 48080);
+ conn->socket().connect(endpoint);
+ conn->socket().set_option(boost::asio::ip::tcp::socket::reuse_address(true));
+ conn->start({}, {});
+ context_t context;
+ conn->get_context(context);
+ event_t handshaked;
+ typename messages::handshake::request_t msg{{
+ ::config::NETWORK_ID,
+ 58080,
+ }};
+ epee::net_utils::async_invoke_remote_command2<typename messages::handshake::response>(
+ context,
+ messages::handshake::ID,
+ msg,
+ *shared_state,
+ [conn, &handshaked](int code, const typename messages::handshake::response &msg, context_t &context){
+ EXPECT_TRUE(code >= 0);
+ handshaked.raise();
+ },
+ P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT
+ );
+ handshaked.wait();
+ conn->strand_.post([conn]{
+ conn->cancel();
+ });
+ conn.reset();
+ work.reset();
+ for (auto& w: workers) {
+ w.join();
+ }
+ };
+ using path_t = boost::filesystem::path;
+ using ec_t = boost::system::error_code;
+ auto create_dir = []{
+ ec_t ec;
+ path_t path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("daemon-%%%%%%%%%%%%%%%%", ec);
+ if (ec)
+ return path_t{};
+ auto success = boost::filesystem::create_directory(path, ec);
+ if (not ec && success)
+ return path;
+ return path_t{};
+ };
+ auto remove_tree = [](const path_t &path){
+ ec_t ec;
+ boost::filesystem::remove_all(path, ec);
+ };
+ const auto dir = create_dir();
+ ASSERT_TRUE(not dir.empty());
+ protocol_t protocol{};
+ node_server_t node_server(protocol);
+ protocol.set_p2p_endpoint(&node_server);
+ node_server.init(
+ [&dir]{
+ options_t options;
+ boost::program_options::store(
+ boost::program_options::command_line_parser({
+ "--p2p-bind-ip=127.0.0.1",
+ "--p2p-bind-port=48080",
+ "--out-peers=0",
+ "--data-dir",
+ dir.string(),
+ "--no-igd",
+ "--add-exclusive-node=127.0.0.1:48080",
+ "--check-updates=disabled",
+ "--disable-dns-checkpoints",
+ }).options([]{
+ options_description_t options_description{};
+ cryptonote::core::init_options(options_description);
+ node_server_t::init_options(options_description);
+ return options_description;
+ }()).run(),
+ options
+ );
+ return options;
+ }()
+ );
+ worker_t worker([&]{
+ node_server.run();
+ });
+ conduct_test(protocol);
+ node_server.send_stop_signal();
+ worker.join();
+ node_server.deinit();
+ remove_tree(dir);
+}
+
namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; }
namespace cryptonote { template class t_cryptonote_protocol_handler<test_core>; }
diff --git a/utils/python-rpc/framework/daemon.py b/utils/python-rpc/framework/daemon.py
index 435bc93f8..3bfe01826 100644
--- a/utils/python-rpc/framework/daemon.py
+++ b/utils/python-rpc/framework/daemon.py
@@ -53,6 +53,14 @@ class Daemon(object):
return self.rpc.send_json_rpc_request(getblocktemplate)
get_block_template = getblocktemplate
+ def get_miner_data(self):
+ get_miner_data = {
+ 'method': 'get_miner_data',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_json_rpc_request(get_miner_data)
+
def send_raw_transaction(self, tx_as_hex, do_not_relay = False, do_sanity_checks = True, client = ""):
send_raw_transaction = {
'client': client,