aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp336
-rw-r--r--src/cryptonote_core/blockchain.h98
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp264
-rw-r--r--src/cryptonote_core/cryptonote_core.h103
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp4
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h1
-rw-r--r--src/cryptonote_core/tx_pool.cpp103
-rw-r--r--src/cryptonote_core/tx_pool.h31
8 files changed, 759 insertions, 181 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 745608b9f..2330b6c42 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -59,6 +59,8 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
+#define FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE (100*1024*1024) // 100 MB
+
//#include "serialization/json_archive.h"
/* TODO:
@@ -99,6 +101,9 @@ static const struct {
// version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
{ 5, 1288616, 0, 1489520158 },
+
+ // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
+ { 6, 1400000, 0, 1503046577 },
};
static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
@@ -118,13 +123,15 @@ static const struct {
{ 3, 800500, 0, 1472415034 },
{ 4, 801219, 0, 1472415035 },
{ 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6
+
+ { 6, 971400, 0, 1501709789 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
//------------------------------------------------------------------
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_sz_limit(0), m_is_in_checkpoint_zone(false),
- m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false)
+ m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0),
+ m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(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_cancel(false)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@@ -279,7 +286,8 @@ uint64_t Blockchain::get_current_blockchain_height() const
bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::test_options *test_options)
{
LOG_PRINT_L3("Blockchain::" << __func__);
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ CRITICAL_REGION_LOCAL(m_tx_pool);
+ CRITICAL_REGION_LOCAL1(m_blockchain_lock);
bool fakechain = test_options != NULL;
@@ -743,7 +751,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_timestamps = timestamps;
m_difficulties = difficulties;
}
- size_t target = get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+ size_t target = get_difficulty_target();
return next_difficulty(timestamps, difficulties, target);
}
//------------------------------------------------------------------
@@ -1350,7 +1358,6 @@ 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
- m_is_in_checkpoint_zone = false;
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;
@@ -1425,7 +1432,9 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
{
//block orphaned
bvc.m_marked_as_orphaned = true;
- MERROR_VER("Block recognized as orphaned and rejected, id = " << id);
+ MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height
+ << ", parent in alt " << (it_prev != m_alternative_chains.end()) << ", parent in main " << parent_in_main
+ << " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")");
}
return true;
@@ -1562,6 +1571,98 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A
output_data_t data = m_db->get_output_key(amount, i);
oen.out_key = data.pubkey;
}
+
+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
+ 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())
+ break;
+ --num_outs;
+ }
+
+ return num_outs;
+}
+
+std::vector<uint64_t> Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const
+{
+ uint64_t num_outs = get_num_mature_outputs(amount);
+
+ std::vector<uint64_t> indices;
+
+ std::unordered_set<uint64_t> seen_indices;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (num_outs <= count)
+ {
+ for (uint64_t i = 0; i < num_outs; i++)
+ {
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if tx is unlocked, add output to indices
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ while (indices.size() < count)
+ {
+ // if we've gone through every possible output, we've gotten all we can
+ if (seen_indices.size() == num_outs)
+ {
+ break;
+ }
+
+ // get a random output index from the DB. If we've already seen it,
+ // return to the top of the loop and try again, otherwise add it to the
+ // list of output indices we've seen.
+
+ // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
+ uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
+ double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
+ uint64_t i = (uint64_t)(frac*num_outs);
+ // just in case rounding up to 1 occurs after sqrt
+ if (i == num_outs)
+ --i;
+
+ if (seen_indices.count(i))
+ {
+ continue;
+ }
+ seen_indices.emplace(i);
+
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if the output's transaction is unlocked, add the output's index to
+ // our list.
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+
+ return indices;
+}
+
+crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const
+{
+ output_data_t data = m_db->get_output_key(amount, global_index);
+ return data.pubkey;
+}
+
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
// with the requested mixins.
@@ -1576,80 +1677,18 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
// from BlockchainDB where <n> is req.outs_count (number of mixins).
for (uint64_t amount : req.amounts)
{
- auto 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
- 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())
- break;
- --num_outs;
- }
-
// create outs_for_amount struct and populate amount field
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
- std::unordered_set<uint64_t> seen_indices;
-
- // if there aren't enough outputs to mix with (or just enough),
- // use all of them. Eventually this should become impossible.
- if (num_outs <= req.outs_count)
- {
- for (uint64_t i = 0; i < num_outs; i++)
- {
- // get tx_hash, tx_out_index from DB
- tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
-
- // if tx is unlocked, add output to result_outs
- if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
- {
- add_out_to_get_random_outs(result_outs, amount, i);
- }
+ std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
- }
- }
- else
+ for (auto i : indices)
{
- // while we still need more mixins
- while (result_outs.outs.size() < req.outs_count)
- {
- // if we've gone through every possible output, we've gotten all we can
- if (seen_indices.size() == num_outs)
- {
- break;
- }
-
- // get a random output index from the DB. If we've already seen it,
- // return to the top of the loop and try again, otherwise add it to the
- // list of output indices we've seen.
-
- // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
- uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
- double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
- uint64_t i = (uint64_t)(frac*num_outs);
- // just in case rounding up to 1 occurs after sqrt
- if (i == num_outs)
- --i;
-
- if (seen_indices.count(i))
- {
- continue;
- }
- seen_indices.emplace(i);
-
- // get tx_hash, tx_out_index from DB
- tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
- // if the output's transaction is unlocked, add the output's index to
- // our list.
- if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
- {
- add_out_to_get_random_outs(result_outs, amount, i);
- }
- }
+ oe.global_amount_index = i;
+ oe.out_key = get_output_key(amount, i);
}
}
return true;
@@ -1807,6 +1846,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
return true;
}
//------------------------------------------------------------------
+void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const
+{
+ const auto o_data = m_db->get_output_key(amount, index);
+ key = o_data.pubkey;
+ mask = o_data.commitment;
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, index);
+ unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
+}
+//------------------------------------------------------------------
// This function takes a list of block hashes from another node
// on the network to find where the split point is between us and them.
// This is used to see what to send another node that needs to sync.
@@ -2016,27 +2064,39 @@ void Blockchain::print_blockchain_outs(const std::string& file) const
// 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, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// if we can't find the split point, return false
- if(!find_blockchain_supplement(qblock_ids, resp.start_height))
+ if(!find_blockchain_supplement(qblock_ids, start_height))
{
return false;
}
m_db->block_txn_start(true);
- resp.total_height = get_current_blockchain_height();
+ current_height = get_current_blockchain_height();
size_t count = 0;
- for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
+ for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{
- resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i));
+ hashes.push_back(m_db->get_block_hash_from_height(i));
}
+
m_db->block_txn_stop();
return true;
}
+
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, 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);
+ resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+
+ return result;
+}
//------------------------------------------------------------------
//FIXME: change argument to std::vector, low priority
// find split point between ours and foreign blockchain (or start at
@@ -2067,8 +2127,8 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
m_db->block_txn_start(true);
total_height = get_current_blockchain_height();
- size_t count = 0;
- for(size_t i = start_height; i < total_height && count < max_count; i++, count++)
+ size_t count = 0, size = 0;
+ for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++)
{
blocks.resize(blocks.size()+1);
blocks.back().first = m_db->get_block_blob_from_height(i);
@@ -2077,6 +2137,9 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
std::list<crypto::hash> mis;
get_transactions_blobs(b.tx_hashes, blocks.back().second, mis);
CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
+ size += blocks.back().first.size();
+ for (const auto &t: blocks.back().second)
+ size += t.size();
}
m_db->block_txn_stop();
return true;
@@ -2254,8 +2317,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t mixin = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() - 1 : 0;
- MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << mixin << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
+ size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
}
return true;
}
@@ -2266,8 +2329,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t mixin = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() - 1 : 0;
- MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << mixin << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx));
+ size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx));
}
if (!res)
return false;
@@ -2459,13 +2522,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
if (n_unmixable == 0)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and no unmixable inputs");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and no unmixable inputs");
tvc.m_low_mixin = true;
return false;
}
if (n_mixable > 1)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and more than one mixable input with unmixable inputs");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and more than one mixable input with unmixable inputs");
tvc.m_low_mixin = true;
return false;
}
@@ -2496,7 +2559,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
assert(it != m_check_txin_table.end());
}
- uint64_t t_t1 = 0;
std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
@@ -2630,7 +2692,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
if (failed)
{
- MERROR_VER("Failed to check ring signatures!, t_loop: " << t_t1);
+ MERROR_VER("Failed to check ring signatures!");
return false;
}
}
@@ -2779,12 +2841,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
//------------------------------------------------------------------
void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
{
- if (m_is_in_checkpoint_zone)
- {
- result = true;
- return;
- }
-
std::vector<const crypto::public_key *> p_output_keys;
for (auto &key : pubkeys)
{
@@ -3097,6 +3153,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
CRITICAL_REGION_LOCAL(m_blockchain_lock);
TIME_MEASURE_START(t1);
+ static bool seen_future_version = false;
+
m_db->block_txn_start(true);
if(bl.prev_id != get_tail_id())
{
@@ -3106,6 +3164,18 @@ leave:
return false;
}
+ // warn users if they're running an old version
+ if (!seen_future_version && bl.major_version > m_hardfork->get_ideal_version())
+ {
+ seen_future_version = true;
+ const el::Level level = el::Level::Warning;
+ MCLOG_RED(level, "global", "**********************************************************************");
+ MCLOG_RED(level, "global", "A block was seen on the network with a version higher than the last");
+ MCLOG_RED(level, "global", "known one. This may be an old version of the daemon, and a software");
+ MCLOG_RED(level, "global", "update may be required to sync further. Try running: update check");
+ MCLOG_RED(level, "global", "**********************************************************************");
+ }
+
// this is a cheap test
if (!m_hardfork->check(bl))
{
@@ -3541,19 +3611,17 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints)
}
//------------------------------------------------------------------
-void Blockchain::block_longhash_worker(const uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const
+void Blockchain::block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const
{
TIME_MEASURE_START(t);
slow_hash_allocate_state();
- //FIXME: height should be changing here, as get_block_longhash expects
- // the height of the block passed to it
for (const auto & block : blocks)
{
if (m_cancel)
- return;
+ break;
crypto::hash id = get_block_hash(block);
- crypto::hash pow = get_block_longhash(block, height);
+ crypto::hash pow = get_block_longhash(block, height++);
map.emplace(id, pow);
}
@@ -3564,12 +3632,23 @@ void Blockchain::block_longhash_worker(const uint64_t height, const std::vector<
//------------------------------------------------------------------
bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
{
+ bool success = false;
+
MTRACE("Blockchain::" << __func__);
CRITICAL_REGION_BEGIN(m_blockchain_lock);
TIME_MEASURE_START(t1);
- m_db->batch_stop();
- if (m_sync_counter > 0)
+ try
+ {
+ m_db->batch_stop();
+ success = true;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Exception in cleanup_handle_incoming_blocks: " << e.what());
+ }
+
+ if (success && m_sync_counter > 0)
{
if (force_sync)
{
@@ -3604,7 +3683,7 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
CRITICAL_REGION_END();
m_tx_pool.unlock();
- return true;
+ return success;
}
//------------------------------------------------------------------
@@ -3745,9 +3824,11 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
if (!blocks_exist)
{
m_blocks_longhash_table.clear();
+ uint64_t thread_height = height;
for (uint64_t i = 0; i < threads; i++)
{
- thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i]))));
+ thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i]))));
+ thread_height += blocks[i].size();
}
for (size_t j = 0; j < thread_list.size(); j++)
@@ -3866,17 +3947,17 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
offset_map[in_to_key.amount].push_back(offset);
}
-
- // sort and remove duplicate absolute_offsets in offset_map
- for (auto &offsets : offset_map)
- {
- std::sort(offsets.second.begin(), offsets.second.end());
- auto last = std::unique(offsets.second.begin(), offsets.second.end());
- offsets.second.erase(last, offsets.second.end());
- }
}
}
+ // sort and remove duplicate absolute_offsets in offset_map
+ for (auto &offsets : offset_map)
+ {
+ std::sort(offsets.second.begin(), offsets.second.end());
+ auto last = std::unique(offsets.second.begin(), offsets.second.end());
+ offsets.second.erase(last, offsets.second.end());
+ }
+
// [output] stores all transactions for each tx_out_index::hash found
std::vector<std::unordered_map<crypto::hash, cryptonote::transaction>> transactions(amounts.size());
@@ -4022,12 +4103,29 @@ bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, con
void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync)
{
+ if (sync_mode == db_defaultsync)
+ {
+ m_db_default_sync = true;
+ sync_mode = db_async;
+ }
m_db_sync_mode = sync_mode;
m_fast_sync = fast_sync;
m_db_blocks_per_sync = blocks_per_sync;
m_max_prepare_blocks_threads = maxthreads;
}
+void Blockchain::safesyncmode(const bool onoff)
+{
+ /* all of this is no-op'd if the user set a specific
+ * --db-sync-mode at startup.
+ */
+ if (m_db_default_sync)
+ {
+ m_db->safesyncmode(onoff);
+ m_db_sync_mode = onoff ? db_nosync : db_async;
+ }
+}
+
HardFork::State Blockchain::get_hard_fork_state() const
{
return m_hardfork->get_state();
@@ -4038,6 +4136,11 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui
return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting);
}
+uint64_t Blockchain::get_difficulty_target() const
+{
+ return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+}
+
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const
{
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff);
@@ -4081,7 +4184,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "23d8a8c73de7b2383c72a016d9a6034e69d62dd48077d1c414e064ceab6daa94";
+static const char expected_block_hashes_hash[] = "d3ca80d50661684cde0e715d46d7c19704d2e216b21ed088af9fd4ef37ed4d65";
void Blockchain::load_compiled_in_block_hashes()
{
if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0)
@@ -4154,6 +4257,15 @@ void Blockchain::load_compiled_in_block_hashes()
}
#endif
+bool Blockchain::is_within_compiled_block_hash_area(uint64_t height) const
+{
+#if defined(PER_BLOCK_CHECKPOINT)
+ return height < m_blocks_hash_check.size();
+#else
+ return false;
+#endif
+}
+
void Blockchain::lock()
{
m_blockchain_lock.lock();
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 4f2e4f0d3..e2da535cd 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -65,6 +65,7 @@ namespace cryptonote
*/
enum blockchain_db_sync_mode
{
+ db_defaultsync, //!< user didn't specify, use db_async
db_sync, //!< handle syncing calls instead of the backing db, synchronously
db_async, //!< handle syncing calls instead of the backing db, asynchronously
db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O)
@@ -373,6 +374,22 @@ 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 hashes the hashes 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
+ *
+ * @return true if a block found in common, else false
+ */
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
+
+ /**
+ * @brief get recent block hashes for a foreign chain
+ *
+ * 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.
+ *
+ * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
* @param resp return-by-reference the split height and subsequent blocks' hashes
*
* @return true if a block found in common, else false
@@ -426,6 +443,35 @@ namespace cryptonote
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
/**
+ * @brief get number of outputs of an amount past the minimum spendable age
+ *
+ * @param amount the output amount
+ *
+ * @return the number of mature outputs
+ */
+ uint64_t get_num_mature_outputs(uint64_t amount) const;
+
+ /**
+ * @brief get random outputs (indices) for an amount
+ *
+ * @param amount the amount
+ * @param count the number of random outputs to choose
+ *
+ * @return the outputs' amount-global indices
+ */
+ std::vector<uint64_t> get_random_outputs(uint64_t amount, uint64_t count) const;
+
+ /**
+ * @brief get the public key for an output
+ *
+ * @param amount the output amount
+ * @param global_index the output amount-global index
+ *
+ * @return the public key
+ */
+ crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const;
+
+ /**
* @brief gets random outputs to mix with
*
* This function takes an RPC request for outputs to mix with
@@ -457,6 +503,17 @@ namespace cryptonote
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
/**
+ * @brief gets an output's key and unlocked state
+ *
+ * @param amount in - the output amount
+ * @param index in - the output global amount index
+ * @param mask out - the output's RingCT mask
+ * @param key out - the output's key
+ * @param unlocked out - the output's unlocked state
+ */
+ void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const;
+
+ /**
* @brief gets random ringct outputs to mix with
*
* This function takes an RPC request for outputs to mix with
@@ -585,7 +642,7 @@ namespace cryptonote
*
* @return true if Blockchain is having the chain stored currently, else false
*/
- bool is_storing_blockchain()const{return m_is_blockchain_storing;}
+ bool is_storing_blockchain()const{return false;}
/**
* @brief gets the difficulty of the block with a given height
@@ -701,6 +758,11 @@ namespace cryptonote
blockchain_db_sync_mode sync_mode, bool fast_sync);
/**
+ * @brief Put DB in safe sync mode
+ */
+ void safesyncmode(const bool onoff);
+
+ /**
* @brief set whether or not to show/print time statistics
*
* @param stats the new time stats setting
@@ -746,6 +808,15 @@ namespace cryptonote
uint8_t get_ideal_hard_fork_version(uint64_t height) const { return m_hardfork->get_ideal_version(height); }
/**
+ * @brief returns the actual hardfork version for a given block height
+ *
+ * @param height the height for which to check version info
+ *
+ * @return the version
+ */
+ uint8_t get_hard_fork_version(uint64_t height) const { return m_hardfork->get(height); }
+
+ /**
* @brief get information about hardfork voting for a version
*
* @param version the version in question
@@ -760,6 +831,13 @@ namespace cryptonote
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const;
/**
+ * @brief get difficulty target based on chain and hardfork version
+ *
+ * @return difficulty target
+ */
+ uint64_t get_difficulty_target() const;
+
+ /**
* @brief remove transactions from the transaction pool (if present)
*
* @param txids a list of hashes of transactions to be removed
@@ -822,6 +900,16 @@ namespace cryptonote
*
* @return a reference to the BlockchainDB instance
*/
+ const BlockchainDB& get_db() const
+ {
+ return *m_db;
+ }
+
+ /**
+ * @brief get a reference to the BlockchainDB in use by Blockchain
+ *
+ * @return a reference to the BlockchainDB instance
+ */
BlockchainDB& get_db()
{
return *m_db;
@@ -846,7 +934,7 @@ namespace cryptonote
* @param blocks the blocks to be hashed
* @param map return-by-reference the hashes for each block
*/
- void block_longhash_worker(const uint64_t height, const std::vector<block> &blocks,
+ void block_longhash_worker(uint64_t height, const std::vector<block> &blocks,
std::unordered_map<crypto::hash, crypto::hash> &map) const;
/**
@@ -865,6 +953,9 @@ 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) 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()); }
+
void lock();
void unlock();
@@ -910,6 +1001,7 @@ namespace cryptonote
blockchain_db_sync_mode m_db_sync_mode;
bool m_fast_sync;
bool m_show_time_stats;
+ bool m_db_default_sync;
uint64_t m_db_blocks_per_sync;
uint64_t m_max_prepare_blocks_threads;
uint64_t m_fake_pow_calc_time;
@@ -931,8 +1023,6 @@ namespace cryptonote
checkpoints m_checkpoints;
- std::atomic<bool> m_is_in_checkpoint_zone;
- std::atomic<bool> m_is_blockchain_storing;
bool m_enforce_dns_checkpoints;
HardFork *m_hardfork;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index c601c066c..01ee64b78 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -37,6 +37,7 @@ using namespace epee;
#include "common/util.h"
#include "common/updates.h"
#include "common/download.h"
+#include "common/task_region.h"
#include "warnings.h"
#include "crypto/crypto.h"
#include "cryptonote_config.h"
@@ -47,10 +48,6 @@ using namespace epee;
#include "cryptonote_basic/checkpoints.h"
#include "ringct/rctTypes.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/lmdb/db_lmdb.h"
-#if defined(BERKELEY_DB)
-#include "blockchain_db/berkeleydb/db_bdb.h"
-#endif
#include "ringct/rctSigs.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -76,8 +73,11 @@ namespace cryptonote
m_checkpoints_path(""),
m_last_dns_checkpoints_update(0),
m_last_json_checkpoints_update(0),
+ m_disable_dns_checkpoints(false),
+ m_threadpool(tools::thread_group::optimal()),
m_update_download(0)
{
+ m_checkpoints_updating.clear();
set_cryptonote_protocol(pprotocol);
}
void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol)
@@ -105,7 +105,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::update_checkpoints()
{
- if (m_testnet || m_fakechain) return true;
+ if (m_testnet || m_fakechain || m_disable_dns_checkpoints) return true;
if (m_checkpoints_updating.test_and_set()) return true;
@@ -156,19 +156,19 @@ namespace cryptonote
command_line::add_arg(desc, command_line::arg_testnet_on);
command_line::add_arg(desc, command_line::arg_dns_checkpoints);
- command_line::add_arg(desc, command_line::arg_db_type);
command_line::add_arg(desc, command_line::arg_prep_blocks_threads);
command_line::add_arg(desc, command_line::arg_fast_block_sync);
- command_line::add_arg(desc, command_line::arg_db_sync_mode);
command_line::add_arg(desc, command_line::arg_show_time_stats);
command_line::add_arg(desc, command_line::arg_block_sync_size);
command_line::add_arg(desc, command_line::arg_check_updates);
+ command_line::add_arg(desc, command_line::arg_fluffy_blocks);
// we now also need some of net_node's options (p2p bind arg, for separate data dir)
command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false);
command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false);
miner::init_options(desc);
+ BlockchainDB::init_options(desc);
}
//-----------------------------------------------------------------------------------------------
bool core::handle_command_line(const boost::program_options::variables_map& vm)
@@ -198,6 +198,7 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, command_line::arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, command_line::arg_test_drop_download_height));
+ m_fluffy_blocks_enabled = m_testnet || get_arg(vm, command_line::arg_fluffy_blocks);
if (command_line::get_arg(vm, command_line::arg_test_drop_download) == true)
test_drop_download();
@@ -241,6 +242,12 @@ namespace cryptonote
return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_txpool_backlog(std::vector<tx_backlog_entry>& backlog) const
+ {
+ m_mempool.get_transaction_backlog(backlog);
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const
{
return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs);
@@ -272,8 +279,9 @@ namespace cryptonote
m_config_folder_mempool = m_config_folder_mempool + "/" + m_port;
}
- std::string db_type = command_line::get_arg(vm, command_line::arg_db_type);
- std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode);
+ std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
+ std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
+ bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0;
bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0;
uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads);
std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates);
@@ -302,18 +310,8 @@ namespace cryptonote
// folder might not be a directory, etc, etc
catch (...) { }
- BlockchainDB* db = nullptr;
- uint64_t DBS_FAST_MODE = 0;
- uint64_t DBS_FASTEST_MODE = 0;
- uint64_t DBS_SAFE_MODE = 0;
- if (db_type == "lmdb")
- {
- db = new BlockchainLMDB();
- DBS_SAFE_MODE = MDB_NORDAHEAD;
- DBS_FAST_MODE = MDB_NORDAHEAD | MDB_NOSYNC;
- DBS_FASTEST_MODE = MDB_NORDAHEAD | MDB_NOSYNC | MDB_WRITEMAP | MDB_MAPASYNC;
- }
- else
+ BlockchainDB* db = new_db(db_type);
+ if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type");
return false;
@@ -324,7 +322,7 @@ namespace cryptonote
const std::string filename = folder.string();
// default to fast:async:1
- blockchain_db_sync_mode sync_mode = db_async;
+ blockchain_db_sync_mode sync_mode = db_defaultsync;
uint64_t blocks_per_sync = 1;
try
@@ -339,7 +337,7 @@ namespace cryptonote
MDEBUG("option: " << option);
// default to fast:async:1
- uint64_t DEFAULT_FLAGS = DBS_FAST_MODE;
+ uint64_t DEFAULT_FLAGS = DBF_FAST;
if(options.size() == 0)
{
@@ -353,15 +351,19 @@ namespace cryptonote
if(options[0] == "safe")
{
safemode = true;
- db_flags = DBS_SAFE_MODE;
+ db_flags = DBF_SAFE;
sync_mode = db_nosync;
}
else if(options[0] == "fast")
- db_flags = DBS_FAST_MODE;
+ {
+ db_flags = DBF_FAST;
+ sync_mode = db_async;
+ }
else if(options[0] == "fastest")
{
- db_flags = DBS_FASTEST_MODE;
+ db_flags = DBF_FASTEST;
blocks_per_sync = 1000; // default to fastest:async:1000
+ sync_mode = db_async;
}
else
db_flags = DEFAULT_FLAGS;
@@ -383,6 +385,9 @@ namespace cryptonote
blocks_per_sync = bps;
}
+ if (db_salvage)
+ db_flags |= DBF_SALVAGE;
+
db->open(filename, db_flags);
if(!db->m_open)
return false;
@@ -410,8 +415,8 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
block_sync_size = command_line::get_arg(vm, command_line::arg_block_sync_size);
- if (block_sync_size == 0)
- block_sync_size = BLOCKS_SYNCHRONIZING_DEFAULT_COUNT;
+
+ MGINFO("Loading checkpoints");
// load json & DNS checkpoints, and verify them
// with respect to what blocks we already have
@@ -482,11 +487,9 @@ namespace cryptonote
return 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_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)
{
tvc = boost::value_initialized<tx_verification_context>();
- //want to process all transactions sequentially
- CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
if(tx_blob.size() > get_max_tx_size())
{
@@ -496,9 +499,8 @@ namespace cryptonote
return false;
}
- crypto::hash tx_hash = null_hash;
- crypto::hash tx_prefixt_hash = null_hash;
- transaction tx;
+ tx_hash = null_hash;
+ tx_prefixt_hash = null_hash;
if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob))
{
@@ -508,15 +510,18 @@ namespace cryptonote
}
//std::cout << "!"<< tx.vin.size() << std::endl;
+ bad_semantics_txes_lock.lock();
for (int idx = 0; idx < 2; ++idx)
{
if (bad_semantics_txes[idx].find(tx_hash) != bad_semantics_txes[idx].end())
{
+ bad_semantics_txes_lock.unlock();
LOG_PRINT_L1("Transaction already seen with bad semantics, rejected");
tvc.m_verifivation_failed = true;
return false;
}
}
+ bad_semantics_txes_lock.unlock();
uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
const size_t max_tx_version = version == 1 ? 1 : 2;
@@ -527,18 +532,11 @@ namespace cryptonote
return false;
}
- if(m_mempool.have_tx(tx_hash))
- {
- LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool");
- return true;
- }
-
- if(m_blockchain_storage.have_tx(tx_hash))
- {
- LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain");
- return true;
- }
-
+ 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)
+ {
if(!check_tx_syntax(tx))
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected");
@@ -563,27 +561,112 @@ namespace cryptonote
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
}
- if(!check_tx_semantic(tx, keeped_by_block))
+ if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
+ {
+ MTRACE("Skipping semantics check for tx kept by block in embedded hash area");
+ }
+ else if(!check_tx_semantic(tx, keeped_by_block))
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected");
tvc.m_verifivation_failed = true;
+ bad_semantics_txes_lock.lock();
bad_semantics_txes[0].insert(tx_hash);
if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE)
{
std::swap(bad_semantics_txes[0], bad_semantics_txes[1]);
bad_semantics_txes[0].clear();
}
+ bad_semantics_txes_lock.unlock();
return false;
}
- bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block, relayed, do_not_relay);
- if(tvc.m_verifivation_failed)
- {MERROR_VER("Transaction verification failed: " << tx_hash);}
- else if(tvc.m_verifivation_impossible)
- {MERROR_VER("Transaction verification impossible: " << tx_hash);}
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ {
+ TRY_ENTRY();
+
+ struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; };
+ std::vector<result> results(tx_blobs.size());
+
+ tvc.resize(tx_blobs.size());
+ tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) {
+ std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ region.run([&, 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);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR_VER("Exception in handle_incoming_tx_pre: " << e.what());
+ results[i].res = false;
+ }
+ });
+ }
+ });
+ tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) {
+ std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ if (!results[i].res)
+ continue;
+ if(m_mempool.have_tx(results[i].hash))
+ {
+ LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
+ }
+ else if(m_blockchain_storage.have_tx(results[i].hash))
+ {
+ LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
+ }
+ else
+ {
+ region.run([&, 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);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR_VER("Exception in handle_incoming_tx_post: " << e.what());
+ results[i].res = false;
+ }
+ });
+ }
+ }
+ });
+
+ bool ok = true;
+ std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ if (!results[i].res)
+ {
+ ok = false;
+ continue;
+ }
+
+ ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, it->size(), 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)
+ {MERROR_VER("Transaction verification impossible: " << results[i].hash);}
- if(tvc.m_added_to_pool)
- MDEBUG("tx added: " << tx_hash);
+ if(tvc[i].m_added_to_pool)
+ MDEBUG("tx added: " << results[i].hash);
+ }
+ return ok;
+
+ 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)
+ {
+ std::list<cryptonote::blobdata> 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);
+ tvc = tvcv[0];
return r;
}
//-----------------------------------------------------------------------------------------------
@@ -659,6 +742,12 @@ namespace cryptonote
return false;
}
+ if (!check_tx_inputs_ring_members_diff(tx))
+ {
+ MERROR_VER("tx uses duplicate ring members");
+ return false;
+ }
+
if (!check_tx_inputs_keyimages_domain(tx))
{
MERROR_VER("tx uses key image not in the valid domain");
@@ -711,6 +800,23 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ size_t core::get_block_sync_size(uint64_t height) const
+ {
+ static const uint64_t quick_height = m_testnet ? 801219 : 1220516;
+ if (block_sync_size > 0)
+ return block_sync_size;
+ if (height >= quick_height)
+ return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT;
+ return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4;
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const
+ {
+ spent.clear();
+
+ return m_mempool.check_for_key_images(key_im, spent);
+ }
+ //-----------------------------------------------------------------------------------------------
std::pair<uint64_t, uint64_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count)
{
uint64_t emission_amount = 0;
@@ -751,6 +857,22 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const
+ {
+ const uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
+ if (version >= 6)
+ {
+ for(const auto& in: tx.vin)
+ {
+ CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
+ for (size_t n = 1; n < tokey_in.key_offsets.size(); ++n)
+ if (tokey_in.key_offsets[n] == 0)
+ return false;
+ }
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const
{
std::unordered_set<crypto::key_image> ki;
@@ -948,6 +1070,11 @@ namespace cryptonote
m_miner.on_synchronized();
}
//-----------------------------------------------------------------------------------------------
+ void core::safesyncmode(const bool onoff)
+ {
+ m_blockchain_storage.safesyncmode(onoff);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::add_new_block(const block& b, block_verification_context& bvc)
{
return m_blockchain_storage.add_new_block(b, bvc);
@@ -964,17 +1091,20 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::cleanup_handle_incoming_blocks(bool force_sync)
{
+ bool success = false;
try {
- m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync);
+ success = m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync);
}
catch (...) {}
m_incoming_tx_lock.unlock();
- return true;
+ return success;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate)
{
+ TRY_ENTRY();
+
// load json & DNS checkpoints every 10min/hour respectively,
// and verify them with respect to what blocks we already have
CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
@@ -998,6 +1128,8 @@ namespace cryptonote
if(update_miner_blocktemplate && bvc.m_added_to_main_chain)
update_miner_block_template();
return true;
+
+ CATCH_ENTRY_L0("core::handle_incoming_block()", false);
}
//-----------------------------------------------------------------------------------------------
// Used by the RPC server to check the size of an incoming
@@ -1017,6 +1149,11 @@ namespace cryptonote
return m_blockchain_storage.get_tail_id();
}
//-----------------------------------------------------------------------------------------------
+ difficulty_type core::get_block_cumulative_difficulty(uint64_t height) const
+ {
+ return m_blockchain_storage.get_db().get_block_cumulative_difficulty(height);
+ }
+ //-----------------------------------------------------------------------------------------------
size_t core::get_pool_transactions_count() const
{
return m_mempool.get_transactions_count();
@@ -1070,6 +1207,11 @@ namespace cryptonote
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
+ {
+ return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_short_chain_history(std::list<crypto::hash>& ids) const
{
return m_blockchain_storage.get_short_chain_history(ids);
@@ -1146,6 +1288,16 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ uint8_t core::get_ideal_hard_fork_version(uint64_t height) const
+ {
+ return get_blockchain_storage().get_ideal_hard_fork_version(height);
+ }
+ //-----------------------------------------------------------------------------------------------
+ uint8_t core::get_hard_fork_version(uint64_t height) const
+ {
+ return get_blockchain_storage().get_hard_fork_version(height);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::check_updates()
{
static const char software[] = "monero";
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index e5fbf7f91..a9ee9e9d0 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -40,6 +40,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "common/download.h"
+#include "common/thread_group.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "cryptonote_basic/miner.h"
@@ -115,6 +116,22 @@ namespace cryptonote
bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
/**
+ * @brief handles a list of incoming transactions
+ *
+ * Parses incoming transactions and, if nothing is obviously wrong,
+ * passes them along to the transaction pool
+ *
+ * @param tx_blobs the txs to handle
+ * @param tvc metadata about the transactions' validity
+ * @param keeped_by_block if the transactions have been in a block
+ * @param relayed whether or not the transactions were relayed to us
+ * @param do_not_relay whether to prevent the transactions from being relayed
+ *
+ * @return true if the transactions made it to the transaction pool, otherwise false
+ */
+ bool handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+
+ /**
* @brief handles an incoming block
*
* periodic update to checkpoints is triggered here
@@ -390,6 +407,13 @@ namespace cryptonote
void set_enforce_dns_checkpoints(bool enforce_dns);
/**
+ * @brief set whether or not to enable or disable DNS checkpoints
+ *
+ * @param disble whether to disable DNS checkpoints
+ */
+ void disable_dns_checkpoints(bool disable = true) { m_disable_dns_checkpoints = disable; }
+
+ /**
* @copydoc tx_memory_pool::have_tx
*
* @note see tx_memory_pool::have_tx
@@ -404,6 +428,13 @@ namespace cryptonote
bool get_pool_transactions(std::list<transaction>& txs) const;
/**
+ * @copydoc tx_memory_pool::get_txpool_backlog
+ *
+ * @note see tx_memory_pool::get_txpool_backlog
+ */
+ bool get_txpool_backlog(std::vector<tx_backlog_entry>& backlog) const;
+
+ /**
* @copydoc tx_memory_pool::get_transactions
*
* @note see tx_memory_pool::get_transactions
@@ -432,6 +463,13 @@ namespace cryptonote
bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/**
+ * @copydoc tx_memory_pool::get_pool_for_rpc
+ *
+ * @note see tx_memory_pool::get_pool_for_rpc
+ */
+ bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
+
+ /**
* @copydoc tx_memory_pool::get_transactions_count
*
* @note see tx_memory_pool::get_transactions_count
@@ -497,6 +535,13 @@ namespace cryptonote
crypto::hash get_tail_id() const;
/**
+ * @copydoc Blockchain::get_block_cumulative_difficulty
+ *
+ * @note see Blockchain::get_block_cumulative_difficulty
+ */
+ difficulty_type get_block_cumulative_difficulty(uint64_t height) const;
+
+ /**
* @copydoc Blockchain::get_random_outs_for_amounts
*
* @note see Blockchain::get_random_outs_for_amounts
@@ -583,6 +628,13 @@ namespace cryptonote
void on_synchronized();
/**
+ * @copydoc Blockchain::safesyncmode
+ *
+ * 2note see Blockchain::safesyncmode
+ */
+ void safesyncmode(const bool onoff);
+
+ /**
* @brief sets the target blockchain height
*
* @param target_blockchain_height the height to set
@@ -597,6 +649,20 @@ namespace cryptonote
uint64_t get_target_blockchain_height() const;
/**
+ * @brief return the ideal hard fork version for a given block height
+ *
+ * @return what it says above
+ */
+ uint8_t get_ideal_hard_fork_version(uint64_t height) const;
+
+ /**
+ * @brief return the hard fork version for a given block height
+ *
+ * @return what it says above
+ */
+ uint8_t get_hard_fork_version(uint64_t height) const;
+
+ /**
* @brief gets start_time
*
*/
@@ -649,11 +715,21 @@ namespace cryptonote
bool are_key_images_spent(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
/**
+ * @brief check if multiple key images are spent in the transaction pool
+ *
+ * @param key_im list of key images to check
+ * @param spent return-by-reference result for each image checked
+ *
+ * @return true
+ */
+ bool are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
+
+ /**
* @brief get the number of blocks to sync in one go
*
* @return the number of blocks to sync in one go
*/
- size_t get_block_sync_size() const { return block_sync_size; }
+ size_t get_block_sync_size(uint64_t height) const;
/**
* @brief get the sum of coinbase tx amounts between blocks
@@ -669,6 +745,13 @@ namespace cryptonote
*/
bool get_testnet() const { return m_testnet; };
+ /**
+ * @brief get whether fluffy blocks are enabled
+ *
+ * @return whether fluffy blocks are enabled
+ */
+ bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
+
private:
/**
@@ -753,6 +836,9 @@ namespace cryptonote
*/
bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const;
+ 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);
+
/**
* @copydoc miner::on_block_chain_update
*
@@ -781,6 +867,15 @@ namespace cryptonote
bool check_tx_inputs_keyimages_diff(const transaction& tx) const;
/**
+ * @brief verify that each ring uses distinct members
+ *
+ * @param tx the transaction to check
+ *
+ * @return false if any ring uses duplicate members, true otherwise
+ */
+ bool check_tx_inputs_ring_members_diff(const transaction& tx) const;
+
+ /**
* @brief verify that each input key image in a transaction is in
* the valid domain
*
@@ -853,12 +948,16 @@ namespace cryptonote
time_t m_last_json_checkpoints_update; //!< time when json checkpoints were last updated
std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once
+ bool m_disable_dns_checkpoints;
size_t block_sync_size;
time_t start_time;
std::unordered_set<crypto::hash> bad_semantics_txes[2];
+ boost::mutex bad_semantics_txes_lock;
+
+ tools::thread_group m_threadpool;
enum {
UPDATES_DISABLED,
@@ -870,6 +969,8 @@ namespace cryptonote
tools::download_async_handle m_update_download;
size_t m_last_update_length;
boost::mutex m_update_mutex;
+
+ bool m_fluffy_blocks_enabled;
};
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 26d5fb767..94f069827 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -265,7 +265,7 @@ namespace cryptonote
// "Shuffle" outs
std::vector<tx_destination_entry> shuffled_dsts(destinations);
- std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } );
+ std::random_shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), [](unsigned int i) { return crypto::rand<unsigned int>() % i; });
uint64_t summary_outs_money = 0;
//fill outputs
@@ -371,7 +371,7 @@ namespace cryptonote
// enforce same mixin for all outputs
for (size_t i = 1; i < sources.size(); ++i) {
if (n_total_outs != sources[i].outputs.size()) {
- LOG_ERROR("Non-simple ringct transaction has varying mixin");
+ LOG_ERROR("Non-simple ringct transaction has varying ring size");
return false;
}
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 933070e1e..7aa7c280d 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -32,6 +32,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include <boost/serialization/vector.hpp>
#include <boost/serialization/utility.hpp>
+#include "ringct/rctOps.h"
namespace cryptonote
{
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index ffb5b478b..7de392036 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -80,7 +80,7 @@ namespace cryptonote
uint64_t get_transaction_size_limit(uint8_t version)
{
- return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
// This class is meant to create a batch when none currently exists.
@@ -92,7 +92,7 @@ namespace cryptonote
LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false) {
m_batch = m_blockchain.get_db().batch_start();
}
- ~LockedTXN() { if (m_batch) { m_blockchain.get_db().batch_stop(); } }
+ ~LockedTXN() { try { if (m_batch) { m_blockchain.get_db().batch_stop(); } } catch (const std::exception &e) { MWARNING("LockedTXN dtor filtering exception: " << e.what()); } }
private:
Blockchain &m_blockchain;
bool m_batch;
@@ -225,6 +225,7 @@ namespace cryptonote
meta.last_relayed_time = time(NULL);
meta.relayed = relayed;
meta.do_not_relay = do_not_relay;
+ memset(meta.padding, 0, sizeof(meta.padding));
try
{
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -261,6 +262,7 @@ namespace cryptonote
meta.last_relayed_time = time(NULL);
meta.relayed = relayed;
meta.do_not_relay = do_not_relay;
+ memset(meta.padding, 0, sizeof(meta.padding));
try
{
@@ -551,6 +553,17 @@ namespace cryptonote
});
}
//------------------------------------------------------------------
+ void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+ const uint64_t now = time(NULL);
+ m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
+ backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now});
+ return true;
+ });
+ }
+ //------------------------------------------------------------------
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -558,7 +571,10 @@ namespace cryptonote
const uint64_t now = time(NULL);
std::map<uint64_t, txpool_histo> agebytes;
stats.txs_total = m_blockchain.get_txpool_tx_count();
- m_blockchain.for_all_txpool_txes([&stats, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
+ std::vector<uint32_t> sizes;
+ sizes.reserve(stats.txs_total);
+ m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
+ sizes.push_back(meta.blob_size);
stats.bytes_total += meta.blob_size;
if (!stats.bytes_min || meta.blob_size < stats.bytes_min)
stats.bytes_min = meta.blob_size;
@@ -573,11 +589,12 @@ namespace cryptonote
stats.num_10m++;
if (meta.last_failed_height)
stats.num_failing++;
- uint64_t age = now - meta.receive_time;
+ uint64_t age = now - meta.receive_time + (now == meta.receive_time);
agebytes[age].txs++;
agebytes[age].bytes += meta.blob_size;
return true;
});
+ stats.bytes_med = epee::misc_utils::median(sizes);
if (stats.txs_total > 1)
{
/* looking for 98th percentile */
@@ -667,6 +684,65 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+ 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;
+ transaction tx;
+ if (!parse_and_validate_tx_from_blob(*bd, tx))
+ {
+ MERROR("Failed to parse tx from txpool");
+ // continue
+ return true;
+ }
+ txi.tx = tx;
+ txi.blob_size = meta.blob_size;
+ txi.fee = meta.fee;
+ txi.kept_by_block = meta.kept_by_block;
+ txi.max_used_block_height = meta.max_used_block_height;
+ txi.max_used_block_hash = meta.max_used_block_id;
+ txi.last_failed_block_height = meta.last_failed_height;
+ txi.last_failed_block_hash = meta.last_failed_id;
+ txi.receive_time = meta.receive_time;
+ txi.relayed = meta.relayed;
+ txi.last_relayed_time = meta.last_relayed_time;
+ txi.do_not_relay = meta.do_not_relay;
+ tx_infos.push_back(txi);
+ return true;
+ }, true);
+
+ for (const key_images_container::value_type& kee : m_spent_key_images) {
+ std::vector<crypto::hash> tx_hashes;
+ const std::unordered_set<crypto::hash>& kei_image_set = kee.second;
+ for (const crypto::hash& tx_id_hash : kei_image_set)
+ {
+ tx_hashes.push_back(tx_id_hash);
+ }
+
+ const crypto::key_image& k_image = kee.first;
+ key_image_infos[k_image] = tx_hashes;
+ }
+ return true;
+ }
+ //---------------------------------------------------------------------------------
+ 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);
+
+ spent.clear();
+
+ for (const auto& image : key_images)
+ {
+ spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true);
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------------
bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -846,6 +922,9 @@ namespace cryptonote
std::unordered_set<crypto::key_image> k_images;
LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
+
+ LockedTXN lock(m_blockchain);
+
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
{
@@ -903,7 +982,21 @@ namespace cryptonote
// Skip transactions that are not ready to be
// included into the blockchain or that are
// missing key images
- if (!is_transaction_ready_to_go(meta, tx))
+ const cryptonote::txpool_tx_meta_t original_meta = meta;
+ bool ready = is_transaction_ready_to_go(meta, tx);
+ if (memcmp(&original_meta, &meta, sizeof(meta)))
+ {
+ try
+ {
+ m_blockchain.update_txpool_tx(sorted_it->second, meta);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to update tx meta: " << e.what());
+ // continue, not fatal
+ }
+ }
+ if (!ready)
{
LOG_PRINT_L2(" not ready to go");
sorted_it++;
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 1858ccdd8..3e4ccb338 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -46,6 +46,7 @@
#include "blockchain_db/blockchain_db.h"
#include "crypto/hash.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/message_data_structs.h"
namespace cryptonote
{
@@ -243,6 +244,13 @@ namespace cryptonote
void get_transaction_hashes(std::vector<crypto::hash>& txs) const;
/**
+ * @brief get (size, fee, receive time) for all transaction in the pool
+ *
+ * @param txs return-by-reference that data
+ */
+ void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const;
+
+ /**
* @brief get a summary statistics of all transaction hashes in the pool
*
* @param stats return-by-reference the pool statistics
@@ -262,6 +270,28 @@ namespace cryptonote
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/**
+ * @brief get information about all transactions and key images in the pool
+ *
+ * see documentation on tx_in_pool and key_images_with_tx_hashes for more details
+ *
+ * @param tx_infos [out] the transactions' information
+ * @param key_image_infos [out] the spent key images' information
+ *
+ * @return true
+ */
+ bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
+
+ /**
+ * @brief check for presence of key images in the pool
+ *
+ * @param key_images [in] vector of key images to check
+ * @param spent [out] vector of bool to return
+ *
+ * @return true
+ */
+ 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
*
* @param h the hash of the transaction to get
@@ -493,7 +523,6 @@ private:
*/
std::unordered_set<crypto::hash> m_timed_out_transactions;
- std::string m_config_folder; //!< the folder to save state to
Blockchain& m_blockchain; //!< reference to the Blockchain object
};
}