aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core/blockchain.cpp
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2019-03-05 14:09:19 +0200
committerRiccardo Spagni <ric@spagni.net>2019-03-05 14:09:19 +0200
commited6aa76cca69e4f6d0b84eb55ef7061dc4b6fc77 (patch)
treeb5cb515fa3db4dea131a3e1a3162ce3ee820b946 /src/cryptonote_core/blockchain.cpp
parentMerge pull request #5231 (diff)
parentwallet_rpc_server: avoid repeated string allocations when parsing (diff)
downloadmonero-ed6aa76cca69e4f6d0b84eb55ef7061dc4b6fc77.tar.xz
Merge pull request #5100
c4851024 wallet_rpc_server: avoid repeated string allocations when parsing (moneromooo-monero) 88c85c18 cryptonote: avoid double parsing blocks when syncing (moneromooo-monero) 9feda0ee cryptonote: speed up calculating coinbase tx prunable hash (moneromooo-monero) 238401d4 core: avoid double parsing blocks after hoh (moneromooo-monero) dc5a7609 blockchain: avoid unneeded block copy (moneromooo-monero) 79b4e9f3 save some database calls when getting top block hash and height (moneromooo-monero) 98278808 blockchain: avoid pointless transaction copy and temporary (moneromooo-monero) 07d655e4 blockchain: avoid duplicate block hash computation (moneromooo-monero) f75d51ab core: avoid calculating tx prefix hash when we don't need it (moneromooo-monero) b044d03a Avoid repeated (de)serialization when syncing (moneromooo-monero) b747e836 wallet2: don't calculate prefix hash when we don't need it (moneromooo-monero) e69477bf db: speedup block addition (moneromooo-monero)
Diffstat (limited to 'src/cryptonote_core/blockchain.cpp')
-rw-r--r--src/cryptonote_core/blockchain.cpp107
1 files changed, 58 insertions, 49 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 9f1f376f3..2c1b9d447 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -468,8 +468,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
uint64_t num_popped_blocks = 0;
while (!m_db->is_read_only())
{
- const uint64_t top_height = m_db->height() - 1;
- const crypto::hash top_id = m_db->top_block_hash();
+ uint64_t top_height;
+ const crypto::hash top_id = m_db->top_block_hash(&top_height);
const block top_block = m_db->get_top_block();
const uint8_t ideal_hf_version = get_ideal_hard_fork_version(top_height);
if (ideal_hf_version <= 1 || ideal_hf_version == top_block.major_version)
@@ -509,7 +509,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
{
m_timestamps_and_difficulties_height = 0;
m_hardfork->reorganize_from_chain_height(get_current_blockchain_height());
- m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ uint64_t top_block_height;
+ crypto::hash top_block_hash = get_tail_id(top_block_height);
+ m_tx_pool.on_blockchain_dec(top_block_height, top_block_hash);
}
if (test_options && test_options->long_term_block_weight_window)
@@ -702,7 +704,9 @@ block Blockchain::pop_block_from_blockchain()
m_check_txin_table.clear();
CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit");
- m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ uint64_t top_block_height;
+ crypto::hash top_block_hash = get_tail_id(top_block_height);
+ m_tx_pool.on_blockchain_dec(top_block_height, top_block_hash);
invalidate_block_template_cache();
return popped_block;
@@ -729,8 +733,7 @@ crypto::hash Blockchain::get_tail_id(uint64_t& height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- height = m_db->height() - 1;
- return get_tail_id();
+ return m_db->top_block_hash(&height);
}
//------------------------------------------------------------------
crypto::hash Blockchain::get_tail_id() const
@@ -891,8 +894,9 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
CRITICAL_REGION_LOCAL(m_blockchain_lock);
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
- auto height = m_db->height();
- top_hash = get_tail_id(); // get it again now that we have the lock
+ uint64_t height;
+ top_hash = get_tail_id(height); // get it again now that we have the lock
+ ++height; // top block height to blockchain height
// ND: Speedup
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
// then when the next block difficulty is queried, push the latest height data and
@@ -1806,11 +1810,12 @@ uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
uint64_t num_outs = m_db->get_num_outputs(amount);
// ensure we don't include outputs that aren't yet eligible to be used
// outpouts are sorted by height
+ const uint64_t blockchain_height = m_db->height();
while (num_outs > 0)
{
const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1);
const uint64_t height = m_db->get_tx_block_height(toi.first);
- if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
+ if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= blockchain_height)
break;
--num_outs;
}
@@ -3325,14 +3330,15 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
return false;
}
+ const auto h = m_db->height();
+
// if not enough blocks, no proper median yet, return true
- if(m_db->height() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
+ if(h < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
{
return true;
}
std::vector<uint64_t> timestamps;
- auto h = m_db->height();
// need most recent 60 blocks, get index of first of those
size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
@@ -3345,7 +3351,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
return check_block_timestamp(timestamps, b, median_ts);
}
//------------------------------------------------------------------
-void Blockchain::return_tx_to_pool(std::vector<transaction> &txs)
+void Blockchain::return_tx_to_pool(std::vector<std::pair<transaction, blobdata>> &txs)
{
uint8_t version = get_current_hard_fork_version();
for (auto& tx : txs)
@@ -3356,9 +3362,11 @@ void Blockchain::return_tx_to_pool(std::vector<transaction> &txs)
// that might not be always true. Unlikely though, and always relaying
// these again might cause a spike of traffic as many nodes re-relay
// all the transactions in a popped block when a reorg happens.
- if (!m_tx_pool.add_tx(tx, tvc, true, true, false, version))
+ const size_t weight = get_transaction_weight(tx.first, tx.second.size());
+ const crypto::hash tx_hash = get_transaction_hash(tx.first);
+ if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, true, true, false, version))
{
- MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx) << " to tx_pool");
+ MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx.first) << " to tx_pool");
}
}
}
@@ -3371,11 +3379,12 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
for (const auto &txid: txids)
{
cryptonote::transaction tx;
+ cryptonote::blobdata txblob;
size_t tx_weight;
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen;
MINFO("Removing txid " << txid << " from the pool");
- if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
+ if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
{
MERROR("Failed to remove txid " << txid << " from the pool");
res = false;
@@ -3398,9 +3407,12 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
static bool seen_future_version = false;
m_db->block_txn_start(true);
- if(bl.prev_id != get_tail_id())
+ uint64_t blockchain_height;
+ const crypto::hash top_hash = get_tail_id(blockchain_height);
+ ++blockchain_height; // block height to chain height
+ if(bl.prev_id != top_hash)
{
- MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << get_tail_id());
+ MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << top_hash);
bvc.m_verifivation_failed = true;
leave:
m_db->block_txn_stop();
@@ -3469,13 +3481,12 @@ leave:
bool precomputed = false;
bool fast_check = false;
#if defined(PER_BLOCK_CHECKPOINT)
- if (m_db->height() < m_blocks_hash_check.size())
+ if (blockchain_height < m_blocks_hash_check.size())
{
- auto hash = get_block_hash(bl);
- const auto &expected_hash = m_blocks_hash_check[m_db->height()];
+ const auto &expected_hash = m_blocks_hash_check[blockchain_height];
if (expected_hash != crypto::null_hash)
{
- if (memcmp(&hash, &expected_hash, sizeof(hash)) != 0)
+ if (memcmp(&id, &expected_hash, sizeof(hash)) != 0)
{
MERROR_VER("Block with id is INVALID: " << id << ", expected " << expected_hash);
bvc.m_verifivation_failed = true;
@@ -3485,7 +3496,7 @@ leave:
}
else
{
- MCINFO("verify", "No pre-validated hash at height " << m_db->height() << ", verifying fully");
+ MCINFO("verify", "No pre-validated hash at height " << blockchain_height << ", verifying fully");
}
}
else
@@ -3498,7 +3509,7 @@ leave:
proof_of_work = it->second;
}
else
- proof_of_work = get_block_longhash(bl, m_db->height());
+ proof_of_work = get_block_longhash(bl, blockchain_height);
// validate proof_of_work versus difficulty target
if(!check_hash(proof_of_work, current_diffic))
@@ -3511,9 +3522,9 @@ leave:
// If we're at a checkpoint, ensure that our hardcoded checkpoint hash
// is correct.
- if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()))
+ if(m_checkpoints.is_in_checkpoint_zone(blockchain_height))
{
- if(!m_checkpoints.check_block(get_current_blockchain_height(), id))
+ if(!m_checkpoints.check_block(blockchain_height, id))
{
LOG_ERROR("CHECKPOINT VALIDATION FAILED");
bvc.m_verifivation_failed = true;
@@ -3528,7 +3539,7 @@ leave:
TIME_MEASURE_START(t3);
// sanity check basic miner tx properties;
- if(!prevalidate_miner_transaction(bl, m_db->height()))
+ if(!prevalidate_miner_transaction(bl, blockchain_height))
{
MERROR_VER("Block with id: " << id << " failed to pass prevalidation");
bvc.m_verifivation_failed = true;
@@ -3538,7 +3549,7 @@ leave:
size_t coinbase_weight = get_transaction_weight(bl.miner_tx);
size_t cumulative_block_weight = coinbase_weight;
- std::vector<transaction> txs;
+ std::vector<std::pair<transaction, blobdata>> txs;
key_images_container keys;
uint64_t fee_summary = 0;
@@ -3557,7 +3568,8 @@ leave:
txs.reserve(bl.tx_hashes.size());
for (const crypto::hash& tx_id : bl.tx_hashes)
{
- transaction tx;
+ transaction tx_tmp;
+ blobdata txblob;
size_t tx_weight = 0;
uint64_t fee = 0;
bool relayed = false, do_not_relay = false, double_spend_seen = false;
@@ -3577,7 +3589,7 @@ leave:
TIME_MEASURE_START(bb);
// get transaction with hash <tx_id> from tx_pool
- if(!m_tx_pool.take_tx(tx_id, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
+ if(!m_tx_pool.take_tx(tx_id, tx_tmp, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
{
MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
bvc.m_verifivation_failed = true;
@@ -3590,7 +3602,8 @@ leave:
// add the transaction to the temp list of transactions, so we can either
// store the list of transactions all at once or return the ones we've
// taken from the tx_pool back to it if the block fails verification.
- txs.push_back(tx);
+ txs.push_back(std::make_pair(std::move(tx_tmp), std::move(txblob)));
+ transaction &tx = txs.back().first;
TIME_MEASURE_START(dd);
// FIXME: the storage should not be responsible for validation.
@@ -3655,7 +3668,7 @@ leave:
TIME_MEASURE_START(vmt);
uint64_t base_reward = 0;
- uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ uint64_t already_generated_coins = blockchain_height ? m_db->get_block_already_generated_coins(blockchain_height - 1) : 0;
if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version()))
{
MERROR_VER("Block with id: " << id << " has incorrect miner transaction");
@@ -3676,8 +3689,8 @@ leave:
// at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a
// subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state.
already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY;
- if(m_db->height())
- cumulative_difficulty += m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+ if(blockchain_height)
+ cumulative_difficulty += m_db->get_block_cumulative_difficulty(blockchain_height - 1);
TIME_MEASURE_FINISH(block_processing_time);
if(precomputed)
@@ -3691,7 +3704,8 @@ leave:
try
{
uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight);
- new_height = m_db->add_block(bl, block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs);
+ cryptonote::blobdata bd = cryptonote::block_to_blob(bl);
+ new_height = m_db->add_block(std::make_pair(std::move(bl), std::move(bd)), block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs);
}
catch (const KEY_IMAGE_EXISTS& e)
{
@@ -3875,11 +3889,9 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
return true;
}
//------------------------------------------------------------------
-bool Blockchain::add_new_block(const block& bl_, block_verification_context& bvc)
+bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
{
LOG_PRINT_L3("Blockchain::" << __func__);
- //copy block here to let modify block.target
- block bl = bl_;
crypto::hash id = get_block_hash(bl);
CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
@@ -3918,10 +3930,11 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor
CRITICAL_REGION_LOCAL(m_blockchain_lock);
stop_batch = m_db->batch_start();
+ const uint64_t blockchain_height = m_db->height();
for (const auto& pt : pts)
{
// if the checkpoint is for a block we don't have yet, move on
- if (pt.first >= m_db->height())
+ if (pt.first >= blockchain_height)
{
continue;
}
@@ -4196,13 +4209,14 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
// vs [k_image, output_keys] (m_scan_table). This is faster because it takes advantage of bulk queries
// and is threaded if possible. The table (m_scan_table) will be used later when querying output
// keys.
-bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry)
+bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry, std::vector<block> &blocks)
{
MTRACE("Blockchain::" << __func__);
TIME_MEASURE_START(prepare);
bool stop_batch;
uint64_t bytes = 0;
size_t total_txs = 0;
+ blocks.clear();
// Order of locking must be:
// m_incoming_tx_lock (optional)
@@ -4249,7 +4263,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
bool blocks_exist = false;
tools::threadpool& tpool = tools::threadpool::getInstance();
unsigned threads = tpool.get_max_concurrency();
- std::vector<block> blocks;
blocks.resize(blocks_entry.size());
if (1)
@@ -4265,6 +4278,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
auto it = blocks_entry.begin();
unsigned blockidx = 0;
+ const crypto::hash tophash = m_db->top_block_hash();
for (unsigned i = 0; i < threads; i++)
{
for (unsigned int j = 0; j < batches; j++, ++blockidx)
@@ -4277,18 +4291,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
// check first block and skip all blocks if its not chained properly
if (blockidx == 0)
{
- crypto::hash tophash = m_db->top_block_hash();
if (block.prev_id != tophash)
{
MDEBUG("Skipping prepare blocks. New blocks don't belong to chain.");
+ blocks.clear();
return true;
}
}
if (have_block(get_block_hash(block)))
- {
blocks_exist = true;
- break;
- }
std::advance(it, 1);
}
@@ -4302,10 +4313,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
return false;
if (have_block(get_block_hash(block)))
- {
blocks_exist = true;
- break;
- }
std::advance(it, 1);
}
@@ -4341,7 +4349,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
if (blocks_exist)
{
- MDEBUG("Skipping prepare blocks. Blocks exist.");
+ MDEBUG("Skipping remainder of prepare blocks. Blocks exist.");
return true;
}
@@ -4770,10 +4778,11 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen;
transaction pool_tx;
+ blobdata txblob;
for(const transaction &tx : txs)
{
crypto::hash tx_hash = get_transaction_hash(tx);
- m_tx_pool.take_tx(tx_hash, pool_tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen);
+ m_tx_pool.take_tx(tx_hash, pool_tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen);
}
}
}