aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp174
-rw-r--r--src/cryptonote_core/blockchain.h16
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp20
-rw-r--r--src/cryptonote_core/cryptonote_core.h8
-rw-r--r--src/cryptonote_core/tx_pool.cpp23
5 files changed, 180 insertions, 61 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index ebdb6ed79..bbac20eaa 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -171,6 +171,11 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) :
LOG_PRINT_L3("Blockchain::" << __func__);
}
//------------------------------------------------------------------
+Blockchain::~Blockchain()
+{
+ deinit();
+}
+//------------------------------------------------------------------
bool Blockchain::have_tx(const crypto::hash &id) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -550,15 +555,13 @@ bool Blockchain::deinit()
// as this should be called if handling a SIGSEGV, need to check
// if m_db is a NULL pointer (and thus may have caused the illegal
// memory operation), otherwise we may cause a loop.
- if (m_db == NULL)
- {
- throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
- }
-
try
{
- m_db->close();
- MTRACE("Local blockchain read/write activity stopped successfully");
+ if (m_db)
+ {
+ m_db->close();
+ MTRACE("Local blockchain read/write activity stopped successfully");
+ }
}
catch (const std::exception& e)
{
@@ -576,6 +579,38 @@ bool Blockchain::deinit()
return true;
}
//------------------------------------------------------------------
+// This function removes blocks from the top of blockchain.
+// It starts a batch and calls private method pop_block_from_blockchain().
+void Blockchain::pop_blocks(uint64_t nblocks)
+{
+ uint64_t i;
+ CRITICAL_REGION_LOCAL(m_tx_pool);
+ CRITICAL_REGION_LOCAL1(m_blockchain_lock);
+
+ while (!m_db->batch_start())
+ {
+ m_blockchain_lock.unlock();
+ m_tx_pool.unlock();
+ epee::misc_utils::sleep_no_w(1000);
+ m_tx_pool.lock();
+ m_blockchain_lock.lock();
+ }
+
+ try
+ {
+ for (i=0; i < nblocks; ++i)
+ {
+ pop_block_from_blockchain();
+ }
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("Error when popping blocks, only " << i << " blocks are popped: " << e.what());
+ }
+
+ m_db->batch_stop();
+}
+//------------------------------------------------------------------
// This function tells BlockchainDB to remove the top block from the
// blockchain and then returns all transactions (except the miner tx, of course)
// from it to the tx_pool
@@ -1237,7 +1272,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
uint64_t already_generated_coins;
uint64_t pool_cookie;
- CRITICAL_REGION_BEGIN(m_blockchain_lock);
+ m_tx_pool.lock();
+ const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); });
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
height = m_db->height();
if (m_btc_valid) {
// The pool cookie is atomic. The lack of locking is OK, as if it changes
@@ -1273,8 +1310,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
median_weight = m_current_block_cumul_weight_limit / 2;
already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
- CRITICAL_REGION_END();
-
size_t txs_weight;
uint64_t fee;
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version()))
@@ -1285,7 +1320,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_weight = 0;
uint64_t real_fee = 0;
- CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
for(crypto::hash &cur_hash: b.tx_hashes)
{
auto cur_res = m_tx_pool.m_transactions.find(cur_hash);
@@ -1329,7 +1363,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
{
LOG_ERROR("Creating block template: error: wrongly calculated fee");
}
- CRITICAL_REGION_END();
MDEBUG("Creating block template: height " << height <<
", median weight " << median_weight <<
", already generated coins " << already_generated_coins <<
@@ -3723,7 +3756,7 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints)
}
//------------------------------------------------------------------
-void Blockchain::block_longhash_worker(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 epee::span<const block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const
{
TIME_MEASURE_START(t);
slow_hash_allocate_state();
@@ -3809,11 +3842,33 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
}
//------------------------------------------------------------------
-void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) const
+void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, const std::vector<output_data_t> &extra_tx_map) const
{
try
{
m_db->get_output_key(epee::span<const uint64_t>(&amount, 1), offsets, outputs, true);
+ if (outputs.size() < offsets.size())
+ {
+ const uint64_t n_outputs = m_db->get_num_outputs(amount);
+ for (size_t i = outputs.size(); i < offsets.size(); ++i)
+ {
+ uint64_t idx = offsets[i];
+ if (idx < n_outputs)
+ {
+ MWARNING("Index " << idx << " not found in db for amount " << amount << ", but it is less than the number of entries");
+ break;
+ }
+ else if (idx < n_outputs + extra_tx_map.size())
+ {
+ outputs.push_back(extra_tx_map[idx - n_outputs]);
+ }
+ else
+ {
+ MWARNING("missed " << amount << "/" << idx << " in " << extra_tx_map.size() << " (chain " << n_outputs << ")");
+ break;
+ }
+ }
+ }
}
catch (const std::exception& e)
{
@@ -3928,6 +3983,34 @@ 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.
+static bool update_output_map(std::map<uint64_t, std::vector<output_data_t>> &extra_tx_map, const transaction &tx, uint64_t height, bool miner)
+{
+ MTRACE("Blockchain::" << __func__);
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ {
+ const auto &out = tx.vout[i];
+ if (out.target.type() != typeid(txout_to_key))
+ continue;
+ const txout_to_key &out_to_key = boost::get<txout_to_key>(out.target);
+ rct::key commitment;
+ uint64_t amount = out.amount;
+ if (miner && tx.version == 2)
+ {
+ commitment = rct::zeroCommit(amount);
+ amount = 0;
+ }
+ else if (tx.version > 1)
+ {
+ CHECK_AND_ASSERT_MES(i < tx.rct_signatures.outPk.size(), false, "Invalid outPk size");
+ commitment = tx.rct_signatures.outPk[i].mask;
+ }
+ else
+ commitment = rct::zero();
+ extra_tx_map[amount].push_back(output_data_t{out_to_key.key, tx.unlock_time, height, commitment});
+ }
+ return true;
+}
+
bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry)
{
MTRACE("Blockchain::" << __func__);
@@ -3974,42 +4057,40 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
m_blockchain_lock.lock();
}
- if ((m_db->height() + blocks_entry.size()) < m_blocks_hash_check.size())
+ const uint64_t height = m_db->height();
+ if ((height + blocks_entry.size()) < m_blocks_hash_check.size())
return true;
bool blocks_exist = false;
tools::threadpool& tpool = tools::threadpool::getInstance();
- uint64_t threads = tpool.get_max_concurrency();
+ unsigned threads = tpool.get_max_concurrency();
+ std::vector<block> blocks;
+ blocks.resize(blocks_entry.size());
- if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1)
+ if (1)
{
// limit threads, default limit = 4
if(threads > m_max_prepare_blocks_threads)
threads = m_max_prepare_blocks_threads;
- uint64_t height = m_db->height();
- int batches = blocks_entry.size() / threads;
- int extra = blocks_entry.size() % threads;
+ unsigned int batches = blocks_entry.size() / threads;
+ unsigned int extra = blocks_entry.size() % threads;
MDEBUG("block_batches: " << batches);
std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads);
- std::vector < std::vector < block >> blocks(threads);
auto it = blocks_entry.begin();
+ unsigned blockidx = 0;
- for (uint64_t i = 0; i < threads; i++)
+ for (unsigned i = 0; i < threads; i++)
{
- blocks[i].reserve(batches + 1);
- for (int j = 0; j < batches; j++)
+ for (unsigned int j = 0; j < batches; j++, ++blockidx)
{
- block block;
+ block &block = blocks[blockidx];
if (!parse_and_validate_block_from_blob(it->block, block))
- {
- std::advance(it, 1);
- continue;
- }
+ return false;
// check first block and skip all blocks if its not chained properly
- if (i == 0 && j == 0)
+ if (blockidx == 0)
{
crypto::hash tophash = m_db->top_block_hash();
if (block.prev_id != tophash)
@@ -4024,20 +4105,16 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
break;
}
- blocks[i].push_back(std::move(block));
std::advance(it, 1);
}
}
- for (int i = 0; i < extra && !blocks_exist; i++)
+ for (unsigned i = 0; i < extra && !blocks_exist; i++, blockidx++)
{
- block block;
+ block &block = blocks[blockidx];
if (!parse_and_validate_block_from_blob(it->block, block))
- {
- std::advance(it, 1);
- continue;
- }
+ return false;
if (have_block(get_block_hash(block)))
{
@@ -4045,7 +4122,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
break;
}
- blocks[i].push_back(std::move(block));
std::advance(it, 1);
}
@@ -4054,10 +4130,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
m_blocks_longhash_table.clear();
uint64_t thread_height = height;
tools::threadpool::waiter waiter;
- for (uint64_t i = 0; i < threads; i++)
+ for (unsigned int i = 0; i < threads; i++)
{
- tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])), true);
- thread_height += blocks[i].size();
+ 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);
+ thread_height += nblocks;
}
waiter.wait(&tpool);
@@ -4100,7 +4179,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
// [input] stores all absolute_offsets for each amount
std::map<uint64_t, std::vector<uint64_t>> offset_map;
// [output] stores all output_data_t for each absolute_offset
- std::map<uint64_t, std::vector<output_data_t>> tx_map;
+ std::map<uint64_t, std::vector<output_data_t>> tx_map, extra_tx_map;
std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs);
#define SCAN_TABLE_QUIT(m) \
@@ -4111,12 +4190,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
- size_t tx_index = 0;
+ size_t tx_index = 0, block_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
return false;
+ if (!update_output_map(extra_tx_map, blocks[block_index].miner_tx, height + block_index, true))
+ SCAN_TABLE_QUIT("Error building extra tx map.");
for (const auto &tx_blob : entry.txs)
{
if (tx_index >= txes.size())
@@ -4175,7 +4256,10 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
offset_map[in_to_key.amount].push_back(offset);
}
+ if (!update_output_map(extra_tx_map, tx, height + block_index, false))
+ SCAN_TABLE_QUIT("Error building extra tx map.");
}
+ ++block_index;
}
// sort and remove duplicate absolute_offsets in offset_map
@@ -4198,7 +4282,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
for (size_t i = 0; i < amounts.size(); i++)
{
uint64_t amount = amounts[i];
- tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount])), true);
+ tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::cref(extra_tx_map[amount])), true);
}
waiter.wait(&tpool);
}
@@ -4207,7 +4291,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
for (size_t i = 0; i < amounts.size(); i++)
{
uint64_t amount = amounts[i];
- output_scan_worker(amount, offset_map[amount], tx_map[amount]);
+ output_scan_worker(amount, offset_map[amount], tx_map[amount], extra_tx_map[amount]);
}
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index dfe833fb4..67bccc6c6 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -121,6 +121,11 @@ namespace cryptonote
Blockchain(tx_memory_pool& tx_pool);
/**
+ * @brief Blockchain destructor
+ */
+ ~Blockchain();
+
+ /**
* @brief Initialize the Blockchain state
*
* @param db a pointer to the backing store to use for the blockchain
@@ -918,7 +923,7 @@ namespace cryptonote
* @param outputs return-by-reference the outputs collected
*/
void output_scan_worker(const uint64_t amount,const std::vector<uint64_t> &offsets,
- std::vector<output_data_t> &outputs) const;
+ std::vector<output_data_t> &outputs, const std::vector<output_data_t> &extra_tx_map) const;
/**
* @brief computes the "short" and "long" hashes for a set of blocks
@@ -927,7 +932,7 @@ namespace cryptonote
* @param blocks the blocks to be hashed
* @param map return-by-reference the hashes for each block
*/
- void block_longhash_worker(uint64_t height, const std::vector<block> &blocks,
+ void block_longhash_worker(uint64_t height, const epee::span<const block> &blocks,
std::unordered_map<crypto::hash, crypto::hash> &map) const;
/**
@@ -967,6 +972,13 @@ namespace cryptonote
*/
std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;
+ /**
+ * @brief removes blocks from the top of the blockchain
+ *
+ * @param nblocks number of blocks to be removed
+ */
+ void pop_blocks(uint64_t nblocks);
+
private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index f918afa33..f3249ea92 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -160,6 +160,11 @@ namespace cryptonote
, "Relay blocks as normal blocks"
, false
};
+ static const command_line::arg_descriptor<bool> arg_pad_transactions = {
+ "pad-transactions"
+ , "Pad relayed transactions to help defend against traffic volume analysis"
+ , false
+ };
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
"max-txpool-weight"
, "Set maximum txpool weight in bytes."
@@ -185,7 +190,8 @@ namespace cryptonote
m_disable_dns_checkpoints(false),
m_update_download(0),
m_nettype(UNDEFINED),
- m_update_available(false)
+ m_update_available(false),
+ m_pad_transactions(false)
{
m_checkpoints_updating.clear();
set_cryptonote_protocol(pprotocol);
@@ -244,6 +250,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------
void core::stop()
{
+ m_miner.stop();
m_blockchain_storage.cancel();
tools::download_async_handle handle;
@@ -279,6 +286,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_offline);
command_line::add_arg(desc, arg_disable_dns_checkpoints);
command_line::add_arg(desc, arg_max_txpool_weight);
+ command_line::add_arg(desc, arg_pad_transactions);
command_line::add_arg(desc, arg_block_notify);
miner::init_options(desc);
@@ -317,6 +325,7 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
+ m_pad_transactions = get_arg(vm, arg_pad_transactions);
m_offline = get_arg(vm, arg_offline);
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
@@ -893,13 +902,15 @@ namespace cryptonote
bool ok = true;
it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
- if (already_have[i])
- continue;
if (!results[i].res)
{
ok = false;
continue;
}
+ if (keeped_by_block)
+ get_blockchain_storage().on_new_tx_from_block(results[i].tx);
+ if (already_have[i])
+ continue;
const size_t weight = get_transaction_weight(results[i].tx, it->size());
ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay);
@@ -1130,9 +1141,6 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
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)
{
- if (keeped_by_block)
- get_blockchain_storage().on_new_tx_from_block(tx);
-
if(m_mempool.have_tx(tx_hash))
{
LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool");
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 0ae063342..cc53fce58 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -755,6 +755,13 @@ namespace cryptonote
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
/**
+ * @brief get whether transaction relay should be padded
+ *
+ * @return whether transaction relay should be padded
+ */
+ bool pad_transactions() const { return m_pad_transactions; }
+
+ /**
* @brief check a set of hashes against the precompiled hash set
*
* @return number of usable blocks
@@ -1013,6 +1020,7 @@ namespace cryptonote
bool m_fluffy_blocks_enabled;
bool m_offline;
+ bool m_pad_transactions;
};
}
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index d4b4e4d34..1c8f1c62c 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -480,6 +480,10 @@ namespace cryptonote
MERROR("Failed to parse tx from txpool");
return false;
}
+ else
+ {
+ tx.set_hash(id);
+ }
tx_weight = meta.weight;
fee = meta.fee;
relayed = meta.relayed;
@@ -659,7 +663,8 @@ namespace cryptonote
// continue
return true;
}
- txs.push_back(tx);
+ tx.set_hash(txid);
+ txs.push_back(std::move(tx));
return true;
}, true, include_unrelayed_txes);
}
@@ -782,6 +787,7 @@ namespace cryptonote
// continue
return true;
}
+ tx.set_hash(txid);
txi.tx_json = obj_to_json_str(tx);
txi.blob_size = bd->size();
txi.weight = meta.weight;
@@ -798,7 +804,7 @@ namespace cryptonote
txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0;
txi.do_not_relay = meta.do_not_relay;
txi.double_spend_seen = meta.double_spend_seen;
- tx_infos.push_back(txi);
+ tx_infos.push_back(std::move(txi));
return true;
}, true, include_sensitive_data);
@@ -847,14 +853,13 @@ namespace cryptonote
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
cryptonote::rpc::tx_in_pool txi;
txi.tx_hash = txid;
- transaction tx;
- if (!parse_and_validate_tx_from_blob(*bd, tx))
+ if (!parse_and_validate_tx_from_blob(*bd, txi.tx))
{
MERROR("Failed to parse tx from txpool");
// continue
return true;
}
- txi.tx = tx;
+ txi.tx.set_hash(txid);
txi.blob_size = bd->size();
txi.weight = meta.weight;
txi.fee = meta.fee;
@@ -881,7 +886,7 @@ namespace cryptonote
}
const crypto::key_image& k_image = kee.first;
- key_image_infos[k_image] = tx_hashes;
+ key_image_infos[k_image] = std::move(tx_hashes);
}
return true;
}
@@ -990,21 +995,23 @@ namespace cryptonote
{
struct transction_parser
{
- transction_parser(const cryptonote::blobdata &txblob, transaction &tx): txblob(txblob), tx(tx), parsed(false) {}
+ transction_parser(const cryptonote::blobdata &txblob, const crypto::hash &txid, transaction &tx): txblob(txblob), txid(txid), tx(tx), parsed(false) {}
cryptonote::transaction &operator()()
{
if (!parsed)
{
if (!parse_and_validate_tx_from_blob(txblob, tx))
throw std::runtime_error("failed to parse transaction blob");
+ tx.set_hash(txid);
parsed = true;
}
return tx;
}
const cryptonote::blobdata &txblob;
+ const crypto::hash &txid;
transaction &tx;
bool parsed;
- } lazy_tx(txblob, tx);
+ } lazy_tx(txblob, txid, tx);
//not the best implementation at this time, sorry :(
//check is ring_signature already checked ?