aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/CMakeLists.txt2
-rw-r--r--src/cryptonote_core/blockchain.cpp287
-rw-r--r--src/cryptonote_core/blockchain.h27
-rw-r--r--src/cryptonote_core/blockchain_storage_boost_serialization.h2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp109
-rw-r--r--src/cryptonote_core/cryptonote_core.h19
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp47
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h8
-rw-r--r--src/cryptonote_core/tx_pool.cpp6
-rw-r--r--src/cryptonote_core/tx_pool.h7
10 files changed, 337 insertions, 177 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 231489fdb..fb96de226 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2018, The Monero Project
+# Copyright (c) 2014-2019, The Monero Project
#
# All rights reserved.
#
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 41357e72e..3e903cd12 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -115,6 +115,12 @@ static const struct {
// version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02.
{ 9, 1686275, 0, 1535889548 },
+
+ // version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10.
+ { 10, 1788000, 0, 1549792439 },
+
+ // version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15.
+ { 11, 1788720, 0, 1550225678 },
};
static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
@@ -139,6 +145,8 @@ static const struct {
{ 7, 1057027, 0, 1512211236 },
{ 8, 1057058, 0, 1533211200 },
{ 9, 1057778, 0, 1533297600 },
+ { 10, 1154318, 0, 1550153694 },
+ { 11, 1155038, 0, 1550225678 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -160,12 +168,16 @@ static const struct {
{ 7, 37000, 0, 1521600000 },
{ 8, 176456, 0, 1537821770 },
{ 9, 177176, 0, 1537821771 },
+ { 10, 269000, 0, 1550153694 },
+ { 11, 269720, 0, 1550225678 },
};
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
+ m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
+ m_long_term_effective_median_block_weight(0),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1),
m_btc_valid(false)
@@ -435,9 +447,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
uint64_t top_block_timestamp = m_db->get_top_block_timestamp();
uint64_t timestamp_diff = time(NULL) - top_block_timestamp;
- // genesis block has no timestamp, could probably change it to have timestamp of 1341378000...
+ // genesis block has no timestamp, could probably change it to have timestamp of 1397818133...
if(!top_block_timestamp)
- timestamp_diff = time(NULL) - 1341378000;
+ timestamp_diff = time(NULL) - 1397818133;
// create general purpose async service queue
@@ -456,8 +468,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
uint64_t num_popped_blocks = 0;
while (!m_db->is_read_only())
{
- const uint64_t top_height = m_db->height() - 1;
- const crypto::hash top_id = m_db->top_block_hash();
+ uint64_t top_height;
+ const crypto::hash top_id = m_db->top_block_hash(&top_height);
const block top_block = m_db->get_top_block();
const uint8_t ideal_hf_version = get_ideal_hard_fork_version(top_height);
if (ideal_hf_version <= 1 || ideal_hf_version == top_block.major_version)
@@ -497,10 +509,16 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
{
m_timestamps_and_difficulties_height = 0;
m_hardfork->reorganize_from_chain_height(get_current_blockchain_height());
- m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ uint64_t top_block_height;
+ crypto::hash top_block_hash = get_tail_id(top_block_height);
+ m_tx_pool.on_blockchain_dec(top_block_height, top_block_hash);
}
- update_next_cumulative_weight_limit();
+ if (test_options && test_options->long_term_block_weight_window)
+ m_long_term_block_weights_window = test_options->long_term_block_weight_window;
+
+ if (!update_next_cumulative_weight_limit())
+ return false;
return true;
}
//------------------------------------------------------------------
@@ -685,8 +703,10 @@ block Blockchain::pop_block_from_blockchain()
m_blocks_txs_check.clear();
m_check_txin_table.clear();
- update_next_cumulative_weight_limit();
- m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit");
+ uint64_t top_block_height;
+ crypto::hash top_block_hash = get_tail_id(top_block_height);
+ m_tx_pool.on_blockchain_dec(top_block_height, top_block_hash);
invalidate_block_template_cache();
return popped_block;
@@ -704,7 +724,8 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc);
- update_next_cumulative_weight_limit();
+ if (!update_next_cumulative_weight_limit())
+ return false;
return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
}
//------------------------------------------------------------------
@@ -712,8 +733,7 @@ crypto::hash Blockchain::get_tail_id(uint64_t& height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- height = m_db->height() - 1;
- return get_tail_id();
+ return m_db->top_block_hash(&height);
}
//------------------------------------------------------------------
crypto::hash Blockchain::get_tail_id() const
@@ -874,8 +894,9 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
CRITICAL_REGION_LOCAL(m_blockchain_lock);
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
- auto height = m_db->height();
- top_hash = get_tail_id(); // get it again now that we have the lock
+ uint64_t height;
+ top_hash = get_tail_id(height); // get it again now that we have the lock
+ ++height; // top block height to blockchain height
// ND: Speedup
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
// then when the next block difficulty is queried, push the latest height data and
@@ -1049,6 +1070,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
}
// if we're to keep the disconnected blocks, add them as alternates
+ const size_t discarded_blocks = disconnected_chain.size();
if(!discard_disconnected_chain)
{
//pushing old chain as alternative chain
@@ -1076,7 +1098,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify;
if (reorg_notify)
reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(),
- "%n", std::to_string(m_db->height() - split_height).c_str(), NULL);
+ "%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL);
MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height());
return true;
@@ -1202,7 +1224,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
}
- std::vector<size_t> last_blocks_weights;
+ 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))
{
@@ -1237,7 +1259,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
//------------------------------------------------------------------
// get the block weights of the last <count> blocks, and return by reference <sz>.
-void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const
+void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1728,7 +1750,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
for (auto& bl: blocks)
{
std::vector<crypto::hash> missed_tx_ids;
- std::vector<cryptonote::blobdata> txs;
rsp.blocks.push_back(block_complete_entry());
block_complete_entry& e = rsp.blocks.back();
@@ -1756,7 +1777,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
e.block = std::move(bl.first);
}
//get and pack other transactions, if needed
- std::vector<cryptonote::blobdata> txs;
get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids);
m_db->block_txn_stop();
@@ -1790,11 +1810,12 @@ uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
uint64_t num_outs = m_db->get_num_outputs(amount);
// ensure we don't include outputs that aren't yet eligible to be used
// outpouts are sorted by height
+ const uint64_t blockchain_height = m_db->height();
while (num_outs > 0)
{
const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1);
const uint64_t height = m_db->get_tx_block_height(toi.first);
- if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
+ if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= blockchain_height)
break;
--num_outs;
}
@@ -2527,7 +2548,19 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
if (tx.version >= 2) {
if (tx.rct_signatures.type == rct::RCTTypeBulletproof2)
{
- MERROR_VER("Bulletproofs v2 are not allowed before v" << HF_VERSION_SMALLER_BP);
+ MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeBulletproof2 << " is not allowed before v" << HF_VERSION_SMALLER_BP);
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+ }
+
+ // from v11, allow only bulletproofs v2
+ if (hf_version > HF_VERSION_SMALLER_BP) {
+ if (tx.version >= 2) {
+ if (tx.rct_signatures.type == rct::RCTTypeBulletproof)
+ {
+ MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeBulletproof << " is not allowed from v" << (HF_VERSION_SMALLER_BP + 1));
tvc.m_invalid_output = true;
return false;
}
@@ -2600,7 +2633,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
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 || rct::RCTTypeBulletproof2)
+ 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)
@@ -3083,6 +3116,7 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
{
const uint8_t version = get_current_hard_fork_version();
+ const uint64_t blockchain_height = m_db->height();
uint64_t median = 0;
uint64_t already_generated_coins = 0;
@@ -3090,7 +3124,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
if (version >= HF_VERSION_DYNAMIC_FEE)
{
median = m_current_block_cumul_weight_limit / 2;
- already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ already_generated_coins = blockchain_height ? m_db->get_block_already_generated_coins(blockchain_height - 1) : 0;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
}
@@ -3098,7 +3132,8 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
uint64_t needed_fee;
if (version >= HF_VERSION_PER_BYTE_FEE)
{
- uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version);
+ const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT;
+ uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version);
MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals
@@ -3135,6 +3170,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
+ const uint64_t db_height = m_db->height();
if (version < HF_VERSION_DYNAMIC_FEE)
return FEE_PER_KB;
@@ -3143,7 +3179,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
const uint64_t min_block_weight = get_min_block_weight(version);
- std::vector<size_t> weights;
+ std::vector<uint64_t> weights;
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
weights.reserve(grace_blocks);
for (size_t i = 0; i < grace_blocks; ++i)
@@ -3153,7 +3189,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
if(median <= min_block_weight)
median = min_block_weight;
- uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0;
uint64_t base_reward;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
{
@@ -3161,7 +3197,8 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
base_reward = BLOCK_REWARD_OVERESTIMATE;
}
- uint64_t fee = get_dynamic_base_fee(base_reward, median, version);
+ 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 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;
@@ -3293,14 +3330,15 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
return false;
}
+ const auto h = m_db->height();
+
// if not enough blocks, no proper median yet, return true
- if(m_db->height() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
+ if(h < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
{
return true;
}
std::vector<uint64_t> timestamps;
- auto h = m_db->height();
// need most recent 60 blocks, get index of first of those
size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
@@ -3313,7 +3351,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
return check_block_timestamp(timestamps, b, median_ts);
}
//------------------------------------------------------------------
-void Blockchain::return_tx_to_pool(std::vector<transaction> &txs)
+void Blockchain::return_tx_to_pool(std::vector<std::pair<transaction, blobdata>> &txs)
{
uint8_t version = get_current_hard_fork_version();
for (auto& tx : txs)
@@ -3324,9 +3362,11 @@ void Blockchain::return_tx_to_pool(std::vector<transaction> &txs)
// that might not be always true. Unlikely though, and always relaying
// these again might cause a spike of traffic as many nodes re-relay
// all the transactions in a popped block when a reorg happens.
- if (!m_tx_pool.add_tx(tx, tvc, true, true, false, version))
+ const size_t weight = get_transaction_weight(tx.first, tx.second.size());
+ const crypto::hash tx_hash = get_transaction_hash(tx.first);
+ if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, true, true, false, version))
{
- MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx) << " to tx_pool");
+ MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx.first) << " to tx_pool");
}
}
}
@@ -3339,11 +3379,12 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
for (const auto &txid: txids)
{
cryptonote::transaction tx;
+ cryptonote::blobdata txblob;
size_t tx_weight;
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen;
MINFO("Removing txid " << txid << " from the pool");
- if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, 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))
{
MERROR("Failed to remove txid " << txid << " from the pool");
res = false;
@@ -3366,9 +3407,12 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
static bool seen_future_version = false;
m_db->block_txn_start(true);
- if(bl.prev_id != get_tail_id())
+ uint64_t blockchain_height;
+ const crypto::hash top_hash = get_tail_id(blockchain_height);
+ ++blockchain_height; // block height to chain height
+ if(bl.prev_id != top_hash)
{
- MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << get_tail_id());
+ MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << top_hash);
bvc.m_verifivation_failed = true;
leave:
m_db->block_txn_stop();
@@ -3437,13 +3481,12 @@ leave:
bool precomputed = false;
bool fast_check = false;
#if defined(PER_BLOCK_CHECKPOINT)
- if (m_db->height() < m_blocks_hash_check.size())
+ if (blockchain_height < m_blocks_hash_check.size())
{
- auto hash = get_block_hash(bl);
- const auto &expected_hash = m_blocks_hash_check[m_db->height()];
+ const auto &expected_hash = m_blocks_hash_check[blockchain_height];
if (expected_hash != crypto::null_hash)
{
- if (memcmp(&hash, &expected_hash, sizeof(hash)) != 0)
+ if (memcmp(&id, &expected_hash, sizeof(hash)) != 0)
{
MERROR_VER("Block with id is INVALID: " << id << ", expected " << expected_hash);
bvc.m_verifivation_failed = true;
@@ -3453,7 +3496,7 @@ leave:
}
else
{
- MCINFO("verify", "No pre-validated hash at height " << m_db->height() << ", verifying fully");
+ MCINFO("verify", "No pre-validated hash at height " << blockchain_height << ", verifying fully");
}
}
else
@@ -3466,12 +3509,12 @@ leave:
proof_of_work = it->second;
}
else
- proof_of_work = get_block_longhash(bl, m_db->height());
+ proof_of_work = get_block_longhash(bl, blockchain_height);
// validate proof_of_work versus difficulty target
if(!check_hash(proof_of_work, current_diffic))
{
- MERROR_VER("Block with id: " << id << std::endl << "does not have enough proof of work: " << proof_of_work << std::endl << "unexpected difficulty: " << current_diffic);
+ MERROR_VER("Block with id: " << id << std::endl << "does not have enough proof of work: " << proof_of_work << " at height " << blockchain_height << ", unexpected difficulty: " << current_diffic);
bvc.m_verifivation_failed = true;
goto leave;
}
@@ -3479,9 +3522,9 @@ leave:
// If we're at a checkpoint, ensure that our hardcoded checkpoint hash
// is correct.
- if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()))
+ if(m_checkpoints.is_in_checkpoint_zone(blockchain_height))
{
- if(!m_checkpoints.check_block(get_current_blockchain_height(), id))
+ if(!m_checkpoints.check_block(blockchain_height, id))
{
LOG_ERROR("CHECKPOINT VALIDATION FAILED");
bvc.m_verifivation_failed = true;
@@ -3496,7 +3539,7 @@ leave:
TIME_MEASURE_START(t3);
// sanity check basic miner tx properties;
- if(!prevalidate_miner_transaction(bl, m_db->height()))
+ if(!prevalidate_miner_transaction(bl, blockchain_height))
{
MERROR_VER("Block with id: " << id << " failed to pass prevalidation");
bvc.m_verifivation_failed = true;
@@ -3506,7 +3549,7 @@ leave:
size_t coinbase_weight = get_transaction_weight(bl.miner_tx);
size_t cumulative_block_weight = coinbase_weight;
- std::vector<transaction> txs;
+ std::vector<std::pair<transaction, blobdata>> txs;
key_images_container keys;
uint64_t fee_summary = 0;
@@ -3525,7 +3568,8 @@ leave:
txs.reserve(bl.tx_hashes.size());
for (const crypto::hash& tx_id : bl.tx_hashes)
{
- transaction tx;
+ transaction tx_tmp;
+ blobdata txblob;
size_t tx_weight = 0;
uint64_t fee = 0;
bool relayed = false, do_not_relay = false, double_spend_seen = false;
@@ -3545,7 +3589,7 @@ leave:
TIME_MEASURE_START(bb);
// get transaction with hash <tx_id> from tx_pool
- if(!m_tx_pool.take_tx(tx_id, tx, 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))
{
MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
bvc.m_verifivation_failed = true;
@@ -3558,7 +3602,8 @@ leave:
// add the transaction to the temp list of transactions, so we can either
// store the list of transactions all at once or return the ones we've
// taken from the tx_pool back to it if the block fails verification.
- txs.push_back(tx);
+ txs.push_back(std::make_pair(std::move(tx_tmp), std::move(txblob)));
+ transaction &tx = txs.back().first;
TIME_MEASURE_START(dd);
// FIXME: the storage should not be responsible for validation.
@@ -3623,7 +3668,7 @@ leave:
TIME_MEASURE_START(vmt);
uint64_t base_reward = 0;
- uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ uint64_t already_generated_coins = blockchain_height ? m_db->get_block_already_generated_coins(blockchain_height - 1) : 0;
if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version()))
{
MERROR_VER("Block with id: " << id << " has incorrect miner transaction");
@@ -3644,8 +3689,8 @@ leave:
// at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a
// subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state.
already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY;
- if(m_db->height())
- cumulative_difficulty += m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+ if(blockchain_height)
+ cumulative_difficulty += m_db->get_block_cumulative_difficulty(blockchain_height - 1);
TIME_MEASURE_FINISH(block_processing_time);
if(precomputed)
@@ -3658,7 +3703,9 @@ leave:
{
try
{
- new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs);
+ uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight);
+ cryptonote::blobdata bd = cryptonote::block_to_blob(bl);
+ new_height = m_db->add_block(std::make_pair(std::move(bl), std::move(bd)), block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs);
}
catch (const KEY_IMAGE_EXISTS& e)
{
@@ -3684,7 +3731,12 @@ leave:
TIME_MEASURE_FINISH(addblock);
// do this after updating the hard fork state since the weight limit may change due to fork
- update_next_cumulative_weight_limit();
+ if (!update_next_cumulative_weight_limit())
+ {
+ MERROR("Failed to update next cumulative weight limit");
+ pop_block_from_blockchain();
+ return false;
+ }
MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms");
if(m_show_time_stats)
@@ -3740,28 +3792,106 @@ bool Blockchain::check_blockchain_pruning()
return m_db->check_pruning();
}
//------------------------------------------------------------------
-bool Blockchain::update_next_cumulative_weight_limit()
+uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const
+{
+ PERF_TIMER(get_next_long_term_block_weight);
+
+ const uint64_t db_height = m_db->height();
+ const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
+
+ const uint8_t hf_version = get_current_hard_fork_version();
+ if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
+ return block_weight;
+
+ std::vector<uint64_t> weights;
+ weights.resize(nblocks);
+ for (uint64_t h = 0; h < nblocks; ++h)
+ weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h);
+ uint64_t long_term_median = epee::misc_utils::median(weights);
+ uint64_t long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
+
+ uint64_t short_term_constraint = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5;
+ uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
+
+ return long_term_block_weight;
+}
+//------------------------------------------------------------------
+bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight)
{
- uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version());
+ PERF_TIMER(update_next_cumulative_weight_limit);
LOG_PRINT_L3("Blockchain::" << __func__);
- std::vector<size_t> weights;
- get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- uint64_t median = epee::misc_utils::median(weights);
- m_current_block_cumul_weight_median = median;
- if(median <= full_reward_zone)
- median = full_reward_zone;
+ // when we reach this, the last hf version is not yet written to the db
+ const uint64_t db_height = m_db->height();
+ const uint8_t hf_version = get_current_hard_fork_version();
+ uint64_t full_reward_zone = get_min_block_weight(hf_version);
+ uint64_t long_term_block_weight;
+
+ if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
+ {
+ std::vector<uint64_t> weights;
+ get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
+ m_current_block_cumul_weight_median = epee::misc_utils::median(weights);
+ long_term_block_weight = weights.back();
+ }
+ else
+ {
+ const uint64_t block_weight = m_db->get_block_weight(db_height - 1);
+
+ std::vector<uint64_t> weights, new_weights;
+ uint64_t long_term_median;
+ if (db_height == 1)
+ {
+ long_term_median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5;
+ }
+ else
+ {
+ uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
+ if (nblocks == db_height)
+ --nblocks;
+ weights.resize(nblocks);
+ for (uint64_t h = 0; h < nblocks; ++h)
+ weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h - 1);
+ new_weights = weights;
+ long_term_median = epee::misc_utils::median(weights);
+ }
+
+ m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
+
+ uint64_t short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5;
+ long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
+
+ if (new_weights.empty())
+ new_weights.resize(1);
+ new_weights[0] = long_term_block_weight;
+ long_term_median = epee::misc_utils::median(new_weights);
+ m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
+ short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5;
+
+ weights.clear();
+ get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
+
+ uint64_t short_term_median = epee::misc_utils::median(weights);
+ uint64_t effective_median_block_weight = std::min<uint64_t>(std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight);
+
+ m_current_block_cumul_weight_median = effective_median_block_weight;
+ }
+
+ if (m_current_block_cumul_weight_median <= full_reward_zone)
+ m_current_block_cumul_weight_median = full_reward_zone;
+
+ m_current_block_cumul_weight_limit = m_current_block_cumul_weight_median * 2;
+
+ if (long_term_effective_median_block_weight)
+ *long_term_effective_median_block_weight = m_long_term_effective_median_block_weight;
- m_current_block_cumul_weight_limit = median*2;
return true;
}
//------------------------------------------------------------------
-bool Blockchain::add_new_block(const block& bl_, block_verification_context& bvc)
+bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
{
LOG_PRINT_L3("Blockchain::" << __func__);
- //copy block here to let modify block.target
- block bl = bl_;
crypto::hash id = get_block_hash(bl);
CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
@@ -3800,10 +3930,11 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor
CRITICAL_REGION_LOCAL(m_blockchain_lock);
stop_batch = m_db->batch_start();
+ const uint64_t blockchain_height = m_db->height();
for (const auto& pt : pts)
{
// if the checkpoint is for a block we don't have yet, move on
- if (pt.first >= m_db->height())
+ if (pt.first >= blockchain_height)
{
continue;
}
@@ -4078,13 +4209,14 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
// vs [k_image, output_keys] (m_scan_table). This is faster because it takes advantage of bulk queries
// and is threaded if possible. The table (m_scan_table) will be used later when querying output
// keys.
-bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry)
+bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry, std::vector<block> &blocks)
{
MTRACE("Blockchain::" << __func__);
TIME_MEASURE_START(prepare);
bool stop_batch;
uint64_t bytes = 0;
size_t total_txs = 0;
+ blocks.clear();
// Order of locking must be:
// m_incoming_tx_lock (optional)
@@ -4131,7 +4263,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
bool blocks_exist = false;
tools::threadpool& tpool = tools::threadpool::getInstance();
unsigned threads = tpool.get_max_concurrency();
- std::vector<block> blocks;
blocks.resize(blocks_entry.size());
if (1)
@@ -4147,6 +4278,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
auto it = blocks_entry.begin();
unsigned blockidx = 0;
+ const crypto::hash tophash = m_db->top_block_hash();
for (unsigned i = 0; i < threads; i++)
{
for (unsigned int j = 0; j < batches; j++, ++blockidx)
@@ -4159,18 +4291,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
// check first block and skip all blocks if its not chained properly
if (blockidx == 0)
{
- crypto::hash tophash = m_db->top_block_hash();
if (block.prev_id != tophash)
{
MDEBUG("Skipping prepare blocks. New blocks don't belong to chain.");
+ blocks.clear();
return true;
}
}
if (have_block(get_block_hash(block)))
- {
blocks_exist = true;
- break;
- }
std::advance(it, 1);
}
@@ -4184,10 +4313,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
return false;
if (have_block(get_block_hash(block)))
- {
blocks_exist = true;
- break;
- }
std::advance(it, 1);
}
@@ -4202,7 +4328,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
unsigned nblocks = batches;
if (i < extra)
++nblocks;
- tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, epee::span<const block>(&blocks[i], nblocks), std::ref(maps[i])), true);
+ tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, epee::span<const block>(&blocks[thread_height - height], nblocks), std::ref(maps[i])), true);
thread_height += nblocks;
}
@@ -4223,7 +4349,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
if (blocks_exist)
{
- MDEBUG("Skipping prepare blocks. Blocks exist.");
+ MDEBUG("Skipping remainder of prepare blocks. Blocks exist.");
return true;
}
@@ -4579,7 +4705,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511";
+static const char expected_block_hashes_hash[] = "570ce2357b08fadac6058e34f95c5e08323f9325de260d07b091a281a948a7b0";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
@@ -4652,10 +4778,11 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen;
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, 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);
}
}
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index c742bec91..4744defc5 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -37,6 +37,7 @@
#include <boost/multi_index/global_fun.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
+#include <boost/circular_buffer.hpp>
#include <atomic>
#include <functional>
#include <unordered_map>
@@ -229,11 +230,12 @@ namespace cryptonote
/**
* @brief performs some preprocessing on a group of incoming blocks to speed up verification
*
- * @param blocks a list of incoming blocks
+ * @param blocks_entry a list of incoming blocks
+ * @param blocks the parsed blocks
*
* @return false on erroneous blocks, else true
*/
- bool prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks);
+ bool prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry, std::vector<block> &blocks);
/**
* @brief incoming blocks post-processing, cleanup, and disk sync
@@ -631,6 +633,13 @@ namespace cryptonote
uint64_t get_current_cumulative_block_weight_limit() const;
/**
+ * @brief gets the long term block weight for a new block
+ *
+ * @return the long term block weight
+ */
+ uint64_t get_next_long_term_block_weight(uint64_t block_weight) const;
+
+ /**
* @brief gets the block weight median based on recent blocks (same window as for the limit)
*
* @return the median
@@ -994,7 +1003,9 @@ namespace cryptonote
*/
void pop_blocks(uint64_t nblocks);
+#ifndef IN_UNIT_TESTS
private:
+#endif
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
@@ -1047,6 +1058,8 @@ namespace cryptonote
std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height;
+ uint64_t m_long_term_block_weights_window;
+ uint64_t m_long_term_effective_median_block_weight;
epee::critical_section m_difficulty_lock;
crypto::hash m_difficulty_for_next_block_top_hash;
@@ -1280,7 +1293,7 @@ namespace cryptonote
* @param sz return-by-reference the list of weights
* @param count the number of blocks to get weights for
*/
- void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const;
+ void get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const;
/**
* @brief checks if a transaction is unlocked (its outputs spendable)
@@ -1379,10 +1392,12 @@ namespace cryptonote
/**
* @brief calculate the block weight limit for the next block to be added
*
+ * @param long_term_effective_median_block_weight optionally return that value
+ *
* @return true
*/
- bool update_next_cumulative_weight_limit();
- void return_tx_to_pool(std::vector<transaction> &txs);
+ bool update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight = NULL);
+ void return_tx_to_pool(std::vector<std::pair<transaction, blobdata>> &txs);
/**
* @brief make sure a transaction isn't attempting a double-spend
diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h
index f4f9f20ca..cfeb5acfc 100644
--- a/src/cryptonote_core/blockchain_storage_boost_serialization.h
+++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 1513bb750..58acdb6bb 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -188,8 +188,21 @@ namespace cryptonote
static const command_line::arg_descriptor<std::string> arg_reorg_notify = {
"reorg-notify"
, "Run a program for each reorg, '%s' will be replaced by the split height, "
- "'%h' will be replaced by the new blockchain height, and '%n' will be "
- "replaced by the number of new blocks in the new chain"
+ "'%h' will be replaced by the new blockchain height, '%n' will be "
+ "replaced by the number of new blocks in the new chain, and '%d' will be "
+ "replaced by the number of blocks discarded from the old chain"
+ , ""
+ };
+ static const command_line::arg_descriptor<std::string> arg_block_rate_notify = {
+ "block-rate-notify"
+ , "Run a program when the block rate undergoes large fluctuations. This might "
+ "be a sign of large amounts of hash rate going on and off the Monero network, "
+ "and thus be of potential interest in predicting attacks. %t will be replaced "
+ "by the number of minutes for the observation window, %b by the number of "
+ "blocks observed within that window, and %e by the number of blocks that was "
+ "expected in that window. It is suggested that this notification is used to "
+ "automatically increase the number of confirmations required before a payment "
+ "is acted upon."
, ""
};
@@ -308,6 +321,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_block_notify);
command_line::add_arg(desc, arg_prune_blockchain);
command_line::add_arg(desc, arg_reorg_notify);
+ command_line::add_arg(desc, arg_block_rate_notify);
miner::init_options(desc);
BlockchainDB::init_options(desc);
@@ -587,7 +601,7 @@ namespace cryptonote
}
catch (const std::exception &e)
{
- MERROR("Failed to parse block notify spec");
+ MERROR("Failed to parse block notify spec: " << e.what());
}
try
@@ -597,12 +611,23 @@ namespace cryptonote
}
catch (const std::exception &e)
{
- MERROR("Failed to parse reorg notify spec");
+ MERROR("Failed to parse reorg notify spec: " << e.what());
+ }
+
+ try
+ {
+ if (!command_line::is_arg_defaulted(vm, arg_block_rate_notify))
+ m_block_rate_notify.reset(new tools::Notify(command_line::get_arg(vm, arg_block_rate_notify).c_str()));
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to parse block rate notify spec: " << e.what());
}
const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
const cryptonote::test_options regtest_test_options = {
- regtest_hard_forks
+ regtest_hard_forks,
+ 0
};
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty, get_checkpoints);
@@ -699,7 +724,7 @@ 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, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay)
+ 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)
{
tvc = boost::value_initialized<tx_verification_context>();
@@ -712,9 +737,8 @@ namespace cryptonote
}
tx_hash = crypto::null_hash;
- tx_prefixt_hash = crypto::null_hash;
- if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob))
+ if(!parse_tx_from_blob(tx, tx_hash, tx_blob))
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to parse, rejected");
tvc.m_verifivation_failed = true;
@@ -747,7 +771,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, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay)
+ 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)
{
if(!check_tx_syntax(tx))
{
@@ -881,7 +905,7 @@ namespace cryptonote
TRY_ENTRY();
CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
- struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; };
+ struct result { bool res; cryptonote::transaction tx; crypto::hash hash; };
std::vector<result> results(tx_blobs.size());
tvc.resize(tx_blobs.size());
@@ -892,7 +916,7 @@ namespace cryptonote
tpool.submit(&waiter, [&, i, it] {
try
{
- results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
+ results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay);
}
catch (const std::exception &e)
{
@@ -922,7 +946,7 @@ namespace cryptonote
tpool.submit(&waiter, [&, i, it] {
try
{
- results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
+ results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay);
}
catch (const std::exception &e)
{
@@ -958,7 +982,7 @@ namespace cryptonote
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], results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay);
+ ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], 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)
@@ -1172,11 +1196,10 @@ namespace cryptonote
bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
crypto::hash tx_hash = get_transaction_hash(tx);
- crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
blobdata bl;
t_serializable_object_to_blob(tx, bl);
size_t tx_weight = get_transaction_weight(tx, bl.size());
- return add_new_tx(tx, tx_hash, bl, tx_prefix_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay);
+ return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, keeped_by_block, relayed, do_not_relay);
}
//-----------------------------------------------------------------------------------------------
size_t core::get_blockchain_total_transactions() const
@@ -1184,7 +1207,7 @@ namespace cryptonote
return m_blockchain_storage.get_total_transactions();
}
//-----------------------------------------------------------------------------------------------
- bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
if(m_mempool.have_tx(tx_hash))
{
@@ -1225,8 +1248,8 @@ namespace cryptonote
{
std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs;
cryptonote::transaction tx;
- crypto::hash tx_hash, tx_prefix_hash;
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
+ crypto::hash tx_hash;
+ if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash))
{
LOG_ERROR("Failed to parse relayed transaction");
return;
@@ -1307,7 +1330,13 @@ namespace cryptonote
m_miner.resume();
return false;
}
- prepare_handle_incoming_blocks(blocks);
+ std::vector<block> pblocks;
+ if (!prepare_handle_incoming_blocks(blocks, pblocks))
+ {
+ MERROR("Block found, but failed to prepare to add");
+ m_miner.resume();
+ return false;
+ }
m_blockchain_storage.add_new_block(b, bvc);
cleanup_handle_incoming_blocks(true);
//anyway - update miner template
@@ -1358,10 +1387,14 @@ namespace cryptonote
}
//-----------------------------------------------------------------------------------------------
- bool core::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks)
+ bool core::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry, std::vector<block> &blocks)
{
m_incoming_tx_lock.lock();
- m_blockchain_storage.prepare_handle_incoming_blocks(blocks);
+ if (!m_blockchain_storage.prepare_handle_incoming_blocks(blocks_entry, blocks))
+ {
+ cleanup_handle_incoming_blocks(false);
+ return false;
+ }
return true;
}
@@ -1378,7 +1411,7 @@ namespace cryptonote
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate)
+ bool core::handle_incoming_block(const blobdata& block_blob, const block *b, block_verification_context& bvc, bool update_miner_blocktemplate)
{
TRY_ENTRY();
@@ -1394,14 +1427,18 @@ namespace cryptonote
return false;
}
- block b = AUTO_VAL_INIT(b);
- if(!parse_and_validate_block_from_blob(block_blob, b))
+ block lb;
+ if (!b)
{
- LOG_PRINT_L1("Failed to parse and validate new block");
- bvc.m_verifivation_failed = true;
- return false;
+ if(!parse_and_validate_block_from_blob(block_blob, lb))
+ {
+ LOG_PRINT_L1("Failed to parse and validate new block");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+ b = &lb;
}
- add_new_block(b, bvc);
+ add_new_block(*b, bvc);
if(update_miner_blocktemplate && bvc.m_added_to_main_chain)
update_miner_block_template();
return true;
@@ -1441,9 +1478,9 @@ namespace cryptonote
return m_blockchain_storage.have_block(id);
}
//-----------------------------------------------------------------------------------------------
- bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) const
+ bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob) const
{
- return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash);
+ return parse_and_validate_tx_from_blob(blob, tx, tx_hash);
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_syntax(const transaction& tx) const
@@ -1764,7 +1801,7 @@ namespace cryptonote
const time_t now = time(NULL);
const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60);
- static const unsigned int seconds[] = { 5400, 1800, 600 };
+ static const unsigned int seconds[] = { 5400, 3600, 1800, 1200, 600 };
for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
{
unsigned int b = 0;
@@ -1775,6 +1812,14 @@ namespace cryptonote
if (p < threshold)
{
MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
+
+ std::shared_ptr<tools::Notify> block_rate_notify = m_block_rate_notify;
+ if (block_rate_notify)
+ {
+ auto expected = seconds[n] / DIFFICULTY_TARGET_V2;
+ block_rate_notify->notify("%t", std::to_string(seconds[n] / 60).c_str(), "%b", std::to_string(b).c_str(), "%e", std::to_string(expected).c_str(), NULL);
+ }
+
break; // no need to look further
}
}
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 4810fc891..356265dd6 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -54,6 +54,7 @@ namespace cryptonote
{
struct test_options {
const std::pair<uint8_t, uint64_t> *hard_forks;
+ const size_t long_term_block_weight_window;
};
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir;
@@ -145,20 +146,21 @@ namespace cryptonote
* optionally updates the miner's block template.
*
* @param block_blob the block to be added
+ * @param block the block to be added, or NULL
* @param bvc return-by-reference metadata context about the block's validity
* @param update_miner_blocktemplate whether or not to update the miner's block template
*
* @return false if loading new checkpoints fails, or the block is not
* added, otherwise true
*/
- bool handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate = true);
+ bool handle_incoming_block(const blobdata& block_blob, const block *b, block_verification_context& bvc, bool update_miner_blocktemplate = true);
/**
* @copydoc Blockchain::prepare_handle_incoming_blocks
*
* @note see Blockchain::prepare_handle_incoming_blocks
*/
- bool prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks);
+ bool prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry, std::vector<block> &blocks);
/**
* @copydoc Blockchain::cleanup_handle_incoming_blocks
@@ -828,13 +830,12 @@ namespace cryptonote
*
* @param tx_hash the transaction's hash
* @param blob the transaction as a blob
- * @param tx_prefix_hash the transaction prefix' hash
* @param tx_weight the weight of the transaction
* @param relayed whether or not the transaction was relayed to us
* @param do_not_relay whether to prevent the transaction from being relayed
*
*/
- bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
/**
* @brief add a new transaction to the transaction pool
@@ -874,7 +875,7 @@ namespace cryptonote
*
* @note see parse_tx_from_blob(transaction&, crypto::hash&, crypto::hash&, const blobdata&) const
*/
- bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) const;
+ bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob) const;
/**
* @brief check a transaction's syntax
@@ -907,8 +908,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, crypto::hash &tx_prefixt_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, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay);
+ 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);
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);
@@ -1061,6 +1062,8 @@ namespace cryptonote
bool m_fluffy_blocks_enabled;
bool m_offline;
bool m_pad_transactions;
+
+ std::shared_ptr<tools::Notify> m_block_rate_notify;
};
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 0a04e0d38..854f3d1c5 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -405,49 +405,12 @@ namespace cryptonote
for(const tx_destination_entry& dst_entr: destinations)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
- crypto::key_derivation derivation;
crypto::public_key out_eph_public_key;
- // make additional tx pubkey if necessary
- keypair additional_txkey;
- if (need_additional_txkeys)
- {
- additional_txkey.sec = additional_tx_keys[output_index];
- if (dst_entr.is_subaddress)
- additional_txkey.pub = rct::rct2pk(hwdev.scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec)));
- else
- additional_txkey.pub = rct::rct2pk(hwdev.scalarmultBase(rct::sk2rct(additional_txkey.sec)));
- }
-
- bool r;
- if (change_addr && dst_entr.addr == *change_addr)
- {
- // sending change to yourself; derivation = a*R
- r = hwdev.generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")");
- }
- else
- {
- // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme)
- r = hwdev.generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")");
- }
-
- if (need_additional_txkeys)
- {
- additional_tx_public_keys.push_back(additional_txkey.pub);
- }
-
- if (tx.version > 1)
- {
- crypto::secret_key scalar1;
- hwdev.derivation_to_scalar(derivation, output_index, scalar1);
- amount_keys.push_back(rct::sk2rct(scalar1));
- }
- r = hwdev.derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
-
- hwdev.add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, output_index, amount_keys.back(), out_eph_public_key);
+ hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key,
+ dst_entr, change_addr, output_index,
+ need_additional_txkeys, additional_tx_keys,
+ additional_tx_public_keys, amount_keys, out_eph_public_key);
tx_out out;
out.amount = dst_entr.amount;
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 85061668b..cb1561c2d 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -97,6 +97,12 @@ namespace cryptonote
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL);
+ bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
+ const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
+ const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
+ std::vector<crypto::public_key> &additional_tx_public_keys,
+ std::vector<rct::key> &amount_keys,
+ crypto::public_key &out_eph_public_key) ;
bool generate_genesis_block(
block& bl
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 1c8f1c62c..c1cbe2acd 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -453,7 +453,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, 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)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -469,7 +469,7 @@ namespace cryptonote
MERROR("Failed to find tx in txpool");
return false;
}
- cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(id);
+ txblob = m_blockchain.get_txpool_tx_blob(id);
auto ci = m_parsed_tx_cache.find(id);
if (ci != m_parsed_tx_cache.end())
{
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 670d70d77..877f2b82f 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -61,7 +61,7 @@ namespace cryptonote
class txCompare
{
public:
- bool operator()(const tx_by_fee_and_receive_time_entry& a, const tx_by_fee_and_receive_time_entry& b)
+ bool operator()(const tx_by_fee_and_receive_time_entry& a, const tx_by_fee_and_receive_time_entry& b) const
{
// sort by greatest first, not least
if (a.first.first > b.first.first) return true;
@@ -133,6 +133,7 @@ namespace cryptonote
*
* @param id the hash of the transaction
* @param tx return-by-reference the transaction taken
+ * @param txblob return-by-reference the transaction as a blob
* @param tx_weight return-by-reference the transaction's weight
* @param fee the transaction fee
* @param relayed return-by-reference was transaction relayed to us by the network?
@@ -141,7 +142,7 @@ namespace cryptonote
*
* @return true unless the transaction cannot be found in the pool
*/
- bool take_tx(const crypto::hash &id, transaction &tx, 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);
/**
* @brief checks if the pool has a transaction with the given hash