7#include <boost/multi_index/detail/hash_index_iterator.hpp>
8#include <boost/operators.hpp>
30 for (
const auto& outpoint : outpoints) {
31 if (!mempool.
exists(outpoint.hash)) {
55 for (
const auto& desc_txiter : descendants) {
66 const auto cluster = mempool.
GatherClusters({txids_needed.begin(), txids_needed.end()});
67 if (cluster.empty()) {
75 for (
const auto& txiter : cluster) {
81 int64_t(ancestor_size),
82 txiter->GetModifiedFee(),
90 for (
const auto& outpoint : outpoints_it->second) {
99 for (
const auto& txiter : cluster) {
100 const auto& txid = txiter->GetTx().GetHash();
103 std::vector<MockEntryMap::iterator> cached_descendants;
107 Assume(descendants.contains(txiter));
108 for (
const auto& desc_txiter : descendants) {
109 const auto txid_desc = desc_txiter->GetTx().GetHash();
113 if (remove)
Assume(remove_desc);
115 if (!remove && !remove_desc) {
116 cached_descendants.push_back(desc_it);
120 Assume(cached_descendants.empty());
134 const std::map<
Txid, std::set<Txid>>& descendant_caches)
136 for (
const auto& entry : manual_entries) {
137 const auto& txid = entry.GetTx().GetHash();
139 if (!
Assume(descendant_caches.contains(txid))) {
149 for (
const auto& [txid, desc_txids] : descendant_caches) {
151 if (!
Assume(!desc_txids.empty())) {
155 std::vector<MockEntryMap::iterator> descendants;
156 for (
const auto& desc_txid : desc_txids) {
163 descendants.emplace_back(desc_it);
185 FeeFrac self_feerate(e.GetModifiedFee(), e.GetTxSize());
186 FeeFrac ancestor_feerate(e.GetModFeesWithAncestors(), e.GetSizeWithAncestors());
187 return std::min(ancestor_feerate, self_feerate);
189 FeeFrac a_feerate{min_feerate(a->second)};
190 FeeFrac b_feerate{min_feerate(b->second)};
191 if (a_feerate != b_feerate) {
192 return a_feerate > b_feerate;
195 return a->first < b->first;
201 Assume(ancestors.size() >= 1);
203 for (
auto& anc : ancestors) {
211 for (
auto& descendant : it->second) {
213 Assume(descendant->second.GetSizeWithAncestors() >= anc->second.GetTxSize());
214 descendant->second.UpdateAncestorState(-anc->second.GetTxSize(), -anc->second.GetModifiedFee());
218 for (
const auto& anc : ancestors) {
222 Assume(anc->second.GetModFeesWithAncestors() == 0);
223 Assume(anc->second.GetSizeWithAncestors() == 0);
238 return entry->second.GetSizeWithAncestors() >= entry->second.GetTxSize();}));
241 [&](
const auto& txid){ return !m_entries_by_txid.contains(txid); }));
247 uint32_t sequence_num{0};
255 const auto ancestor_package_size = (*best_iter)->second.GetSizeWithAncestors();
256 const auto ancestor_package_fee = (*best_iter)->second.GetModFeesWithAncestors();
258 if (target_feerate.has_value() &&
259 ancestor_package_fee < target_feerate->GetFee(ancestor_package_size)) {
265 std::set<MockEntryMap::iterator, IteratorComparator> ancestors;
267 std::set<MockEntryMap::iterator, IteratorComparator> to_process;
268 to_process.insert(*best_iter);
269 while (!to_process.empty()) {
270 auto iter = to_process.begin();
271 Assume(iter != to_process.end());
272 ancestors.insert(*iter);
273 for (
const auto& input : (*iter)->second.GetTx().vin) {
275 if (!ancestors.contains(parent_it)) {
276 to_process.insert(parent_it);
280 to_process.erase(iter);
284 for (
const auto& ancestor : ancestors) {
291 if (!target_feerate.has_value()) {
321 for (
const auto& outpoint : it->second) {
376 Assume(target_feerate.
GetFee(it->second.GetSizeWithAncestors()) > std::min(it->second.GetModifiedFee(), it->second.GetModFeesWithAncestors()));
377 CAmount bump_fee_with_ancestors = target_feerate.
GetFee(it->second.GetSizeWithAncestors()) - it->second.GetModFeesWithAncestors();
378 CAmount bump_fee_individual = target_feerate.
GetFee(it->second.GetTxSize()) - it->second.GetModifiedFee();
379 const CAmount bump_fee{std::max(bump_fee_with_ancestors, bump_fee_individual)};
381 for (
const auto& outpoint : outpoints) {
396 std::set<MockEntryMap::iterator, IteratorComparator> ancestors;
397 std::set<MockEntryMap::iterator, IteratorComparator> to_process;
404 to_process.insert(iter);
405 ancestors.insert(iter);
408 std::set<Txid> has_been_processed;
409 while (!to_process.empty()) {
410 auto iter = to_process.begin();
412 for (
const auto& input : tx.
vin) {
414 if (!has_been_processed.contains(input.prevout.hash)) {
415 to_process.insert(parent_it);
417 ancestors.insert(parent_it);
420 has_been_processed.insert(tx.
GetHash());
421 to_process.erase(iter);
423 const auto ancestor_package_size = std::accumulate(ancestors.cbegin(), ancestors.cend(), int64_t{0},
424 [](int64_t
sum,
const auto it) {return sum + it->second.GetTxSize();});
425 const auto ancestor_package_fee = std::accumulate(ancestors.cbegin(), ancestors.cend(),
CAmount{0},
426 [](
CAmount sum,
const auto it) {return sum + it->second.GetModifiedFee();});
427 return target_feerate.
GetFee(ancestor_package_size) - ancestor_package_fee;
int64_t CAmount
Amount in satoshis (Can be negative)
#define Assume(val)
Assume is the identity function.
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
CAmount GetFee(int32_t virtual_bytes) const
Return the fee in satoshis for the given vsize in vbytes.
The basic transaction that is broadcasted on the network and contained in blocks.
const Txid & GetHash() const LIFETIMEBOUND
const std::vector< CTxIn > vin
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
std::optional< txiter > GetIter(const Txid &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given hash, if found.
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
bool exists(const Txid &txid) const
std::set< txiter, CompareIteratorByHash > setEntries
std::vector< txiter > GatherClusters(const std::vector< Txid > &txids) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Collect the entire cluster of connected transactions for each transaction in txids.
const CTransaction * GetConflictTx(const COutPoint &prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Get the transaction in the pool that spends the same prevout.
void CalculateDescendants(txiter it, setEntries &setDescendants) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Populate setDescendants with all in-mempool descendants of given transaction.
std::tuple< size_t, size_t, CAmount > CalculateAncestorData(const CTxMemPoolEntry &entry) const EXCLUSIVE_LOCKS_REQUIRED(cs)
std::set< Txid > m_in_block
std::map< Txid, uint32_t > Linearize()
Construct a new block template with all of the transactions and calculate the order in which they are...
std::map< COutPoint, CAmount > CalculateBumpFees(const CFeeRate &target_feerate)
Construct a new block template and, for each outpoint corresponding to a transaction that did not mak...
std::optional< CAmount > CalculateTotalBumpFees(const CFeeRate &target_feerate)
Construct a new block template and, calculate the cost of bumping all transactions that did not make ...
std::map< Txid, MiniMinerMempoolEntry > m_entries_by_txid
Main data structure holding the entries, can be indexed by txid.
std::map< Txid, uint32_t > m_inclusion_order
void DeleteAncestorPackage(const std::set< MockEntryMap::iterator, IteratorComparator > &ancestors)
Consider this ancestor package "mined" so remove all these entries from our data structures.
void SanityCheck() const
Perform some checks.
std::vector< MockEntryMap::iterator > m_entries
Vector of entries, can be sorted by ancestor feerate.
MiniMiner(const CTxMemPool &mempool, const std::vector< COutPoint > &outpoints)
Constructor that takes a list of outpoints that may or may not belong to transactions in the mempool.
std::map< COutPoint, CAmount > m_bump_fees
std::map< Txid, std::vector< MockEntryMap::iterator > > m_descendant_set_by_txid
Map of txid to its descendants.
bool m_ready_to_calculate
std::set< Txid > m_to_be_replaced
std::map< Txid, std::vector< COutPoint > > m_requested_outpoints_by_txid
void BuildMockTemplate(std::optional< CFeeRate > target_feerate)
Build a block template until the target feerate is hit.
Data structure storing a fee and size, ordered by increasing fee/size.
bool operator()(const I &a, const I &b) const