From 628428a0df589b03b0020572e45d888c4ba17471 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 21:36:38 +0100 Subject: [PATCH] blockchain_ancestry: faster and uses less memory --- .../blockchain_ancestry.cpp | 164 ++++++++++++------ 1 file changed, 114 insertions(+), 50 deletions(-) diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index c988b7250..2f0bbffd6 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -73,31 +73,101 @@ namespace std { size_t operator()(const ancestor &a) const { - crypto::hash h; - crypto::cn_fast_hash(&a, sizeof(a), h); - return reinterpret_cast(h); + return a.amount ^ a.offset; // not that bad, since amount almost always have a high bit set, and offset doesn't } }; } +struct tx_data_t +{ + std::vector>> vin; + std::vector vout; + bool coinbase; + + tx_data_t(): coinbase(false) {} + tx_data_t(const cryptonote::transaction &tx) + { + coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); + if (!coinbase) + { + vin.reserve(tx.vin.size()); + for (size_t ring = 0; ring < tx.vin.size(); ++ring) + { + if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + { + const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); + vin.push_back(std::make_pair(txin.amount, cryptonote::relative_output_offsets_to_absolute(txin.key_offsets))); + } + else + { + LOG_PRINT_L0("Bad vin type in txid " << get_transaction_hash(tx)); + throw std::runtime_error("Bad vin type"); + } + } + } + vout.reserve(tx.vout.size()); + for (size_t out = 0; out < tx.vout.size(); ++out) + { + if (tx.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(tx.vout[out].target); + vout.push_back(txout.key); + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << get_transaction_hash(tx)); + throw std::runtime_error("Bad vout type"); + } + } + } + + template void serialize(t_archive &a, const unsigned int ver) + { + a & coinbase; + a & vin; + a & vout; + } +}; + struct ancestry_state_t { uint64_t height; std::unordered_map> ancestry; std::unordered_map output_cache; - std::unordered_map tx_cache; - std::unordered_map block_cache; + std::unordered_map tx_cache; + std::vector block_cache; template void serialize(t_archive &a, const unsigned int ver) { a & height; a & ancestry; a & output_cache; - a & tx_cache; - a & block_cache; + if (ver < 1) + { + std::unordered_map old_tx_cache; + a & old_tx_cache; + for (const auto i: old_tx_cache) + tx_cache.insert(std::make_pair(i.first, ::tx_data_t(i.second))); + } + else + { + a & tx_cache; + } + if (ver < 2) + { + std::unordered_map old_block_cache; + a & old_block_cache; + block_cache.resize(old_block_cache.size()); + for (const auto i: old_block_cache) + block_cache[i.first] = i.second; + } + else + { + a & block_cache; + } } }; -BOOST_CLASS_VERSION(ancestry_state_t, 0) +BOOST_CLASS_VERSION(ancestry_state_t, 2) static void add_ancestor(std::unordered_map &ancestry, uint64_t amount, uint64_t offset) { @@ -333,6 +403,7 @@ int main(int argc, char* argv[]) MINFO("Starting from height " << state.height); const uint64_t db_height = db->height(); + state.block_cache.reserve(db_height); for (uint64_t h = state.height; h < db_height; ++h) { size_t block_ancestry_size = 0; @@ -346,7 +417,10 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_blocks) - state.block_cache.insert(std::make_pair(h, b)); + { + state.block_cache.resize(h + 1); + state.block_cache[h] = b; + } std::vector txids; txids.reserve(1 + b.tx_hashes.size()); if (opt_include_coinbase) @@ -357,13 +431,13 @@ int main(int argc, char* argv[]) { printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height); fflush(stdout); - cryptonote::transaction tx; - std::unordered_map::const_iterator i = state.tx_cache.find(txid); + ::tx_data_t tx_data; + std::unordered_map::const_iterator i = state.tx_cache.find(txid); ++total_txes; if (i != state.tx_cache.end()) { ++cached_txes; - tx = i->second; + tx_data = i->second; } else { @@ -373,39 +447,38 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Failed to get txid " << txid << " from db"); return 1; } + cryptonote::transaction tx; if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) { LOG_PRINT_L0("Bad tx: " << txid); return 1; } + tx_data = ::tx_data_t(tx); if (opt_cache_txes) - state.tx_cache.insert(std::make_pair(txid, tx)); + state.tx_cache.insert(std::make_pair(txid, tx_data)); } - const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); - if (coinbase) + if (tx_data.coinbase) { add_ancestry(state.ancestry, txid, std::unordered_set()); } else { - for (size_t ring = 0; ring < tx.vin.size(); ++ring) + for (size_t ring = 0; ring < tx_data.vin.size(); ++ring) { - if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + if (1) { - const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); - const uint64_t amount = txin.amount; - auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); + const uint64_t amount = tx_data.vin[ring].first; + const std::vector &absolute_offsets = tx_data.vin[ring].second; for (uint64_t offset: absolute_offsets) { const output_data_t od = db->get_output_key(amount, offset); add_ancestry(state.ancestry, txid, ancestor{amount, offset}); cryptonote::block b; - auto iblock = state.block_cache.find(od.height); ++total_blocks; - if (iblock != state.block_cache.end()) + if (state.block_cache.size() > od.height && !state.block_cache[od.height].miner_tx.vin.empty()) { ++cached_blocks; - b = iblock->second; + b = state.block_cache[od.height]; } else { @@ -417,7 +490,10 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_blocks) - state.block_cache.insert(std::make_pair(od.height, b)); + { + state.block_cache.resize(od.height + 1); + state.block_cache[od.height] = b; + } } // find the tx which created this output bool found = false; @@ -453,13 +529,13 @@ int main(int argc, char* argv[]) { if (found) break; - cryptonote::transaction tx2; - std::unordered_map::const_iterator i = state.tx_cache.find(block_txid); + ::tx_data_t tx_data2; + std::unordered_map::const_iterator i = state.tx_cache.find(block_txid); ++total_txes; if (i != state.tx_cache.end()) { ++cached_txes; - tx2 = i->second; + tx_data2 = i->second; } else { @@ -469,32 +545,25 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Failed to get txid " << block_txid << " from db"); return 1; } - if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2)) + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) { LOG_PRINT_L0("Bad tx: " << block_txid); return 1; } + tx_data2 = ::tx_data_t(tx); if (opt_cache_txes) - state.tx_cache.insert(std::make_pair(block_txid, tx2)); + state.tx_cache.insert(std::make_pair(block_txid, tx_data2)); } - for (size_t out = 0; out < tx2.vout.size(); ++out) + for (size_t out = 0; out < tx_data2.vout.size(); ++out) { - if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + if (tx_data2.vout[out] == od.pubkey) { - const auto &txout = boost::get(tx2.vout[out].target); - if (txout.key == od.pubkey) - { - found = true; - add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid)); - if (opt_cache_outputs) - state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); - break; - } - } - else - { - LOG_PRINT_L0("Bad vout type in txid " << block_txid); - return 1; + found = true; + add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid)); + if (opt_cache_outputs) + state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); + break; } } } @@ -505,11 +574,6 @@ int main(int argc, char* argv[]) } } } - else - { - LOG_PRINT_L0("Bad vin type in txid " << txid); - return 1; - } } } const size_t ancestry_size = get_ancestry(state.ancestry, txid).size();