|
5 | 5 | #ifndef BITCOIN_CLUSTER_LINEARIZE_H
|
6 | 6 | #define BITCOIN_CLUSTER_LINEARIZE_H
|
7 | 7 |
|
| 8 | +#include <optional> |
8 | 9 | #include <stdint.h>
|
9 | 10 | #include <vector>
|
10 | 11 | #include <utility>
|
@@ -166,6 +167,122 @@ class DepGraph
|
166 | 167 | }
|
167 | 168 | };
|
168 | 169 |
|
| 170 | +/** A set of transactions together with their aggregate feerate. */ |
| 171 | +template<typename SetType> |
| 172 | +struct SetInfo |
| 173 | +{ |
| 174 | + /** The transactions in the set. */ |
| 175 | + SetType transactions; |
| 176 | + /** Their combined fee and size. */ |
| 177 | + FeeFrac feerate; |
| 178 | + |
| 179 | + /** Construct a SetInfo for a specified set and feerate. */ |
| 180 | + SetInfo(const SetType& txn, const FeeFrac& fr) noexcept : transactions(txn), feerate(fr) {} |
| 181 | + |
| 182 | + /** Construct a SetInfo for a set of transactions in a depgraph. */ |
| 183 | + explicit SetInfo(const DepGraph<SetType>& depgraph, const SetType& txn) noexcept : |
| 184 | + transactions(txn), feerate(depgraph.FeeRate(txn)) {} |
| 185 | + |
| 186 | + /** Permit equality testing. */ |
| 187 | + friend bool operator==(const SetInfo&, const SetInfo&) noexcept = default; |
| 188 | +}; |
| 189 | + |
| 190 | +/** Class encapsulating the state needed to find the best remaining ancestor set. |
| 191 | + * |
| 192 | + * It is initialized for an entire DepGraph, and parts of the graph can be dropped by calling |
| 193 | + * MarkDone. |
| 194 | + * |
| 195 | + * As long as any part of the graph remains, FindCandidateSet() can be called which will return a |
| 196 | + * SetInfo with the highest-feerate ancestor set that remains (an ancestor set is a single |
| 197 | + * transaction together with all its remaining ancestors). |
| 198 | + */ |
| 199 | +template<typename SetType> |
| 200 | +class AncestorCandidateFinder |
| 201 | +{ |
| 202 | + /** Internal dependency graph. */ |
| 203 | + const DepGraph<SetType>& m_depgraph; |
| 204 | + /** Which transaction are left to include. */ |
| 205 | + SetType m_todo; |
| 206 | + /** Precomputed ancestor-set feerates (only kept up-to-date for indices in m_todo). */ |
| 207 | + std::vector<FeeFrac> m_ancestor_set_feerates; |
| 208 | + |
| 209 | +public: |
| 210 | + /** Construct an AncestorCandidateFinder for a given cluster. |
| 211 | + * |
| 212 | + * Complexity: O(N^2) where N=depgraph.TxCount(). |
| 213 | + */ |
| 214 | + AncestorCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND) noexcept : |
| 215 | + m_depgraph(depgraph), |
| 216 | + m_todo{SetType::Fill(depgraph.TxCount())}, |
| 217 | + m_ancestor_set_feerates(depgraph.TxCount()) |
| 218 | + { |
| 219 | + // Precompute ancestor-set feerates. |
| 220 | + for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) { |
| 221 | + /** The remaining ancestors for transaction i. */ |
| 222 | + SetType anc_to_add = m_depgraph.Ancestors(i); |
| 223 | + FeeFrac anc_feerate; |
| 224 | + // Reuse accumulated feerate from first ancestor, if usable. |
| 225 | + Assume(anc_to_add.Any()); |
| 226 | + ClusterIndex first = anc_to_add.First(); |
| 227 | + if (first < i) { |
| 228 | + anc_feerate = m_ancestor_set_feerates[first]; |
| 229 | + Assume(!anc_feerate.IsEmpty()); |
| 230 | + anc_to_add -= m_depgraph.Ancestors(first); |
| 231 | + } |
| 232 | + // Add in other ancestors (which necessarily include i itself). |
| 233 | + Assume(anc_to_add[i]); |
| 234 | + anc_feerate += m_depgraph.FeeRate(anc_to_add); |
| 235 | + // Store the result. |
| 236 | + m_ancestor_set_feerates[i] = anc_feerate; |
| 237 | + } |
| 238 | + } |
| 239 | + |
| 240 | + /** Remove a set of transactions from the set of to-be-linearized ones. |
| 241 | + * |
| 242 | + * The same transaction may not be MarkDone()'d twice. |
| 243 | + * |
| 244 | + * Complexity: O(N*M) where N=depgraph.TxCount(), M=select.Count(). |
| 245 | + */ |
| 246 | + void MarkDone(SetType select) noexcept |
| 247 | + { |
| 248 | + Assume(select.Any()); |
| 249 | + Assume(select.IsSubsetOf(m_todo)); |
| 250 | + m_todo -= select; |
| 251 | + for (auto i : select) { |
| 252 | + auto feerate = m_depgraph.FeeRate(i); |
| 253 | + for (auto j : m_depgraph.Descendants(i) & m_todo) { |
| 254 | + m_ancestor_set_feerates[j] -= feerate; |
| 255 | + } |
| 256 | + } |
| 257 | + } |
| 258 | + |
| 259 | + /** Check whether any unlinearized transactions remain. */ |
| 260 | + bool AllDone() const noexcept |
| 261 | + { |
| 262 | + return m_todo.None(); |
| 263 | + } |
| 264 | + |
| 265 | + /** Find the best (highest-feerate, smallest among those in case of a tie) ancestor set |
| 266 | + * among the remaining transactions. Requires !AllDone(). |
| 267 | + * |
| 268 | + * Complexity: O(N) where N=depgraph.TxCount(); |
| 269 | + */ |
| 270 | + SetInfo<SetType> FindCandidateSet() const noexcept |
| 271 | + { |
| 272 | + Assume(!AllDone()); |
| 273 | + std::optional<ClusterIndex> best; |
| 274 | + for (auto i : m_todo) { |
| 275 | + if (best.has_value()) { |
| 276 | + Assume(!m_ancestor_set_feerates[i].IsEmpty()); |
| 277 | + if (!(m_ancestor_set_feerates[i] > m_ancestor_set_feerates[*best])) continue; |
| 278 | + } |
| 279 | + best = i; |
| 280 | + } |
| 281 | + Assume(best.has_value()); |
| 282 | + return {m_depgraph.Ancestors(*best) & m_todo, m_ancestor_set_feerates[*best]}; |
| 283 | + } |
| 284 | +}; |
| 285 | + |
169 | 286 | } // namespace cluster_linearize
|
170 | 287 |
|
171 | 288 | #endif // BITCOIN_CLUSTER_LINEARIZE_H
|
0 commit comments