Implement "feefilter" P2P message.
The "feefilter" p2p message is used to inform other nodes of your mempool min fee which is the feerate that any new transaction must meet to be accepted to your mempool. This will allow them to filter invs to you according to this feerate.
This commit is contained in:
parent
29b2be6ad7
commit
9e072a6e66
15 changed files with 152 additions and 32 deletions
|
@ -330,6 +330,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
}
|
}
|
||||||
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
|
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
|
||||||
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
|
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
|
||||||
|
strUsage += HelpMessageOpt("-feefilter", strprintf(_("Tell other nodes to filter invs to us by our mempool min fee (default: %u)"), DEFAULT_FEEFILTER));
|
||||||
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"));
|
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"));
|
||||||
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
|
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
|
||||||
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
|
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
|
||||||
|
|
73
src/main.cpp
73
src/main.cpp
|
@ -17,10 +17,12 @@
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "merkleblock.h"
|
#include "merkleblock.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
#include "policy/fees.h"
|
||||||
#include "policy/policy.h"
|
#include "policy/policy.h"
|
||||||
#include "pow.h"
|
#include "pow.h"
|
||||||
#include "primitives/block.h"
|
#include "primitives/block.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
#include "random.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "script/sigcache.h"
|
#include "script/sigcache.h"
|
||||||
#include "script/standard.h"
|
#include "script/standard.h"
|
||||||
|
@ -81,6 +83,7 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
||||||
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
|
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
|
||||||
|
|
||||||
CTxMemPool mempool(::minRelayTxFee);
|
CTxMemPool mempool(::minRelayTxFee);
|
||||||
|
FeeFilterRounder filterRounder(::minRelayTxFee);
|
||||||
|
|
||||||
struct COrphanTx {
|
struct COrphanTx {
|
||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
|
@ -987,7 +990,7 @@ std::string FormatStateMessage(const CValidationState &state)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree,
|
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree,
|
||||||
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee,
|
bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
|
||||||
std::vector<uint256>& vHashTxnToUncache)
|
std::vector<uint256>& vHashTxnToUncache)
|
||||||
{
|
{
|
||||||
const uint256 hash = tx.GetHash();
|
const uint256 hash = tx.GetHash();
|
||||||
|
@ -1144,6 +1147,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||||
|
|
||||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp);
|
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp);
|
||||||
unsigned int nSize = entry.GetTxSize();
|
unsigned int nSize = entry.GetTxSize();
|
||||||
|
if (txFeeRate) {
|
||||||
|
*txFeeRate = CFeeRate(nFees, nSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the transaction doesn't have an excessive number of
|
// Check that the transaction doesn't have an excessive number of
|
||||||
// sigops, making it impossible to mine. Since the coinbase transaction
|
// sigops, making it impossible to mine. Since the coinbase transaction
|
||||||
|
@ -1392,10 +1398,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
||||||
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
|
bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
|
||||||
{
|
{
|
||||||
std::vector<uint256> vHashTxToUncache;
|
std::vector<uint256> vHashTxToUncache;
|
||||||
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
|
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
|
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
|
||||||
pcoinsTip->Uncache(hashTx);
|
pcoinsTip->Uncache(hashTx);
|
||||||
|
@ -2620,7 +2626,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons
|
||||||
// ignore validation errors in resurrected transactions
|
// ignore validation errors in resurrected transactions
|
||||||
list<CTransaction> removed;
|
list<CTransaction> removed;
|
||||||
CValidationState stateDummy;
|
CValidationState stateDummy;
|
||||||
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
|
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, NULL, true)) {
|
||||||
mempool.removeRecursive(tx, removed);
|
mempool.removeRecursive(tx, removed);
|
||||||
} else if (mempool.exists(tx.GetHash())) {
|
} else if (mempool.exists(tx.GetHash())) {
|
||||||
vHashUpdate.push_back(tx.GetHash());
|
vHashUpdate.push_back(tx.GetHash());
|
||||||
|
@ -4916,10 +4922,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
pfrom->setAskFor.erase(inv.hash);
|
pfrom->setAskFor.erase(inv.hash);
|
||||||
mapAlreadyAskedFor.erase(inv);
|
mapAlreadyAskedFor.erase(inv);
|
||||||
|
|
||||||
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
|
CFeeRate txFeeRate = CFeeRate(0);
|
||||||
{
|
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate)) {
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(pcoinsTip);
|
||||||
RelayTransaction(tx);
|
RelayTransaction(tx, txFeeRate);
|
||||||
vWorkQueue.push_back(inv.hash);
|
vWorkQueue.push_back(inv.hash);
|
||||||
|
|
||||||
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
|
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
|
||||||
|
@ -4950,10 +4956,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
|
|
||||||
if (setMisbehaving.count(fromPeer))
|
if (setMisbehaving.count(fromPeer))
|
||||||
continue;
|
continue;
|
||||||
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2))
|
CFeeRate orphanFeeRate = CFeeRate(0);
|
||||||
{
|
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2, &orphanFeeRate)) {
|
||||||
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
|
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
|
||||||
RelayTransaction(orphanTx);
|
RelayTransaction(orphanTx, orphanFeeRate);
|
||||||
vWorkQueue.push_back(orphanHash);
|
vWorkQueue.push_back(orphanHash);
|
||||||
vEraseQueue.push_back(orphanHash);
|
vEraseQueue.push_back(orphanHash);
|
||||||
}
|
}
|
||||||
|
@ -5006,7 +5012,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
int nDoS = 0;
|
int nDoS = 0;
|
||||||
if (!state.IsInvalid(nDoS) || nDoS == 0) {
|
if (!state.IsInvalid(nDoS) || nDoS == 0) {
|
||||||
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
|
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
|
||||||
RelayTransaction(tx);
|
RelayTransaction(tx, txFeeRate);
|
||||||
} else {
|
} else {
|
||||||
LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
|
LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
|
||||||
}
|
}
|
||||||
|
@ -5200,6 +5206,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
if (!fInMemPool) continue; // another thread removed since queryHashes, maybe...
|
if (!fInMemPool) continue; // another thread removed since queryHashes, maybe...
|
||||||
if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue;
|
if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue;
|
||||||
}
|
}
|
||||||
|
if (pfrom->minFeeFilter) {
|
||||||
|
CFeeRate feeRate;
|
||||||
|
mempool.lookupFeeRate(hash, feeRate);
|
||||||
|
LOCK(pfrom->cs_feeFilter);
|
||||||
|
if (feeRate.GetFeePerK() < pfrom->minFeeFilter)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
vInv.push_back(inv);
|
vInv.push_back(inv);
|
||||||
if (vInv.size() == MAX_INV_SZ) {
|
if (vInv.size() == MAX_INV_SZ) {
|
||||||
pfrom->PushMessage(NetMsgType::INV, vInv);
|
pfrom->PushMessage(NetMsgType::INV, vInv);
|
||||||
|
@ -5362,8 +5375,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else if (strCommand == NetMsgType::FEEFILTER) {
|
||||||
{
|
CAmount newFeeFilter = 0;
|
||||||
|
vRecv >> newFeeFilter;
|
||||||
|
if (MoneyRange(newFeeFilter)) {
|
||||||
|
{
|
||||||
|
LOCK(pfrom->cs_feeFilter);
|
||||||
|
pfrom->minFeeFilter = newFeeFilter;
|
||||||
|
}
|
||||||
|
LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
// Ignore unknown commands for extensibility
|
// Ignore unknown commands for extensibility
|
||||||
LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
|
LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
|
||||||
}
|
}
|
||||||
|
@ -5845,6 +5869,29 @@ bool SendMessages(CNode* pto)
|
||||||
if (!vGetData.empty())
|
if (!vGetData.empty())
|
||||||
pto->PushMessage(NetMsgType::GETDATA, vGetData);
|
pto->PushMessage(NetMsgType::GETDATA, vGetData);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Message: feefilter
|
||||||
|
//
|
||||||
|
// We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
|
||||||
|
if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
|
||||||
|
!(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
|
||||||
|
CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
|
||||||
|
int64_t timeNow = GetTimeMicros();
|
||||||
|
if (timeNow > pto->nextSendTimeFeeFilter) {
|
||||||
|
CAmount filterToSend = filterRounder.round(currentFilter);
|
||||||
|
if (filterToSend != pto->lastSentFeeFilter) {
|
||||||
|
pto->PushMessage(NetMsgType::FEEFILTER, filterToSend);
|
||||||
|
pto->lastSentFeeFilter = filterToSend;
|
||||||
|
}
|
||||||
|
pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
|
||||||
|
}
|
||||||
|
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
|
||||||
|
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
|
||||||
|
else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
|
||||||
|
(currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
|
||||||
|
pto->nextSendTimeFeeFilter = timeNow + (insecure_rand() % MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,10 @@ static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
|
||||||
/** Average delay between trickled inventory broadcasts in seconds.
|
/** Average delay between trickled inventory broadcasts in seconds.
|
||||||
* Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */
|
* Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */
|
||||||
static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5;
|
static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5;
|
||||||
|
/** Average delay between feefilter broadcasts in seconds. */
|
||||||
|
static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
|
||||||
|
/** Maximum feefilter broadcast delay after significant change. */
|
||||||
|
static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
|
||||||
|
|
||||||
static const unsigned int DEFAULT_LIMITFREERELAY = 15;
|
static const unsigned int DEFAULT_LIMITFREERELAY = 15;
|
||||||
static const bool DEFAULT_RELAYPRIORITY = true;
|
static const bool DEFAULT_RELAYPRIORITY = true;
|
||||||
|
@ -117,6 +121,8 @@ static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||||
static const bool DEFAULT_TESTSAFEMODE = false;
|
static const bool DEFAULT_TESTSAFEMODE = false;
|
||||||
/** Default for -mempoolreplacement */
|
/** Default for -mempoolreplacement */
|
||||||
static const bool DEFAULT_ENABLE_REPLACEMENT = true;
|
static const bool DEFAULT_ENABLE_REPLACEMENT = true;
|
||||||
|
/** Default for using fee filter */
|
||||||
|
static const bool DEFAULT_FEEFILTER = true;
|
||||||
|
|
||||||
/** Maximum number of headers to announce when relaying blocks with headers message.*/
|
/** Maximum number of headers to announce when relaying blocks with headers message.*/
|
||||||
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
|
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
|
||||||
|
@ -282,7 +288,7 @@ void PruneAndFlush();
|
||||||
|
|
||||||
/** (try to) add transaction to memory pool **/
|
/** (try to) add transaction to memory pool **/
|
||||||
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
||||||
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
|
bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
|
||||||
|
|
||||||
/** Convert CValidationState to a human-readable message for logging */
|
/** Convert CValidationState to a human-readable message for logging */
|
||||||
std::string FormatStateMessage(const CValidationState &state);
|
std::string FormatStateMessage(const CValidationState &state);
|
||||||
|
|
20
src/net.cpp
20
src/net.cpp
|
@ -2053,20 +2053,15 @@ public:
|
||||||
instance_of_cnetcleanup;
|
instance_of_cnetcleanup;
|
||||||
|
|
||||||
|
|
||||||
|
void RelayTransaction(const CTransaction& tx, CFeeRate feerate)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void RelayTransaction(const CTransaction& tx)
|
|
||||||
{
|
{
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ss.reserve(10000);
|
ss.reserve(10000);
|
||||||
ss << tx;
|
ss << tx;
|
||||||
RelayTransaction(tx, ss);
|
RelayTransaction(tx, feerate, ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
|
void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStream& ss)
|
||||||
{
|
{
|
||||||
CInv inv(MSG_TX, tx.GetHash());
|
CInv inv(MSG_TX, tx.GetHash());
|
||||||
{
|
{
|
||||||
|
@ -2087,6 +2082,11 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
|
||||||
{
|
{
|
||||||
if(!pnode->fRelayTxes)
|
if(!pnode->fRelayTxes)
|
||||||
continue;
|
continue;
|
||||||
|
{
|
||||||
|
LOCK(pnode->cs_feeFilter);
|
||||||
|
if (feerate.GetFeePerK() < pnode->minFeeFilter)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
LOCK(pnode->cs_filter);
|
LOCK(pnode->cs_filter);
|
||||||
if (pnode->pfilter)
|
if (pnode->pfilter)
|
||||||
{
|
{
|
||||||
|
@ -2390,6 +2390,10 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
|
||||||
nPingUsecTime = 0;
|
nPingUsecTime = 0;
|
||||||
fPingQueued = false;
|
fPingQueued = false;
|
||||||
nMinPingUsecTime = std::numeric_limits<int64_t>::max();
|
nMinPingUsecTime = std::numeric_limits<int64_t>::max();
|
||||||
|
minFeeFilter = 0;
|
||||||
|
lastSentFeeFilter = 0;
|
||||||
|
nextSendTimeFeeFilter = 0;
|
||||||
|
|
||||||
BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes())
|
BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes())
|
||||||
mapRecvBytesPerMsgCmd[msg] = 0;
|
mapRecvBytesPerMsgCmd[msg] = 0;
|
||||||
mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
|
mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
|
||||||
|
|
10
src/net.h
10
src/net.h
|
@ -6,6 +6,7 @@
|
||||||
#ifndef BITCOIN_NET_H
|
#ifndef BITCOIN_NET_H
|
||||||
#define BITCOIN_NET_H
|
#define BITCOIN_NET_H
|
||||||
|
|
||||||
|
#include "amount.h"
|
||||||
#include "bloom.h"
|
#include "bloom.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "limitedmap.h"
|
#include "limitedmap.h"
|
||||||
|
@ -415,6 +416,11 @@ public:
|
||||||
int64_t nMinPingUsecTime;
|
int64_t nMinPingUsecTime;
|
||||||
// Whether a ping is requested.
|
// Whether a ping is requested.
|
||||||
bool fPingQueued;
|
bool fPingQueued;
|
||||||
|
// Minimum fee rate with which to filter inv's to this node
|
||||||
|
CAmount minFeeFilter;
|
||||||
|
CCriticalSection cs_feeFilter;
|
||||||
|
CAmount lastSentFeeFilter;
|
||||||
|
int64_t nextSendTimeFeeFilter;
|
||||||
|
|
||||||
CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false);
|
CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false);
|
||||||
~CNode();
|
~CNode();
|
||||||
|
@ -766,8 +772,8 @@ public:
|
||||||
|
|
||||||
|
|
||||||
class CTransaction;
|
class CTransaction;
|
||||||
void RelayTransaction(const CTransaction& tx);
|
void RelayTransaction(const CTransaction& tx, CFeeRate feerate);
|
||||||
void RelayTransaction(const CTransaction& tx, const CDataStream& ss);
|
void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStream& ss);
|
||||||
|
|
||||||
/** Access to the (IP) address database (peers.dat) */
|
/** Access to the (IP) address database (peers.dat) */
|
||||||
class CAddrDB
|
class CAddrDB
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
#include "random.h"
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "txmempool.h"
|
#include "txmempool.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -580,3 +581,21 @@ void CBlockPolicyEstimator::Read(CAutoFile& filein)
|
||||||
priStats.Read(filein);
|
priStats.Read(filein);
|
||||||
nBestSeenHeight = nFileBestSeenHeight;
|
nBestSeenHeight = nFileBestSeenHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
|
||||||
|
{
|
||||||
|
CAmount minFeeLimit = minIncrementalFee.GetFeePerK() / 2;
|
||||||
|
feeset.insert(0);
|
||||||
|
for (double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) {
|
||||||
|
feeset.insert(bucketBoundary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CAmount FeeFilterRounder::round(CAmount currentMinFee)
|
||||||
|
{
|
||||||
|
std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
|
||||||
|
if ((it != feeset.begin() && insecure_rand() % 3 != 0) || it == feeset.end()) {
|
||||||
|
it--;
|
||||||
|
}
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
|
@ -286,4 +286,17 @@ private:
|
||||||
CFeeRate feeLikely, feeUnlikely;
|
CFeeRate feeLikely, feeUnlikely;
|
||||||
double priLikely, priUnlikely;
|
double priLikely, priUnlikely;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FeeFilterRounder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Create new FeeFilterRounder */
|
||||||
|
FeeFilterRounder(const CFeeRate& minIncrementalFee);
|
||||||
|
|
||||||
|
/** Quantize a minimum fee for privacy purpose before broadcast **/
|
||||||
|
CAmount round(CAmount currentMinFee);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::set<double> feeset;
|
||||||
|
};
|
||||||
#endif /*BITCOIN_POLICYESTIMATOR_H */
|
#endif /*BITCOIN_POLICYESTIMATOR_H */
|
||||||
|
|
|
@ -34,6 +34,7 @@ const char *FILTERADD="filteradd";
|
||||||
const char *FILTERCLEAR="filterclear";
|
const char *FILTERCLEAR="filterclear";
|
||||||
const char *REJECT="reject";
|
const char *REJECT="reject";
|
||||||
const char *SENDHEADERS="sendheaders";
|
const char *SENDHEADERS="sendheaders";
|
||||||
|
const char *FEEFILTER="feefilter";
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* ppszTypeName[] =
|
static const char* ppszTypeName[] =
|
||||||
|
@ -68,7 +69,8 @@ const static std::string allNetMessageTypes[] = {
|
||||||
NetMsgType::FILTERADD,
|
NetMsgType::FILTERADD,
|
||||||
NetMsgType::FILTERCLEAR,
|
NetMsgType::FILTERCLEAR,
|
||||||
NetMsgType::REJECT,
|
NetMsgType::REJECT,
|
||||||
NetMsgType::SENDHEADERS
|
NetMsgType::SENDHEADERS,
|
||||||
|
NetMsgType::FEEFILTER
|
||||||
};
|
};
|
||||||
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
|
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,12 @@ extern const char *REJECT;
|
||||||
* @see https://bitcoin.org/en/developer-reference#sendheaders
|
* @see https://bitcoin.org/en/developer-reference#sendheaders
|
||||||
*/
|
*/
|
||||||
extern const char *SENDHEADERS;
|
extern const char *SENDHEADERS;
|
||||||
|
/**
|
||||||
|
* The feefilter message tells the receiving peer not to inv us any txs
|
||||||
|
* which do not meet the specified min fee rate.
|
||||||
|
* @since protocol version 70013 as described by BIP133
|
||||||
|
*/
|
||||||
|
extern const char *FEEFILTER;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Get a vector of all valid message types (see above) */
|
/* Get a vector of all valid message types (see above) */
|
||||||
|
|
|
@ -818,11 +818,12 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
|
||||||
const CCoins* existingCoins = view.AccessCoins(hashTx);
|
const CCoins* existingCoins = view.AccessCoins(hashTx);
|
||||||
bool fHaveMempool = mempool.exists(hashTx);
|
bool fHaveMempool = mempool.exists(hashTx);
|
||||||
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
|
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
|
||||||
|
CFeeRate txFeeRate = CFeeRate(0);
|
||||||
if (!fHaveMempool && !fHaveChain) {
|
if (!fHaveMempool && !fHaveChain) {
|
||||||
// push to local node and sync with wallets
|
// push to local node and sync with wallets
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
bool fMissingInputs;
|
bool fMissingInputs;
|
||||||
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, nMaxRawTxFee)) {
|
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, &txFeeRate, false, nMaxRawTxFee)) {
|
||||||
if (state.IsInvalid()) {
|
if (state.IsInvalid()) {
|
||||||
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
|
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -835,7 +836,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
|
||||||
} else if (fHaveChain) {
|
} else if (fHaveChain) {
|
||||||
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
|
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
|
||||||
}
|
}
|
||||||
RelayTransaction(tx);
|
RelayTransaction(tx, txFeeRate);
|
||||||
|
|
||||||
return hashTx.GetHex();
|
return hashTx.GetHex();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx)
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
return AcceptToMemoryPool(mempool, state, tx, false, NULL, true, 0);
|
return AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
||||||
|
|
|
@ -771,6 +771,16 @@ bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
indexed_transaction_set::const_iterator i = mapTx.find(hash);
|
||||||
|
if (i == mapTx.end())
|
||||||
|
return false;
|
||||||
|
feeRate = CFeeRate(i->GetFee(), i->GetTxSize());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
|
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|
|
@ -600,6 +600,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lookup(uint256 hash, CTransaction& result) const;
|
bool lookup(uint256 hash, CTransaction& result) const;
|
||||||
|
bool lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const;
|
||||||
|
|
||||||
/** Estimate fee rate needed to get into the next nBlocks
|
/** Estimate fee rate needed to get into the next nBlocks
|
||||||
* If no answer can be given at nBlocks, return an estimate
|
* If no answer can be given at nBlocks, return an estimate
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* network protocol versioning
|
* network protocol versioning
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const int PROTOCOL_VERSION = 70012;
|
static const int PROTOCOL_VERSION = 70013;
|
||||||
|
|
||||||
//! initial proto version, to be increased after version/verack negotiation
|
//! initial proto version, to be increased after version/verack negotiation
|
||||||
static const int INIT_PROTO_VERSION = 209;
|
static const int INIT_PROTO_VERSION = 209;
|
||||||
|
@ -36,4 +36,7 @@ static const int NO_BLOOM_VERSION = 70011;
|
||||||
//! "sendheaders" command and announcing blocks with headers starts with this version
|
//! "sendheaders" command and announcing blocks with headers starts with this version
|
||||||
static const int SENDHEADERS_VERSION = 70012;
|
static const int SENDHEADERS_VERSION = 70012;
|
||||||
|
|
||||||
|
//! "feefilter" tells peers to filter invs to you by fee starts with this version
|
||||||
|
static const int FEEFILTER_VERSION = 70013;
|
||||||
|
|
||||||
#endif // BITCOIN_VERSION_H
|
#endif // BITCOIN_VERSION_H
|
||||||
|
|
|
@ -1268,7 +1268,9 @@ bool CWalletTx::RelayWalletTransaction()
|
||||||
{
|
{
|
||||||
if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) {
|
if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) {
|
||||||
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
|
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
|
||||||
RelayTransaction((CTransaction)*this);
|
CFeeRate feeRate;
|
||||||
|
mempool.lookupFeeRate(GetHash(), feeRate);
|
||||||
|
RelayTransaction((CTransaction)*this, feeRate);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3231,5 +3233,5 @@ int CMerkleTx::GetBlocksToMaturity() const
|
||||||
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee)
|
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee)
|
||||||
{
|
{
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee);
|
return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, NULL, false, nAbsurdFee);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue