diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 313 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 32 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 80 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 29 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 99 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 10 |
6 files changed, 451 insertions, 112 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index bcf99bbed..b7e9f4ca2 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1176,16 +1176,25 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } } - std::vector<uint64_t> last_blocks_weights; - get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version)) + uint64_t median_weight; + if (version >= HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY) + { + median_weight = m_current_block_cumul_weight_median; + } + else + { + std::vector<uint64_t> last_blocks_weights; + get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + median_weight = epee::misc_utils::median(last_blocks_weights); + } + if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version)) { MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain"); return false; } 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 @@ -1315,7 +1324,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; @@ -1692,7 +1703,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // Check the block's hash against the difficulty target for its alt chain 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; + crypto::hash proof_of_work; + memset(proof_of_work.data, 0xff, sizeof(proof_of_work.data)); if (b.major_version >= RX_BLOCK_VERSION) { crypto::hash seedhash = null_hash; @@ -1721,6 +1733,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id { 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; } @@ -1747,6 +1760,34 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id } bei.cumulative_difficulty += current_diff; + bei.block_cumulative_weight = cryptonote::get_transaction_weight(b.miner_tx); + for (const crypto::hash &txid: b.tx_hashes) + { + cryptonote::tx_memory_pool::tx_details td; + cryptonote::blobdata blob; + if (m_tx_pool.get_transaction_info(txid, td)) + { + bei.block_cumulative_weight += td.weight; + } + else if (m_db->get_pruned_tx_blob(txid, blob)) + { + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_base_from_blob(blob, tx)) + { + MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) refers to unparsable transaction hash " << txid << "."); + bvc.m_verifivation_failed = true; + return false; + } + bei.block_cumulative_weight += cryptonote::get_pruned_transaction_weight(tx); + } + else + { + // we can't determine the block weight, set it to 0 and break out of the loop + bei.block_cumulative_weight = 0; + break; + } + } + // add block to alternate blocks storage, // as well as the current "alt chain" container CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists"); @@ -1861,8 +1902,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()); @@ -1870,8 +1912,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 @@ -1892,6 +1934,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; @@ -2170,23 +2215,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); @@ -2279,7 +2396,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); @@ -2296,25 +2413,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); @@ -2753,18 +2879,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 @@ -2790,6 +2922,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(); @@ -3221,8 +3357,8 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b if (version >= HF_VERSION_PER_BYTE_FEE) { lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi); - div128_32(hi, lo, min_block_weight, &hi, &lo); - div128_32(hi, lo, median_block_weight, &hi, &lo); + div128_64(hi, lo, min_block_weight, &hi, &lo, NULL, NULL); + div128_64(hi, lo, median_block_weight, &hi, &lo, NULL, NULL); assert(hi == 0); lo /= 5; return lo; @@ -3232,12 +3368,7 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight); lo = mul128(unscaled_fee_base, block_reward, &hi); - static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); - static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); - - // divide in two steps, since the divisor must be 32 bits, but DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't - div128_32(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo); - div128_32(hi, lo, 1000000, &hi, &lo); + div128_64(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD, &hi, &lo, NULL, NULL); assert(hi == 0); // quantize fee up to 8 decimals @@ -3334,7 +3465,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; @@ -3518,9 +3650,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; @@ -3603,7 +3735,8 @@ leave: TIME_MEASURE_START(longhash_calculating_time); - crypto::hash proof_of_work = null_hash; + crypto::hash proof_of_work; + memset(proof_of_work.data, 0xff, sizeof(proof_of_work.data)); // Formerly the code below contained an if loop with the following condition // !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()) @@ -3619,7 +3752,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) @@ -3652,6 +3785,7 @@ leave: { 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; } } @@ -3693,6 +3827,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 @@ -3708,7 +3843,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 @@ -3725,13 +3860,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; @@ -3802,6 +3939,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); @@ -4242,11 +4390,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(); @@ -4265,8 +4415,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()); @@ -4281,18 +4434,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 @@ -4302,12 +4461,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) @@ -4319,9 +4483,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; } @@ -4338,6 +4508,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) @@ -4379,7 +4561,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(); } @@ -4539,7 +4721,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); @@ -4838,7 +5020,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) @@ -4882,19 +5064,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 @@ -4909,13 +5093,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); } } } @@ -4988,6 +5172,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 552a53e89..6467031c2 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -393,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 @@ -409,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 @@ -687,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> @@ -968,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(); @@ -1000,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 @@ -1027,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; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 245be5778..acb494a49 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -114,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" @@ -324,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); @@ -746,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 = {}; - 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; @@ -760,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; @@ -786,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; } @@ -793,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)) { @@ -922,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); @@ -933,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 @@ -1005,8 +1027,8 @@ 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); + const uint64_t weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : 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) @@ -1020,9 +1042,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); @@ -1030,6 +1052,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(); @@ -1292,9 +1319,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 @@ -1336,11 +1363,12 @@ namespace cryptonote { block_complete_entry bce; bce.block = cryptonote::block_to_blob(b); + bce.block_weight = 0; // we can leave it to 0, those txes aren't pruned for (const auto &tx_hash: b.tx_hashes) { 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; } @@ -1393,7 +1421,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); } @@ -1883,6 +1911,14 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + void core::flush_bad_txs_cache() + { + bad_semantics_txes_lock.lock(); + for (int idx = 0; idx < 2; ++idx) + bad_semantics_txes[idx].clear(); + bad_semantics_txes_lock.unlock(); + } + //----------------------------------------------------------------------------------------------- bool core::update_blockchain_pruning() { return m_blockchain_storage.update_blockchain_pruning(); @@ -1903,9 +1939,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 @@ -1925,6 +1961,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..f69ac3509 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,23 @@ 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; + + /** + * @brief flushes the bad txs cache + */ + void flush_bad_txs_cache(); + private: /** @@ -910,8 +929,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/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 49d5a8ccc..392e611e9 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 @@ -258,7 +259,7 @@ namespace cryptonote m_blockchain.add_txpool_tx(id, blob, meta); if (!insert_key_images(tx, id, kept_by_block)) return false; - m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id); + m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id); lock.commit(); } catch (const std::exception &e) @@ -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)); @@ -303,7 +305,7 @@ namespace cryptonote m_blockchain.add_txpool_tx(id, blob, meta); if (!insert_key_images(tx, id, kept_by_block)) return false; - m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id); + m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id); lock.commit(); } catch (const std::exception &e) @@ -322,7 +324,7 @@ namespace cryptonote ++m_cookie; - MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)tx_weight)); + MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)(tx_weight ? tx_weight : 1))); prune(m_txpool_max_weight); @@ -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); @@ -515,6 +518,59 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_transaction_info(const crypto::hash &txid, tx_details &td) const + { + PERF_TIMER(get_transaction_info); + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + + try + { + LockedTXN lock(m_blockchain); + txpool_tx_meta_t meta; + if (!m_blockchain.get_txpool_tx_meta(txid, meta)) + { + MERROR("Failed to find tx in txpool"); + return false; + } + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + auto ci = m_parsed_tx_cache.find(txid); + if (ci != m_parsed_tx_cache.end()) + { + td.tx = ci->second; + } + else if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(txblob, td.tx) : parse_and_validate_tx_from_blob(txblob, td.tx))) + { + MERROR("Failed to parse tx from txpool"); + return false; + } + else + { + td.tx.set_hash(txid); + } + td.blob_size = txblob.size(); + td.weight = meta.weight; + td.fee = meta.fee; + td.max_used_block_id = meta.max_used_block_id; + td.max_used_block_height = meta.max_used_block_height; + td.kept_by_block = meta.kept_by_block; + td.last_failed_height = meta.last_failed_height; + td.last_failed_id = meta.last_failed_id; + td.receive_time = meta.receive_time; + td.last_relayed_time = meta.last_relayed_time; + td.relayed = meta.relayed; + td.do_not_relay = meta.do_not_relay; + td.double_spend_seen = meta.double_spend_seen; + } + catch (const std::exception &e) + { + MERROR("Failed to get tx from txpool: " << e.what()); + return false; + } + + return true; + } + //--------------------------------------------------------------------------------- void tx_memory_pool::on_idle() { m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();}); @@ -601,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 @@ -667,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 @@ -733,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) { @@ -746,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; @@ -791,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 @@ -863,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 @@ -901,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); @@ -1143,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 @@ -1199,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) { @@ -1322,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 877f2b82f..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 @@ -428,6 +429,11 @@ namespace cryptonote bool double_spend_seen; //!< true iff another tx was seen double spending this one }; + /** + * @brief get infornation about a single transaction + */ + bool get_transaction_info(const crypto::hash &txid, tx_details &td) const; + private: /** |