aboutsummaryrefslogtreecommitdiff
path: root/src/blockchain_db
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2020-02-26 18:06:00 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2020-02-27 15:05:34 +0000
commit8958b4e7aa24d7f7de6667360b3558e52780e66f (patch)
tree0fca62135738b6ef82e4f8dcd14e975af469a4fe /src/blockchain_db
parentMerge pull request #6048 (diff)
downloadmonero-8958b4e7aa24d7f7de6667360b3558e52780e66f.tar.xz
blockchain_db: faster fetching of consecutive txes
Useful for wallet refresh or node sync
Diffstat (limited to 'src/blockchain_db')
-rw-r--r--src/blockchain_db/blockchain_db.h19
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp98
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h1
-rw-r--r--src/blockchain_db/testdb.h1
4 files changed, 119 insertions, 0 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index e9fc85803..d3a218365 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1291,6 +1291,25 @@ public:
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const = 0;
/**
+ * @brief fetches a variable number of blocks and transactions from the given height, in canonical blockchain order
+ *
+ * The subclass should return the blocks and transactions stored from the one with the given
+ * height. The number of blocks returned is variable, based on the max_size passed.
+ *
+ * @param start_height the height of the first block
+ * @param min_count the minimum number of blocks to return, if they exist
+ * @param max_count the maximum number of blocks to return
+ * @param max_size the maximum size of block/transaction data to return (will be exceeded by one blocks's worth at most, if min_count is met)
+ * @param blocks the returned block/transaction data
+ * @param pruned whether to return full or pruned tx data
+ * @param skip_coinbase whether to return or skip coinbase transactions (they're in blocks regardless)
+ * @param get_miner_tx_hash whether to calculate and return the miner (coinbase) tx hash
+ *
+ * @return true iff the blocks and transactions were found
+ */
+ virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0;
+
+ /**
* @brief fetches the prunable transaction blob with the given hash
*
* The subclass should return the prunable transaction stored which has the given
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 5093015f2..05027c0ef 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -3107,6 +3107,104 @@ bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t coun
return true;
}
+bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(blocks);
+ RCURSOR(tx_indices);
+ RCURSOR(txs_pruned);
+ if (!pruned)
+ {
+ RCURSOR(txs_prunable);
+ }
+
+ blocks.reserve(std::min<size_t>(max_count, 10000)); // guard against very large max count if only checking bytes
+ const uint64_t blockchain_height = height();
+ uint64_t size = 0;
+ MDB_val_copy<uint64_t> key(start_height);
+ MDB_val k, v, val_tx_id;
+ uint64_t tx_id = ~0;
+ MDB_cursor_op op = MDB_SET;
+ for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_count && (size < max_size || blocks.size() < min_count); ++h)
+ {
+ MDB_cursor_op op = h == start_height ? MDB_SET : MDB_NEXT;
+ int result = mdb_cursor_get(m_cur_blocks, &key, &v, op);
+ if (result == MDB_NOTFOUND)
+ throw0(BLOCK_DNE(std::string("Attempt to get block from height ").append(boost::lexical_cast<std::string>(h)).append(" failed -- block not in db").c_str()));
+ else if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve a block from the db", result).c_str()));
+
+ blocks.resize(blocks.size() + 1);
+ auto &current_block = blocks.back();
+
+ current_block.first.first.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
+ size += v.mv_size;
+
+ cryptonote::block b;
+ if (!parse_and_validate_block_from_blob(current_block.first.first, b))
+ throw0(DB_ERROR("Invalid block"));
+ current_block.first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash;
+
+ // get the tx_id for the first tx (the first block's coinbase tx)
+ if (h == start_height)
+ {
+ crypto::hash hash = cryptonote::get_transaction_hash(b.miner_tx);
+ MDB_val_set(v, hash);
+ result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve block coinbase transaction from the db: ", result).c_str()));
+
+ const txindex *tip = (const txindex *)v.mv_data;
+ tx_id = tip->data.tx_id;
+ val_tx_id.mv_data = &tx_id;
+ val_tx_id.mv_size = sizeof(tx_id);
+ }
+
+ if (skip_coinbase)
+ {
+ result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ if (!pruned)
+ {
+ result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ }
+ }
+
+ op = MDB_NEXT;
+
+ current_block.second.reserve(b.tx_hashes.size());
+ for (const auto &tx_hash: b.tx_hashes)
+ {
+ // get pruned data
+ cryptonote::blobdata tx_blob;
+ result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ tx_blob.assign((const char*)v.mv_data, v.mv_size);
+
+ if (!pruned)
+ {
+ result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ tx_blob.append(reinterpret_cast<const char*>(v.mv_data), v.mv_size);
+ }
+ current_block.second.push_back(std::make_pair(tx_hash, std::move(tx_blob)));
+ size += current_block.second.back().second.size();
+ }
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return true;
+}
+
bool BlockchainLMDB::get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 7c0b4c72c..6ddeed671 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -255,6 +255,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const;
+ virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const;
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const;
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index 46de38c7e..638dd3b37 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -70,6 +70,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const { return false; }
+ virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const { return false; }
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; }
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }