mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Merge pull request #2934 from PastaPastaPasta/backports-0.15-pr12
Backports 0.15 pr12
This commit is contained in:
commit
d3bd9b633e
@ -27,6 +27,7 @@
|
||||
#include "netbase.h"
|
||||
#include "net.h"
|
||||
#include "net_processing.h"
|
||||
#include "policy/fees.h"
|
||||
#include "policy/policy.h"
|
||||
#include "rpc/server.h"
|
||||
#include "rpc/register.h"
|
||||
@ -287,7 +288,7 @@ void PrepareShutdown()
|
||||
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
|
||||
CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION);
|
||||
if (!est_fileout.IsNull())
|
||||
mempool.WriteFeeEstimates(est_fileout);
|
||||
::feeEstimator.Write(est_fileout);
|
||||
else
|
||||
LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string());
|
||||
fFeeEstimatesInitialized = false;
|
||||
@ -1926,7 +1927,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
|
||||
// Allowed to fail as this file IS missing on first startup.
|
||||
if (!est_filein.IsNull())
|
||||
mempool.ReadFeeEstimates(est_filein);
|
||||
::feeEstimator.Read(est_filein);
|
||||
fFeeEstimatesInitialized = true;
|
||||
|
||||
// ********************************************************* Step 8: load wallet
|
||||
|
@ -7,13 +7,120 @@
|
||||
#include "policy/policy.h"
|
||||
|
||||
#include "amount.h"
|
||||
#include "clientversion.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "streams.h"
|
||||
#include "txmempool.h"
|
||||
#include "util.h"
|
||||
|
||||
void TxConfirmStats::Initialize(std::vector<double>& defaultBuckets,
|
||||
unsigned int maxConfirms, double _decay)
|
||||
/**
|
||||
* We will instantiate an instance of this class to track transactions that were
|
||||
* included in a block. We will lump transactions into a bucket according to their
|
||||
* approximate feerate and then track how long it took for those txs to be included in a block
|
||||
*
|
||||
* The tracking of unconfirmed (mempool) transactions is completely independent of the
|
||||
* historical tracking of transactions that have been confirmed in a block.
|
||||
*/
|
||||
class TxConfirmStats
|
||||
{
|
||||
private:
|
||||
//Define the buckets we will group transactions into
|
||||
std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
|
||||
std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
|
||||
|
||||
// For each bucket X:
|
||||
// Count the total # of txs in each bucket
|
||||
// Track the historical moving average of this total over blocks
|
||||
std::vector<double> txCtAvg;
|
||||
// and calculate the total for the current block to update the moving average
|
||||
std::vector<int> curBlockTxCt;
|
||||
|
||||
// Count the total # of txs confirmed within Y blocks in each bucket
|
||||
// Track the historical moving average of theses totals over blocks
|
||||
std::vector<std::vector<double> > confAvg; // confAvg[Y][X]
|
||||
// and calculate the totals for the current block to update the moving averages
|
||||
std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
|
||||
|
||||
// Sum the total feerate of all tx's in each bucket
|
||||
// Track the historical moving average of this total over blocks
|
||||
std::vector<double> avg;
|
||||
// and calculate the total for the current block to update the moving average
|
||||
std::vector<double> curBlockVal;
|
||||
|
||||
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
|
||||
// Combine the total value with the tx counts to calculate the avg feerate per bucket
|
||||
|
||||
double decay;
|
||||
|
||||
// Mempool counts of outstanding transactions
|
||||
// For each bucket X, track the number of transactions in the mempool
|
||||
// that are unconfirmed for each possible confirmation value Y
|
||||
std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
|
||||
// transactions still unconfirmed after MAX_CONFIRMS for each bucket
|
||||
std::vector<int> oldUnconfTxs;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create new TxConfirmStats. This is called by BlockPolicyEstimator's
|
||||
* constructor with default values.
|
||||
* @param defaultBuckets contains the upper limits for the bucket boundaries
|
||||
* @param maxConfirms max number of confirms to track
|
||||
* @param decay how much to decay the historical moving average per block
|
||||
*/
|
||||
TxConfirmStats(const std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
|
||||
|
||||
/** Clear the state of the curBlock variables to start counting for the new block */
|
||||
void ClearCurrent(unsigned int nBlockHeight);
|
||||
|
||||
/**
|
||||
* Record a new transaction data point in the current block stats
|
||||
* @param blocksToConfirm the number of blocks it took this transaction to confirm
|
||||
* @param val the feerate of the transaction
|
||||
* @warning blocksToConfirm is 1-based and has to be >= 1
|
||||
*/
|
||||
void Record(int blocksToConfirm, double val);
|
||||
|
||||
/** Record a new transaction entering the mempool*/
|
||||
unsigned int NewTx(unsigned int nBlockHeight, double val);
|
||||
|
||||
/** Remove a transaction from mempool tracking stats*/
|
||||
void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
|
||||
unsigned int bucketIndex);
|
||||
|
||||
/** Update our estimates by decaying our historical moving average and updating
|
||||
with the data gathered from the current block */
|
||||
void UpdateMovingAverages();
|
||||
|
||||
/**
|
||||
* Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
|
||||
* to make sure we have enough data points) whose transactions still have sufficient likelihood
|
||||
* of being confirmed within the target number of confirmations
|
||||
* @param confTarget target number of confirmations
|
||||
* @param sufficientTxVal required average number of transactions per block in a bucket range
|
||||
* @param minSuccess the success probability we require
|
||||
* @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
|
||||
* return the highest feerate such that all lower values fail minSuccess
|
||||
* @param nBlockHeight the current block height
|
||||
*/
|
||||
double EstimateMedianVal(int confTarget, double sufficientTxVal,
|
||||
double minSuccess, bool requireGreater, unsigned int nBlockHeight) const;
|
||||
|
||||
/** Return the max number of confirms we're tracking */
|
||||
unsigned int GetMaxConfirms() const { return confAvg.size(); }
|
||||
|
||||
/** Write state of estimation data to a file*/
|
||||
void Write(CAutoFile& fileout) const;
|
||||
|
||||
/**
|
||||
* Read saved state of estimation data from a file and replace all internal data structures and
|
||||
* variables with this state.
|
||||
*/
|
||||
void Read(CAutoFile& filein);
|
||||
};
|
||||
|
||||
|
||||
TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
|
||||
unsigned int maxConfirms, double _decay)
|
||||
{
|
||||
decay = _decay;
|
||||
for (unsigned int i = 0; i < defaultBuckets.size(); i++) {
|
||||
@ -76,7 +183,7 @@ void TxConfirmStats::UpdateMovingAverages()
|
||||
// returns -1 on error conditions
|
||||
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
|
||||
double successBreakPoint, bool requireGreater,
|
||||
unsigned int nBlockHeight)
|
||||
unsigned int nBlockHeight) const
|
||||
{
|
||||
// Counters for a bucket (or range of buckets)
|
||||
double nConf = 0; // Number of tx's confirmed within the confTarget
|
||||
@ -172,7 +279,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
|
||||
return median;
|
||||
}
|
||||
|
||||
void TxConfirmStats::Write(CAutoFile& fileout)
|
||||
void TxConfirmStats::Write(CAutoFile& fileout) const
|
||||
{
|
||||
fileout << decay;
|
||||
fileout << buckets;
|
||||
@ -289,9 +396,10 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
|
||||
// of no harm to try to remove them again.
|
||||
bool CBlockPolicyEstimator::removeTx(uint256 hash)
|
||||
{
|
||||
LOCK(cs_feeEstimator);
|
||||
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
|
||||
if (pos != mapMemPoolTxs.end()) {
|
||||
feeStats.removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex);
|
||||
feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex);
|
||||
mapMemPoolTxs.erase(hash);
|
||||
return true;
|
||||
} else {
|
||||
@ -309,11 +417,17 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
|
||||
vfeelist.push_back(bucketBoundary);
|
||||
}
|
||||
vfeelist.push_back(INF_FEERATE);
|
||||
feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
|
||||
feeStats = new TxConfirmStats(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
|
||||
}
|
||||
|
||||
CBlockPolicyEstimator::~CBlockPolicyEstimator()
|
||||
{
|
||||
delete feeStats;
|
||||
}
|
||||
|
||||
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
|
||||
{
|
||||
LOCK(cs_feeEstimator);
|
||||
unsigned int txHeight = entry.GetHeight();
|
||||
uint256 hash = entry.GetTx().GetHash();
|
||||
if (mapMemPoolTxs.count(hash)) {
|
||||
@ -341,7 +455,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
|
||||
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
|
||||
|
||||
mapMemPoolTxs[hash].blockHeight = txHeight;
|
||||
mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK());
|
||||
mapMemPoolTxs[hash].bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
||||
}
|
||||
|
||||
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
|
||||
@ -365,13 +479,14 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
|
||||
// Feerates are stored and reported as BTC-per-kb:
|
||||
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
|
||||
|
||||
feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
||||
feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
||||
std::vector<const CTxMemPoolEntry*>& entries)
|
||||
{
|
||||
LOCK(cs_feeEstimator);
|
||||
if (nBlockHeight <= nBestSeenHeight) {
|
||||
// Ignore side chains and re-orgs; assuming they are random
|
||||
// they don't affect the estimate.
|
||||
@ -387,7 +502,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
||||
nBestSeenHeight = nBlockHeight;
|
||||
|
||||
// Clear the current block state and update unconfirmed circular buffer
|
||||
feeStats.ClearCurrent(nBlockHeight);
|
||||
feeStats->ClearCurrent(nBlockHeight);
|
||||
|
||||
unsigned int countedTxs = 0;
|
||||
// Repopulate the current block states
|
||||
@ -397,7 +512,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
||||
}
|
||||
|
||||
// Update all exponential averages with the current block state
|
||||
feeStats.UpdateMovingAverages();
|
||||
feeStats->UpdateMovingAverages();
|
||||
|
||||
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy after updating estimates for %u of %u txs in block, since last block %u of %u tracked, new mempool map size %u\n",
|
||||
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size());
|
||||
@ -406,14 +521,15 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
||||
untrackedTxs = 0;
|
||||
}
|
||||
|
||||
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
|
||||
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const
|
||||
{
|
||||
LOCK(cs_feeEstimator);
|
||||
// Return failure if trying to analyze a target we're not tracking
|
||||
// It's not possible to get reasonable estimates for confTarget of 1
|
||||
if (confTarget <= 1 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
|
||||
if (confTarget <= 1 || (unsigned int)confTarget > feeStats->GetMaxConfirms())
|
||||
return CFeeRate(0);
|
||||
|
||||
double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
|
||||
double median = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
|
||||
|
||||
if (median < 0)
|
||||
return CFeeRate(0);
|
||||
@ -421,22 +537,28 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
|
||||
return CFeeRate(median);
|
||||
}
|
||||
|
||||
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
|
||||
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const
|
||||
{
|
||||
if (answerFoundAtTarget)
|
||||
*answerFoundAtTarget = confTarget;
|
||||
// Return failure if trying to analyze a target we're not tracking
|
||||
if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
|
||||
return CFeeRate(0);
|
||||
|
||||
// It's not possible to get reasonable estimates for confTarget of 1
|
||||
if (confTarget == 1)
|
||||
confTarget = 2;
|
||||
|
||||
double median = -1;
|
||||
while (median < 0 && (unsigned int)confTarget <= feeStats.GetMaxConfirms()) {
|
||||
median = feeStats.EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_feeEstimator);
|
||||
|
||||
// Return failure if trying to analyze a target we're not tracking
|
||||
if (confTarget <= 0 || (unsigned int)confTarget > feeStats->GetMaxConfirms())
|
||||
return CFeeRate(0);
|
||||
|
||||
// It's not possible to get reasonable estimates for confTarget of 1
|
||||
if (confTarget == 1)
|
||||
confTarget = 2;
|
||||
|
||||
while (median < 0 && (unsigned int)confTarget <= feeStats->GetMaxConfirms()) {
|
||||
median = feeStats->EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
|
||||
}
|
||||
} // Must unlock cs_feeEstimator before taking mempool locks
|
||||
|
||||
if (answerFoundAtTarget)
|
||||
*answerFoundAtTarget = confTarget - 1;
|
||||
@ -452,18 +574,39 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
|
||||
return CFeeRate(median);
|
||||
}
|
||||
|
||||
void CBlockPolicyEstimator::Write(CAutoFile& fileout)
|
||||
bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
|
||||
{
|
||||
fileout << nBestSeenHeight;
|
||||
feeStats.Write(fileout);
|
||||
try {
|
||||
LOCK(cs_feeEstimator);
|
||||
fileout << 140100; // version required to read: 0.14.1 or later
|
||||
fileout << CLIENT_VERSION; // version that wrote the file
|
||||
fileout << nBestSeenHeight;
|
||||
feeStats->Write(fileout);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
LogPrintf("CBlockPolicyEstimator::Write(): unable to read policy estimator data (non-fatal)\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBlockPolicyEstimator::Read(CAutoFile& filein, int nFileVersion)
|
||||
bool CBlockPolicyEstimator::Read(CAutoFile& filein)
|
||||
{
|
||||
int nFileBestSeenHeight;
|
||||
filein >> nFileBestSeenHeight;
|
||||
feeStats.Read(filein);
|
||||
nBestSeenHeight = nFileBestSeenHeight;
|
||||
// if nVersionThatWrote < 120300 then another TxConfirmStats (for priority) follows but can be ignored.
|
||||
try {
|
||||
LOCK(cs_feeEstimator);
|
||||
int nVersionRequired, nVersionThatWrote, nFileBestSeenHeight;
|
||||
filein >> nVersionRequired >> nVersionThatWrote;
|
||||
if (nVersionRequired > CLIENT_VERSION)
|
||||
return error("CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
|
||||
filein >> nFileBestSeenHeight;
|
||||
feeStats->Read(filein);
|
||||
nBestSeenHeight = nFileBestSeenHeight;
|
||||
// if nVersionThatWrote < 120300 then another TxConfirmStats (for priority) follows but can be ignored.
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
LogPrintf("CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal)\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "amount.h"
|
||||
#include "uint256.h"
|
||||
#include "random.h"
|
||||
#include "sync.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@ -17,6 +18,7 @@ class CAutoFile;
|
||||
class CFeeRate;
|
||||
class CTxMemPoolEntry;
|
||||
class CTxMemPool;
|
||||
class TxConfirmStats;
|
||||
|
||||
/** \class CBlockPolicyEstimator
|
||||
* The BlockPolicyEstimator is used for estimating the feerate needed
|
||||
@ -59,113 +61,6 @@ class CTxMemPool;
|
||||
* they've been outstanding.
|
||||
*/
|
||||
|
||||
/**
|
||||
* We will instantiate an instance of this class to track transactions that were
|
||||
* included in a block. We will lump transactions into a bucket according to their
|
||||
* approximate feerate and then track how long it took for those txs to be included in a block
|
||||
*
|
||||
* The tracking of unconfirmed (mempool) transactions is completely independent of the
|
||||
* historical tracking of transactions that have been confirmed in a block.
|
||||
*/
|
||||
class TxConfirmStats
|
||||
{
|
||||
private:
|
||||
//Define the buckets we will group transactions into
|
||||
std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
|
||||
std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
|
||||
|
||||
// For each bucket X:
|
||||
// Count the total # of txs in each bucket
|
||||
// Track the historical moving average of this total over blocks
|
||||
std::vector<double> txCtAvg;
|
||||
// and calculate the total for the current block to update the moving average
|
||||
std::vector<int> curBlockTxCt;
|
||||
|
||||
// Count the total # of txs confirmed within Y blocks in each bucket
|
||||
// Track the historical moving average of theses totals over blocks
|
||||
std::vector<std::vector<double> > confAvg; // confAvg[Y][X]
|
||||
// and calculate the totals for the current block to update the moving averages
|
||||
std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
|
||||
|
||||
// Sum the total feerate of all tx's in each bucket
|
||||
// Track the historical moving average of this total over blocks
|
||||
std::vector<double> avg;
|
||||
// and calculate the total for the current block to update the moving average
|
||||
std::vector<double> curBlockVal;
|
||||
|
||||
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
|
||||
// Combine the total value with the tx counts to calculate the avg feerate per bucket
|
||||
|
||||
double decay;
|
||||
|
||||
// Mempool counts of outstanding transactions
|
||||
// For each bucket X, track the number of transactions in the mempool
|
||||
// that are unconfirmed for each possible confirmation value Y
|
||||
std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
|
||||
// transactions still unconfirmed after MAX_CONFIRMS for each bucket
|
||||
std::vector<int> oldUnconfTxs;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize the data structures. This is called by BlockPolicyEstimator's
|
||||
* constructor with default values.
|
||||
* @param defaultBuckets contains the upper limits for the bucket boundaries
|
||||
* @param maxConfirms max number of confirms to track
|
||||
* @param decay how much to decay the historical moving average per block
|
||||
*/
|
||||
void Initialize(std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
|
||||
|
||||
/** Clear the state of the curBlock variables to start counting for the new block */
|
||||
void ClearCurrent(unsigned int nBlockHeight);
|
||||
|
||||
/**
|
||||
* Record a new transaction data point in the current block stats
|
||||
* @param blocksToConfirm the number of blocks it took this transaction to confirm
|
||||
* @param val the feerate of the transaction
|
||||
* @warning blocksToConfirm is 1-based and has to be >= 1
|
||||
*/
|
||||
void Record(int blocksToConfirm, double val);
|
||||
|
||||
/** Record a new transaction entering the mempool*/
|
||||
unsigned int NewTx(unsigned int nBlockHeight, double val);
|
||||
|
||||
/** Remove a transaction from mempool tracking stats*/
|
||||
void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
|
||||
unsigned int bucketIndex);
|
||||
|
||||
/** Update our estimates by decaying our historical moving average and updating
|
||||
with the data gathered from the current block */
|
||||
void UpdateMovingAverages();
|
||||
|
||||
/**
|
||||
* Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
|
||||
* to make sure we have enough data points) whose transactions still have sufficient likelihood
|
||||
* of being confirmed within the target number of confirmations
|
||||
* @param confTarget target number of confirmations
|
||||
* @param sufficientTxVal required average number of transactions per block in a bucket range
|
||||
* @param minSuccess the success probability we require
|
||||
* @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
|
||||
* return the highest feerate such that all lower values fail minSuccess
|
||||
* @param nBlockHeight the current block height
|
||||
*/
|
||||
double EstimateMedianVal(int confTarget, double sufficientTxVal,
|
||||
double minSuccess, bool requireGreater, unsigned int nBlockHeight);
|
||||
|
||||
/** Return the max number of confirms we're tracking */
|
||||
unsigned int GetMaxConfirms() { return confAvg.size(); }
|
||||
|
||||
/** Write state of estimation data to a file*/
|
||||
void Write(CAutoFile& fileout);
|
||||
|
||||
/**
|
||||
* Read saved state of estimation data from a file and replace all internal data structures and
|
||||
* variables with this state.
|
||||
*/
|
||||
void Read(CAutoFile& filein);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Track confirm delays up to 25 blocks, can't estimate beyond that */
|
||||
static const unsigned int MAX_BLOCK_CONFIRMS = 25;
|
||||
|
||||
@ -204,14 +99,12 @@ class CBlockPolicyEstimator
|
||||
public:
|
||||
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
|
||||
CBlockPolicyEstimator();
|
||||
~CBlockPolicyEstimator();
|
||||
|
||||
/** Process all the transactions that have been included in a block */
|
||||
void processBlock(unsigned int nBlockHeight,
|
||||
std::vector<const CTxMemPoolEntry*>& entries);
|
||||
|
||||
/** Process a transaction confirmed in a block*/
|
||||
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
|
||||
|
||||
/** Process a transaction accepted to the mempool*/
|
||||
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate);
|
||||
|
||||
@ -219,19 +112,19 @@ public:
|
||||
bool removeTx(uint256 hash);
|
||||
|
||||
/** Return a feerate estimate */
|
||||
CFeeRate estimateFee(int confTarget);
|
||||
CFeeRate estimateFee(int confTarget) const;
|
||||
|
||||
/** Estimate feerate needed to get be included in a block within
|
||||
* confTarget blocks. If no answer can be given at confTarget, return an
|
||||
* estimate at the lowest target where one can be given.
|
||||
*/
|
||||
CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
|
||||
CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const;
|
||||
|
||||
/** Write estimation data to a file */
|
||||
void Write(CAutoFile& fileout);
|
||||
bool Write(CAutoFile& fileout) const;
|
||||
|
||||
/** Read estimation data from a file */
|
||||
void Read(CAutoFile& filein, int nFileVersion);
|
||||
bool Read(CAutoFile& filein);
|
||||
|
||||
private:
|
||||
CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main
|
||||
@ -247,9 +140,15 @@ private:
|
||||
std::map<uint256, TxStatsInfo> mapMemPoolTxs;
|
||||
|
||||
/** Classes to track historical data on transaction confirmations */
|
||||
TxConfirmStats feeStats;
|
||||
TxConfirmStats* feeStats;
|
||||
|
||||
unsigned int trackedTxs;
|
||||
unsigned int untrackedTxs;
|
||||
|
||||
mutable CCriticalSection cs_feeEstimator;
|
||||
|
||||
/** Process a transaction confirmed in a block*/
|
||||
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
|
||||
|
||||
};
|
||||
#endif /*BITCOIN_POLICYESTIMATOR_H */
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "wallet/coincontrol.h"
|
||||
#include "init.h"
|
||||
#include "policy/fees.h"
|
||||
#include "policy/policy.h"
|
||||
#include "validation.h" // For mempool
|
||||
#include "wallet/wallet.h"
|
||||
@ -556,7 +557,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
||||
nBytes -= 34;
|
||||
|
||||
// Fee
|
||||
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
|
||||
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, ::mempool, ::feeEstimator);
|
||||
if (nPayFee > 0 && coinControl->nMinimumTotalFee > nPayFee)
|
||||
nPayFee = coinControl->nMinimumTotalFee;
|
||||
|
||||
@ -645,7 +646,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
||||
if (payTxFee.GetFeePerK() > 0)
|
||||
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
|
||||
else {
|
||||
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000;
|
||||
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(nTxConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000;
|
||||
}
|
||||
QString toolTip4 = tr("Can vary +/- %1 duff(s) per input.").arg(dFeeVary);
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "validation.h" // mempool and minRelayTxFee
|
||||
#include "ui_interface.h"
|
||||
#include "txmempool.h"
|
||||
#include "policy/fees.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
#include "privatesend/privatesend.h"
|
||||
@ -760,7 +761,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
|
||||
|
||||
int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
|
||||
int estimateFoundAtBlocks = nBlocksToConfirm;
|
||||
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
|
||||
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks, ::mempool);
|
||||
if (feeRate <= CFeeRate(0)) // not enough data => minfee
|
||||
{
|
||||
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
|
||||
|
@ -21,14 +21,8 @@
|
||||
*/
|
||||
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
|
||||
{
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
// Ensures we show generated coins / mined transactions at depth 1
|
||||
if (!wtx.IsInMainChain())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// There are currently no cases where we hide transactions, but
|
||||
// we may want to use this in the future for things like RBF.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -158,6 +158,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "getspecialtxes", 2, "count" },
|
||||
{ "getspecialtxes", 3, "skip" },
|
||||
{ "getspecialtxes", 4, "verbosity" },
|
||||
{ "disconnectnode", 1, "nodeid" },
|
||||
// Echo with conversion (For testing only)
|
||||
{ "echojson", 0, "arg0" },
|
||||
{ "echojson", 1, "arg1" },
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "validation.h"
|
||||
#include "miner.h"
|
||||
#include "net.h"
|
||||
#include "policy/fees.h"
|
||||
#include "pow.h"
|
||||
#include "rpc/blockchain.h"
|
||||
#include "rpc/server.h"
|
||||
@ -851,7 +852,7 @@ UniValue estimatefee(const JSONRPCRequest& request)
|
||||
if (nBlocks < 1)
|
||||
nBlocks = 1;
|
||||
|
||||
CFeeRate feeRate = mempool.estimateFee(nBlocks);
|
||||
CFeeRate feeRate = ::feeEstimator.estimateFee(nBlocks);
|
||||
if (feeRate == CFeeRate(0))
|
||||
return -1.0;
|
||||
|
||||
@ -888,7 +889,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
int answerFound;
|
||||
CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound);
|
||||
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &answerFound, ::mempool);
|
||||
result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
|
||||
result.push_back(Pair("blocks", answerFound));
|
||||
return result;
|
||||
|
@ -235,23 +235,43 @@ UniValue addnode(const JSONRPCRequest& request)
|
||||
|
||||
UniValue disconnectnode(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
|
||||
throw std::runtime_error(
|
||||
"disconnectnode \"address\" \n"
|
||||
"\nImmediately disconnects from the specified node.\n"
|
||||
"disconnectnode \"[address]\" [nodeid]\n"
|
||||
"\nImmediately disconnects from the specified peer node.\n"
|
||||
"\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
|
||||
"\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) The IP address/port of the node\n"
|
||||
"1. \"address\" (string, optional) The IP address/port of the node\n"
|
||||
"2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:9999\"")
|
||||
+ HelpExampleCli("disconnectnode", "\"\" 1")
|
||||
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:9999\"")
|
||||
+ HelpExampleRpc("disconnectnode", "\"\", 1")
|
||||
);
|
||||
|
||||
if(!g_connman)
|
||||
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
|
||||
|
||||
bool ret = g_connman->DisconnectNode(request.params[0].get_str());
|
||||
if (!ret)
|
||||
bool success;
|
||||
const UniValue &address_arg = request.params[0];
|
||||
const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1];
|
||||
|
||||
if (!address_arg.isNull() && id_arg.isNull()) {
|
||||
/* handle disconnect-by-address */
|
||||
success = g_connman->DisconnectNode(address_arg.get_str());
|
||||
} else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
|
||||
/* handle disconnect-by-id */
|
||||
NodeId nodeid = (NodeId) id_arg.get_int64();
|
||||
success = g_connman->DisconnectNode(nodeid);
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
@ -608,7 +628,7 @@ static const CRPCCommand commands[] =
|
||||
{ "network", "ping", &ping, true, {} },
|
||||
{ "network", "getpeerinfo", &getpeerinfo, true, {} },
|
||||
{ "network", "addnode", &addnode, true, {"node","command"} },
|
||||
{ "network", "disconnectnode", &disconnectnode, true, {"address"} },
|
||||
{ "network", "disconnectnode", &disconnectnode, true, {"address", "nodeid"} },
|
||||
{ "network", "getaddednodeinfo", &getaddednodeinfo, true, {"node"} },
|
||||
{ "network", "getnettotals", &getnettotals, true, {} },
|
||||
{ "network", "getnetworkinfo", &getnetworkinfo, true, {} },
|
||||
|
@ -15,28 +15,6 @@
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* We're hashing a nonce into the entries themselves, so we don't need extra
|
||||
* blinding in the set hash computation.
|
||||
*
|
||||
* This may exhibit platform endian dependent behavior but because these are
|
||||
* nonced hashes (random) and this state is only ever used locally it is safe.
|
||||
* All that matters is local consistency.
|
||||
*/
|
||||
class SignatureCacheHasher
|
||||
{
|
||||
public:
|
||||
template <uint8_t hash_select>
|
||||
uint32_t operator()(const uint256& key) const
|
||||
{
|
||||
static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
|
||||
uint32_t u;
|
||||
std::memcpy(&u, key.begin()+4*hash_select, 4);
|
||||
return u;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Valid signature cache, to avoid doing expensive ECDSA signature checking
|
||||
* twice for every transaction (once when accepted into memory pool, and
|
||||
|
@ -19,6 +19,27 @@ static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384;
|
||||
|
||||
class CPubKey;
|
||||
|
||||
/**
|
||||
* We're hashing a nonce into the entries themselves, so we don't need extra
|
||||
* blinding in the set hash computation.
|
||||
*
|
||||
* This may exhibit platform endian dependent behavior but because these are
|
||||
* nonced hashes (random) and this state is only ever used locally it is safe.
|
||||
* All that matters is local consistency.
|
||||
*/
|
||||
class SignatureCacheHasher
|
||||
{
|
||||
public:
|
||||
template <uint8_t hash_select>
|
||||
uint32_t operator()(const uint256& key) const
|
||||
{
|
||||
static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
|
||||
uint32_t u;
|
||||
std::memcpy(&u, key.begin()+4*hash_select, 4);
|
||||
return u;
|
||||
}
|
||||
};
|
||||
|
||||
class CachingTransactionSignatureChecker : public TransactionSignatureChecker
|
||||
{
|
||||
private:
|
||||
|
@ -3,6 +3,7 @@
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "cuckoocache.h"
|
||||
#include "script/sigcache.h"
|
||||
#include "test/test_dash.h"
|
||||
#include "random.h"
|
||||
#include <thread>
|
||||
@ -36,20 +37,6 @@ void insecure_GetRandHash(uint256& t)
|
||||
*(ptr++) = insecure_rand.rand32();
|
||||
}
|
||||
|
||||
/** Definition copied from /src/script/sigcache.cpp
|
||||
*/
|
||||
class uint256Hasher
|
||||
{
|
||||
public:
|
||||
template <uint8_t hash_select>
|
||||
uint32_t operator()(const uint256& key) const
|
||||
{
|
||||
static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
|
||||
uint32_t u;
|
||||
std::memcpy(&u, key.begin() + 4 * hash_select, 4);
|
||||
return u;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Test that no values not inserted into the cache are read out of it.
|
||||
@ -59,7 +46,7 @@ public:
|
||||
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
|
||||
{
|
||||
insecure_rand = FastRandomContext(true);
|
||||
CuckooCache::cache<uint256, uint256Hasher> cc{};
|
||||
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
|
||||
size_t megabytes = 4;
|
||||
cc.setup_bytes(megabytes << 20);
|
||||
uint256 v;
|
||||
@ -138,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
|
||||
double HitRateThresh = 0.98;
|
||||
size_t megabytes = 4;
|
||||
for (double load = 0.1; load < 2; load *= 2) {
|
||||
double hits = test_cache<CuckooCache::cache<uint256, uint256Hasher>>(megabytes, load);
|
||||
double hits = test_cache<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes, load);
|
||||
BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh);
|
||||
}
|
||||
}
|
||||
@ -206,7 +193,7 @@ void test_cache_erase(size_t megabytes)
|
||||
BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok)
|
||||
{
|
||||
size_t megabytes = 4;
|
||||
test_cache_erase<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
|
||||
test_cache_erase<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes);
|
||||
}
|
||||
|
||||
template <typename Cache>
|
||||
@ -293,7 +280,7 @@ void test_cache_erase_parallel(size_t megabytes)
|
||||
BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok)
|
||||
{
|
||||
size_t megabytes = 4;
|
||||
test_cache_erase_parallel<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
|
||||
test_cache_erase_parallel<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes);
|
||||
}
|
||||
|
||||
|
||||
@ -389,7 +376,7 @@ void test_cache_generations()
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(cuckoocache_generations)
|
||||
{
|
||||
test_cache_generations<CuckooCache::cache<uint256, uint256Hasher>>();
|
||||
test_cache_generations<CuckooCache::cache<uint256, SignatureCacheHasher>>();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
@ -15,7 +15,8 @@ BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
{
|
||||
CTxMemPool mpool;
|
||||
CBlockPolicyEstimator feeEst;
|
||||
CTxMemPool mpool(&feeEst);
|
||||
TestMemPoolEntryHelper entry;
|
||||
CAmount basefee(2000);
|
||||
CAmount deltaFee(100);
|
||||
@ -77,16 +78,16 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
// At this point we should need to combine 5 buckets to get enough data points
|
||||
// So estimateFee(1,2,3) should fail and estimateFee(4) should return somewhere around
|
||||
// 8*baserate. estimateFee(4) %'s are 100,100,100,100,90 = average 98%
|
||||
BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
|
||||
BOOST_CHECK(mpool.estimateFee(2) == CFeeRate(0));
|
||||
BOOST_CHECK(mpool.estimateFee(3) == CFeeRate(0));
|
||||
BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
|
||||
BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||
BOOST_CHECK(feeEst.estimateFee(2) == CFeeRate(0));
|
||||
BOOST_CHECK(feeEst.estimateFee(3) == CFeeRate(0));
|
||||
BOOST_CHECK(feeEst.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
|
||||
int answerFound;
|
||||
BOOST_CHECK(mpool.estimateSmartFee(1, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
|
||||
BOOST_CHECK(mpool.estimateSmartFee(3, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
|
||||
BOOST_CHECK(mpool.estimateSmartFee(4, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
|
||||
BOOST_CHECK(mpool.estimateSmartFee(8, &answerFound) == mpool.estimateFee(8) && answerFound == 8);
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(1, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(3, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(4, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(8, &answerFound, mpool) == feeEst.estimateFee(8) && answerFound == 8);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +99,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
// Second highest feerate has 100% chance of being included by 2 blocks,
|
||||
// so estimateFee(2) should return 9*baseRate etc...
|
||||
for (int i = 1; i < 10;i++) {
|
||||
origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
|
||||
origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
|
||||
if (i > 2) { // Fee estimates should be monotonically decreasing
|
||||
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
|
||||
}
|
||||
@ -117,10 +118,10 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
while (blocknum < 250)
|
||||
mpool.removeForBlock(block, ++blocknum);
|
||||
|
||||
BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
|
||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||
for (int i = 2; i < 10;i++) {
|
||||
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
|
||||
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
|
||||
}
|
||||
|
||||
|
||||
@ -140,8 +141,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
|
||||
int answerFound;
|
||||
for (int i = 1; i < 10;i++) {
|
||||
BOOST_CHECK(mpool.estimateFee(i) == CFeeRate(0) || mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
|
||||
BOOST_CHECK(mpool.estimateSmartFee(i, &answerFound).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, &answerFound, mpool).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
|
||||
}
|
||||
|
||||
// Mine all those transactions
|
||||
@ -156,9 +157,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
}
|
||||
mpool.removeForBlock(block, 265);
|
||||
block.clear();
|
||||
BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
|
||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||
for (int i = 2; i < 10;i++) {
|
||||
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
|
||||
}
|
||||
|
||||
// Mine 200 more blocks where everything is mined every block
|
||||
@ -178,9 +179,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
mpool.removeForBlock(block, ++blocknum);
|
||||
block.clear();
|
||||
}
|
||||
BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
|
||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||
for (int i = 2; i < 10; i++) {
|
||||
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
|
||||
BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
|
||||
}
|
||||
|
||||
// Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
|
||||
@ -189,8 +190,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||
mpool.TrimToSize(1);
|
||||
BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]);
|
||||
for (int i = 1; i < 10; i++) {
|
||||
BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.estimateFee(i).GetFeePerK());
|
||||
BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= feeEst.estimateFee(i).GetFeePerK());
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include "txmempool.h"
|
||||
|
||||
#include "clientversion.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "instantsend.h"
|
||||
@ -18,7 +17,6 @@
|
||||
#include "util.h"
|
||||
#include "utilmoneystr.h"
|
||||
#include "utiltime.h"
|
||||
#include "version.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include "evo/specialtx.h"
|
||||
@ -336,8 +334,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
|
||||
assert(int(nSigOpCountWithAncestors) >= 0);
|
||||
}
|
||||
|
||||
CTxMemPool::CTxMemPool() :
|
||||
nTransactionsUpdated(0)
|
||||
CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) :
|
||||
nTransactionsUpdated(0), minerPolicyEstimator(estimator)
|
||||
{
|
||||
_clear(); //lock free clear
|
||||
|
||||
@ -345,13 +343,6 @@ CTxMemPool::CTxMemPool() :
|
||||
// accepting transactions becomes O(N^2) where N is the number
|
||||
// of transactions in the pool
|
||||
nCheckFrequency = 0;
|
||||
|
||||
minerPolicyEstimator = new CBlockPolicyEstimator();
|
||||
}
|
||||
|
||||
CTxMemPool::~CTxMemPool()
|
||||
{
|
||||
delete minerPolicyEstimator;
|
||||
}
|
||||
|
||||
bool CTxMemPool::isSpent(const COutPoint& outpoint)
|
||||
@ -423,7 +414,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||
|
||||
nTransactionsUpdated++;
|
||||
totalTxSize += entry.GetTxSize();
|
||||
minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
|
||||
if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);}
|
||||
|
||||
vTxHashes.emplace_back(hash, newit);
|
||||
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
||||
@ -701,7 +692,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
||||
mapLinks.erase(it);
|
||||
mapTx.erase(it);
|
||||
nTransactionsUpdated++;
|
||||
minerPolicyEstimator->removeTx(hash);
|
||||
if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash);}
|
||||
removeAddressIndex(hash);
|
||||
removeSpentIndex(hash);
|
||||
}
|
||||
@ -976,7 +967,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
||||
entries.push_back(&*i);
|
||||
}
|
||||
// Before the txs in the new block have been removed from the mempool, update policy estimates
|
||||
minerPolicyEstimator->processBlock(nBlockHeight, entries);
|
||||
if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
|
||||
for (const auto& tx : vtx)
|
||||
{
|
||||
txiter it = mapTx.find(tx->GetHash());
|
||||
@ -1325,51 +1316,6 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
|
||||
{
|
||||
LOCK(cs);
|
||||
return minerPolicyEstimator->estimateFee(nBlocks);
|
||||
}
|
||||
CFeeRate CTxMemPool::estimateSmartFee(int nBlocks, int *answerFoundAtBlocks) const
|
||||
{
|
||||
LOCK(cs);
|
||||
return minerPolicyEstimator->estimateSmartFee(nBlocks, answerFoundAtBlocks, *this);
|
||||
}
|
||||
|
||||
bool
|
||||
CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const
|
||||
{
|
||||
try {
|
||||
LOCK(cs);
|
||||
fileout << 120300; // version required to read: 0.12.00 or later
|
||||
fileout << CLIENT_VERSION; // version that wrote the file
|
||||
minerPolicyEstimator->Write(fileout);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
LogPrintf("CTxMemPool::WriteFeeEstimates(): unable to write policy estimator data (non-fatal)\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
|
||||
{
|
||||
try {
|
||||
int nVersionRequired, nVersionThatWrote;
|
||||
filein >> nVersionRequired >> nVersionThatWrote;
|
||||
if (nVersionRequired > CLIENT_VERSION)
|
||||
return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired);
|
||||
LOCK(cs);
|
||||
minerPolicyEstimator->Read(filein, nVersionThatWrote);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
|
||||
{
|
||||
{
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "random.h"
|
||||
#include "netaddress.h"
|
||||
#include "bls/bls.h"
|
||||
#include "pubkey.h"
|
||||
|
||||
#include "boost/multi_index_container.hpp"
|
||||
#include "boost/multi_index/ordered_index.hpp"
|
||||
@ -533,8 +534,7 @@ public:
|
||||
|
||||
/** Create a new CTxMemPool.
|
||||
*/
|
||||
CTxMemPool();
|
||||
~CTxMemPool();
|
||||
CTxMemPool(CBlockPolicyEstimator* estimator = nullptr);
|
||||
|
||||
/**
|
||||
* If sanity-checking is turned on, check makes sure the pool is
|
||||
@ -679,19 +679,6 @@ public:
|
||||
|
||||
bool existsProviderTxConflict(const CTransaction &tx) const;
|
||||
|
||||
/** Estimate fee rate needed to get into the next nBlocks
|
||||
* If no answer can be given at nBlocks, return an estimate
|
||||
* at the lowest number of blocks where one can be given
|
||||
*/
|
||||
CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks = NULL) const;
|
||||
|
||||
/** Estimate fee rate needed to get into the next nBlocks */
|
||||
CFeeRate estimateFee(int nBlocks) const;
|
||||
|
||||
/** Write/Read estimates to disk */
|
||||
bool WriteFeeEstimates(CAutoFile& fileout) const;
|
||||
bool ReadFeeEstimates(CAutoFile& filein);
|
||||
|
||||
size_t DynamicMemoryUsage() const;
|
||||
// returns share of the used memory to maximum allowed memory
|
||||
double UsedMemoryShare() const;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "fs.h"
|
||||
#include "hash.h"
|
||||
#include "init.h"
|
||||
#include "policy/fees.h"
|
||||
#include "policy/policy.h"
|
||||
#include "pow.h"
|
||||
#include "primitives/block.h"
|
||||
@ -101,7 +102,8 @@ uint256 hashAssumeValid;
|
||||
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
||||
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
|
||||
|
||||
CTxMemPool mempool;
|
||||
CBlockPolicyEstimator feeEstimator;
|
||||
CTxMemPool mempool(&feeEstimator);
|
||||
std::map<uint256, int64_t> mapRejectedBlocks GUARDED_BY(cs_main);
|
||||
|
||||
static void CheckBlockIndex(const Consensus::Params& consensusParams);
|
||||
|
@ -42,6 +42,7 @@ class CCoinsViewDB;
|
||||
class CInv;
|
||||
class CConnman;
|
||||
class CScriptCheck;
|
||||
class CBlockPolicyEstimator;
|
||||
class CTxMemPool;
|
||||
class CValidationInterface;
|
||||
class CValidationState;
|
||||
@ -153,6 +154,7 @@ struct BlockHasher
|
||||
|
||||
extern CScript COINBASE_FLAGS;
|
||||
extern CCriticalSection cs_main;
|
||||
extern CBlockPolicyEstimator feeEstimator;
|
||||
extern CTxMemPool mempool;
|
||||
typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
||||
typedef std::unordered_multimap<uint256, CBlockIndex*, BlockHasher> PrevBlockMap;
|
||||
|
@ -157,6 +157,31 @@ UniValue importprivkey(const JSONRPCRequest& request)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue abortrescan(const JSONRPCRequest& request)
|
||||
{
|
||||
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() > 0)
|
||||
throw std::runtime_error(
|
||||
"abortrescan\n"
|
||||
"\nStops current wallet rescan triggered e.g. by an importprivkey call.\n"
|
||||
"\nExamples:\n"
|
||||
"\nImport a private key\n"
|
||||
+ HelpExampleCli("importprivkey", "\"mykey\"") +
|
||||
"\nAbort the running wallet rescan\n"
|
||||
+ HelpExampleCli("abortrescan", "") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
+ HelpExampleRpc("abortrescan", "")
|
||||
);
|
||||
|
||||
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
|
||||
pwallet->AbortRescan();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel);
|
||||
void ImportScript(CWallet * const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "init.h"
|
||||
#include "instantsend.h"
|
||||
#include "net.h"
|
||||
#include "policy/fees.h"
|
||||
#include "rpc/server.h"
|
||||
#include "timedata.h"
|
||||
#include "util.h"
|
||||
@ -2998,6 +2999,7 @@ UniValue setbip69enabled(const JSONRPCRequest& request)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
extern UniValue importprivkey(const JSONRPCRequest& request);
|
||||
extern UniValue importaddress(const JSONRPCRequest& request);
|
||||
@ -3017,6 +3019,7 @@ static const CRPCCommand commands[] =
|
||||
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} },
|
||||
{ "hidden", "resendwallettransactions", &resendwallettransactions, true, {} },
|
||||
{ "wallet", "abandontransaction", &abandontransaction, false, {"txid"} },
|
||||
{ "wallet", "abortrescan", &abortrescan, false, {} },
|
||||
{ "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} },
|
||||
{ "wallet", "backupwallet", &backupwallet, true, {"destination"} },
|
||||
{ "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} },
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "keystore.h"
|
||||
#include "validation.h"
|
||||
#include "net.h"
|
||||
#include "policy/fees.h"
|
||||
#include "policy/policy.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
@ -1868,6 +1869,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
|
||||
CBlockIndex* ret = pindexStart;
|
||||
{
|
||||
LOCK2(cs_main, cs_wallet);
|
||||
fAbortRescan = false;
|
||||
fScanningWallet = true;
|
||||
|
||||
// no need to read and scan block, if block was created before
|
||||
// our wallet birthday (as adjusted for block time variability)
|
||||
@ -1877,7 +1880,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
|
||||
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
|
||||
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
|
||||
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
|
||||
while (pindex)
|
||||
while (pindex && !fAbortRescan)
|
||||
{
|
||||
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
|
||||
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
|
||||
@ -1899,7 +1902,12 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
|
||||
}
|
||||
pindex = chainActive.Next(pindex);
|
||||
}
|
||||
if (pindex && fAbortRescan) {
|
||||
LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
|
||||
}
|
||||
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
|
||||
|
||||
fScanningWallet = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -3670,7 +3678,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
||||
if (coinControl && coinControl->nConfirmTarget > 0)
|
||||
currentConfirmationTarget = coinControl->nConfirmTarget;
|
||||
|
||||
CAmount nFeeNeeded = std::max(nFeePay, GetMinimumFee(nBytes, currentConfirmationTarget, mempool));
|
||||
CAmount nFeeNeeded = std::max(nFeePay, GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator));
|
||||
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
|
||||
nFeeNeeded = coinControl->nMinimumTotalFee;
|
||||
}
|
||||
@ -3843,19 +3851,19 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
|
||||
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
|
||||
}
|
||||
|
||||
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
|
||||
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator)
|
||||
{
|
||||
// payTxFee is the user-set global for desired feerate
|
||||
return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));
|
||||
return GetMinimumFee(nTxBytes, nConfirmTarget, pool, estimator, payTxFee.GetFee(nTxBytes));
|
||||
}
|
||||
|
||||
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)
|
||||
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, CAmount targetFee)
|
||||
{
|
||||
CAmount nFeeNeeded = targetFee;
|
||||
// User didn't set: use -txconfirmtarget to estimate...
|
||||
if (nFeeNeeded == 0) {
|
||||
int estimateFoundTarget = nConfirmTarget;
|
||||
nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);
|
||||
nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, &estimateFoundTarget, pool).GetFee(nTxBytes);
|
||||
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
|
||||
if (nFeeNeeded == 0)
|
||||
nFeeNeeded = fallbackFee.GetFee(nTxBytes);
|
||||
@ -5044,7 +5052,7 @@ bool CWallet::InitLoadWallet()
|
||||
|
||||
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
|
||||
|
||||
if (walletFile.find_first_of("/\\") != std::string::npos) {
|
||||
if (boost::filesystem::path(walletFile).filename() != walletFile) {
|
||||
return InitError(_("-wallet parameter must only specify a filename (not a path)"));
|
||||
} else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
|
||||
return InitError(_("Invalid characters in -wallet filename"));
|
||||
|
@ -79,6 +79,7 @@ class CReserveKey;
|
||||
class CScript;
|
||||
class CScheduler;
|
||||
class CTxMemPool;
|
||||
class CBlockPolicyEstimator;
|
||||
class CWalletTx;
|
||||
|
||||
/** (client) version numbers for particular wallet features */
|
||||
@ -671,6 +672,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
|
||||
{
|
||||
private:
|
||||
static std::atomic<bool> fFlushScheduled;
|
||||
std::atomic<bool> fAbortRescan;
|
||||
std::atomic<bool> fScanningWallet;
|
||||
|
||||
/**
|
||||
* Select a set of coins such that nValueRet >= nTargetValue and at least
|
||||
@ -806,6 +809,8 @@ public:
|
||||
nTimeFirstKey = 0;
|
||||
fBroadcastTransactions = false;
|
||||
nRelockTime = 0;
|
||||
fAbortRescan = false;
|
||||
fScanningWallet = false;
|
||||
fAnonymizableTallyCached = false;
|
||||
fAnonymizableTallyCachedNonDenom = false;
|
||||
vecAnonymizableTallyCached.clear();
|
||||
@ -881,6 +886,13 @@ public:
|
||||
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
|
||||
void ListProTxCoins(std::vector<COutPoint>& vOutpts);
|
||||
|
||||
/*
|
||||
* Rescan abort properties
|
||||
*/
|
||||
void AbortRescan() { fAbortRescan = true; }
|
||||
bool IsAbortingRescan() { return fAbortRescan; }
|
||||
bool IsScanning() { return fScanningWallet; }
|
||||
|
||||
/**
|
||||
* keystore implementation
|
||||
* Generate a new key
|
||||
@ -1002,12 +1014,12 @@ public:
|
||||
* Estimate the minimum fee considering user set parameters
|
||||
* and the required fee
|
||||
*/
|
||||
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
|
||||
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator);
|
||||
/**
|
||||
* Estimate the minimum fee considering required fee and targetFee or if 0
|
||||
* then fee estimation for nConfirmTarget
|
||||
*/
|
||||
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee);
|
||||
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, CAmount targetFee);
|
||||
/**
|
||||
* Return the minimum required fee taking into account the
|
||||
* floating relay fee and user set minimum transaction fee
|
||||
|
@ -25,6 +25,8 @@ The ZMQ functional test requires a python ZMQ library. To install it:
|
||||
Running tests locally
|
||||
=====================
|
||||
|
||||
Build for your system first. Be sure to enable wallet, utils and daemon when you configure. Tests will not run otherwise.
|
||||
|
||||
Functional tests
|
||||
----------------
|
||||
|
||||
|
114
test/functional/disconnect_ban.py
Executable file
114
test/functional/disconnect_ban.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test node disconnect and ban behavior"""
|
||||
|
||||
from test_framework.mininode import wait_until
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (assert_equal,
|
||||
assert_raises_jsonrpc,
|
||||
connect_nodes_bi,
|
||||
start_node,
|
||||
stop_node,
|
||||
set_mocktime,
|
||||
get_mocktime,
|
||||
set_node_times,
|
||||
)
|
||||
|
||||
class DisconnectBanTest(BitcoinTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 2
|
||||
self.setup_clean_chain = False
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = self.setup_nodes()
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
|
||||
def run_test(self):
|
||||
self.log.info("Test setban and listbanned RPCs")
|
||||
|
||||
self.log.info("setban: successfully ban single IP address")
|
||||
assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point
|
||||
self.nodes[1].setban("127.0.0.1", "add")
|
||||
wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0)
|
||||
assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point
|
||||
assert_equal(len(self.nodes[1].listbanned()), 1)
|
||||
|
||||
self.log.info("clearbanned: successfully clear ban list")
|
||||
self.nodes[1].clearbanned()
|
||||
assert_equal(len(self.nodes[1].listbanned()), 0)
|
||||
self.nodes[1].setban("127.0.0.0/24", "add")
|
||||
|
||||
self.log.info("setban: fail to ban an already banned subnet")
|
||||
assert_equal(len(self.nodes[1].listbanned()), 1)
|
||||
assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")
|
||||
|
||||
self.log.info("setban: fail to ban an invalid subnet")
|
||||
assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
|
||||
assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
|
||||
|
||||
self.log.info("setban remove: fail to unban a non-banned subnet")
|
||||
assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
|
||||
assert_equal(len(self.nodes[1].listbanned()), 1)
|
||||
|
||||
self.log.info("setban remove: successfully unban subnet")
|
||||
self.nodes[1].setban("127.0.0.0/24", "remove")
|
||||
assert_equal(len(self.nodes[1].listbanned()), 0)
|
||||
self.nodes[1].clearbanned()
|
||||
assert_equal(len(self.nodes[1].listbanned()), 0)
|
||||
|
||||
self.log.info("setban: test persistence across node restart")
|
||||
self.nodes[1].setban("127.0.0.0/32", "add")
|
||||
self.nodes[1].setban("127.0.0.0/24", "add")
|
||||
self.nodes[1].setban("192.168.0.1", "add", 1) # ban for 1 seconds
|
||||
self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) # ban for 1000 seconds
|
||||
listBeforeShutdown = self.nodes[1].listbanned()
|
||||
assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address'])
|
||||
set_mocktime(get_mocktime() + 2)
|
||||
set_node_times(self.nodes, get_mocktime())
|
||||
wait_until(lambda: len(self.nodes[1].listbanned()) == 3)
|
||||
|
||||
stop_node(self.nodes[1], 1)
|
||||
|
||||
self.nodes[1] = start_node(1, self.options.tmpdir)
|
||||
listAfterShutdown = self.nodes[1].listbanned()
|
||||
assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
|
||||
assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
|
||||
assert_equal("/19" in listAfterShutdown[2]['address'], True)
|
||||
|
||||
# Clear ban lists
|
||||
self.nodes[1].clearbanned()
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
|
||||
self.log.info("Test disconnectrnode RPCs")
|
||||
|
||||
self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
|
||||
address1 = self.nodes[0].getpeerinfo()[0]['addr']
|
||||
node1 = self.nodes[0].getpeerinfo()[0]['addr']
|
||||
assert_raises_jsonrpc(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
|
||||
|
||||
self.log.info("disconnectnode: fail to disconnect when calling with junk address")
|
||||
assert_raises_jsonrpc(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")
|
||||
|
||||
self.log.info("disconnectnode: successfully disconnect node by address")
|
||||
address1 = self.nodes[0].getpeerinfo()[0]['addr']
|
||||
self.nodes[0].disconnectnode(address=address1)
|
||||
wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1)
|
||||
assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
|
||||
|
||||
self.log.info("disconnectnode: successfully reconnect node")
|
||||
connect_nodes_bi(self.nodes, 0, 1) # reconnect the node
|
||||
assert_equal(len(self.nodes[0].getpeerinfo()), 2)
|
||||
assert [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
|
||||
|
||||
self.log.info("disconnectnode: successfully disconnect node by node id")
|
||||
id1 = self.nodes[0].getpeerinfo()[0]['id']
|
||||
self.nodes[0].disconnectnode(nodeid=id1)
|
||||
wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1)
|
||||
assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1]
|
||||
|
||||
if __name__ == '__main__':
|
||||
DisconnectBanTest().main()
|
@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test node handling."""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
import urllib.parse
|
||||
|
||||
class NodeHandlingTest (BitcoinTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 4
|
||||
self.setup_clean_chain = False
|
||||
|
||||
def run_test(self):
|
||||
###########################
|
||||
# setban/listbanned tests #
|
||||
###########################
|
||||
assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point
|
||||
self.nodes[2].setban("127.0.0.1", "add")
|
||||
time.sleep(3) #wait till the nodes are disconected
|
||||
assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
# This will throw an exception because 127.0.0.1 is within range 127.0.0.0/24
|
||||
assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[2].setban, "127.0.0.1", "add")
|
||||
# This will throw an exception because 127.0.0.1/42 is not a real subnet
|
||||
assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[2].setban, "127.0.0.1/42", "add")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
|
||||
# This will throw an exception because 127.0.0.1 was not added above
|
||||
assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[2].setban, "127.0.0.1", "remove")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
self.nodes[2].setban("127.0.0.0/24", "remove")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
|
||||
##test persisted banlist
|
||||
self.nodes[2].setban("127.0.0.0/32", "add")
|
||||
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
|
||||
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
|
||||
listBeforeShutdown = self.nodes[2].listbanned()
|
||||
assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here
|
||||
set_mocktime(get_mocktime() + 2) #make 100% sure we expired 192.168.0.1 node time
|
||||
set_node_times(self.nodes, get_mocktime()) #make 100% sure we expired 192.168.0.1 node time
|
||||
|
||||
#stop node
|
||||
stop_node(self.nodes[2], 2)
|
||||
|
||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
||||
listAfterShutdown = self.nodes[2].listbanned()
|
||||
assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
|
||||
assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
|
||||
assert_equal("/19" in listAfterShutdown[2]['address'], True)
|
||||
|
||||
###########################
|
||||
# RPC disconnectnode test #
|
||||
###########################
|
||||
url = urllib.parse.urlparse(self.nodes[1].url)
|
||||
self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
|
||||
time.sleep(2) #disconnecting a node needs a little bit of time
|
||||
for node in self.nodes[0].getpeerinfo():
|
||||
assert(node['addr'] != url.hostname+":"+str(p2p_port(1)))
|
||||
|
||||
connect_nodes_bi(self.nodes,0,1) #reconnect the node
|
||||
found = False
|
||||
for node in self.nodes[0].getpeerinfo():
|
||||
if node['addr'] == url.hostname+":"+str(p2p_port(1)):
|
||||
found = True
|
||||
assert(found)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NodeHandlingTest ().main ()
|
@ -75,7 +75,7 @@ BASE_SCRIPTS= [
|
||||
'multi_rpc.py',
|
||||
'proxy_test.py',
|
||||
'signrawtransactions.py',
|
||||
'nodehandling.py',
|
||||
'disconnect_ban.py',
|
||||
'addressindex.py',
|
||||
'timestampindex.py',
|
||||
'spentindex.py',
|
||||
@ -140,7 +140,8 @@ EXTENDED_SCRIPTS = [
|
||||
'p2p-acceptblock.py', # NOTE: needs dash_hash to pass
|
||||
]
|
||||
|
||||
ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS
|
||||
# Place EXTENDED_SCRIPTS first since it has the 3 longest running tests
|
||||
ALL_SCRIPTS = EXTENDED_SCRIPTS + BASE_SCRIPTS + ZMQ_SCRIPTS
|
||||
|
||||
NON_SCRIPTS = [
|
||||
# These are python files that live in the functional tests directory, but are not test scripts.
|
||||
@ -212,10 +213,9 @@ def main():
|
||||
if enable_zmq:
|
||||
test_list += ZMQ_SCRIPTS
|
||||
if args.extended:
|
||||
test_list += EXTENDED_SCRIPTS
|
||||
# TODO: BASE_SCRIPTS and EXTENDED_SCRIPTS are sorted by runtime
|
||||
# (for parallel running efficiency). This combined list will is no
|
||||
# longer sorted.
|
||||
# place the EXTENDED_SCRIPTS first since the three longest ones
|
||||
# are there and the list is shorter
|
||||
test_list = EXTENDED_SCRIPTS + test_list
|
||||
|
||||
# Remove the test cases that the user has explicitly asked to exclude.
|
||||
if args.exclude:
|
||||
|
Loading…
Reference in New Issue
Block a user