aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp257
-rw-r--r--src/cryptonote_core/blockchain.h23
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp37
-rw-r--r--src/cryptonote_core/cryptonote_core.h4
4 files changed, 213 insertions, 108 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 73c60760b..670f77d9d 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -428,6 +428,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
block bl;
block_verification_context bvc = boost::value_initialized<block_verification_context>();
generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
+ db_wtxn_guard wtxn_guard(m_db);
add_new_block(bl, bvc);
CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain");
}
@@ -443,7 +444,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
m_db->fixup();
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
+
// check how far behind we are
uint64_t top_block_timestamp = m_db->get_top_block_timestamp();
uint64_t timestamp_diff = time(NULL) - top_block_timestamp;
@@ -464,7 +466,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
#endif
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
- m_db->block_txn_stop();
+
+ rtxn_guard.stop();
uint64_t num_popped_blocks = 0;
while (!m_db->is_read_only())
@@ -518,8 +521,11 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
if (test_options && test_options->long_term_block_weight_window)
m_long_term_block_weights_window = test_options->long_term_block_weight_window;
- if (!update_next_cumulative_weight_limit())
- return false;
+ {
+ db_txn_guard txn_guard(m_db, m_db->is_read_only());
+ if (!update_next_cumulative_weight_limit())
+ return false;
+ }
return true;
}
//------------------------------------------------------------------
@@ -725,6 +731,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
m_db->reset();
m_hardfork->init();
+ db_wtxn_guard wtxn_guard(m_db);
block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc);
if (!update_next_cumulative_weight_limit())
@@ -772,7 +779,7 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const
if(!sz)
return true;
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
bool genesis_included = false;
uint64_t current_back_offset = 1;
while(current_back_offset < sz)
@@ -799,7 +806,6 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const
{
ids.push_back(m_db->get_block_hash_from_height(0));
}
- m_db->block_txn_stop();
return true;
}
@@ -1008,7 +1014,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
//------------------------------------------------------------------
// This function attempts to switch to an alternate chain, returning
// boolean based on success therein.
-bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain)
+bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1109,7 +1115,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
//------------------------------------------------------------------
// This function calculates the difficulty target for the block being added to
// an alternate chain.
-difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
+difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const
{
if (m_fixed_difficulty)
{
@@ -1351,7 +1357,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
-bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_weight;
@@ -1361,8 +1367,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
m_tx_pool.lock();
const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); });
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- height = m_db->height();
- if (m_btc_valid) {
+ if (m_btc_valid && !from_block) {
// 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
@@ -1372,16 +1377,79 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
m_btc.timestamp = time(NULL); // update timestamp unconditionally
b = m_btc;
diffic = m_btc_difficulty;
+ height = m_btc_height;
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()));
+ 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()) << ", from_block " << (!!from_block));
invalidate_block_template_cache();
}
- b.major_version = m_hardfork->get_current_version();
- b.minor_version = m_hardfork->get_ideal_version();
- b.prev_id = get_tail_id();
+ if (from_block)
+ {
+ //build alternative subchain, front -> mainchain, back -> alternative head
+ //block is not related with head of main chain
+ //first of all - look in alternative chains container
+ auto it_prev = m_alternative_chains.find(*from_block);
+ bool parent_in_main = m_db->block_exists(*from_block);
+ if(it_prev == m_alternative_chains.end() && !parent_in_main)
+ {
+ MERROR("Unknown from block");
+ return false;
+ }
+
+ //we have new block in alternative chain
+ std::list<blocks_ext_by_hash::const_iterator> alt_chain;
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ std::vector<uint64_t> timestamps;
+ if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
+ return false;
+
+ if (parent_in_main)
+ {
+ cryptonote::block prev_block;
+ CHECK_AND_ASSERT_MES(get_block_by_hash(*from_block, prev_block), false, "From block not found"); // TODO
+ uint64_t from_block_height = cryptonote::get_block_height(prev_block);
+ height = from_block_height + 1;
+ }
+ else
+ {
+ height = alt_chain.back()->second.height + 1;
+ }
+ b.major_version = m_hardfork->get_ideal_version(height);
+ b.minor_version = m_hardfork->get_ideal_version();
+ b.prev_id = *from_block;
+
+ // cheat and use the weight of the block we start from, virtually certain to be acceptable
+ // and use 1.9 times rather than 2 times so we're even more sure
+ if (parent_in_main)
+ {
+ median_weight = m_db->get_block_weight(height - 1);
+ already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+ }
+ else
+ {
+ median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20;
+ already_generated_coins = alt_chain.back()->second.already_generated_coins;
+ }
+
+ // FIXME: consider moving away from block_extended_info at some point
+ block_extended_info bei = boost::value_initialized<block_extended_info>();
+ bei.bl = b;
+ bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(*from_block) + 1;
+
+ diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei);
+ }
+ else
+ {
+ height = m_db->height();
+ b.major_version = m_hardfork->get_current_version();
+ b.minor_version = m_hardfork->get_ideal_version();
+ b.prev_id = get_tail_id();
+ median_weight = m_current_block_cumul_weight_limit / 2;
+ diffic = get_difficulty_for_next_block();
+ already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+ }
b.timestamp = time(NULL);
uint64_t median_ts;
@@ -1390,15 +1458,11 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
b.timestamp = median_ts;
}
- diffic = get_difficulty_for_next_block();
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
- median_weight = m_current_block_cumul_weight_limit / 2;
- already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
-
size_t txs_weight;
uint64_t fee;
- if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version()))
+ if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
{
return false;
}
@@ -1461,7 +1525,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
- uint8_t hf_version = m_hardfork->get_current_version();
+ uint8_t hf_version = b.major_version;
size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
@@ -1516,16 +1580,22 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
", cumulative weight " << cumulative_weight << " is now good");
#endif
- cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
+ if (!from_block)
+ cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, pool_cookie);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
return false;
}
//------------------------------------------------------------------
+bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+{
+ return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce);
+}
+//------------------------------------------------------------------
// 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)
+bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1545,6 +1615,52 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
return true;
}
//------------------------------------------------------------------
+bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const
+{
+ //build alternative subchain, front -> mainchain, back -> alternative head
+ blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id);
+ timestamps.clear();
+ while(alt_it != m_alternative_chains.end())
+ {
+ alt_chain.push_front(alt_it);
+ timestamps.push_back(alt_it->second.bl.timestamp);
+ alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
+ }
+
+ // if block to be added connects to known blocks that aren't part of the
+ // main chain -- that is, if we're adding on to an alternate chain
+ if(!alt_chain.empty())
+ {
+ // make sure alt chain doesn't somehow start past the end of the main chain
+ CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
+
+ // make sure that the blockchain contains the block that should connect
+ // this alternate chain with it.
+ if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
+ {
+ MERROR("alternate chain does not appear to connect to main chain...");
+ return false;
+ }
+
+ // make sure block connects correctly to the main chain
+ auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
+ CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
+ complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
+ }
+ // if block not associated with known alternate chain
+ else
+ {
+ // if block parent is not part of main chain or an alternate chain,
+ // we ignore it
+ bool parent_in_main = m_db->block_exists(prev_id);
+ CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition: parent_in_main");
+
+ complete_timestamps_vector(m_db->get_block_height(prev_id), timestamps);
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
// If a block is to be added and its parent block is not the current
// main chain top block, then we need to see if we know about its parent block.
// If its parent block is part of a known forked chain, then we need to see
@@ -1589,47 +1705,18 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
if(it_prev != m_alternative_chains.end() || parent_in_main)
{
//we have new block in alternative chain
-
- //build alternative subchain, front -> mainchain, back -> alternative head
- blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
- std::list<blocks_ext_by_hash::iterator> alt_chain;
+ std::list<blocks_ext_by_hash::const_iterator> alt_chain;
std::vector<uint64_t> timestamps;
- while(alt_it != m_alternative_chains.end())
- {
- alt_chain.push_front(alt_it);
- timestamps.push_back(alt_it->second.bl.timestamp);
- alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
- }
-
- // if block to be added connects to known blocks that aren't part of the
- // main chain -- that is, if we're adding on to an alternate chain
- if(!alt_chain.empty())
- {
- // make sure alt chain doesn't somehow start past the end of the main chain
- CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
-
- // make sure that the blockchain contains the block that should connect
- // this alternate chain with it.
- if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
- {
- MERROR("alternate chain does not appear to connect to main chain...");
- return false;
- }
-
- // make sure block connects correctly to the main chain
- auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
- CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
- complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
- }
- // if block not associated with known alternate chain
- else
- {
- // if block parent is not part of main chain or an alternate chain,
- // we ignore it
- CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition: parent_in_main");
+ if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
+ return false;
- complete_timestamps_vector(m_db->get_block_height(b.prev_id), timestamps);
- }
+ // FIXME: consider moving away from block_extended_info at some point
+ block_extended_info bei = boost::value_initialized<block_extended_info>();
+ bei.bl = b;
+ const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id);
+ bei.height = prev_height + 1;
+ uint64_t block_reward = get_outs_money_amount(b.miner_tx);
+ bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
// verify that the block's timestamp is within the acceptable range
// (not earlier than the median of the last X blocks)
@@ -1640,11 +1727,6 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return false;
}
- // FIXME: consider moving away from block_extended_info at some point
- block_extended_info bei = boost::value_initialized<block_extended_info>();
- bei.bl = b;
- bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(b.prev_id) + 1;
-
bool is_a_checkpoint;
if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
{
@@ -1790,7 +1872,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard (m_db);
rsp.current_blockchain_height = get_current_blockchain_height();
std::vector<std::pair<cryptonote::blobdata,block>> blocks;
get_blocks(arg.blocks, blocks, rsp.missed_ids);
@@ -1817,7 +1899,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
// as done below if any standalone transactions were requested
// and missed.
rsp.missed_ids.insert(rsp.missed_ids.end(), missed_tx_ids.begin(), missed_tx_ids.end());
- m_db->block_txn_stop();
return false;
}
@@ -1827,7 +1908,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
//get and pack other transactions, if needed
get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids);
- m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
@@ -1999,14 +2079,13 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
return false;
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
// make sure that the last block in the request's block list matches
// the genesis block
auto gen_hash = m_db->get_block_hash_from_height(0);
if(qblock_ids.back() != gen_hash)
{
MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block mismatch: " << std::endl << "id: " << qblock_ids.back() << ", " << std::endl << "expected: " << gen_hash << "," << std::endl << " dropping connection");
- m_db->block_txn_abort();
return false;
}
@@ -2024,11 +2103,9 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
catch (const std::exception& e)
{
MWARNING("Non-critical error trying to find block by hash in BlockchainDB, hash: " << *bl_it);
- m_db->block_txn_abort();
return false;
}
}
- m_db->block_txn_stop();
// this should be impossible, as we checked that we share the genesis block,
// but just in case...
@@ -2219,7 +2296,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
return false;
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
current_height = get_current_blockchain_height();
const uint32_t pruning_seed = get_blockchain_pruning_seed();
start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed);
@@ -2231,7 +2308,6 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
hashes.push_back(m_db->get_block_hash_from_height(i));
}
- m_db->block_txn_stop();
return true;
}
@@ -2278,7 +2354,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
}
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
total_height = get_current_blockchain_height();
size_t count = 0, size = 0;
blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height)));
@@ -2304,7 +2380,6 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
blocks.back().second.push_back(std::make_pair(b.tx_hashes[i], std::move(txs[i])));
}
}
- m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
@@ -3459,7 +3534,7 @@ 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);
+ db_rtxn_guard rtxn_guard(m_db);
uint64_t blockchain_height;
const crypto::hash top_hash = get_tail_id(blockchain_height);
++blockchain_height; // block height to chain height
@@ -3468,7 +3543,6 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
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();
return false;
}
@@ -3689,6 +3763,8 @@ leave:
//TODO: why is this done? make sure that keeping invalid blocks makes sense.
add_block_as_invalid(bl, id);
MERROR_VER("Block with id " << id << " added as invalid because of wrong inputs in transactions");
+ MERROR_VER("tx_index " << tx_index << ", m_blocks_txs_check " << m_blocks_txs_check.size() << ":");
+ for (const auto &h: m_blocks_txs_check) MERROR_VER(" " << h);
bvc.m_verifivation_failed = true;
return_tx_to_pool(txs);
goto leave;
@@ -3749,7 +3825,7 @@ leave:
if(precomputed)
block_processing_time += m_fake_pow_calc_time;
- m_db->block_txn_stop();
+ rtxn_guard.stop();
TIME_MEASURE_START(addblock);
uint64_t new_height = 0;
if (!bvc.m_verifivation_failed)
@@ -3818,12 +3894,6 @@ leave:
//------------------------------------------------------------------
bool Blockchain::prune_blockchain(uint32_t pruning_seed)
{
- uint8_t hf_version = m_hardfork->get_current_version();
- if (hf_version < 10)
- {
- MERROR("Most of the network will only be ready for pruned blockchains from v10, not pruning");
- return false;
- }
return m_db->prune_blockchain(pruning_seed);
}
//------------------------------------------------------------------
@@ -3935,6 +4005,9 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
if (long_term_effective_median_block_weight)
*long_term_effective_median_block_weight = m_long_term_effective_median_block_weight;
+ if (!m_db->is_read_only())
+ m_db->add_max_block_size(m_current_block_cumul_weight_limit);
+
return true;
}
//------------------------------------------------------------------
@@ -3944,12 +4017,11 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
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);
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
if(have_block(id))
{
LOG_PRINT_L3("block with id = " << id << " already exists");
bvc.m_already_exists = true;
- m_db->block_txn_stop();
m_blocks_txs_check.clear();
return false;
}
@@ -3959,14 +4031,14 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
{
//chain switching or wrong block
bvc.m_added_to_main_chain = false;
- m_db->block_txn_stop();
+ rtxn_guard.stop();
bool r = handle_alternative_block(bl, id, bvc);
m_blocks_txs_check.clear();
return r;
//never relay alternative blocks
}
- m_db->block_txn_stop();
+ rtxn_guard.stop();
return handle_block_to_main_chain(bl, id, bvc);
}
//------------------------------------------------------------------
@@ -4891,13 +4963,14 @@ void Blockchain::invalidate_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)
+void Blockchain::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 pool_cookie)
{
MDEBUG("Setting block template cache");
m_btc = b;
m_btc_address = address;
m_btc_nonce = nonce;
m_btc_difficulty = diff;
+ m_btc_height = height;
m_btc_expected_reward = expected_reward;
m_btc_pool_cookie = pool_cookie;
m_btc_valid = true;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 2cd4dc31b..3588bbd1b 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -336,6 +336,7 @@ namespace cryptonote
* @brief creates a new block to mine against
*
* @param b return-by-reference block to be filled in
+ * @param from_block optional block hash to start mining from (main chain tip if NULL)
* @param miner_address address new coins for the block will go to
* @param di return-by-reference tells the miner what the difficulty target is
* @param height return-by-reference tells the miner what height it's mining against
@@ -345,6 +346,7 @@ namespace cryptonote
* @return true if block template filled in successfully, else false
*/
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);
+ 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);
/**
* @brief checks if a block is known about with a given hash
@@ -1094,6 +1096,7 @@ namespace cryptonote
account_public_address m_btc_address;
blobdata m_btc_nonce;
difficulty_type m_btc_difficulty;
+ uint64_t m_btc_height;
uint64_t m_btc_pool_cookie;
uint64_t m_btc_expected_reward;
bool m_btc_valid;
@@ -1179,7 +1182,7 @@ namespace cryptonote
*
* @return false if the reorganization fails, otherwise true
*/
- bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
+ bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain);
/**
* @brief removes the most recent block from the blockchain
@@ -1233,6 +1236,18 @@ namespace cryptonote
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
/**
+ * @brief builds a list of blocks connecting a block to the main chain
+ *
+ * @param prev_id the block hash of the tip of the alt chain
+ * @param alt_chain the chain to be added to
+ * @param timestamps returns the timestamps of previous blocks
+ * @param bvc the block verification context for error return
+ *
+ * @return true on success, false otherwise
+ */
+ bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const;
+
+ /**
* @brief gets the difficulty requirement for a new block on an alternate chain
*
* @param alt_chain the chain to be added to
@@ -1240,7 +1255,7 @@ namespace cryptonote
*
* @return the difficulty requirement
*/
- difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
+ difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const;
/**
* @brief sanity checks a miner transaction before validating an entire block
@@ -1400,7 +1415,7 @@ namespace cryptonote
*
* @return true unless start_height is greater than the current blockchain height
*/
- bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
+ bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps) const;
/**
* @brief calculate the block weight limit for the next block to be added
@@ -1464,6 +1479,6 @@ 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 expected_reward, uint64_t pool_cookie);
+ 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 pool_cookie);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 3284edd9d..be1ea5a17 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -62,6 +62,9 @@ DISABLE_VS_WARNINGS(4355)
#define BAD_SEMANTICS_TXES_MAX_SIZE 100
+// basically at least how many bytes the block itself serializes to without the miner tx
+#define BLOCK_SIZE_SANITY_LEEWAY 100
+
namespace cryptonote
{
const command_line::arg_descriptor<bool, false> arg_testnet_on = {
@@ -1265,6 +1268,11 @@ namespace cryptonote
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce);
}
//-----------------------------------------------------------------------------------------------
+ bool core::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)
+ {
+ return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
@@ -1318,9 +1326,9 @@ namespace cryptonote
return bce;
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_block_found(block& b)
+ bool core::handle_block_found(block& b, block_verification_context &bvc)
{
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ bvc = boost::value_initialized<block_verification_context>();
m_miner.pause();
std::vector<block_complete_entry> blocks;
try
@@ -1370,7 +1378,7 @@ namespace cryptonote
m_pprotocol->relay_block(arg, exclude_context);
}
- return bvc.m_added_to_main_chain;
+ return true;
}
//-----------------------------------------------------------------------------------------------
void core::on_synchronized()
@@ -1417,18 +1425,21 @@ namespace cryptonote
{
TRY_ENTRY();
- // load json & DNS checkpoints every 10min/hour respectively,
- // and verify them with respect to what blocks we already have
- CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
-
bvc = boost::value_initialized<block_verification_context>();
- if(block_blob.size() > get_max_block_size())
+
+ if (!check_incoming_block_size(block_blob))
{
- LOG_PRINT_L1("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected");
bvc.m_verifivation_failed = true;
return false;
}
+ if (((size_t)-1) <= 0xffffffff && block_blob.size() >= 0x3fffffff)
+ MWARNING("This block's size is " << block_blob.size() << ", closing on the 32 bit limit");
+
+ // load json & DNS checkpoints every 10min/hour respectively,
+ // and verify them with respect to what blocks we already have
+ CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
+
block lb;
if (!b)
{
@@ -1453,9 +1464,13 @@ namespace cryptonote
// block_blob
bool core::check_incoming_block_size(const blobdata& block_blob) const
{
- if(block_blob.size() > get_max_block_size())
+ // note: we assume block weight is always >= block blob size, so we check incoming
+ // blob size against the block weight limit, which acts as a sanity check without
+ // having to parse/weigh first; in fact, since the block blob is the block header
+ // plus the tx hashes, the weight will typically be much larger than the blob size
+ if(block_blob.size() > m_blockchain_storage.get_current_cumulative_block_weight_limit() + BLOCK_SIZE_SANITY_LEEWAY)
{
- LOG_PRINT_L1("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected");
+ LOG_PRINT_L1("WRONG BLOCK BLOB, sanity check failed on size " << block_blob.size() << ", rejected");
return false;
}
return true;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 356265dd6..2fcf26a17 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -195,10 +195,11 @@ namespace cryptonote
* the network.
*
* @param b the block found
+ * @param bvc returns the block verification flags
*
* @return true if the block was added to the main chain, otherwise false
*/
- virtual bool handle_block_found( block& b);
+ virtual bool handle_block_found(block& b, block_verification_context &bvc);
/**
* @copydoc Blockchain::create_block_template
@@ -206,6 +207,7 @@ namespace cryptonote
* @note see Blockchain::create_block_template
*/
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);
+ 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);
/**
* @brief called when a transaction is relayed