@@ -42,53 +42,57 @@ class TxConfirmStats;
42
42
* within your desired 5 blocks.
43
43
*
44
44
* Here is a brief description of the implementation:
45
- * When a transaction enters the mempool, we
46
- * track the height of the block chain at entry. Whenever a block comes in,
47
- * we count the number of transactions in each bucket and the total amount of feerate
48
- * paid in each bucket. Then we calculate how many blocks Y it took each
49
- * transaction to be mined and we track an array of counters in each bucket
50
- * for how long it to took transactions to get confirmed from 1 to a max of 25
51
- * and we increment all the counters from Y up to 25. This is because for any
52
- * number Z>=Y the transaction was successfully mined within Z blocks. We
53
- * want to save a history of this information, so at any time we have a
54
- * counter of the total number of transactions that happened in a given feerate
55
- * bucket and the total number that were confirmed in each number 1-25 blocks
56
- * or less for any bucket. We save this history by keeping an exponentially
57
- * decaying moving average of each one of these stats. Furthermore we also
58
- * keep track of the number unmined (in mempool) transactions in each bucket
59
- * and for how many blocks they have been outstanding and use that to increase
60
- * the number of transactions we've seen in that feerate bucket when calculating
61
- * an estimate for any number of confirmations below the number of blocks
62
- * they've been outstanding.
45
+ * When a transaction enters the mempool, we track the height of the block chain
46
+ * at entry. All further calculations are conducted only on this set of "seen"
47
+ * transactions. Whenever a block comes in, we count the number of transactions
48
+ * in each bucket and the total amount of feerate paid in each bucket. Then we
49
+ * calculate how many blocks Y it took each transaction to be mined. We convert
50
+ * from a number of blocks to a number of periods Y' each encompassing "scale"
51
+ * blocks. This is is tracked in 3 different data sets each up to a maximum
52
+ * number of periods. Within each data set we have an array of counters in each
53
+ * feerate bucket and we increment all the counters from Y' up to max periods
54
+ * representing that a tx was successfullly confirmed in less than or equal to
55
+ * that many periods. We want to save a history of this information, so at any
56
+ * time we have a counter of the total number of transactions that happened in a
57
+ * given feerate bucket and the total number that were confirmed in each of the
58
+ * periods or less for any bucket. We save this history by keeping an
59
+ * exponentially decaying moving average of each one of these stats. This is
60
+ * done for a different decay in each of the 3 data sets to keep relevant data
61
+ * from different time horizons. Furthermore we also keep track of the number
62
+ * unmined (in mempool or left mempool without being included in a block)
63
+ * transactions in each bucket and for how many blocks they have been
64
+ * outstanding and use both of these numbers to increase the number of transactions
65
+ * we've seen in that feerate bucket when calculating an estimate for any number
66
+ * of confirmations below the number of blocks they've been outstanding.
63
67
*/
64
68
65
- /* * Track confirm delays up to 25 blocks, can't estimate beyond that */
66
- static const unsigned int MAX_BLOCK_CONFIRMS = 25 ;
67
-
68
- /* * Decay of .998 is a half-life of 346 blocks or about 2.4 days */
69
- static const double DEFAULT_DECAY = .998 ;
70
-
71
- /* * Require greater than 95% of X feerate transactions to be confirmed within Y blocks for X to be big enough */
72
- static const double MIN_SUCCESS_PCT = .95 ;
73
-
74
- /* * Require an avg of 1 tx in the combined feerate bucket per block to have stat significance */
75
- static const double SUFFICIENT_FEETXS = 1 ;
69
+ /* Identifier for each of the 3 different TxConfirmStats which will track
70
+ * history over different time horizons. */
71
+ enum FeeEstimateHorizon {
72
+ SHORT_HALFLIFE = 0 ,
73
+ MED_HALFLIFE = 1 ,
74
+ LONG_HALFLIFE = 2
75
+ };
76
76
77
- // Minimum and Maximum values for tracking feerates
78
- // The MIN_BUCKET_FEERATE should just be set to the lowest reasonable feerate we
79
- // might ever want to track. Historically this has been 1000 since it was
80
- // inheriting DEFAULT_MIN_RELAY_TX_FEE and changing it is disruptive as it
81
- // invalidates old estimates files. So leave it at 1000 unless it becomes
82
- // necessary to lower it, and then lower it substantially.
83
- static constexpr double MIN_BUCKET_FEERATE = 1000 ;
84
- static const double MAX_BUCKET_FEERATE = 1e7 ;
85
- static const double INF_FEERATE = MAX_MONEY;
77
+ /* Used to return detailed information about a feerate bucket */
78
+ struct EstimatorBucket
79
+ {
80
+ double start = -1 ;
81
+ double end = -1 ;
82
+ double withinTarget = 0 ;
83
+ double totalConfirmed = 0 ;
84
+ double inMempool = 0 ;
85
+ double leftMempool = 0 ;
86
+ };
86
87
87
- // We have to lump transactions into buckets based on feerate, but we want to be able
88
- // to give accurate estimates over a large range of potential feerates
89
- // Therefore it makes sense to exponentially space the buckets
90
- /* * Spacing of FeeRate buckets */
91
- static const double FEE_SPACING = 1.1 ;
88
+ /* Used to return detailed information about a fee estimate calculation */
89
+ struct EstimationResult
90
+ {
91
+ EstimatorBucket pass;
92
+ EstimatorBucket fail;
93
+ double decay;
94
+ unsigned int scale;
95
+ };
92
96
93
97
/* *
94
98
* We want to be able to estimate feerates that are needed on tx's to be included in
@@ -97,6 +101,55 @@ static const double FEE_SPACING = 1.1;
97
101
*/
98
102
class CBlockPolicyEstimator
99
103
{
104
+ private:
105
+ /* * Track confirm delays up to 12 blocks for short horizon */
106
+ static constexpr unsigned int SHORT_BLOCK_PERIODS = 12 ;
107
+ static constexpr unsigned int SHORT_SCALE = 1 ;
108
+ /* * Track confirm delays up to 48 blocks for medium horizon */
109
+ static constexpr unsigned int MED_BLOCK_PERIODS = 24 ;
110
+ static constexpr unsigned int MED_SCALE = 2 ;
111
+ /* * Track confirm delays up to 1008 blocks for long horizon */
112
+ static constexpr unsigned int LONG_BLOCK_PERIODS = 42 ;
113
+ static constexpr unsigned int LONG_SCALE = 24 ;
114
+ /* * Historical estimates that are older than this aren't valid */
115
+ static const unsigned int OLDEST_ESTIMATE_HISTORY = 6 * 1008 ;
116
+
117
+ /* * Decay of .962 is a half-life of 18 blocks or about 3 hours */
118
+ static constexpr double SHORT_DECAY = .962 ;
119
+ /* * Decay of .998 is a half-life of 144 blocks or about 1 day */
120
+ static constexpr double MED_DECAY = .9952 ;
121
+ /* * Decay of .9995 is a half-life of 1008 blocks or about 1 week */
122
+ static constexpr double LONG_DECAY = .99931 ;
123
+
124
+ /* * Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks*/
125
+ static constexpr double HALF_SUCCESS_PCT = .6 ;
126
+ /* * Require greater than 85% of X feerate transactions to be confirmed within Y blocks*/
127
+ static constexpr double SUCCESS_PCT = .85 ;
128
+ /* * Require greater than 95% of X feerate transactions to be confirmed within 2 * Y blocks*/
129
+ static constexpr double DOUBLE_SUCCESS_PCT = .95 ;
130
+
131
+ /* * Require an avg of 0.1 tx in the combined feerate bucket per block to have stat significance */
132
+ static constexpr double SUFFICIENT_FEETXS = 0.1 ;
133
+ /* * Require an avg of 0.5 tx when using short decay since there are fewer blocks considered*/
134
+ static constexpr double SUFFICIENT_TXS_SHORT = 0.5 ;
135
+
136
+ /* * Minimum and Maximum values for tracking feerates
137
+ * The MIN_BUCKET_FEERATE should just be set to the lowest reasonable feerate we
138
+ * might ever want to track. Historically this has been 1000 since it was
139
+ * inheriting DEFAULT_MIN_RELAY_TX_FEE and changing it is disruptive as it
140
+ * invalidates old estimates files. So leave it at 1000 unless it becomes
141
+ * necessary to lower it, and then lower it substantially.
142
+ */
143
+ static constexpr double MIN_BUCKET_FEERATE = 1000 ;
144
+ static constexpr double MAX_BUCKET_FEERATE = 1e7 ;
145
+
146
+ /* * Spacing of FeeRate buckets
147
+ * We have to lump transactions into buckets based on feerate, but we want to be able
148
+ * to give accurate estimates over a large range of potential feerates
149
+ * Therefore it makes sense to exponentially space the buckets
150
+ */
151
+ static constexpr double FEE_SPACING = 1.05 ;
152
+
100
153
public:
101
154
/* * Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
102
155
CBlockPolicyEstimator ();
@@ -110,26 +163,39 @@ class CBlockPolicyEstimator
110
163
void processTransaction (const CTxMemPoolEntry& entry, bool validFeeEstimate);
111
164
112
165
/* * Remove a transaction from the mempool tracking stats*/
113
- bool removeTx (uint256 hash);
166
+ bool removeTx (uint256 hash, bool inBlock );
114
167
115
- /* * Return a feerate estimate */
168
+ /* * DEPRECATED. Return a feerate estimate */
116
169
CFeeRate estimateFee (int confTarget) const ;
117
170
118
- /* * Estimate feerate needed to get be included in a block within
119
- * confTarget blocks. If no answer can be given at confTarget, return an
120
- * estimate at the lowest target where one can be given.
171
+ /* * Estimate feerate needed to get be included in a block within confTarget
172
+ * blocks. If no answer can be given at confTarget, return an estimate at
173
+ * the closest target where one can be given. 'conservative' estimates are
174
+ * valid over longer time horizons also.
175
+ */
176
+ CFeeRate estimateSmartFee (int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool, bool conservative = true ) const ;
177
+
178
+ /* * Return a specific fee estimate calculation with a given success
179
+ * threshold and time horizon, and optionally return detailed data about
180
+ * calculation
121
181
*/
122
- CFeeRate estimateSmartFee (int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool ) const ;
182
+ CFeeRate estimateRawFee (int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult *result = nullptr ) const ;
123
183
124
184
/* * Write estimation data to a file */
125
185
bool Write (CAutoFile& fileout) const ;
126
186
127
187
/* * Read estimation data from a file */
128
188
bool Read (CAutoFile& filein);
129
189
190
+ /* * Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */
191
+ void FlushUnconfirmed (CTxMemPool& pool);
192
+
130
193
private:
131
- CFeeRate minTrackedFee; // !< Passed to constructor to avoid dependency on main
132
194
unsigned int nBestSeenHeight;
195
+ unsigned int firstRecordedHeight;
196
+ unsigned int historicalFirst;
197
+ unsigned int historicalBest;
198
+
133
199
struct TxStatsInfo
134
200
{
135
201
unsigned int blockHeight;
@@ -142,19 +208,42 @@ class CBlockPolicyEstimator
142
208
143
209
/* * Classes to track historical data on transaction confirmations */
144
210
TxConfirmStats* feeStats;
211
+ TxConfirmStats* shortStats;
212
+ TxConfirmStats* longStats;
145
213
146
214
unsigned int trackedTxs;
147
215
unsigned int untrackedTxs;
148
216
217
+ std::vector<double > buckets; // The upper-bound of the range for the bucket (inclusive)
218
+ std::map<double , unsigned int > bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
219
+
149
220
mutable CCriticalSection cs_feeEstimator;
150
221
151
222
/* * Process a transaction confirmed in a block*/
152
223
bool processBlockTx (unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
153
224
225
+ /* * Helper for estimateSmartFee */
226
+ double estimateCombinedFee (unsigned int confTarget, double successThreshold, bool checkShorterHorizon) const ;
227
+ /* * Helper for estimateSmartFee */
228
+ double estimateConservativeFee (unsigned int doubleTarget) const ;
229
+ /* * Number of blocks of data recorded while fee estimates have been running */
230
+ unsigned int BlockSpan () const ;
231
+ /* * Number of blocks of recorded fee estimate data represented in saved data file */
232
+ unsigned int HistoricalBlockSpan () const ;
233
+ /* * Calculation of highest target that reasonable estimate can be provided for */
234
+ unsigned int MaxUsableEstimate () const ;
154
235
};
155
236
156
237
class FeeFilterRounder
157
238
{
239
+ private:
240
+ static constexpr double MAX_FILTER_FEERATE = 1e7 ;
241
+ /* * FEE_FILTER_SPACING is just used to provide some quantization of fee
242
+ * filter results. Historically it reused FEE_SPACING, but it is completely
243
+ * unrelated, and was made a separate constant so the two concepts are not
244
+ * tied together */
245
+ static constexpr double FEE_FILTER_SPACING = 1.1 ;
246
+
158
247
public:
159
248
/* * Create new FeeFilterRounder */
160
249
FeeFilterRounder (const CFeeRate& minIncrementalFee);
@@ -166,4 +255,5 @@ class FeeFilterRounder
166
255
std::set<double > feeset;
167
256
FastRandomContext insecure_rand;
168
257
};
258
+
169
259
#endif /* BITCOIN_POLICYESTIMATOR_H */
0 commit comments