diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 452 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 56 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 97 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 24 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 55 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.h | 7 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 40 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 5 |
9 files changed, 485 insertions, 252 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 2cbe89b01..cb3875878 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -58,6 +58,7 @@ target_link_libraries(cryptonote_core multisig ringct device + hardforks ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 833e8120c..b4942d03a 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -41,6 +41,7 @@ #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" #include "cryptonote_basic/miner.h" +#include "hardforks/hardforks.h" #include "misc_language.h" #include "profile_tools.h" #include "file_io_utils.h" @@ -83,95 +84,6 @@ DISABLE_VS_WARNINGS(4267) // used to overestimate the block reward when estimating a per kB to use #define BLOCK_REWARD_OVERESTIMATE (10 * 1000000000000) -static const struct { - uint8_t version; - uint64_t height; - uint8_t threshold; - time_t time; -} mainnet_hard_forks[] = { - // version 1 from the start of the blockchain - { 1, 1, 0, 1341378000 }, - - // version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork. - { 2, 1009827, 0, 1442763710 }, - - // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21. - { 3, 1141317, 0, 1458558528 }, - - // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18. - { 4, 1220516, 0, 1483574400 }, - - // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14. - { 5, 1288616, 0, 1489520158 }, - - // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18. - { 6, 1400000, 0, 1503046577 }, - - // version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17. - { 7, 1546000, 0, 1521303150 }, - - // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02. - { 8, 1685555, 0, 1535889547 }, - - // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02. - { 9, 1686275, 0, 1535889548 }, - - // version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10. - { 10, 1788000, 0, 1549792439 }, - - // version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15. - { 11, 1788720, 0, 1550225678 }, -}; -static const uint64_t mainnet_hard_fork_version_1_till = 1009826; - -static const struct { - uint8_t version; - uint64_t height; - uint8_t threshold; - time_t time; -} testnet_hard_forks[] = { - // version 1 from the start of the blockchain - { 1, 1, 0, 1341378000 }, - - // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork. - { 2, 624634, 0, 1445355000 }, - - // versions 3-5 were passed in rapid succession from September 18th, 2016 - { 3, 800500, 0, 1472415034 }, - { 4, 801219, 0, 1472415035 }, - { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 - - { 6, 971400, 0, 1501709789 }, - { 7, 1057027, 0, 1512211236 }, - { 8, 1057058, 0, 1533211200 }, - { 9, 1057778, 0, 1533297600 }, - { 10, 1154318, 0, 1550153694 }, - { 11, 1155038, 0, 1550225678 }, -}; -static const uint64_t testnet_hard_fork_version_1_till = 624633; - -static const struct { - uint8_t version; - uint64_t height; - uint8_t threshold; - time_t time; -} stagenet_hard_forks[] = { - // version 1 from the start of the blockchain - { 1, 1, 0, 1341378000 }, - - // versions 2-7 in rapid succession from March 13th, 2018 - { 2, 32000, 0, 1521000000 }, - { 3, 33000, 0, 1521120000 }, - { 4, 34000, 0, 1521240000 }, - { 5, 35000, 0, 1521360000 }, - { 6, 36000, 0, 1521480000 }, - { 7, 37000, 0, 1521600000 }, - { 8, 176456, 0, 1537821770 }, - { 9, 177176, 0, 1537821771 }, - { 10, 269000, 0, 1550153694 }, - { 11, 269720, 0, 1550225678 }, -}; - //------------------------------------------------------------------ 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_weight_limit(0), m_current_block_cumul_weight_median(0), @@ -183,7 +95,8 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1), m_btc_valid(false), - m_batch_success(true) + m_batch_success(true), + m_prepare_height(0) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -403,17 +316,17 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline } else if (m_nettype == TESTNET) { - for (size_t n = 0; n < sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); ++n) + for (size_t n = 0; n < num_testnet_hard_forks; ++n) m_hardfork->add_fork(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].threshold, testnet_hard_forks[n].time); } else if (m_nettype == STAGENET) { - for (size_t n = 0; n < sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]); ++n) + for (size_t n = 0; n < num_stagenet_hard_forks; ++n) m_hardfork->add_fork(stagenet_hard_forks[n].version, stagenet_hard_forks[n].height, stagenet_hard_forks[n].threshold, stagenet_hard_forks[n].time); } else { - for (size_t n = 0; n < sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); ++n) + for (size_t n = 0; n < num_mainnet_hard_forks; ++n) m_hardfork->add_fork(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].threshold, mainnet_hard_forks[n].time); } m_hardfork->init(); @@ -428,7 +341,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline { MINFO("Blockchain not loaded, generating genesis block."); block bl; - block_verification_context bvc = boost::value_initialized<block_verification_context>(); + block_verification_context bvc = {}; 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); @@ -737,7 +650,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) m_hardfork->init(); db_wtxn_guard wtxn_guard(m_db); - block_verification_context bvc = boost::value_initialized<block_verification_context>(); + block_verification_context bvc = {}; add_new_block(b, bvc); if (!update_next_cumulative_weight_limit()) return false; @@ -842,6 +755,13 @@ crypto::hash Blockchain::get_block_id_by_height(uint64_t height) const return null_hash; } //------------------------------------------------------------------ +crypto::hash Blockchain::get_pending_block_id_by_height(uint64_t height) const +{ + if (m_prepare_height && height >= m_prepare_height && height - m_prepare_height < m_prepare_nblocks) + return (*m_prepare_blocks)[height - m_prepare_height].hash; + return get_block_id_by_height(height); +} +//------------------------------------------------------------------ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -1007,7 +927,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, //return back original chain for (auto& bl : original_chain) { - block_verification_context bvc = boost::value_initialized<block_verification_context>(); + block_verification_context bvc = {}; bool r = handle_block_to_main_chain(bl, bvc); CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!"); } @@ -1056,7 +976,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) { const auto &bei = *alt_ch_iter; - block_verification_context bvc = boost::value_initialized<block_verification_context>(); + block_verification_context bvc = {}; // add block to main chain bool r = handle_block_to_main_chain(bei.bl, bvc); @@ -1099,7 +1019,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> //pushing old chain as alternative chain for (auto& old_ch_ent : disconnected_chain) { - block_verification_context bvc = boost::value_initialized<block_verification_context>(); + block_verification_context bvc = {}; bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); if(!r) { @@ -1117,6 +1037,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> } m_hardfork->reorganize_from_chain_height(split_height); + get_block_longhash_reorg(split_height); std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify; if (reorg_notify) @@ -1264,7 +1185,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } if(base_reward + fee < money_in_use) { - MERROR_VER("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + MERROR_VER("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << "), cumulative_block_weight " << cumulative_block_weight); return false; } // From hard fork 2, we allow a miner to claim less block reward than is allowed, in case a miner wants less dust @@ -1394,7 +1315,9 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, 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() && m_btc.prev_id == get_tail_id()) { MDEBUG("Using cached template"); - m_btc.timestamp = time(NULL); // update timestamp unconditionally + const uint64_t now = time(NULL); + if (m_btc.timestamp < now) // ensures it can't get below the median of the last few blocks + m_btc.timestamp = now; b = m_btc; diffic = m_btc_difficulty; height = m_btc_height; @@ -1421,7 +1344,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, //we have new block in alternative chain std::list<block_extended_info> alt_chain; - block_verification_context bvc = boost::value_initialized<block_verification_context>(); + block_verification_context bvc = {}; std::vector<uint64_t> timestamps; if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc)) return false; @@ -1455,7 +1378,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, } // FIXME: consider moving away from block_extended_info at some point - block_extended_info bei = boost::value_initialized<block_extended_info>(); + block_extended_info bei = {}; bei.bl = b; bei.height = alt_chain.size() ? prev_data.height + 1 : m_db->get_block_height(*from_block) + 1; @@ -1743,7 +1666,7 @@ 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>(); + block_extended_info bei = {}; bei.bl = b; const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id); bei.height = prev_height + 1; @@ -1772,11 +1695,35 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; - get_block_longhash(bei.bl, proof_of_work, bei.height); + if (b.major_version >= RX_BLOCK_VERSION) + { + crypto::hash seedhash = null_hash; + uint64_t seedheight = rx_seedheight(bei.height); + // seedblock is on the alt chain somewhere + if (alt_chain.size() && alt_chain.front().height <= seedheight) + { + for (auto it=alt_chain.begin(); it != alt_chain.end(); it++) + { + if (it->height == seedheight+1) + { + seedhash = it->bl.prev_id; + break; + } + } + } else + { + seedhash = get_block_id_by_height(seedheight); + } + get_altblock_longhash(bei.bl, proof_of_work, get_current_blockchain_height(), bei.height, seedheight, seedhash); + } else + { + get_block_longhash(this, bei.bl, proof_of_work, bei.height, 0); + } if(!check_hash(proof_of_work, current_diff)) { MERROR_VER("Block with id: " << id << std::endl << " for alternative chain, does not have enough proof of work: " << proof_of_work << std::endl << " expected difficulty: " << current_diff); bvc.m_verifivation_failed = true; + bvc.m_bad_pow = true; return false; } @@ -1945,8 +1892,9 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO std::vector<std::pair<cryptonote::blobdata,block>> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); - for (auto& bl: blocks) + for (size_t i = 0; i < blocks.size(); ++i) { + auto& bl = blocks[i]; std::vector<crypto::hash> missed_tx_ids; rsp.blocks.push_back(block_complete_entry()); @@ -1954,8 +1902,8 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. - get_transactions_blobs(bl.second.tx_hashes, e.txs, missed_tx_ids); - + e.pruned = arg.prune; + get_transactions_blobs(bl.second.tx_hashes, e.txs, missed_tx_ids, arg.prune); if (missed_tx_ids.size() != 0) { // do not display an error if the peer asked for an unpruned block which we are not meant to have @@ -1976,6 +1924,9 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO //pack block e.block = std::move(bl.first); + e.block_weight = 0; + if (arg.prune && m_db->block_exists(arg.blocks[i])) + e.block_weight = m_db->get_block_weight(m_db->get_block_height(arg.blocks[i])); } return true; @@ -2254,23 +2205,95 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container return true; } //------------------------------------------------------------------ +static bool fill(BlockchainDB *db, const crypto::hash &tx_hash, cryptonote::blobdata &tx, bool pruned) +{ + if (pruned) + { + if (!db->get_pruned_tx_blob(tx_hash, tx)) + { + MDEBUG("Pruned transaction blob not found for " << tx_hash); + return false; + } + } + else + { + if (!db->get_tx_blob(tx_hash, tx)) + { + MDEBUG("Transaction blob not found for " << tx_hash); + return false; + } + } + return true; +} +//------------------------------------------------------------------ +static bool fill(BlockchainDB *db, const crypto::hash &tx_hash, tx_blob_entry &tx, bool pruned) +{ + if (!fill(db, tx_hash, tx.blob, pruned)) + return false; + if (pruned) + { + if (is_v1_tx(tx.blob)) + { + // v1 txes aren't pruned, so fetch the whole thing + cryptonote::blobdata prunable_blob; + if (!db->get_prunable_tx_blob(tx_hash, prunable_blob)) + { + MDEBUG("Prunable transaction blob not found for " << tx_hash); + return false; + } + tx.blob.append(prunable_blob); + tx.prunable_hash = crypto::null_hash; + } + else + { + if (!db->get_prunable_tx_hash(tx_hash, tx.prunable_hash)) + { + MDEBUG("Prunable transaction data hash not found for " << tx_hash); + return false; + } + } + } + return true; +} +//------------------------------------------------------------------ //TODO: return type should be void, throw on exception // alternatively, return true only if no transactions missed -template<class t_ids_container, class t_tx_container, class t_missed_container> -bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned) const +bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - reserve_container(txs, txs_ids.size()); + txs.reserve(txs_ids.size()); for (const auto& tx_hash : txs_ids) { try { cryptonote::blobdata tx; - if (pruned && m_db->get_pruned_tx_blob(tx_hash, tx)) + if (fill(m_db, tx_hash, tx, pruned)) txs.push_back(std::move(tx)); - else if (!pruned && m_db->get_tx_blob(tx_hash, tx)) + else + missed_txs.push_back(tx_hash); + } + catch (const std::exception& e) + { + return false; + } + } + return true; +} +//------------------------------------------------------------------ +bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<tx_blob_entry>& txs, std::vector<crypto::hash>& missed_txs, bool pruned) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + txs.reserve(txs_ids.size()); + for (const auto& tx_hash : txs_ids) + { + try + { + tx_blob_entry tx; + if (fill(m_db, tx_hash, tx, pruned)) txs.push_back(std::move(tx)); else missed_txs.push_back(tx_hash); @@ -2363,7 +2386,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container // Find the split point between us and foreign blockchain and return // (by reference) the most recent common block hash along with up to // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. -bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const +bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, std::vector<uint64_t>* weights, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2380,25 +2403,34 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc if (clip_pruned) { const uint32_t pruning_seed = get_blockchain_pruning_seed(); - start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed); + if (start_height < tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed)) + { + MDEBUG("We only have a pruned version of the common ancestor"); + return false; + } stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed); } size_t count = 0; - hashes.reserve(std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT)); + const size_t reserve = std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT); + hashes.reserve(reserve); + if (weights) + weights->reserve(reserve); for(size_t i = start_height; i < stop_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { hashes.push_back(m_db->get_block_hash_from_height(i)); + if (weights) + weights->push_back(m_db->get_block_weight(i)); } return true; } -bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const +bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height, true); + bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, &resp.m_block_weights, resp.start_height, resp.total_height, clip_pruned); if (result) { cryptonote::difficulty_type wide_cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); @@ -2837,18 +2869,24 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr // II if (rv.type == rct::RCTTypeFull) { - rv.p.MGs.resize(1); - rv.p.MGs[0].II.resize(tx.vin.size()); - for (size_t n = 0; n < tx.vin.size(); ++n) - rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + if (!tx.pruned) + { + rv.p.MGs.resize(1); + rv.p.MGs[0].II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + } } else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) { - CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); - for (size_t n = 0; n < tx.vin.size(); ++n) + if (!tx.pruned) { - rv.p.MGs[n].II.resize(1); - rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + rv.p.MGs[n].II.resize(1); + rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + } } } else @@ -2874,6 +2912,10 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if(pmax_used_block_height) *pmax_used_block_height = 0; + // pruned txes are skipped, as they're only allowed in sync-pruned-blocks mode, which is within the builtin hashes + if (tx.pruned) + return true; + crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); const uint8_t hf_version = m_hardfork->get_current_version(); @@ -3010,6 +3052,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(&tpool); }); int threads = tpool.get_max_concurrency(); + uint64_t max_used_block_height = 0; + if (!pmax_used_block_height) + pmax_used_block_height = &max_used_block_height; for (const auto& txin : tx.vin) { // make sure output being spent is of type txin_to_key, rather than @@ -3076,6 +3121,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if (tx.version == 1 && threads > 1) waiter.wait(&tpool); + // enforce min output age + if (hf_version >= HF_VERSION_ENFORCE_MIN_AGE) + { + CHECK_AND_ASSERT_MES(*pmax_used_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height(), + false, "Transaction spends at least one output which is too young"); + } + if (tx.version == 1) { if (threads > 1) @@ -3408,7 +3460,8 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const } const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - uint64_t fee = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version); + const uint64_t use_median_value = use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median; + const uint64_t fee = get_dynamic_base_fee(base_reward, use_median_value, version); const bool per_byte = version < HF_VERSION_PER_BYTE_FEE; MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB")); return fee; @@ -3592,9 +3645,9 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids) cryptonote::blobdata txblob; size_t tx_weight; uint64_t fee; - bool relayed, do_not_relay, double_spend_seen; + bool relayed, do_not_relay, double_spend_seen, pruned; MINFO("Removing txid " << txid << " from the pool"); - 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)) + 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, pruned)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; @@ -3693,7 +3746,7 @@ leave: #if defined(PER_BLOCK_CHECKPOINT) if (blockchain_height < m_blocks_hash_check.size()) { - const auto &expected_hash = m_blocks_hash_check[blockchain_height]; + const auto &expected_hash = m_blocks_hash_check[blockchain_height].first; if (expected_hash != crypto::null_hash) { if (memcmp(&id, &expected_hash, sizeof(hash)) != 0) @@ -3719,13 +3772,14 @@ leave: proof_of_work = it->second; } else - proof_of_work = get_block_longhash(bl, blockchain_height); + proof_of_work = get_block_longhash(this, bl, blockchain_height, 0); // validate proof_of_work versus difficulty target if(!check_hash(proof_of_work, current_diffic)) { MERROR_VER("Block with id: " << id << std::endl << "does not have enough proof of work: " << proof_of_work << " at height " << blockchain_height << ", unexpected difficulty: " << current_diffic); bvc.m_verifivation_failed = true; + bvc.m_bad_pow = true; goto leave; } } @@ -3767,6 +3821,7 @@ leave: uint64_t t_exists = 0; uint64_t t_pool = 0; uint64_t t_dblspnd = 0; + uint64_t n_pruned = 0; TIME_MEASURE_FINISH(t3); // XXX old code adds miner tx here @@ -3782,7 +3837,7 @@ leave: blobdata txblob; size_t tx_weight = 0; uint64_t fee = 0; - bool relayed = false, do_not_relay = false, double_spend_seen = false; + bool relayed = false, do_not_relay = false, double_spend_seen = false, pruned = false; TIME_MEASURE_START(aa); // XXX old code does not check whether tx exists @@ -3799,13 +3854,15 @@ leave: TIME_MEASURE_START(bb); // get transaction with hash <tx_id> from tx_pool - if(!m_tx_pool.take_tx(tx_id, tx_tmp, txblob, 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, pruned)) { MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); bvc.m_verifivation_failed = true; return_tx_to_pool(txs); goto leave; } + if (pruned) + ++n_pruned; TIME_MEASURE_FINISH(bb); t_pool += bb; @@ -3876,6 +3933,17 @@ leave: cumulative_block_weight += tx_weight; } + // if we were syncing pruned blocks + if (n_pruned > 0) + { + if (blockchain_height >= m_blocks_hash_check.size() || m_blocks_hash_check[blockchain_height].second == 0) + { + MERROR("Block at " << blockchain_height << " is pruned, but we do not have a weight for it"); + goto leave; + } + cumulative_block_weight = m_blocks_hash_check[blockchain_height].second; + } + m_blocks_txs_check.clear(); TIME_MEASURE_START(vmt); @@ -4220,7 +4288,7 @@ void Blockchain::block_longhash_worker(uint64_t height, const epee::span<const b if (m_cancel) break; crypto::hash id = get_block_hash(block); - crypto::hash pow = get_block_longhash(block, height++); + crypto::hash pow = get_block_longhash(this, block, height++, 0); map.emplace(id, pow); } @@ -4316,11 +4384,13 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin } } -uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) +uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights) { // new: . . . . . X X X X X . . . . . . // pre: A A A A B B B B C C C C D D D D + CHECK_AND_ASSERT_MES(weights.empty() || weights.size() == hashes.size(), 0, "Unexpected weights size"); + // easy case: height >= hashes if (height >= m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP) return hashes.size(); @@ -4339,8 +4409,11 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector return hashes.size(); // build hashes vector to hash hashes together - std::vector<crypto::hash> data; - data.reserve(hashes.size() + HASH_OF_HASHES_STEP - 1); // may be a bit too much + std::vector<crypto::hash> data_hashes; + std::vector<uint64_t> data_weights; + data_hashes.reserve(hashes.size() + HASH_OF_HASHES_STEP - 1); // may be a bit too much + if (!weights.empty()) + data_weights.reserve(data_hashes.size()); // we expect height to be either equal or a bit below db height bool disconnected = (height > m_db->height()); @@ -4355,18 +4428,24 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector // we might need some already in the chain for the first part of the first hash for (uint64_t h = first_index * HASH_OF_HASHES_STEP; h < height; ++h) { - data.push_back(m_db->get_block_hash_from_height(h)); + data_hashes.push_back(m_db->get_block_hash_from_height(h)); + if (!weights.empty()) + data_weights.push_back(m_db->get_block_weight(h)); } pop = 0; } // push the data to check - for (const auto &h: hashes) + for (size_t i = 0; i < hashes.size(); ++i) { if (pop) --pop; else - data.push_back(h); + { + data_hashes.push_back(hashes[i]); + if (!weights.empty()) + data_weights.push_back(weights[i]); + } } // hash and check @@ -4376,12 +4455,17 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector if (n < m_blocks_hash_of_hashes.size()) { // if the last index isn't fully filled, we can't tell if valid - if (data.size() < (n - first_index) * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP) + if (data_hashes.size() < (n - first_index) * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP) break; crypto::hash hash; - cn_fast_hash(data.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash); - bool valid = hash == m_blocks_hash_of_hashes[n]; + cn_fast_hash(data_hashes.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash); + bool valid = hash == m_blocks_hash_of_hashes[n].first; + if (valid && !weights.empty()) + { + cn_fast_hash(data_weights.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(uint64_t), hash); + valid &= hash == m_blocks_hash_of_hashes[n].second; + } // add to the known hashes array if (!valid) @@ -4393,9 +4477,15 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector size_t end = n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP; for (size_t i = n * HASH_OF_HASHES_STEP; i < end; ++i) { - CHECK_AND_ASSERT_MES(m_blocks_hash_check[i] == crypto::null_hash || m_blocks_hash_check[i] == data[i - first_index * HASH_OF_HASHES_STEP], + CHECK_AND_ASSERT_MES(m_blocks_hash_check[i].first == crypto::null_hash || m_blocks_hash_check[i].first == data_hashes[i - first_index * HASH_OF_HASHES_STEP], 0, "Consistency failure in m_blocks_hash_check construction"); - m_blocks_hash_check[i] = data[i - first_index * HASH_OF_HASHES_STEP]; + m_blocks_hash_check[i].first = data_hashes[i - first_index * HASH_OF_HASHES_STEP]; + if (!weights.empty()) + { + CHECK_AND_ASSERT_MES(m_blocks_hash_check[i].second == 0 || m_blocks_hash_check[i].second == data_weights[i - first_index * HASH_OF_HASHES_STEP], + 0, "Consistency failure in m_blocks_hash_check construction"); + m_blocks_hash_check[i].second = data_weights[i - first_index * HASH_OF_HASHES_STEP]; + } } usable += HASH_OF_HASHES_STEP; } @@ -4412,6 +4502,18 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector return usable; } +bool Blockchain::has_block_weights(uint64_t height, uint64_t nblocks) const +{ + CHECK_AND_ASSERT_MES(nblocks > 0, false, "nblocks is 0"); + uint64_t last_block_height = height + nblocks - 1; + if (last_block_height >= m_blocks_hash_check.size()) + return false; + for (uint64_t h = height; h <= last_block_height; ++h) + if (m_blocks_hash_check[h].second == 0) + return false; + return true; +} + //------------------------------------------------------------------ // ND: Speedups: // 1. Thread long_hash computations if possible (m_max_prepare_blocks_threads = nthreads, default = 4) @@ -4453,7 +4555,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete bytes += entry.block.size(); for (const auto &tx_blob : entry.txs) { - bytes += tx_blob.size(); + bytes += tx_blob.blob.size(); } total_txs += entry.txs.size(); } @@ -4536,6 +4638,9 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete m_blocks_longhash_table.clear(); uint64_t thread_height = height; tools::threadpool::waiter waiter; + m_prepare_height = height; + m_prepare_nblocks = blocks_entry.size(); + m_prepare_blocks = &blocks; for (unsigned int i = 0; i < threads; i++) { unsigned nblocks = batches; @@ -4546,6 +4651,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete } waiter.wait(&tpool); + m_prepare_height = 0; if (m_cancel) return false; @@ -4609,7 +4715,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete crypto::hash &tx_prefix_hash = txes[tx_index].second; ++tx_index; - if (!parse_and_validate_tx_base_from_blob(tx_blob, tx)) + if (!parse_and_validate_tx_base_from_blob(tx_blob.blob, tx)) SCAN_TABLE_QUIT("Could not parse tx from incoming blocks."); cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash); @@ -4830,39 +4936,6 @@ HardFork::State Blockchain::get_hard_fork_state() const return m_hardfork->get_state(); } -const std::vector<HardFork::Params>& Blockchain::get_hard_fork_heights(network_type nettype) -{ - static const std::vector<HardFork::Params> mainnet_heights = []() - { - std::vector<HardFork::Params> heights; - for (const auto& i : mainnet_hard_forks) - heights.emplace_back(i.version, i.height, i.threshold, i.time); - return heights; - }(); - static const std::vector<HardFork::Params> testnet_heights = []() - { - std::vector<HardFork::Params> heights; - for (const auto& i : testnet_hard_forks) - heights.emplace_back(i.version, i.height, i.threshold, i.time); - return heights; - }(); - static const std::vector<HardFork::Params> stagenet_heights = []() - { - std::vector<HardFork::Params> heights; - for (const auto& i : stagenet_hard_forks) - heights.emplace_back(i.version, i.height, i.threshold, i.time); - return heights; - }(); - static const std::vector<HardFork::Params> dummy; - switch (nettype) - { - case MAINNET: return mainnet_heights; - case TESTNET: return testnet_heights; - case STAGENET: return stagenet_heights; - default: return dummy; - } -} - bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const { return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting); @@ -4941,7 +5014,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "7dafb40b414a0e59bfced6682ef519f0b416bc914dd3d622b72e0dd1a47117c2"; +static const char expected_block_hashes_hash[] = "95e60612c1a16f4cd992c335b66daabd98e2d351c2b02b66e43ced0296848d33"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) @@ -4985,19 +5058,21 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get MERROR("Block hash data is too large"); return; } - const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); + const size_t size_needed = 4 + nblocks * (sizeof(crypto::hash) * 2); if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); for (uint32_t i = 0; i < nblocks; i++) { - crypto::hash hash; - memcpy(hash.data, p, sizeof(hash.data)); - p += sizeof(hash.data); - m_blocks_hash_of_hashes.push_back(hash); + crypto::hash hash_hashes, hash_weights; + memcpy(hash_hashes.data, p, sizeof(hash_hashes.data)); + p += sizeof(hash_hashes.data); + memcpy(hash_weights.data, p, sizeof(hash_weights.data)); + p += sizeof(hash_weights.data); + m_blocks_hash_of_hashes.push_back(std::make_pair(hash_hashes, hash_weights)); } - m_blocks_hash_check.resize(m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP, crypto::null_hash); + m_blocks_hash_check.resize(m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP, std::make_pair(crypto::null_hash, 0)); MINFO(nblocks << " block hashes loaded"); // FIXME: clear tx_pool because the process might have been @@ -5012,13 +5087,13 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get size_t tx_weight; uint64_t fee; - bool relayed, do_not_relay, double_spend_seen; + bool relayed, do_not_relay, double_spend_seen, pruned; 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, txblob, 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, pruned); } } } @@ -5091,6 +5166,5 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_ 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; template bool Blockchain::get_split_transactions_blobs(const std::vector<crypto::hash>&, std::vector<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>>&, std::vector<crypto::hash>&) const; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index f32645949..6467031c2 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -206,6 +206,18 @@ namespace cryptonote crypto::hash get_block_id_by_height(uint64_t height) const; /** + * @brief gets a block's hash given a height + * + * Used only by prepare_handle_incoming_blocks. Will look in the list of incoming blocks + * if the height is contained there. + * + * @param height the height of the block + * + * @return the hash of the block at the requested height, or a zeroed hash if there is no such block + */ + crypto::hash get_pending_block_id_by_height(uint64_t height) const; + + /** * @brief gets the block with a given hash * * @param h the hash to look for @@ -381,13 +393,14 @@ namespace cryptonote * * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history) * @param hashes the hashes to be returned, return-by-reference + * @param weights the block weights to be returned, return-by-reference * @param start_height the start height, return-by-reference * @param current_height the current blockchain height, return-by-reference * @param clip_pruned whether to constrain results to unpruned data * * @return true if a block found in common, else false */ - bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const; + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, std::vector<uint64_t>* weights, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const; /** * @brief get recent block hashes for a foreign chain @@ -397,11 +410,12 @@ namespace cryptonote * BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. * * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history) + * @param clip_pruned clip pruned blocks if true, include them otherwise * @param resp return-by-reference the split height and subsequent blocks' hashes * * @return true if a block found in common, else false */ - bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; /** * @brief find the most recent common point between ours and a foreign chain @@ -675,8 +689,8 @@ namespace cryptonote * * @return false if an unexpected exception occurs, else true */ - template<class t_ids_container, class t_tx_container, class t_missed_container> - bool get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned = false) const; + bool get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const; + bool get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<tx_blob_entry>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const; template<class t_ids_container, class t_tx_container, class t_missed_container> bool get_split_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; template<class t_ids_container, class t_tx_container, class t_missed_container> @@ -763,13 +777,6 @@ namespace cryptonote HardFork::State get_hard_fork_state() const; /** - * @brief gets the hardfork heights of given network - * - * @return the HardFork object - */ - static const std::vector<HardFork::Params>& get_hard_fork_heights(network_type nettype); - - /** * @brief gets the current hardfork version in use/voted for * * @return the version @@ -963,9 +970,8 @@ namespace cryptonote cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const; - bool is_within_compiled_block_hash_area(uint64_t height) const; bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } - uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes); + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights); uint32_t get_blockchain_pruning_seed() const { return m_db->get_blockchain_pruning_seed(); } bool prune_blockchain(uint32_t pruning_seed = 0); bool update_blockchain_pruning(); @@ -995,6 +1001,21 @@ namespace cryptonote */ void pop_blocks(uint64_t nblocks); + /** + * @brief checks whether a given block height is included in the precompiled block hash area + * + * @param height the height to check for + */ + bool is_within_compiled_block_hash_area(uint64_t height) const; + + /** + * @brief checks whether we have known weights for the given block heights + * + * @param height the start height to check for + * @param nblocks how many blocks to check from that height + */ + bool has_block_weights(uint64_t height, uint64_t nblocks) const; + #ifndef IN_UNIT_TESTS private: #endif @@ -1022,8 +1043,8 @@ namespace cryptonote std::unordered_map<crypto::hash, crypto::hash> m_blocks_longhash_table; // SHA-3 hashes for each block and for fast pow checking - std::vector<crypto::hash> m_blocks_hash_of_hashes; - std::vector<crypto::hash> m_blocks_hash_check; + std::vector<std::pair<crypto::hash, crypto::hash>> m_blocks_hash_of_hashes; + std::vector<std::pair<crypto::hash, uint64_t>> m_blocks_hash_check; std::vector<crypto::hash> m_blocks_txs_check; blockchain_db_sync_mode m_db_sync_mode; @@ -1084,6 +1105,11 @@ namespace cryptonote std::shared_ptr<tools::Notify> m_block_notify; std::shared_ptr<tools::Notify> m_reorg_notify; + // for prepare_handle_incoming_blocks + uint64_t m_prepare_height; + uint64_t m_prepare_nblocks; + std::vector<block> *m_prepare_blocks; + /** * @brief collects the keys for all outputs being "spent" as an input * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 0147bde23..b831cc9ff 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -51,6 +51,7 @@ using namespace epee; #include "blockchain_db/blockchain_db.h" #include "ringct/rctSigs.h" #include "common/notify.h" +#include "hardforks/hardforks.h" #include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -113,6 +114,10 @@ namespace cryptonote , "Set maximum size of block download queue in bytes (0 for default)" , 0 }; + const command_line::arg_descriptor<bool> arg_sync_pruned_blocks = { + "sync-pruned-blocks" + , "Allow syncing from nodes with only pruned blocks" + }; static const command_line::arg_descriptor<bool> arg_test_drop_download = { "test-drop-download" @@ -218,7 +223,7 @@ namespace cryptonote core::core(i_cryptonote_protocol* pprotocol): m_mempool(m_blockchain_storage), m_blockchain_storage(m_mempool), - m_miner(this), + m_miner(this, &m_blockchain_storage), m_starter_message_showed(false), m_target_blockchain_height(0), m_checkpoints_path(""), @@ -323,6 +328,7 @@ namespace cryptonote command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_block_download_max_size); + command_line::add_arg(desc, arg_sync_pruned_blocks); command_line::add_arg(desc, arg_max_txpool_weight); command_line::add_arg(desc, arg_pad_transactions); command_line::add_arg(desc, arg_block_notify); @@ -454,7 +460,6 @@ namespace cryptonote bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to handle command line"); - std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0; bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0; @@ -488,10 +493,10 @@ namespace cryptonote // folder might not be a directory, etc, etc catch (...) { } - std::unique_ptr<BlockchainDB> db(new_db(db_type)); + std::unique_ptr<BlockchainDB> db(new_db()); if (db == NULL) { - LOG_ERROR("Attempted to use non-existent database type"); + LOG_ERROR("Failed to initialize a database"); return false; } @@ -634,7 +639,7 @@ namespace cryptonote MERROR("Failed to parse block rate notify spec: " << e.what()); } - 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 std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(mainnet_hard_forks[num_mainnet_hard_forks-1].version, 1), std::make_pair(0, 0)}; const cryptonote::test_options regtest_test_options = { regtest_hard_forks, 0 @@ -655,6 +660,8 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); block_sync_size = command_line::get_arg(vm, arg_block_sync_size); + if (block_sync_size > BLOCKS_SYNCHRONIZING_MAX_COUNT) + MERROR("Error --block-sync-size cannot be greater than " << BLOCKS_SYNCHRONIZING_MAX_COUNT); MGINFO("Loading checkpoints"); @@ -744,13 +751,13 @@ namespace cryptonote return false; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) { - tvc = boost::value_initialized<tx_verification_context>(); + tvc = {}; - if(tx_blob.size() > get_max_tx_size()) + if(tx_blob.blob.size() > get_max_tx_size()) { - LOG_PRINT_L1("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"); + LOG_PRINT_L1("WRONG TRANSACTION BLOB, too big size " << tx_blob.blob.size() << ", rejected"); tvc.m_verifivation_failed = true; tvc.m_too_big = true; return false; @@ -758,7 +765,23 @@ namespace cryptonote tx_hash = crypto::null_hash; - if(!parse_tx_from_blob(tx, tx_hash, tx_blob)) + bool r; + if (tx_blob.prunable_hash == crypto::null_hash) + { + r = parse_tx_from_blob(tx, tx_hash, tx_blob.blob); + } + else + { + r = parse_and_validate_tx_base_from_blob(tx_blob.blob, tx); + if (r) + { + tx.set_prunable_hash(tx_blob.prunable_hash); + tx_hash = cryptonote::get_pruned_transaction_hash(tx, tx_blob.prunable_hash); + tx.set_hash(tx_hash); + } + } + + if (!r) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to parse, rejected"); tvc.m_verifivation_failed = true; @@ -784,6 +807,7 @@ namespace cryptonote if (tx.version == 0 || tx.version > max_tx_version) { // v2 is the latest one we know + MERROR_VER("Bad tx version (" << tx.version << ", max is " << max_tx_version << ")"); tvc.m_verifivation_failed = true; return false; } @@ -791,7 +815,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) { if(!check_tx_syntax(tx)) { @@ -920,7 +944,7 @@ namespace cryptonote return ret; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { TRY_ENTRY(); CRITICAL_REGION_LOCAL(m_incoming_tx_lock); @@ -931,7 +955,7 @@ namespace cryptonote tvc.resize(tx_blobs.size()); tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::vector<blobdata>::const_iterator it = tx_blobs.begin(); + std::vector<tx_blob_entry>::const_iterator it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { tpool.submit(&waiter, [&, i, it] { try @@ -1003,8 +1027,12 @@ namespace cryptonote if (already_have[i]) continue; - const size_t weight = get_transaction_weight(results[i].tx, it->size()); - ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], weight, tvc[i], keeped_by_block, relayed, do_not_relay); + // if it's a pruned tx from an incoming block, we'll get a weight that's technically + // different from the actual transaction weight, but it's OK for our use. Those txes + // will be ignored when mining, and using that "pruned" weight seems appropriate for + // keeping the txpool size constrained + const uint64_t weight = results[i].tx.pruned ? 0 : get_transaction_weight(results[i].tx, it->blob.size()); + ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], keeped_by_block, relayed, do_not_relay); if(tvc[i].m_verifivation_failed) {MERROR_VER("Transaction verification failed: " << results[i].hash);} else if(tvc[i].m_verifivation_impossible) @@ -1018,9 +1046,9 @@ namespace cryptonote CATCH_ENTRY_L0("core::handle_incoming_txs()", false); } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { - std::vector<cryptonote::blobdata> tx_blobs; + std::vector<tx_blob_entry> tx_blobs; tx_blobs.push_back(tx_blob); std::vector<tx_verification_context> tvcv(1); bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay); @@ -1028,6 +1056,11 @@ namespace cryptonote return r; } //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + { + return handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, keeped_by_block, relayed, do_not_relay); + } + //----------------------------------------------------------------------------------------------- bool core::get_stat_info(core_stat_info& st_inf) const { st_inf.mining_speed = m_miner.get_speed(); @@ -1290,9 +1323,9 @@ namespace cryptonote 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 + bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp); } //----------------------------------------------------------------------------------------------- bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const @@ -1338,14 +1371,14 @@ namespace cryptonote { cryptonote::blobdata txblob; CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob), "Transaction not found in pool"); - bce.txs.push_back(txblob); + bce.txs.push_back({txblob, crypto::null_hash}); } return bce; } //----------------------------------------------------------------------------------------------- bool core::handle_block_found(block& b, block_verification_context &bvc) { - bvc = boost::value_initialized<block_verification_context>(); + bvc = {}; m_miner.pause(); std::vector<block_complete_entry> blocks; try @@ -1374,7 +1407,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "mined block failed verification"); if(bvc.m_added_to_main_chain) { - cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>(); + cryptonote_connection_context exclude_context = {}; NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); std::vector<crypto::hash> missed_txs; @@ -1391,7 +1424,7 @@ namespace cryptonote block_to_blob(b, arg.b.block); //pack transactions for(auto& tx: txs) - arg.b.txs.push_back(tx); + arg.b.txs.push_back({tx, crypto::null_hash}); m_pprotocol->relay_block(arg, exclude_context); } @@ -1442,7 +1475,7 @@ namespace cryptonote { TRY_ENTRY(); - bvc = boost::value_initialized<block_verification_context>(); + bvc = {}; if (!check_incoming_block_size(block_blob)) { @@ -1641,7 +1674,7 @@ namespace cryptonote break; case HardFork::UpdateNeeded: level = el::Level::Info; - MCLOG(level, "global", "Last scheduled hard fork time suggests a daemon update will be released within the next couple months."); + MCLOG(level, "global", el::Color::Default, "Last scheduled hard fork time suggests a daemon update will be released within the next couple months."); break; default: break; @@ -1901,9 +1934,9 @@ namespace cryptonote return m_target_blockchain_height; } //----------------------------------------------------------------------------------------------- - uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) + uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights) { - return get_blockchain_storage().prevalidate_block_hashes(height, hashes); + return get_blockchain_storage().prevalidate_block_hashes(height, hashes, weights); } //----------------------------------------------------------------------------------------------- uint64_t core::get_free_space() const @@ -1923,6 +1956,16 @@ namespace cryptonote return get_blockchain_storage().prune_blockchain(pruning_seed); } //----------------------------------------------------------------------------------------------- + bool core::is_within_compiled_block_hash_area(uint64_t height) const + { + return get_blockchain_storage().is_within_compiled_block_hash_area(height); + } + //----------------------------------------------------------------------------------------------- + bool core::has_block_weights(uint64_t height, uint64_t nblocks) const + { + return get_blockchain_storage().has_block_weights(height, nblocks); + } + //----------------------------------------------------------------------------------------------- std::time_t core::get_start_time() const { return start_time; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index badbaf936..0db6350be 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -64,6 +64,7 @@ namespace cryptonote extern const command_line::arg_descriptor<difficulty_type> arg_fixed_difficulty; extern const command_line::arg_descriptor<bool> arg_offline; extern const command_line::arg_descriptor<size_t> arg_block_download_max_size; + extern const command_line::arg_descriptor<bool> arg_sync_pruned_blocks; /************************************************************************/ /* */ @@ -120,6 +121,7 @@ namespace cryptonote * * @return true if the transaction was accepted, false otherwise */ + bool handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** @@ -136,7 +138,7 @@ namespace cryptonote * * @return true if the transactions were accepted, false otherwise */ - bool handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** * @brief handles an incoming block @@ -522,7 +524,7 @@ namespace cryptonote * * @note see Blockchain::find_blockchain_supplement(const std::list<crypto::hash>&, NOTIFY_RESPONSE_CHAIN_ENTRY::request&) const */ - bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; /** * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const @@ -779,7 +781,7 @@ namespace cryptonote * * @return number of usable blocks */ - uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes); + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights); /** * @brief get free disk space on the blockchain partition @@ -825,6 +827,18 @@ namespace cryptonote */ bool check_blockchain_pruning(); + /** + * @brief checks whether a given block height is included in the precompiled block hash area + * + * @param height the height to check for + */ + bool is_within_compiled_block_hash_area(uint64_t height) const; + + /** + * @brief checks whether block weights are known for the given range + */ + bool has_block_weights(uint64_t height, uint64_t nblocks) const; + private: /** @@ -910,8 +924,8 @@ namespace cryptonote bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; void set_semantics_failed(const crypto::hash &tx_hash); - bool handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx_post(const tx_blob_entry &tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); struct tx_verification_batch_info { const cryptonote::transaction *tx; crypto::hash tx_hash; tx_verification_context &tvc; bool &result; }; bool handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block); diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4cf71e558..d2e022347 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -37,6 +37,7 @@ using namespace epee; #include "common/apply_permutation.h" #include "cryptonote_tx_utils.h" #include "cryptonote_config.h" +#include "blockchain.h" #include "cryptonote_basic/miner.h" #include "cryptonote_basic/tx_extra.h" #include "crypto/crypto.h" @@ -647,7 +648,7 @@ namespace cryptonote ) { //genesis block - bl = boost::value_initialized<block>(); + bl = {}; blobdata tx_bl; bool r = string_tools::parse_hexstr_to_binbuff(genesis_tx, tx_bl); @@ -658,9 +659,59 @@ namespace cryptonote bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; bl.timestamp = 0; bl.nonce = nonce; - miner::find_nonce_for_given_block(bl, 1, 0); + miner::find_nonce_for_given_block(NULL, bl, 1, 0); bl.invalidate_hashes(); return true; } //--------------------------------------------------------------- + void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height, const uint64_t seed_height, const crypto::hash& seed_hash) + { + blobdata bd = get_block_hashing_blob(b); + rx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1); + } + + bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners) + { + // block 202612 bug workaround + if (height == 202612) + { + static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; + epee::string_tools::hex_to_pod(longhash_202612, res); + return true; + } + blobdata bd = get_block_hashing_blob(b); + if (b.major_version >= RX_BLOCK_VERSION) + { + uint64_t seed_height, main_height; + crypto::hash hash; + if (pbc != NULL) + { + seed_height = rx_seedheight(height); + hash = pbc->get_pending_block_id_by_height(seed_height); + main_height = pbc->get_current_blockchain_height(); + } else + { + memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block + seed_height = 0; + main_height = 0; + } + rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, miners, 0); + } else { + const int pow_variant = b.major_version >= 7 ? b.major_version - 6 : 0; + crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height); + } + return true; + } + + crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const int miners) + { + crypto::hash p = crypto::null_hash; + get_block_longhash(pbc, b, p, height, miners); + return p; + } + + void get_block_longhash_reorg(const uint64_t split_height) + { + rx_reorg(split_height); + } } diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index b03eb6e70..309e4177f 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -132,6 +132,13 @@ namespace cryptonote , uint32_t nonce ); + class Blockchain; + bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners); + void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height, + const uint64_t seed_height, const crypto::hash& seed_hash); + crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const int miners); + void get_block_longhash_reorg(const uint64_t split_height); + } BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 71c32c133..648e691d0 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -247,6 +247,7 @@ namespace cryptonote meta.relayed = relayed; meta.do_not_relay = do_not_relay; meta.double_spend_seen = have_tx_keyimges_as_spent(tx); + meta.pruned = tx.pruned; meta.bf_padding = 0; memset(meta.padding, 0, sizeof(meta.padding)); try @@ -290,6 +291,7 @@ namespace cryptonote meta.relayed = relayed; meta.do_not_relay = do_not_relay; meta.double_spend_seen = false; + meta.pruned = tx.pruned; meta.bf_padding = 0; memset(meta.padding, 0, sizeof(meta.padding)); @@ -460,7 +462,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen, bool &pruned) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -482,7 +484,7 @@ namespace cryptonote { tx = ci->second; } - else if (!parse_and_validate_tx_from_blob(txblob, tx)) + else if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(txblob, tx) : parse_and_validate_tx_from_blob(txblob, tx))) { MERROR("Failed to parse tx from txpool"); return false; @@ -496,6 +498,7 @@ namespace cryptonote relayed = meta.relayed; do_not_relay = meta.do_not_relay; double_spend_seen = meta.double_spend_seen; + pruned = meta.pruned; // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); @@ -654,7 +657,7 @@ namespace cryptonote txs.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){ // 0 fee transactions are never relayed - if(meta.fee > 0 && !meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time)) + if(!meta.pruned && meta.fee > 0 && !meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time)) { // if the tx is older than half the max lifetime, we don't re-relay it, to avoid a problem // mentioned by smooth where nodes would flush txes at slightly different times, causing @@ -720,7 +723,7 @@ namespace cryptonote txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ transaction tx; - if (!parse_and_validate_tx_from_blob(*bd, tx)) + if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx))) { MERROR("Failed to parse tx from txpool"); // continue @@ -786,7 +789,7 @@ namespace cryptonote if (meta.double_spend_seen) ++stats.num_double_spends; return true; - }, false, include_unrelayed_txes); + }, false, include_unrelayed_txes); stats.bytes_med = epee::misc_utils::median(weights); if (stats.txs_total > 1) { @@ -799,8 +802,15 @@ namespace cryptonote /* If enough txs, spread the first 98% of results across * the first 9 bins, drop final 2% in last bin. */ - it=agebytes.end(); - for (size_t n=0; n <= end; n++, it--); + it = agebytes.end(); + size_t cumulative_num = 0; + /* Since agebytes is not empty and end is nonzero, the + * below loop can always run at least once. + */ + do { + --it; + cumulative_num += it->second.txs; + } while (it != agebytes.begin() && cumulative_num < end); stats.histo_98pc = it->first; factor = 9; delta = it->first; @@ -844,7 +854,7 @@ namespace cryptonote txi.id_hash = epee::string_tools::pod_to_hex(txid); txi.tx_blob = *bd; transaction tx; - if (!parse_and_validate_tx_from_blob(*bd, tx)) + if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx))) { MERROR("Failed to parse tx from txpool"); // continue @@ -916,7 +926,7 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ cryptonote::rpc::tx_in_pool txi; txi.tx_hash = txid; - if (!parse_and_validate_tx_from_blob(*bd, txi.tx)) + if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, txi.tx) : parse_and_validate_tx_from_blob(*bd, txi.tx))) { MERROR("Failed to parse tx from txpool"); // continue @@ -954,7 +964,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const + bool tx_memory_pool::check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool>& spent) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1196,7 +1206,7 @@ namespace cryptonote ss << "id: " << txid << std::endl; if (!short_format) { cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(*txblob, tx)) + if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*txblob, tx) : parse_and_validate_tx_from_blob(*txblob, tx))) { MERROR("Failed to parse tx from txpool"); return true; // continue @@ -1252,6 +1262,12 @@ namespace cryptonote } LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase)); + if (meta.pruned) + { + LOG_PRINT_L2(" tx is pruned"); + continue; + } + // Can not exceed maximum block weight if (max_total_weight < total_weight + meta.weight) { @@ -1375,7 +1391,7 @@ namespace cryptonote { cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(txblob, tx)) + if (!parse_and_validate_tx_from_blob(txblob, tx)) // remove pruned ones on startup, they're meant to be temporary { MERROR("Failed to parse tx from txpool"); continue; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index eade2493a..dec7e3cd9 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -139,10 +139,11 @@ namespace cryptonote * @param relayed return-by-reference was transaction relayed to us by the network? * @param do_not_relay return-by-reference is transaction not to be relayed to the network? * @param double_spend_seen return-by-reference was a double spend seen for that transaction? + * @param pruned return-by-reference is the tx pruned * * @return true unless the transaction cannot be found in the pool */ - bool take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); + bool take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen, bool &pruned); /** * @brief checks if the pool has a transaction with the given hash @@ -300,7 +301,7 @@ namespace cryptonote * * @return true */ - bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const; + bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool>& spent) const; /** * @brief get a specific transaction from the pool |