blockchain_ancestry: faster and uses less memory
This commit is contained in:
parent
2382484dcd
commit
628428a0df
@ -73,31 +73,101 @@ namespace std
|
|||||||
{
|
{
|
||||||
size_t operator()(const ancestor &a) const
|
size_t operator()(const ancestor &a) const
|
||||||
{
|
{
|
||||||
crypto::hash h;
|
return a.amount ^ a.offset; // not that bad, since amount almost always have a high bit set, and offset doesn't
|
||||||
crypto::cn_fast_hash(&a, sizeof(a), h);
|
|
||||||
return reinterpret_cast<const std::size_t &>(h);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct tx_data_t
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint64_t, std::vector<uint64_t>>> vin;
|
||||||
|
std::vector<crypto::public_key> 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<cryptonote::txin_to_key>(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<cryptonote::txout_to_key>(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 <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
|
||||||
|
{
|
||||||
|
a & coinbase;
|
||||||
|
a & vin;
|
||||||
|
a & vout;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct ancestry_state_t
|
struct ancestry_state_t
|
||||||
{
|
{
|
||||||
uint64_t height;
|
uint64_t height;
|
||||||
std::unordered_map<crypto::hash, std::unordered_set<ancestor>> ancestry;
|
std::unordered_map<crypto::hash, std::unordered_set<ancestor>> ancestry;
|
||||||
std::unordered_map<ancestor, crypto::hash> output_cache;
|
std::unordered_map<ancestor, crypto::hash> output_cache;
|
||||||
std::unordered_map<crypto::hash, cryptonote::transaction> tx_cache;
|
std::unordered_map<crypto::hash, ::tx_data_t> tx_cache;
|
||||||
std::unordered_map<uint64_t, cryptonote::block> block_cache;
|
std::vector<cryptonote::block> block_cache;
|
||||||
|
|
||||||
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
|
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
|
||||||
{
|
{
|
||||||
a & height;
|
a & height;
|
||||||
a & ancestry;
|
a & ancestry;
|
||||||
a & output_cache;
|
a & output_cache;
|
||||||
a & tx_cache;
|
if (ver < 1)
|
||||||
a & block_cache;
|
{
|
||||||
|
std::unordered_map<crypto::hash, cryptonote::transaction> 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<uint64_t, cryptonote::block> 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<ancestor, unsigned int> &ancestry, uint64_t amount, uint64_t offset)
|
static void add_ancestor(std::unordered_map<ancestor, unsigned int> &ancestry, uint64_t amount, uint64_t offset)
|
||||||
{
|
{
|
||||||
@ -333,6 +403,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
MINFO("Starting from height " << state.height);
|
MINFO("Starting from height " << state.height);
|
||||||
const uint64_t db_height = db->height();
|
const uint64_t db_height = db->height();
|
||||||
|
state.block_cache.reserve(db_height);
|
||||||
for (uint64_t h = state.height; h < db_height; ++h)
|
for (uint64_t h = state.height; h < db_height; ++h)
|
||||||
{
|
{
|
||||||
size_t block_ancestry_size = 0;
|
size_t block_ancestry_size = 0;
|
||||||
@ -346,7 +417,10 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (opt_cache_blocks)
|
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<crypto::hash> txids;
|
std::vector<crypto::hash> txids;
|
||||||
txids.reserve(1 + b.tx_hashes.size());
|
txids.reserve(1 + b.tx_hashes.size());
|
||||||
if (opt_include_coinbase)
|
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);
|
printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
cryptonote::transaction tx;
|
::tx_data_t tx_data;
|
||||||
std::unordered_map<crypto::hash, cryptonote::transaction>::const_iterator i = state.tx_cache.find(txid);
|
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(txid);
|
||||||
++total_txes;
|
++total_txes;
|
||||||
if (i != state.tx_cache.end())
|
if (i != state.tx_cache.end())
|
||||||
{
|
{
|
||||||
++cached_txes;
|
++cached_txes;
|
||||||
tx = i->second;
|
tx_data = i->second;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -373,39 +447,38 @@ int main(int argc, char* argv[])
|
|||||||
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
|
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
cryptonote::transaction tx;
|
||||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
|
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
|
||||||
{
|
{
|
||||||
LOG_PRINT_L0("Bad tx: " << txid);
|
LOG_PRINT_L0("Bad tx: " << txid);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
tx_data = ::tx_data_t(tx);
|
||||||
if (opt_cache_txes)
|
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 (tx_data.coinbase)
|
||||||
if (coinbase)
|
|
||||||
{
|
{
|
||||||
add_ancestry(state.ancestry, txid, std::unordered_set<ancestor>());
|
add_ancestry(state.ancestry, txid, std::unordered_set<ancestor>());
|
||||||
}
|
}
|
||||||
else
|
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<cryptonote::txin_to_key>(tx.vin[ring]);
|
const uint64_t amount = tx_data.vin[ring].first;
|
||||||
const uint64_t amount = txin.amount;
|
const std::vector<uint64_t> &absolute_offsets = tx_data.vin[ring].second;
|
||||||
auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
|
|
||||||
for (uint64_t offset: absolute_offsets)
|
for (uint64_t offset: absolute_offsets)
|
||||||
{
|
{
|
||||||
const output_data_t od = db->get_output_key(amount, offset);
|
const output_data_t od = db->get_output_key(amount, offset);
|
||||||
add_ancestry(state.ancestry, txid, ancestor{amount, offset});
|
add_ancestry(state.ancestry, txid, ancestor{amount, offset});
|
||||||
cryptonote::block b;
|
cryptonote::block b;
|
||||||
auto iblock = state.block_cache.find(od.height);
|
|
||||||
++total_blocks;
|
++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;
|
++cached_blocks;
|
||||||
b = iblock->second;
|
b = state.block_cache[od.height];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -417,7 +490,10 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (opt_cache_blocks)
|
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
|
// find the tx which created this output
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@ -453,13 +529,13 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
if (found)
|
if (found)
|
||||||
break;
|
break;
|
||||||
cryptonote::transaction tx2;
|
::tx_data_t tx_data2;
|
||||||
std::unordered_map<crypto::hash, cryptonote::transaction>::const_iterator i = state.tx_cache.find(block_txid);
|
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(block_txid);
|
||||||
++total_txes;
|
++total_txes;
|
||||||
if (i != state.tx_cache.end())
|
if (i != state.tx_cache.end())
|
||||||
{
|
{
|
||||||
++cached_txes;
|
++cached_txes;
|
||||||
tx2 = i->second;
|
tx_data2 = i->second;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -469,32 +545,25 @@ int main(int argc, char* argv[])
|
|||||||
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
|
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
|
||||||
return 1;
|
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);
|
LOG_PRINT_L0("Bad tx: " << block_txid);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
tx_data2 = ::tx_data_t(tx);
|
||||||
if (opt_cache_txes)
|
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<cryptonote::txout_to_key>(tx2.vout[out].target);
|
found = true;
|
||||||
if (txout.key == od.pubkey)
|
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid));
|
||||||
{
|
if (opt_cache_outputs)
|
||||||
found = true;
|
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid));
|
||||||
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid));
|
break;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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();
|
const size_t ancestry_size = get_ancestry(state.ancestry, txid).size();
|
||||||
|
Loading…
Reference in New Issue
Block a user