aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.h7
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp55
-rw-r--r--src/cryptonote_core/cryptonote_core.h14
-rw-r--r--src/cryptonote_core/tx_pool.cpp106
-rw-r--r--src/cryptonote_core/tx_pool.h17
5 files changed, 172 insertions, 27 deletions
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 7a94f6358..355d0de1a 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -159,6 +159,13 @@ namespace cryptonote
bool deinit();
/**
+ * @brief get a set of blockchain checkpoint hashes
+ *
+ * @return set of blockchain checkpoint hashes
+ */
+ const checkpoints& get_checkpoints() const { return m_checkpoints; }
+
+ /**
* @brief assign a set of blockchain checkpoint hashes
*
* @param chk_pts the set of checkpoints to assign
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index a78f5d673..31e4e0414 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -252,6 +252,10 @@ namespace cryptonote
m_pprotocol = &m_protocol_stub;
}
//-----------------------------------------------------------------------------------
+ const checkpoints& core::get_checkpoints() const
+ {
+ return m_blockchain_storage.get_checkpoints();
+ }
void core::set_checkpoints(checkpoints&& chk_pts)
{
m_blockchain_storage.set_checkpoints(std::move(chk_pts));
@@ -1406,21 +1410,66 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ bool core::notify_txpool_event(const epee::span<const cryptonote::blobdata> tx_blobs, epee::span<const crypto::hash> tx_hashes, epee::span<const cryptonote::transaction> txs, const std::vector<bool> &just_broadcasted) const
+ {
+ if (!m_zmq_pub)
+ return true;
+
+ if (tx_blobs.size() != tx_hashes.size() || tx_blobs.size() != txs.size() || tx_blobs.size() != just_broadcasted.size())
+ return false;
+
+ /* Publish txs via ZMQ that are "just broadcasted" by the daemon. This is
+ done here in addition to `handle_incoming_txs` in order to guarantee txs
+ are pub'd via ZMQ when we know the daemon has/will broadcast to other
+ nodes & *after* the tx is visible in the pool. This should get called
+ when the user submits a tx to a daemon in the "fluff" epoch relaying txs
+ via a public network. */
+ if (std::count(just_broadcasted.begin(), just_broadcasted.end(), true) == 0)
+ return true;
+
+ std::vector<txpool_event> results{};
+ results.resize(tx_blobs.size());
+ for (std::size_t i = 0; i < results.size(); ++i)
+ {
+ results[i].tx = std::move(txs[i]);
+ results[i].hash = std::move(tx_hashes[i]);
+ results[i].blob_size = tx_blobs[i].size();
+ results[i].weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, results[i].blob_size);
+ results[i].res = just_broadcasted[i];
+ }
+
+ m_zmq_pub(std::move(results));
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
void core::on_transactions_relayed(const epee::span<const cryptonote::blobdata> tx_blobs, const relay_method tx_relay)
{
+ // lock ensures duplicate txs aren't pub'd via zmq
+ CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
+
std::vector<crypto::hash> tx_hashes{};
tx_hashes.resize(tx_blobs.size());
+ std::vector<cryptonote::transaction> txs{};
+ txs.resize(tx_blobs.size());
+
for (std::size_t i = 0; i < tx_blobs.size(); ++i)
{
- cryptonote::transaction tx{};
- if (!parse_and_validate_tx_from_blob(tx_blobs[i], tx, tx_hashes[i]))
+ if (!parse_and_validate_tx_from_blob(tx_blobs[i], txs[i], tx_hashes[i]))
{
LOG_ERROR("Failed to parse relayed transaction");
return;
}
}
- m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay);
+
+ std::vector<bool> just_broadcasted{};
+ just_broadcasted.reserve(tx_hashes.size());
+
+ m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay, just_broadcasted);
+
+ if (m_zmq_pub && matches_category(tx_relay, relay_category::legacy))
+ notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted);
}
//-----------------------------------------------------------------------------------------------
bool core::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)
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 0b36730b6..6dc513570 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -437,6 +437,13 @@ namespace cryptonote
void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol);
/**
+ * @copydoc Blockchain::get_checkpoints
+ *
+ * @note see Blockchain::get_checkpoints()
+ */
+ const checkpoints& get_checkpoints() const;
+
+ /**
* @copydoc Blockchain::set_checkpoints
*
* @note see Blockchain::set_checkpoints()
@@ -1036,6 +1043,13 @@ namespace cryptonote
bool relay_txpool_transactions();
/**
+ * @brief sends notification of txpool events to subscribers
+ *
+ * @return true on success, false otherwise
+ */
+ bool notify_txpool_event(const epee::span<const cryptonote::blobdata> tx_blobs, epee::span<const crypto::hash> tx_hashes, epee::span<const cryptonote::transaction> txs, const std::vector<bool> &just_broadcasted) const;
+
+ /**
* @brief checks DNS versions
*
* @return true on success, false otherwise
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index a68da0e62..2a514ceae 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -402,6 +402,19 @@ namespace cryptonote
m_txpool_max_weight = bytes;
}
//---------------------------------------------------------------------------------
+ void tx_memory_pool::reduce_txpool_weight(size_t weight)
+ {
+ if (weight > m_txpool_weight)
+ {
+ MERROR("Underflow in txpool weight");
+ m_txpool_weight = 0;
+ }
+ else
+ {
+ m_txpool_weight -= weight;
+ }
+ }
+ //---------------------------------------------------------------------------------
void tx_memory_pool::prune(size_t bytes)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -423,8 +436,14 @@ namespace cryptonote
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
{
- MERROR("Failed to find tx_meta in txpool");
- return;
+ static bool warned = false;
+ if (!warned)
+ {
+ MERROR("Failed to find tx_meta in txpool (will only print once)");
+ warned = true;
+ }
+ --it;
+ continue;
}
// don't prune the kept_by_block ones, they're likely added because we're adding a block with those
if (meta.kept_by_block)
@@ -442,7 +461,7 @@ namespace cryptonote
// remove first, in case this throws, so key images aren't removed
MINFO("Pruning tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first);
m_blockchain.remove_txpool_tx(txid);
- m_txpool_weight -= meta.weight;
+ reduce_txpool_weight(meta.weight);
remove_transaction_keyimages(tx, txid);
MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--);
@@ -562,7 +581,7 @@ namespace cryptonote
// remove first, in case this throws, so key images aren't removed
m_blockchain.remove_txpool_tx(id);
- m_txpool_weight -= tx_weight;
+ reduce_txpool_weight(tx_weight);
remove_transaction_keyimages(tx, id);
lock.commit();
}
@@ -725,7 +744,7 @@ namespace cryptonote
{
// remove first, so we only remove key images if the tx removal succeeds
m_blockchain.remove_txpool_tx(txid);
- m_txpool_weight -= entry.second;
+ reduce_txpool_weight(entry.second);
remove_transaction_keyimages(tx, txid);
}
}
@@ -820,8 +839,10 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
- void tx_memory_pool::set_relayed(const epee::span<const crypto::hash> hashes, const relay_method method)
+ void tx_memory_pool::set_relayed(const epee::span<const crypto::hash> hashes, const relay_method method, std::vector<bool> &just_broadcasted)
{
+ just_broadcasted.clear();
+
crypto::random_poisson_seconds embargo_duration{dandelionpp_embargo_average};
const auto now = std::chrono::system_clock::now();
uint64_t next_relay = uint64_t{std::numeric_limits<time_t>::max()};
@@ -831,12 +852,14 @@ namespace cryptonote
LockedTXN lock(m_blockchain.get_db());
for (const auto& hash : hashes)
{
+ bool was_just_broadcasted = false;
try
{
txpool_tx_meta_t meta;
if (m_blockchain.get_txpool_tx_meta(hash, meta))
{
// txes can be received as "stem" or "fluff" in either order
+ const bool already_broadcasted = meta.matches(relay_category::broadcasted);
meta.upgrade_relay_method(method);
meta.relayed = true;
@@ -849,6 +872,9 @@ namespace cryptonote
meta.last_relayed_time = std::chrono::system_clock::to_time_t(now);
m_blockchain.update_txpool_tx(hash, meta);
+
+ // wait until db update succeeds to ensure tx is visible in the pool
+ was_just_broadcasted = !already_broadcasted && meta.matches(relay_category::broadcasted);
}
}
catch (const std::exception &e)
@@ -856,6 +882,7 @@ namespace cryptonote
MERROR("Failed to update txpool transaction metadata: " << e.what());
// continue
}
+ just_broadcasted.emplace_back(was_just_broadcasted);
}
lock.commit();
set_if_less(m_next_check, time_t(next_relay));
@@ -917,26 +944,61 @@ namespace cryptonote
{
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)))
+
+ std::vector<tx_block_template_backlog_entry> tmp;
+ uint64_t total_weight = 0;
+
+ // First get everything from the mempool, filter it later
+ m_blockchain.for_all_txpool_txes([&tmp, &total_weight](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref*){
+ tmp.emplace_back(tx_block_template_backlog_entry{txid, meta.weight, meta.fee});
+ total_weight += meta.weight;
+ return true;
+ }, false, include_sensitive ? relay_category::all : relay_category::broadcasted);
+
+ // Limit backlog to 112.5% of current median weight. This is enough to mine a full block with the optimal block reward
+ const uint64_t median_weight = m_blockchain.get_current_cumulative_block_weight_median();
+ const uint64_t max_backlog_weight = median_weight + (median_weight / 8);
+
+ // If the total weight is too high, choose the best paying transactions
+ if (total_weight > max_backlog_weight)
+ std::sort(tmp.begin(), tmp.end(), [](const auto& a, const auto& b){ return a.fee * b.weight > b.fee * a.weight; });
+
+ backlog.clear();
+ uint64_t w = 0;
+
+ std::unordered_set<crypto::key_image> k_images;
+
+ for (const tx_block_template_backlog_entry& e : tmp)
+ {
+ try
{
- MERROR("Failed to parse tx from txpool");
- // continue
- return true;
- }
- tx.set_hash(txid);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(e.id, meta))
+ continue;
- tmp_meta = meta;
+ cryptonote::blobdata txblob;
+ if (!m_blockchain.get_txpool_tx_blob(e.id, txblob, relay_category::all))
+ continue;
- if (is_transaction_ready_to_go(tmp_meta, txid, *bd, tx))
- backlog.push_back({txid, meta.weight, meta.fee});
+ cryptonote::transaction tx;
+ if (is_transaction_ready_to_go(meta, e.id, txblob, tx))
+ {
+ if (have_key_images(k_images, tx))
+ continue;
+ append_key_images(k_images, tx);
- return true;
- }, true, category);
+ backlog.push_back(e);
+ w += e.weight;
+ if (w > max_backlog_weight)
+ break;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to check transaction readiness: " << e.what());
+ // continue, not fatal
+ }
+ }
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 62bef6c06..45623fd14 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -266,7 +266,11 @@ 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
+ * @brief get (hash, weight, fee) for transactions in the pool - the minimum required information to create a block template
+ *
+ * Not all transactions in the pool will be returned for performance reasons
+ * If there are too many transactions in the pool, only the highest-paying transactions
+ * will be returned - but enough for the miner to create a full block
*
* @param backlog return-by-reference that data
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
@@ -353,8 +357,10 @@ namespace cryptonote
*
* @param hashes list of tx hashes that are about to be relayed
* @param tx_relay update how the tx left this node
+ * @param just_broadcasted true if a tx was just broadcasted
+ *
*/
- void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay);
+ void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay, std::vector<bool> &just_broadcasted);
/**
* @brief get the total number of transactions in the pool
@@ -406,6 +412,13 @@ namespace cryptonote
*/
void set_txpool_max_weight(size_t bytes);
+ /**
+ * @brief reduce the cumulative txpool weight by the weight provided
+ *
+ * @param weight the weight to reduce the total txpool weight by
+ */
+ void reduce_txpool_weight(size_t weight);
+
#define CURRENT_MEMPOOL_ARCHIVE_VER 11
#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13