@@ -644,16 +644,18 @@ namespace cryptonote
644
644
return true ;
645
645
}
646
646
// ------------------------------------------------------------------
647
- bool tx_memory_pool::get_transactions_info (const std::vector< crypto::hash>& txids, std::vector<std::pair<crypto::hash, tx_details>>& txs, bool include_sensitive, size_t limit_size) const
647
+ bool tx_memory_pool::get_transactions_info (const epee::span< const crypto::hash> txids, std::vector<std::pair<crypto::hash, tx_details>>& txs, bool include_sensitive, size_t limit_size) const
648
648
{
649
649
CRITICAL_REGION_LOCAL (m_transactions_lock);
650
650
CRITICAL_REGION_LOCAL1 (m_blockchain);
651
651
652
652
txs.clear ();
653
+ txs.reserve (std::min<size_t >(txids.size (), 1000000 )); // reserve limited to 1 million
653
654
size_t size = 0 ;
654
655
655
- for (const auto &it: txids)
656
+ for (size_t i = 0 ; i < txids. size (); ++i )
656
657
{
658
+ const crypto::hash &it{txids[i]};
657
659
tx_details details;
658
660
bool success = get_transaction_info (it, details, include_sensitive, true /* include_blob*/ );
659
661
if (success && (!limit_size || (size + details.blob_size < limit_size)))
@@ -964,47 +966,80 @@ namespace cryptonote
964
966
remaining_added_txids.clear ();
965
967
removed_txs.clear ();
966
968
967
- std::vector<crypto::hash> txids;
968
969
if (!incremental)
969
- {
970
970
LOG_PRINT_L2 (" Giving back the whole pool" );
971
- // Give back the whole pool in 'added_txs'; because calling 'get_transaction_info' right inside the
972
- // anonymous method somehow results in an LMDB error with transactions we have to build a list of
973
- // ids first and get the full info afterwards
974
- get_transaction_hashes (txids, include_sensitive);
975
- if (txids.size () > max_tx_count)
971
+
972
+ // If incremental, handle removed TXIDs first since it's important that txs are removed
973
+ // from synchronizers' pools, and we need need to estimate how much space we have left to
974
+ // request full-bodied txs
975
+ if (incremental)
976
+ {
977
+ std::multimap<time_t , removed_tx_info>::const_iterator rit = m_removed_txs_by_time.lower_bound (start_time);
978
+ while (rit != m_removed_txs_by_time.end ())
976
979
{
977
- remaining_added_txids = std::vector<crypto::hash>(txids.begin () + max_tx_count, txids.end ());
978
- txids.erase (txids.begin () + max_tx_count, txids.end ());
980
+ if (include_sensitive || !rit->second .sensitive )
981
+ {
982
+ removed_txs.push_back (rit->second .txid );
983
+ }
984
+ ++rit;
979
985
}
980
- get_transactions_info (txids, added_txs, include_sensitive, limit_size);
981
- return true ;
982
986
}
983
987
984
- // Give back incrementally, based on time of entry into the map
988
+ // Build a list of TXIDs that we want information for eventually. When `!incremental`, we might
989
+ // collect TXIDs that have already left the pool which will cause a followup RPC call that
990
+ // fetches non-existent txs, but that's *okay* since a non-incremental caller probably doesn't
991
+ // care about being incredibly efficient. This problem is also unavoidable anyways if we need to
992
+ // ever return anything inside `remaining_added_txids`, since those txs might have left the mempool
993
+ // by the time the followup RPC call reaches the daemon
994
+ std::vector<crypto::hash> txids;
995
+ txids.reserve (m_added_txs_by_id.size ());
985
996
for (const auto &pit : m_added_txs_by_id)
986
997
{
987
- if (pit.second >= start_time)
998
+ const bool relevant_txid{!incremental || pit.second >= start_time};
999
+ if (relevant_txid)
988
1000
txids.push_back (pit.first );
989
1001
}
990
- get_transactions_info (txids, added_txs, include_sensitive, limit_size);
991
- if (added_txs.size () > max_tx_count)
1002
+
1003
+ // Estimate max cumulative size left for full tx blobs
1004
+ const size_t removed_txids_clawback{32 * removed_txs.size ()};
1005
+ const size_t remaining_txids_clawback{32 * txids.size ()};
1006
+ const size_t added_tx_txid_clawback (32 * txids.size ());
1007
+ const size_t total_clawback{removed_txids_clawback + remaining_txids_clawback + added_tx_txid_clawback};
1008
+ const size_t cumulative_txblob_size_limit{limit_size > total_clawback ? limit_size - total_clawback : 0 };
1009
+
1010
+ // Perform TX info fetch, limited to max_tx_count and cumulative_txblob_size_limit
1011
+ const size_t max_full_tx_fetch_count{std::min (txids.size (), max_tx_count)};
1012
+ if (cumulative_txblob_size_limit && max_full_tx_fetch_count)
992
1013
{
993
- remaining_added_txids.reserve (added_txs.size () - max_tx_count);
994
- for (size_t i = max_tx_count; i < added_txs.size (); ++i)
995
- remaining_added_txids.push_back (added_txs[i].first );
996
- added_txs.erase (added_txs.begin () + max_tx_count, added_txs.end ());
1014
+ const epee::span<const crypto::hash> txid_fetch_span{&txids[0 ], max_full_tx_fetch_count};
1015
+ if (!get_transactions_info (txid_fetch_span, added_txs, include_sensitive, cumulative_txblob_size_limit))
1016
+ return false ;
997
1017
}
998
1018
999
- std::multimap< time_t , removed_tx_info>::const_iterator rit = m_removed_txs_by_time. lower_bound (start_time);
1000
- while (rit != m_removed_txs_by_time. end ())
1019
+ // Populate `remaining_added_txids` with all TXIDs in `txids` and not in `added_txs`
1020
+ if (added_txs. size () < txids. size ())
1001
1021
{
1002
- if (include_sensitive || !rit->second .sensitive )
1022
+ const size_t num_remaining{added_txs.size () - max_tx_count};
1023
+ remaining_added_txids.reserve (num_remaining);
1024
+
1025
+ // This iteration code assumes that the get_transactions_info() method A) returns elements in
1026
+ // the same order as they are passed in (skipping failures) and B) does not return TXIDs which
1027
+ // don't exist in the input. If these assumptions don't hold then remaining_added_txids will
1028
+ // be wrong, but it shouldn't cause any undefined behavior outside of that and should terminate
1029
+ // in a short finite time period O(N) with N = txids.size()
1030
+ auto txid_it = txids.cbegin ();
1031
+ auto added_it = added_txs.cbegin ();
1032
+ while (txid_it != txids.cend ())
1003
1033
{
1004
- removed_txs.push_back (rit->second .txid );
1034
+ if (added_it != added_txs.cend () && added_it->first == *txid_it)
1035
+ ++added_it;
1036
+ else
1037
+ remaining_added_txids.push_back (*txid_it);
1038
+
1039
+ ++txid_it;
1005
1040
}
1006
- ++rit;
1007
1041
}
1042
+
1008
1043
return true ;
1009
1044
}
1010
1045
// ------------------------------------------------------------------
0 commit comments