diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 257 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 23 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 37 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 4 |
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> ×tamps, 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> ×tamps, 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 |