Skip to content

Commit

Permalink
RPC and ZeroMQ APIs to support p2pool
Browse files Browse the repository at this point in the history
Adds the following:

- "get_miner_data" to RPC API
- "json-miner-data" to ZeroMQ subscriber contexts

Both provide the necessary data to create a custom block template. They are used by p2pool.

Data provided:

- major fork version
- current height
- previous block id
- RandomX seed hash
- network difficulty
- median block weight
- coins mined by the network so far
- mineable mempool transactions
  • Loading branch information
SChernykh committed Sep 11, 2021
1 parent 2d3ce2d commit dfee15e
Show file tree
Hide file tree
Showing 20 changed files with 403 additions and 24 deletions.
2 changes: 2 additions & 0 deletions docs/ZMQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
1 change: 1 addition & 0 deletions src/cryptonote_basic/cryptonote_basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "misc_language.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
#include "cryptonote_basic/fwd.h"

namespace cryptonote
{
Expand Down
2 changes: 2 additions & 0 deletions src/cryptonote_basic/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};
}
1 change: 1 addition & 0 deletions src/cryptonote_basic/fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ namespace cryptonote
struct block;
class transaction;
struct txpool_event;
struct tx_block_template_backlog_entry;
}
69 changes: 68 additions & 1 deletion src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,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;
Expand Down Expand Up @@ -1784,6 +1790,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
Expand Down Expand Up @@ -4362,6 +4392,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
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});
Expand Down Expand Up @@ -5270,7 +5301,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)
{
Expand All @@ -5279,6 +5310,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
Expand Down Expand Up @@ -5531,6 +5571,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>&, bool) 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;
Expand Down
39 changes: 37 additions & 2 deletions src/cryptonote_core/blockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,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;

/************************************************************************/
/* */
/************************************************************************/
Expand Down Expand Up @@ -370,6 +373,22 @@ namespace cryptonote
bool create_block_template(block& b, 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);
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
*
Expand Down Expand Up @@ -775,7 +794,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
Expand Down Expand Up @@ -1157,7 +1183,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
Expand Down Expand Up @@ -1537,5 +1564,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
10 changes: 8 additions & 2 deletions src/cryptonote_core/cryptonote_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1063,8 +1063,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);}
Expand Down Expand Up @@ -1403,6 +1404,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);
Expand Down
7 changes: 7 additions & 0 deletions src/cryptonote_core/cryptonote_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ namespace cryptonote
virtual bool get_block_template(block& b, 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) override;
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`.
Expand Down
9 changes: 9 additions & 0 deletions src/cryptonote_core/cryptonote_tx_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ namespace cryptonote
END_SERIALIZE()
};

//---------------------------------------------------------------

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);
Expand Down
39 changes: 35 additions & 4 deletions src/cryptonote_core/tx_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,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);
Expand Down Expand Up @@ -1222,11 +1248,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)
Expand All @@ -1238,7 +1264,7 @@ namespace cryptonote
}
return tx;
}
const cryptonote::blobdata &txblob;
const cryptonote::blobdata_ref &txblob;
const crypto::hash &txid;
transaction &tx;
bool parsed;
Expand Down Expand Up @@ -1289,6 +1315,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++)
Expand Down
10 changes: 10 additions & 0 deletions src/cryptonote_core/tx_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,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
*
Expand Down Expand Up @@ -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;

/**
Expand Down
1 change: 1 addition & 0 deletions src/daemon/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ struct t_internals {
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});
}
}
Expand Down
Loading

0 comments on commit dfee15e

Please sign in to comment.