Skip to content

Commit

Permalink
Change CPolicy's interface for putting isolated node policy code and …
Browse files Browse the repository at this point in the history
…parameters on

Initially populated with policy-specific code moved from AcceptToMemoryPool.
  • Loading branch information
luke-jr authored and jtimon committed Jan 12, 2015
1 parent ce76848 commit 0f69efa
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 70 deletions.
84 changes: 14 additions & 70 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -740,13 +740,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),
REJECT_INVALID, "coinbase");

// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
if (!Policy().CheckTxPreInputs(tx, reason))
return state.DoS(0,
error("AcceptToMemoryPool : nonstandard transaction: %s", reason),
REJECT_NONSTANDARD, reason);

// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
Expand All @@ -772,18 +765,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if (pool.exists(hash))
return false;

// Check for conflicts with in-memory transactions
if (pool.lookupConflicts(tx, NULL))
{
// Disable replacement feature for now
if (!Policy().AcceptTxPoolPreInputs(pool, state, tx))
return false;
}

{
CCoinsView dummy;
CCoinsViewCache view(&dummy);

CAmount nValueIn = 0;
{
LOCK(pool.cs);
CCoinsViewMemPool viewMemPool(pcoinsTip, pool);
Expand Down Expand Up @@ -812,72 +800,28 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Bring the best block into scope
view.GetBestBlock();

nValueIn = view.GetValueIn(tx);

// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
view.SetBackend(dummy);
}

// Check for non-standard pay-to-script-hash in inputs
if (!Policy().CheckTxWithInputs(tx, view))
return error("AcceptToMemoryPool: nonstandard transaction input");

// Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction
// itself can contain sigops MAX_TX_SIGOPS is less than
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
// merely non-standard transaction.
unsigned int nSigOps = GetLegacySigOpCount(tx);
nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_TX_SIGOPS)
return state.DoS(0,
error("AcceptToMemoryPool : too many sigops %s, %d > %d",
hash.ToString(), nSigOps, MAX_TX_SIGOPS),
REJECT_NONSTANDARD, "bad-txns-too-many-sigops");

CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut;
if (!Policy().AcceptTxWithInputs(pool, state, tx, view))
return false;

CAmount nFees = view.GetTxFees(tx);
double dPriority = view.GetPriority(tx, chainActive.Height());

CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height());
unsigned int nSize = entry.GetTxSize();

// Don't accept it if it can't get into a block
CAmount txMinFee = Policy().GetMinRelayFee(tx, nSize, true);
if (fLimitFree && nFees < txMinFee)
return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d",
hash.ToString(), nFees, txMinFee),
REJECT_INSUFFICIENTFEE, "insufficient fee");

// Require that free transactions have sufficient priority to be mined in the next block.
if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
}

// Continuously rate-limit free (really, very-low-fee) transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
{
static CCriticalSection csFreeLimiter;
static double dFreeCount;
static int64_t nLastTime;
int64_t nNow = GetTime();

LOCK(csFreeLimiter);

// Use an exponentially decaying ~10-minute window:
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"),
REJECT_INSUFFICIENTFEE, "rate limited free transaction");
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
}
bool fRateLimit = false;
// Policy().AcceptMemPoolEntry is expected to set fRateLimit (passed by reference) if it wants to
if (!Policy().AcceptMemPoolEntry(pool, state, entry, view, fRateLimit))
return false;
if (!fLimitFree)
fRateLimit = false;
if (fRateLimit && !Policy().RateLimitTx(pool, state, entry, view))
return false;

unsigned int nSize = entry.GetTxSize();
if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000)
return error("AcceptToMemoryPool: insane fees %s, %d > %d",
hash.ToString(),
Expand Down
93 changes: 93 additions & 0 deletions src/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "util.h"
#include "utilmoneystr.h"

#include <cmath>

#include <boost/foreach.hpp>

static bool fIsBareMultisigStd = true;
Expand All @@ -41,6 +43,10 @@ class CStandardPolicy : public CPolicy
*/
virtual bool CheckTxWithInputs(const CTransaction& tx, const CCoinsViewCache& mapInputs) const;
virtual CAmount GetMinRelayFee(const CTransaction&, unsigned int nBytes, bool fAllowFree) const;
virtual bool AcceptTxPoolPreInputs(CTxMemPool&, CValidationState&, const CTransaction&) const;
virtual bool AcceptTxWithInputs(CTxMemPool&, CValidationState&, const CTransaction&, CCoinsViewCache&) const;
virtual bool AcceptMemPoolEntry(CTxMemPool&, CValidationState&, CTxMemPoolEntry&, CCoinsViewCache&, bool& fRateLimit) const;
virtual bool RateLimitTx(CTxMemPool&, CValidationState&, CTxMemPoolEntry&, CCoinsViewCache&) const;
};

/** Default Policy for testnet and regtest */
Expand Down Expand Up @@ -315,3 +321,90 @@ CAmount CStandardPolicy::GetMinRelayFee(const CTransaction& tx, unsigned int nBy
nMinFee = MAX_MONEY;
return nMinFee;
}

bool CStandardPolicy::AcceptTxPoolPreInputs(CTxMemPool& pool, CValidationState& state, const CTransaction& tx) const
{
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason;
if (!CheckTxPreInputs(tx, reason))
return state.DoS(0,
error("%s : nonstandard transaction: %s", __func__, reason),
REJECT_NONSTANDARD, reason);

// Check for conflicts with in-memory transactions
if (pool.lookupConflicts(tx, NULL))
{
// Disable replacement feature for now
return false;
}

return true;
}

bool CStandardPolicy::AcceptTxWithInputs(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, CCoinsViewCache& view) const
{
// Check for non-standard pay-to-script-hash in inputs
if (!CheckTxWithInputs(tx, view))
return error("%s : nonstandard transaction input", __func__);

// Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction
// itself can contain sigops MAX_TX_SIGOPS is less than
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
// merely non-standard transaction.
unsigned int nSigOps = GetLegacySigOpCount(tx);
nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_TX_SIGOPS)
return state.DoS(0,
error("%s : too many sigops %s, %d > %d",
__func__, tx.GetHash().ToString(), nSigOps, MAX_TX_SIGOPS),
REJECT_NONSTANDARD, "bad-txns-too-many-sigops");

return true;
}

bool CStandardPolicy::AcceptMemPoolEntry(CTxMemPool& pool, CValidationState& state, CTxMemPoolEntry& entry, CCoinsViewCache& view, bool& fRateLimit) const
{
const CTransaction& tx = entry.GetTx();

CAmount nFees = entry.GetFee();
unsigned int nSize = entry.GetTxSize();

// Don't accept it if it can't get into a block
CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
if (nFees < txMinFee)
return state.DoS(0, error("%s : not enough fees %s, %d < %d",
__func__, tx.GetHash().ToString(), nFees, txMinFee),
REJECT_INSUFFICIENTFEE, "insufficient fee");

// Continuously rate-limit free (really, very-low-fee)transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
fRateLimit = (nFees < ::minRelayTxFee.GetFee(nSize));

return true;
}

bool CStandardPolicy::RateLimitTx(CTxMemPool& pool, CValidationState& state, CTxMemPoolEntry& entry, CCoinsViewCache& view) const
{
static CCriticalSection csFreeLimiter;
static double dFreeCount;
static int64_t nLastTime;
int64_t nNow = GetTime();

LOCK(csFreeLimiter);

// Use an exponentially decaying ~10-minute window:
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
return state.DoS(0, error("%s : free transaction rejected by rate limiter", __func__),
REJECT_INSUFFICIENTFEE, "insufficient priority");
unsigned int nSize = entry.GetTxSize();
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;

return true;
}
7 changes: 7 additions & 0 deletions src/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
class CCoinsViewCache;
class CFeeRate;
class CTransaction;
class CTxMemPool;
class CTxMemPoolEntry;
class CTxOut;
class CValidationState;

/** The maximum size for transactions we're willing to relay/mine */
static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
Expand All @@ -41,6 +44,10 @@ class CPolicy
*/
virtual bool CheckTxWithInputs(const CTransaction& tx, const CCoinsViewCache& mapInputs) const = 0;
virtual CAmount GetMinRelayFee(const CTransaction&, unsigned int nBytes, bool fAllowFree) const = 0;
virtual bool AcceptTxPoolPreInputs(CTxMemPool&, CValidationState&, const CTransaction&) const = 0;
virtual bool AcceptTxWithInputs(CTxMemPool&, CValidationState&, const CTransaction&, CCoinsViewCache&) const = 0;
virtual bool AcceptMemPoolEntry(CTxMemPool&, CValidationState&, CTxMemPoolEntry&, CCoinsViewCache&, bool& fRateLimit) const = 0;
virtual bool RateLimitTx(CTxMemPool&, CValidationState&, CTxMemPoolEntry&, CCoinsViewCache&) const = 0;
};

void SelectPolicy(std::string policyType);
Expand Down

0 comments on commit 0f69efa

Please sign in to comment.