aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp90
-rw-r--r--src/cryptonote_core/blockchain.h30
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp28
-rw-r--r--src/cryptonote_core/tx_pool.cpp22
-rw-r--r--src/cryptonote_core/tx_pool.h9
5 files changed, 148 insertions, 31 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index c2445bba5..e96dc6bb6 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -156,9 +156,10 @@ static const struct {
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0),
- m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false),
+ m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
- m_difficulty_for_next_block(1)
+ m_difficulty_for_next_block(1),
+ m_btc_valid(false)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@@ -632,6 +633,7 @@ block Blockchain::pop_block_from_blockchain()
update_next_cumulative_size_limit();
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ invalidate_block_template_cache();
return popped_block;
}
@@ -642,6 +644,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_timestamps_and_difficulties_height = 0;
m_alternative_chains.clear();
+ invalidate_block_template_cache();
m_db->reset();
m_hardfork->init();
@@ -1212,9 +1215,26 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size;
uint64_t already_generated_coins;
+ uint64_t pool_cookie;
CRITICAL_REGION_BEGIN(m_blockchain_lock);
height = m_db->height();
+ if (m_btc_valid) {
+ // The pool cookie is atomic. The lack of locking is OK, as if it changes
+ // just as we compare it, we'll just use a slightly old template, but
+ // this would be the case anyway if we'd lock, and the change happened
+ // just after the block template was created
+ if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce && m_btc_pool_cookie == m_tx_pool.cookie()) {
+ MDEBUG("Using cached template");
+ m_btc.timestamp = time(NULL); // update timestamp unconditionally
+ b = m_btc;
+ diffic = m_btc_difficulty;
+ expected_reward = m_btc_expected_reward;
+ return true;
+ }
+ MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()));
+ invalidate_block_template_cache();
+ }
b.major_version = m_hardfork->get_current_version();
b.minor_version = m_hardfork->get_ideal_version();
@@ -1241,6 +1261,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
{
return false;
}
+ pool_cookie = m_tx_pool.cookie();
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0;
uint64_t real_fee = 0;
@@ -1355,6 +1376,8 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is now good");
#endif
+
+ cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
@@ -3697,6 +3720,7 @@ leave:
// appears to be a NOP *and* is called elsewhere. wat?
m_tx_pool.on_blockchain_inc(new_height, id);
get_difficulty_for_next_block(); // just to cache it
+ invalidate_block_template_cache();
return true;
}
@@ -3877,11 +3901,13 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
store_blockchain();
m_sync_counter = 0;
}
- else if (m_db_blocks_per_sync && m_sync_counter >= m_db_blocks_per_sync)
+ else if (m_db_sync_threshold && ((m_db_sync_on_blocks && m_sync_counter >= m_db_sync_threshold) || (!m_db_sync_on_blocks && m_bytes_to_sync >= m_db_sync_threshold)))
{
+ MDEBUG("Sync threshold met, syncing");
if(m_db_sync_mode == db_async)
{
m_sync_counter = 0;
+ m_bytes_to_sync = 0;
m_async_service.dispatch(boost::bind(&Blockchain::store_blockchain, this));
}
else if(m_db_sync_mode == db_sync)
@@ -4042,6 +4068,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
TIME_MEASURE_START(prepare);
bool stop_batch;
uint64_t bytes = 0;
+ size_t total_txs = 0;
// Order of locking must be:
// m_incoming_tx_lock (optional)
@@ -4070,7 +4097,9 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
{
bytes += tx_blob.size();
}
+ total_txs += entry.txs.size();
}
+ m_bytes_to_sync += bytes;
while (!(stop_batch = m_db->batch_start(blocks_entry.size(), bytes))) {
m_blockchain_lock.unlock();
m_tx_pool.unlock();
@@ -4129,7 +4158,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
break;
}
- blocks[i].push_back(block);
+ blocks[i].push_back(std::move(block));
std::advance(it, 1);
}
}
@@ -4150,7 +4179,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
break;
}
- blocks[i].push_back(block);
+ blocks[i].push_back(std::move(block));
std::advance(it, 1);
}
@@ -4206,6 +4235,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
std::map<uint64_t, std::vector<uint64_t>> offset_map;
// [output] stores all output_data_t for each absolute_offset
std::map<uint64_t, std::vector<output_data_t>> tx_map;
+ std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs);
#define SCAN_TABLE_QUIT(m) \
do { \
@@ -4215,6 +4245,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
+ size_t tx_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -4222,12 +4253,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
for (const auto &tx_blob : entry.txs)
{
- crypto::hash tx_hash = null_hash;
- crypto::hash tx_prefix_hash = null_hash;
- transaction tx;
+ if (tx_index >= txes.size())
+ SCAN_TABLE_QUIT("tx_index is out of sync");
+ transaction &tx = txes[tx_index].first;
+ crypto::hash &tx_prefix_hash = txes[tx_index].second;
+ ++tx_index;
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
+ if (!parse_and_validate_tx_base_from_blob(tx_blob, tx))
SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
+ cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash);
auto its = m_scan_table.find(tx_prefix_hash);
if (its != m_scan_table.end())
@@ -4313,9 +4347,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
}
- int total_txs = 0;
-
// now generate a table for each tx_prefix and k_image hashes
+ tx_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -4323,14 +4356,12 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
for (const auto &tx_blob : entry.txs)
{
- crypto::hash tx_hash = null_hash;
- crypto::hash tx_prefix_hash = null_hash;
- transaction tx;
-
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
- SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
+ if (tx_index >= txes.size())
+ SCAN_TABLE_QUIT("tx_index is out of sync");
+ const transaction &tx = txes[tx_index].first;
+ const crypto::hash &tx_prefix_hash = txes[tx_index].second;
+ ++tx_index;
- ++total_txs;
auto its = m_scan_table.find(tx_prefix_hash);
if (its == m_scan_table.end())
SCAN_TABLE_QUIT("Tx not found on scan table from incoming blocks.");
@@ -4419,7 +4450,7 @@ bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, con
return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes);
}
-void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync)
+void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync)
{
if (sync_mode == db_defaultsync)
{
@@ -4428,7 +4459,8 @@ void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync,
}
m_db_sync_mode = sync_mode;
m_fast_sync = fast_sync;
- m_db_blocks_per_sync = blocks_per_sync;
+ m_db_sync_on_blocks = sync_on_blocks;
+ m_db_sync_threshold = sync_threshold;
m_max_prepare_blocks_threads = maxthreads;
}
@@ -4662,6 +4694,24 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t he
return m_db->for_all_outputs(amount, f);;
}
+void Blockchain::invalidate_block_template_cache()
+{
+ MDEBUG("Invalidating block template cache");
+ m_btc_valid = false;
+}
+
+void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie)
+{
+ MDEBUG("Setting block template cache");
+ m_btc = b;
+ m_btc_address = address;
+ m_btc_nonce = nonce;
+ m_btc_difficulty = diff;
+ m_btc_expected_reward = expected_reward;
+ m_btc_pool_cookie = pool_cookie;
+ m_btc_valid = true;
+}
+
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const;
template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::vector<cryptonote::blobdata>&, std::vector<crypto::hash>&, bool) const;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index d95c8ed15..2292ffbf3 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -729,11 +729,12 @@ namespace cryptonote
* @brief sets various performance options
*
* @param maxthreads max number of threads when preparing blocks for addition
- * @param blocks_per_sync number of blocks to cache before syncing to database
+ * @param sync_on_blocks whether to sync based on blocks or bytes
+ * @param sync_threshold number of blocks/bytes to cache before syncing to database
* @param sync_mode the ::blockchain_db_sync_mode to use
* @param fast_sync sync using built-in block hashes as trusted
*/
- void set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync,
+ void set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold,
blockchain_db_sync_mode sync_mode, bool fast_sync);
/**
@@ -1017,11 +1018,13 @@ namespace cryptonote
bool m_fast_sync;
bool m_show_time_stats;
bool m_db_default_sync;
- uint64_t m_db_blocks_per_sync;
+ bool m_db_sync_on_blocks;
+ uint64_t m_db_sync_threshold;
uint64_t m_max_prepare_blocks_threads;
uint64_t m_fake_pow_calc_time;
uint64_t m_fake_scan_time;
uint64_t m_sync_counter;
+ uint64_t m_bytes_to_sync;
std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height;
@@ -1052,6 +1055,15 @@ namespace cryptonote
std::atomic<bool> m_cancel;
+ // block template cache
+ block m_btc;
+ account_public_address m_btc_address;
+ blobdata m_btc_nonce;
+ difficulty_type m_btc_difficulty;
+ uint64_t m_btc_pool_cookie;
+ uint64_t m_btc_expected_reward;
+ bool m_btc_valid;
+
/**
* @brief collects the keys for all outputs being "spent" as an input
*
@@ -1407,5 +1419,17 @@ namespace cryptonote
* that implicit data.
*/
bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
+
+ /**
+ * @brief invalidates any cached block template
+ */
+ void invalidate_block_template_cache();
+
+ /**
+ * @brief stores a new cached block template
+ *
+ * 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 expected_reward, uint64_t pool_cookie);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 18490c65e..d0db38799 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -439,9 +439,10 @@ namespace cryptonote
MGINFO("Loading blockchain from folder " << folder.string() << " ...");
const std::string filename = folder.string();
- // default to fast:async:1
+ // default to fast:async:1 if overridden
blockchain_db_sync_mode sync_mode = db_defaultsync;
- uint64_t blocks_per_sync = 1;
+ bool sync_on_blocks = true;
+ uint64_t sync_threshold = 1;
if (m_nettype == FAKECHAIN)
{
@@ -491,7 +492,7 @@ namespace cryptonote
else if(options[0] == "fastest")
{
db_flags = DBF_FASTEST;
- blocks_per_sync = 1000; // default to fastest:async:1000
+ sync_threshold = 1000; // default to fastest:async:1000
sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async;
}
else
@@ -509,9 +510,22 @@ namespace cryptonote
if(options.size() >= 3 && !safemode)
{
char *endptr;
- uint64_t bps = strtoull(options[2].c_str(), &endptr, 0);
- if (*endptr == '\0')
- blocks_per_sync = bps;
+ uint64_t threshold = strtoull(options[2].c_str(), &endptr, 0);
+ if (*endptr == '\0' || !strcmp(endptr, "blocks"))
+ {
+ sync_on_blocks = true;
+ sync_threshold = threshold;
+ }
+ else if (!strcmp(endptr, "bytes"))
+ {
+ sync_on_blocks = false;
+ sync_threshold = threshold;
+ }
+ else
+ {
+ LOG_ERROR("Invalid db sync mode: " << options[2]);
+ return false;
+ }
}
if (db_salvage)
@@ -528,7 +542,7 @@ namespace cryptonote
}
m_blockchain_storage.set_user_options(blocks_threads,
- blocks_per_sync, sync_mode, fast_sync);
+ sync_on_blocks, sync_threshold, sync_mode, fast_sync);
const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
const cryptonote::test_options regtest_test_options = {
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index eac0f1f57..5807867d9 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -102,7 +102,7 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
- tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0)
+ tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0)
{
}
@@ -306,6 +306,8 @@ namespace cryptonote
tvc.m_verifivation_failed = false;
m_txpool_size += blob_size;
+ ++m_cookie;
+
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size));
prune(m_txpool_max_size);
@@ -341,6 +343,7 @@ namespace cryptonote
bytes = m_txpool_max_size;
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
+ bool changed = false;
// this will never remove the first one, but we don't care
auto it = --m_txs_by_fee_and_receive_time.end();
@@ -377,6 +380,7 @@ namespace cryptonote
remove_transaction_keyimages(tx);
MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--);
+ changed = true;
}
catch (const std::exception &e)
{
@@ -384,6 +388,8 @@ namespace cryptonote
return;
}
}
+ if (changed)
+ ++m_cookie;
if (m_txpool_size > bytes)
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes);
}
@@ -401,6 +407,7 @@ namespace cryptonote
auto ins_res = kei_image_set.insert(id);
CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set");
}
+ ++m_cookie;
return true;
}
//---------------------------------------------------------------------------------
@@ -435,6 +442,7 @@ namespace cryptonote
}
}
+ ++m_cookie;
return true;
}
//---------------------------------------------------------------------------------
@@ -480,6 +488,7 @@ namespace cryptonote
}
m_txs_by_fee_and_receive_time.erase(sorted_it);
+ ++m_cookie;
return true;
}
//---------------------------------------------------------------------------------
@@ -553,6 +562,7 @@ namespace cryptonote
// ignore error
}
}
+ ++m_cookie;
}
return true;
}
@@ -1051,6 +1061,7 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
+ bool changed = false;
LockedTXN lock(m_blockchain);
for(size_t i = 0; i!= tx.vin.size(); i++)
{
@@ -1071,6 +1082,7 @@ namespace cryptonote
{
MDEBUG("Marking " << txid << " as double spending " << itk.k_image);
meta.double_spend_seen = true;
+ changed = true;
try
{
m_blockchain.update_txpool_tx(txid, meta);
@@ -1084,6 +1096,8 @@ namespace cryptonote
}
}
}
+ if (changed)
+ ++m_cookie;
}
//---------------------------------------------------------------------------------
std::string tx_memory_pool::print_pool(bool short_format) const
@@ -1305,6 +1319,8 @@ namespace cryptonote
}
}
}
+ if (n_removed > 0)
+ ++m_cookie;
return n_removed;
}
//---------------------------------------------------------------------------------
@@ -1361,6 +1377,10 @@ namespace cryptonote
}
}
}
+
+ m_cookie = 0;
+
+ // Ignore deserialization error
return true;
}
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 4ade7ddbe..4abfef85c 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -362,6 +362,13 @@ namespace cryptonote
*/
size_t validate(uint8_t version);
+ /**
+ * @brief return the cookie
+ *
+ * @return the cookie
+ */
+ uint64_t cookie() const { return m_cookie; }
+
/**
* @brief get the cumulative txpool size in bytes
*
@@ -549,6 +556,8 @@ private:
//!< container for transactions organized by fee per size and receive time
sorted_tx_container m_txs_by_fee_and_receive_time;
+ std::atomic<uint64_t> m_cookie; //!< incremented at each change
+
/**
* @brief get an iterator to a transaction in the sorted container
*