aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp313
-rw-r--r--src/cryptonote_core/blockchain.h32
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp80
-rw-r--r--src/cryptonote_core/cryptonote_core.h29
-rw-r--r--src/cryptonote_core/tx_pool.cpp99
-rw-r--r--src/cryptonote_core/tx_pool.h10
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:
/**