BIP141: Other consensus critical limits, and BIP145

Includes changes by Suhas Daftuar, Luke-jr, and mruddy.
This commit is contained in:
Pieter Wuille 2016-01-03 18:54:50 +01:00
parent 7c4bf779e8
commit 2b1f6f9ccf
31 changed files with 344 additions and 136 deletions

View File

@ -97,7 +97,7 @@ class MaxUploadTest(BitcoinTestFramework):
def setup_network(self): def setup_network(self):
# Start a node with maxuploadtarget of 200 MB (/24h) # Start a node with maxuploadtarget of 200 MB (/24h)
self.nodes = [] self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-maxuploadtarget=200", "-blockmaxsize=999000"])) self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-maxuploadtarget=800", "-blockmaxsize=999000"]))
def mine_full_block(self, node, address): def mine_full_block(self, node, address):
# Want to create a full block # Want to create a full block
@ -175,13 +175,13 @@ class MaxUploadTest(BitcoinTestFramework):
getdata_request = msg_getdata() getdata_request = msg_getdata()
getdata_request.inv.append(CInv(2, big_old_block)) getdata_request.inv.append(CInv(2, big_old_block))
max_bytes_per_day = 200*1024*1024 max_bytes_per_day = 800*1024*1024
daily_buffer = 144 * MAX_BLOCK_SIZE daily_buffer = 144 * 4000000
max_bytes_available = max_bytes_per_day - daily_buffer max_bytes_available = max_bytes_per_day - daily_buffer
success_count = max_bytes_available // old_block_size success_count = max_bytes_available // old_block_size
# 144MB will be reserved for relaying new blocks, so expect this to # 576MB will be reserved for relaying new blocks, so expect this to
# succeed for ~70 tries. # succeed for ~235 tries.
for i in range(success_count): for i in range(success_count):
test_nodes[0].send_message(getdata_request) test_nodes[0].send_message(getdata_request)
test_nodes[0].sync_with_ping() test_nodes[0].sync_with_ping()
@ -198,9 +198,9 @@ class MaxUploadTest(BitcoinTestFramework):
# Requesting the current block on test_nodes[1] should succeed indefinitely, # Requesting the current block on test_nodes[1] should succeed indefinitely,
# even when over the max upload target. # even when over the max upload target.
# We'll try 200 times # We'll try 800 times
getdata_request.inv = [CInv(2, big_new_block)] getdata_request.inv = [CInv(2, big_new_block)]
for i in range(200): for i in range(800):
test_nodes[1].send_message(getdata_request) test_nodes[1].send_message(getdata_request)
test_nodes[1].sync_with_ping() test_nodes[1].sync_with_ping()
assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1) assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)

View File

@ -195,7 +195,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
uint256 txid(uint256S(strTxid)); uint256 txid(uint256S(strTxid));
static const unsigned int minTxOutSz = 9; static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz; static const unsigned int maxVout = MAX_BLOCK_BASE_SIZE / minTxOutSz;
// extract and validate vout // extract and validate vout
string strVout = vStrInputParts[1]; string strVout = vStrInputParts[1];

View File

@ -15,7 +15,7 @@
#include <unordered_map> #include <unordered_map>
#define MIN_TRANSACTION_SIZE (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION)) #define MIN_TRANSACTION_BASE_SIZE (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS))
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) : CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) :
nonce(GetRand(std::numeric_limits<uint64_t>::max())), nonce(GetRand(std::numeric_limits<uint64_t>::max())),
@ -50,7 +50,7 @@ uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256& txhash) const {
ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock) { ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock) {
if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty())) if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
return READ_STATUS_INVALID; return READ_STATUS_INVALID;
if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_SIZE / MIN_TRANSACTION_SIZE) if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_BASE_SIZE / MIN_TRANSACTION_BASE_SIZE)
return READ_STATUS_INVALID; return READ_STATUS_INVALID;
assert(header.IsNull() && txn_available.empty()); assert(header.IsNull() && txn_available.empty());

View File

@ -6,10 +6,16 @@
#ifndef BITCOIN_CONSENSUS_CONSENSUS_H #ifndef BITCOIN_CONSENSUS_CONSENSUS_H
#define BITCOIN_CONSENSUS_CONSENSUS_H #define BITCOIN_CONSENSUS_CONSENSUS_H
/** The maximum allowed size for a serialized block, in bytes (network rule) */ #include <stdint.h>
static const unsigned int MAX_BLOCK_SIZE = 1000000;
/** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */
static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000;
/** The maximum allowed cost for a block, see BIP 141 (network rule) */
static const unsigned int MAX_BLOCK_COST = 4000000;
/** The maximum allowed size for a block excluding witness data, in bytes (network rule) */
static const unsigned int MAX_BLOCK_BASE_SIZE = 1000000;
/** The maximum allowed number of signature check operations in a block (network rule) */ /** The maximum allowed number of signature check operations in a block (network rule) */
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const int64_t MAX_BLOCK_SIGOPS_COST = 80000;
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
static const int COINBASE_MATURITY = 100; static const int COINBASE_MATURITY = 100;

View File

@ -452,6 +452,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT)); strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT));
strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageGroup(_("Block creation options:"));
strUsage += HelpMessageOpt("-blockmaxcost=<n>", strprintf(_("Set maximum block cost (default: %d)"), DEFAULT_BLOCK_MAX_COST));
strUsage += HelpMessageOpt("-blockminsize=<n>", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE)); strUsage += HelpMessageOpt("-blockminsize=<n>", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE));
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));

View File

@ -684,8 +684,8 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c
// have been mined or received. // have been mined or received.
// 100 orphans, each of which is at most 99,999 bytes big is // 100 orphans, each of which is at most 99,999 bytes big is
// at most 10 megabytes of orphans and somewhat more byprev index (in the worst case): // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); unsigned int sz = GetTransactionCost(tx);
if (sz >= MAX_STANDARD_TX_SIZE) if (sz >= MAX_STANDARD_TX_COST)
{ {
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
return false; return false;
@ -1018,8 +1018,24 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
return nSigOps; return nSigOps;
} }
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags)
{
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
if (tx.IsCoinBase())
return nSigOps;
if (flags & SCRIPT_VERIFY_P2SH) {
nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR;
}
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]);
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, i < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[i].scriptWitness : NULL, flags);
}
return nSigOps;
}
@ -1033,7 +1049,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
if (tx.vout.empty()) if (tx.vout.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE) if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values // Check for negative or overflow output values
@ -1239,8 +1255,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
if (fRequireStandard && !AreInputsStandard(tx, view)) if (fRequireStandard && !AreInputsStandard(tx, view))
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
unsigned int nSigOps = GetLegacySigOpCount(tx); int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
nSigOps += GetP2SHSigOpCount(tx, view);
CAmount nValueOut = tx.GetValueOut(); CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut; CAmount nFees = nValueIn-nValueOut;
@ -1263,7 +1278,7 @@ 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, nSigOpsCost, lp);
unsigned int nSize = entry.GetTxSize(); unsigned int nSize = entry.GetTxSize();
// Check that the transaction doesn't have an excessive number of // Check that the transaction doesn't have an excessive number of
@ -1271,9 +1286,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
// merely non-standard transaction. // merely non-standard transaction.
if ((nSigOps > MAX_STANDARD_TX_SIGOPS) || (nBytesPerSigOp && nSigOps > nSize / nBytesPerSigOp)) if ((nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) || (nBytesPerSigOp && nSigOpsCost > nSize * WITNESS_SCALE_FACTOR / nBytesPerSigOp))
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
strprintf("%d", nSigOps)); strprintf("%d", nSigOpsCost));
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
@ -2439,7 +2454,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
std::vector<int> prevheights; std::vector<int> prevheights;
CAmount nFees = 0; CAmount nFees = 0;
int nInputs = 0; int nInputs = 0;
unsigned int nSigOps = 0; int64_t nSigOpsCost = 0;
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
std::vector<std::pair<uint256, CDiskTxPos> > vPos; std::vector<std::pair<uint256, CDiskTxPos> > vPos;
vPos.reserve(block.vtx.size()); vPos.reserve(block.vtx.size());
@ -2449,10 +2464,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
const CTransaction &tx = block.vtx[i]; const CTransaction &tx = block.vtx[i];
nInputs += tx.vin.size(); nInputs += tx.vin.size();
nSigOps += GetLegacySigOpCount(tx);
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("ConnectBlock(): too many sigops"),
REJECT_INVALID, "bad-blk-sigops");
if (!tx.IsCoinBase()) if (!tx.IsCoinBase())
{ {
@ -2483,18 +2494,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__), return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__),
REJECT_INVALID, "bad-txns-nonfinal"); REJECT_INVALID, "bad-txns-nonfinal");
} }
}
if (fStrictPayToScriptHash) // GetTransactionSigOpCost counts 3 types of sigops:
{ // * legacy (always)
// Add in sigops done by pay-to-script-hash inputs; // * p2sh (when P2SH enabled in flags and excludes coinbase)
// this is to prevent a "rogue miner" from creating // * witness (when witness enabled in flags and excludes coinbase)
// an incredibly-expensive-to-validate block. nSigOpsCost += GetTransactionSigOpCost(tx, view, flags);
nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST)
if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"),
return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops");
REJECT_INVALID, "bad-blk-sigops");
}
if (!tx.IsCoinBase())
{
nFees += view.GetValueIn(tx)-tx.GetValueOut(); nFees += view.GetValueIn(tx)-tx.GetValueOut();
std::vector<CScriptCheck> vChecks; std::vector<CScriptCheck> vChecks;
@ -3417,9 +3429,11 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// All potential-corruption validation must be done before we do any // All potential-corruption validation must be done before we do any
// transaction validation, as otherwise we may mark the header as invalid // transaction validation, as otherwise we may mark the header as invalid
// because we receive the wrong transactions for it. // because we receive the wrong transactions for it.
// Note that witness malleability is checked in ContextualCheckBlock, so no
// checks that use witness data may be performed here.
// Size limits // Size limits
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE) if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_BASE_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// First transaction must be coinbase, the rest must not be // First transaction must be coinbase, the rest must not be
@ -3440,7 +3454,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
{ {
nSigOps += GetLegacySigOpCount(tx); nSigOps += GetLegacySigOpCount(tx);
} }
if (nSigOps > MAX_BLOCK_SIGOPS) if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
if (fCheckPOW && fCheckMerkleRoot) if (fCheckPOW && fCheckMerkleRoot)
@ -3621,6 +3635,16 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
} }
} }
// After the coinbase witness nonce and commitment are verified,
// we can check if the block cost passes (before we've checked the
// coinbase witness, it would be possible for the cost to be too
// large by filling up the coinbase witness, which doesn't change
// the block hash, so we couldn't mark the block as permanently
// failed).
if (GetBlockCost(block) > MAX_BLOCK_COST) {
return state.DoS(100, error("ContextualCheckBlock(): cost limit failed"), REJECT_INVALID, "bad-blk-cost");
}
return true; return true;
} }
@ -4284,7 +4308,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
int nLoaded = 0; int nLoaded = 0;
try { try {
// This takes over fileIn and calls fclose() on it in the CBufferedFile destructor // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE+8, SER_DISK, CLIENT_VERSION);
uint64_t nRewind = blkdat.GetPos(); uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) { while (!blkdat.eof()) {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
@ -4303,7 +4327,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
continue; continue;
// read size // read size
blkdat >> nSize; blkdat >> nSize;
if (nSize < 80 || nSize > MAX_BLOCK_SIZE) if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE)
continue; continue;
} catch (const std::exception&) { } catch (const std::exception&) {
// no valid block header found; don't complain // no valid block header found; don't complain

View File

@ -152,6 +152,7 @@ typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap mapBlockIndex; extern BlockMap mapBlockIndex;
extern uint64_t nLastBlockTx; extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize; extern uint64_t nLastBlockSize;
extern uint64_t nLastBlockCost;
extern const std::string strMessageMagic; extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock; extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange; extern CConditionVariable cvBlockChange;

View File

@ -155,7 +155,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch, std::ve
if (nTransactions == 0) if (nTransactions == 0)
return uint256(); return uint256();
// check for excessively high numbers of transactions // check for excessively high numbers of transactions
if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction if (nTransactions > MAX_BLOCK_BASE_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction
return uint256(); return uint256();
// there can never be more hashes provided than one for every txid // there can never be more hashes provided than one for every txid
if (vHash.size() > nTransactions) if (vHash.size() > nTransactions)

View File

@ -45,6 +45,7 @@ using namespace std;
uint64_t nLastBlockTx = 0; uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0; uint64_t nLastBlockSize = 0;
uint64_t nLastBlockCost = 0;
class ScoreCompare class ScoreCompare
{ {
@ -75,15 +76,36 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
BlockAssembler::BlockAssembler(const CChainParams& _chainparams) BlockAssembler::BlockAssembler(const CChainParams& _chainparams)
: chainparams(_chainparams) : chainparams(_chainparams)
{ {
// Largest block you're willing to create: // Block resource limits
nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); // If neither -blockmaxsize or -blockmaxcost is given, limit to DEFAULT_BLOCK_MAX_*
// Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: // If only one is given, only restrict the specified resource.
nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); // If both are given, restrict both.
nBlockMaxCost = DEFAULT_BLOCK_MAX_COST;
nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
bool fCostSet = false;
if (mapArgs.count("-blockmaxcost")) {
nBlockMaxCost = GetArg("-blockmaxcost", DEFAULT_BLOCK_MAX_COST);
nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE;
fCostSet = true;
}
if (mapArgs.count("-blockmaxsize")) {
nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
if (!fCostSet) {
nBlockMaxCost = nBlockMaxSize * WITNESS_SCALE_FACTOR;
}
}
// Limit cost to between 4K and MAX_BLOCK_COST-4K for sanity:
nBlockMaxCost = std::max((unsigned int)4000, std::min((unsigned int)(MAX_BLOCK_COST-4000), nBlockMaxCost));
// Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity:
nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SERIALIZED_SIZE-1000), nBlockMaxSize));
// Minimum block size you want to create; block will be filled with free transactions // Minimum block size you want to create; block will be filled with free transactions
// until there are no more or the block reaches this size: // until there are no more or the block reaches this size:
nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
// Whether we need to account for byte usage (in addition to cost usage)
fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE-1000) || (nBlockMinSize > 0);
} }
void BlockAssembler::resetBlock() void BlockAssembler::resetBlock()
@ -92,7 +114,8 @@ void BlockAssembler::resetBlock()
// Reserve space for coinbase tx // Reserve space for coinbase tx
nBlockSize = 1000; nBlockSize = 1000;
nBlockSigOps = 100; nBlockCost = 4000;
nBlockSigOpsCost = 400;
fIncludeWitness = false; fIncludeWitness = false;
// These counters do not include coinbase tx // These counters do not include coinbase tx
@ -116,7 +139,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
// Add dummy coinbase tx as first transaction // Add dummy coinbase tx as first transaction
pblock->vtx.push_back(CTransaction()); pblock->vtx.push_back(CTransaction());
pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, mempool.cs); LOCK2(cs_main, mempool.cs);
CBlockIndex* pindexPrev = chainActive.Tip(); CBlockIndex* pindexPrev = chainActive.Tip();
@ -144,11 +167,18 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()); fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());
addPriorityTxs(); addPriorityTxs();
addPackageTxs(); if (fNeedSizeAccounting) {
// addPackageTxs (the CPFP-based algorithm) cannot deal with size based
// accounting, so fall back to the old algorithm.
addScoreTxs();
} else {
addPackageTxs();
}
nLastBlockTx = nBlockTx; nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize; nLastBlockSize = nBlockSize;
LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps); nLastBlockCost = nBlockCost;
LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOpsCost);
// Create coinbase transaction. // Create coinbase transaction.
CMutableTransaction coinbaseTx; CMutableTransaction coinbaseTx;
@ -167,7 +197,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0; pblock->nNonce = 0;
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); pblocktemplate->vTxSigOpsCost[0] = GetLegacySigOpCount(pblock->vtx[0]);
CValidationState state; CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
@ -201,11 +231,12 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet)
} }
} }
bool BlockAssembler::TestPackage(uint64_t packageSize, unsigned int packageSigOps) bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost)
{ {
if (nBlockSize + packageSize >= nBlockMaxSize) // TODO: switch to cost-based accounting for packages instead of vsize-based accounting.
if (nBlockCost + WITNESS_SCALE_FACTOR * packageSize >= nBlockMaxCost)
return false; return false;
if (nBlockSigOps + packageSigOps >= MAX_BLOCK_SIGOPS) if (nBlockSigOpsCost + packageSigOpsCost >= MAX_BLOCK_SIGOPS_COST)
return false; return false;
return true; return true;
} }
@ -223,26 +254,39 @@ bool BlockAssembler::TestPackageFinality(const CTxMemPool::setEntries& package)
bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter) bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter)
{ {
if (nBlockSize + iter->GetTxSize() >= nBlockMaxSize) { if (nBlockCost + iter->GetTxCost() >= nBlockMaxCost) {
// If the block is so close to full that no more txs will fit // If the block is so close to full that no more txs will fit
// or if we've tried more than 50 times to fill remaining space // or if we've tried more than 50 times to fill remaining space
// then flag that the block is finished // then flag that the block is finished
if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { if (nBlockCost > nBlockMaxCost - 400 || lastFewTxs > 50) {
blockFinished = true; blockFinished = true;
return false; return false;
} }
// Once we're within 1000 bytes of a full block, only look at 50 more txs // Once we're within 4000 cost of a full block, only look at 50 more txs
// to try to fill the remaining space. // to try to fill the remaining space.
if (nBlockSize > nBlockMaxSize - 1000) { if (nBlockCost > nBlockMaxCost - 4000) {
lastFewTxs++; lastFewTxs++;
} }
return false; return false;
} }
if (nBlockSigOps + iter->GetSigOpCount() >= MAX_BLOCK_SIGOPS) { if (fNeedSizeAccounting) {
if (nBlockSize + ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION) >= nBlockMaxSize) {
if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) {
blockFinished = true;
return false;
}
if (nBlockSize > nBlockMaxSize - 1000) {
lastFewTxs++;
}
return false;
}
}
if (nBlockSigOpsCost + iter->GetSigOpCost() >= MAX_BLOCK_SIGOPS_COST) {
// If the block has room for no more sig ops then // If the block has room for no more sig ops then
// flag that the block is finished // flag that the block is finished
if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { if (nBlockSigOpsCost > MAX_BLOCK_SIGOPS_COST - 8) {
blockFinished = true; blockFinished = true;
return false; return false;
} }
@ -264,10 +308,13 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
{ {
pblock->vtx.push_back(iter->GetTx()); pblock->vtx.push_back(iter->GetTx());
pblocktemplate->vTxFees.push_back(iter->GetFee()); pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOps.push_back(iter->GetSigOpCount()); pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
nBlockSize += iter->GetTxSize(); if (fNeedSizeAccounting) {
nBlockSize += ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
}
nBlockCost += iter->GetTxCost();
++nBlockTx; ++nBlockTx;
nBlockSigOps += iter->GetSigOpCount(); nBlockSigOpsCost += iter->GetSigOpCost();
nFees += iter->GetFee(); nFees += iter->GetFee();
inBlock.insert(iter); inBlock.insert(iter);
@ -358,7 +405,7 @@ void BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& alread
CTxMemPoolModifiedEntry modEntry(desc); CTxMemPoolModifiedEntry modEntry(desc);
modEntry.nSizeWithAncestors -= it->GetTxSize(); modEntry.nSizeWithAncestors -= it->GetTxSize();
modEntry.nModFeesWithAncestors -= it->GetModifiedFee(); modEntry.nModFeesWithAncestors -= it->GetModifiedFee();
modEntry.nSigOpCountWithAncestors -= it->GetSigOpCount(); modEntry.nSigOpCostWithAncestors -= it->GetSigOpCost();
mapModifiedTx.insert(modEntry); mapModifiedTx.insert(modEntry);
} else { } else {
mapModifiedTx.modify(mit, update_for_parent_inclusion(it)); mapModifiedTx.modify(mit, update_for_parent_inclusion(it));
@ -460,19 +507,19 @@ void BlockAssembler::addPackageTxs()
uint64_t packageSize = iter->GetSizeWithAncestors(); uint64_t packageSize = iter->GetSizeWithAncestors();
CAmount packageFees = iter->GetModFeesWithAncestors(); CAmount packageFees = iter->GetModFeesWithAncestors();
unsigned int packageSigOps = iter->GetSigOpCountWithAncestors(); int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors();
if (fUsingModified) { if (fUsingModified) {
packageSize = modit->nSizeWithAncestors; packageSize = modit->nSizeWithAncestors;
packageFees = modit->nModFeesWithAncestors; packageFees = modit->nModFeesWithAncestors;
packageSigOps = modit->nSigOpCountWithAncestors; packageSigOpsCost = modit->nSigOpCostWithAncestors;
} }
if (packageFees < ::minRelayTxFee.GetFee(packageSize) && nBlockSize >= nBlockMinSize) { if (packageFees < ::minRelayTxFee.GetFee(packageSize)) {
// Everything else we might consider has a lower fee rate // Everything else we might consider has a lower fee rate
return; return;
} }
if (!TestPackage(packageSize, packageSigOps)) { if (!TestPackage(packageSize, packageSigOpsCost)) {
if (fUsingModified) { if (fUsingModified) {
// Since we always look at the best entry in mapModifiedTx, // Since we always look at the best entry in mapModifiedTx,
// we must erase failed entries so that we can consider the // we must erase failed entries so that we can consider the
@ -526,6 +573,8 @@ void BlockAssembler::addPriorityTxs()
return; return;
} }
fNeedSizeAccounting = true;
// This vector will be sorted into a priority queue: // This vector will be sorted into a priority queue:
vector<TxCoinAgePriority> vecPriority; vector<TxCoinAgePriority> vecPriority;
TxCoinAgePriorityCompare pricomparer; TxCoinAgePriorityCompare pricomparer;

View File

@ -28,7 +28,7 @@ struct CBlockTemplate
{ {
CBlock block; CBlock block;
std::vector<CAmount> vTxFees; std::vector<CAmount> vTxFees;
std::vector<int64_t> vTxSigOps; std::vector<int64_t> vTxSigOpsCost;
std::vector<unsigned char> vchCoinbaseCommitment; std::vector<unsigned char> vchCoinbaseCommitment;
}; };
@ -40,13 +40,13 @@ struct CTxMemPoolModifiedEntry {
iter = entry; iter = entry;
nSizeWithAncestors = entry->GetSizeWithAncestors(); nSizeWithAncestors = entry->GetSizeWithAncestors();
nModFeesWithAncestors = entry->GetModFeesWithAncestors(); nModFeesWithAncestors = entry->GetModFeesWithAncestors();
nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors(); nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors();
} }
CTxMemPool::txiter iter; CTxMemPool::txiter iter;
uint64_t nSizeWithAncestors; uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors; CAmount nModFeesWithAncestors;
unsigned int nSigOpCountWithAncestors; int64_t nSigOpCostWithAncestors;
}; };
/** Comparator for CTxMemPool::txiter objects. /** Comparator for CTxMemPool::txiter objects.
@ -124,7 +124,7 @@ struct update_for_parent_inclusion
{ {
e.nModFeesWithAncestors -= iter->GetFee(); e.nModFeesWithAncestors -= iter->GetFee();
e.nSizeWithAncestors -= iter->GetTxSize(); e.nSizeWithAncestors -= iter->GetTxSize();
e.nSigOpCountWithAncestors -= iter->GetSigOpCount(); e.nSigOpCostWithAncestors -= iter->GetSigOpCost();
} }
CTxMemPool::txiter iter; CTxMemPool::txiter iter;
@ -141,12 +141,14 @@ private:
// Configuration parameters for the block size // Configuration parameters for the block size
bool fIncludeWitness; bool fIncludeWitness;
unsigned int nBlockMaxSize, nBlockMinSize; unsigned int nBlockMaxCost, nBlockMaxSize, nBlockMinSize;
bool fNeedSizeAccounting;
// Information on the current status of the block // Information on the current status of the block
uint64_t nBlockCost;
uint64_t nBlockSize; uint64_t nBlockSize;
uint64_t nBlockTx; uint64_t nBlockTx;
unsigned int nBlockSigOps; uint64_t nBlockSigOpsCost;
CAmount nFees; CAmount nFees;
CTxMemPool::setEntries inBlock; CTxMemPool::setEntries inBlock;
@ -189,7 +191,7 @@ private:
/** Remove confirmed (inBlock) entries from given set */ /** Remove confirmed (inBlock) entries from given set */
void onlyUnconfirmed(CTxMemPool::setEntries& testSet); void onlyUnconfirmed(CTxMemPool::setEntries& testSet);
/** Test if a new package would "fit" in the block */ /** Test if a new package would "fit" in the block */
bool TestPackage(uint64_t packageSize, unsigned int packageSigOps); bool TestPackage(uint64_t packageSize, int64_t packageSigOpsCost);
/** Test if a set of transactions are all final */ /** Test if a set of transactions are all final */
bool TestPackageFinality(const CTxMemPool::setEntries& package); bool TestPackageFinality(const CTxMemPool::setEntries& package);
/** Return true if given transaction from mapTx has already been evaluated, /** Return true if given transaction from mapTx has already been evaluated,

View File

@ -2182,7 +2182,7 @@ void CNode::RecordBytesSent(uint64_t bytes)
void CNode::SetMaxOutboundTarget(uint64_t limit) void CNode::SetMaxOutboundTarget(uint64_t limit)
{ {
LOCK(cs_totalBytesSent); LOCK(cs_totalBytesSent);
uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SIZE; uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SERIALIZED_SIZE;
nMaxOutboundLimit = limit; nMaxOutboundLimit = limit;
if (limit > 0 && limit < recommendedMinimum) if (limit > 0 && limit < recommendedMinimum)
@ -2237,7 +2237,7 @@ bool CNode::OutboundTargetReached(bool historicalBlockServingLimit)
{ {
// keep a large enough buffer to at least relay each block once // keep a large enough buffer to at least relay each block once
uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle();
uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SIZE; uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SERIALIZED_SIZE;
if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer)
return true; return true;
} }

View File

@ -45,8 +45,8 @@ static const int TIMEOUT_INTERVAL = 20 * 60;
static const unsigned int MAX_INV_SZ = 50000; static const unsigned int MAX_INV_SZ = 50000;
/** The maximum number of new addresses to accumulate before announcing. */ /** The maximum number of new addresses to accumulate before announcing. */
static const unsigned int MAX_ADDR_TO_SEND = 1000; static const unsigned int MAX_ADDR_TO_SEND = 1000;
/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ /** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
/** Maximum length of strSubVer in `version` message */ /** Maximum length of strSubVer in `version` message */
static const unsigned int MAX_SUBVERSION_LENGTH = 256; static const unsigned int MAX_SUBVERSION_LENGTH = 256;
/** -listen default */ /** -listen default */

View File

@ -64,8 +64,8 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
// almost as much to process as they cost the sender in fees, because // almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions // computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); unsigned int sz = GetTransactionCost(tx);
if (sz >= MAX_STANDARD_TX_SIZE) { if (sz >= MAX_STANDARD_TX_COST) {
reason = "tx-size"; reason = "tx-size";
return false; return false;
} }
@ -150,3 +150,13 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true; return true;
} }
int64_t GetVirtualTransactionSize(int64_t nCost)
{
return (nCost + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
int64_t GetVirtualTransactionSize(const CTransaction& tx)
{
return GetVirtualTransactionSize(GetTransactionCost(tx));
}

View File

@ -19,12 +19,14 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0;
/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0; static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0;
/** Default for -blockmaxcost, which control the range of block costs the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_COST = 3000000;
/** The maximum size for transactions we're willing to relay/mine */ /** The maximum size for transactions we're willing to relay/mine */
static const unsigned int MAX_STANDARD_TX_SIZE = 100000; static const unsigned int MAX_STANDARD_TX_COST = 400000;
/** Maximum number of signature check operations in an IsStandard() P2SH script */ /** Maximum number of signature check operations in an IsStandard() P2SH script */
static const unsigned int MAX_P2SH_SIGOPS = 15; static const unsigned int MAX_P2SH_SIGOPS = 15;
/** The maximum number of sigops we're willing to relay/mine in a single tx */ /** The maximum number of sigops we're willing to relay/mine in a single tx */
static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5; static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5;
/** Default for -maxmempool, maximum megabytes of mempool memory usage */ /** Default for -maxmempool, maximum megabytes of mempool memory usage */
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
/** /**
@ -65,4 +67,8 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason);
*/ */
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
/** Compute the virtual transaction size (cost reinterpreted as bytes). */
int64_t GetVirtualTransactionSize(int64_t nCost);
int64_t GetVirtualTransactionSize(const CTransaction& tx);
#endif // BITCOIN_POLICY_POLICY_H #endif // BITCOIN_POLICY_POLICY_H

View File

@ -31,3 +31,12 @@ std::string CBlock::ToString() const
} }
return s.str(); return s.str();
} }
int64_t GetBlockCost(const CBlock& block)
{
// This implements the cost = (stripped_size * 4) + witness_size formula,
// using only serialization with and without witness data. As witness_size
// is equal to total_size - stripped_size, this formula is identical to:
// cost = (stripped_size * 3) + total_size.
return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
}

View File

@ -154,4 +154,7 @@ struct CBlockLocator
} }
}; };
/** Compute the consensus-critical block cost (see BIP 141). */
int64_t GetBlockCost(const CBlock& tx);
#endif // BITCOIN_PRIMITIVES_BLOCK_H #endif // BITCOIN_PRIMITIVES_BLOCK_H

View File

@ -121,7 +121,7 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const
// Providing any more cleanup incentive than making additional inputs free would // Providing any more cleanup incentive than making additional inputs free would
// risk encouraging people to create junk outputs to redeem later. // risk encouraging people to create junk outputs to redeem later.
if (nTxSize == 0) if (nTxSize == 0)
nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); nTxSize = (GetTransactionCost(*this) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
for (std::vector<CTxIn>::const_iterator it(vin.begin()); it != vin.end(); ++it) for (std::vector<CTxIn>::const_iterator it(vin.begin()); it != vin.end(); ++it)
{ {
unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size()); unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size());
@ -148,3 +148,8 @@ std::string CTransaction::ToString() const
str += " " + vout[i].ToString() + "\n"; str += " " + vout[i].ToString() + "\n";
return str; return str;
} }
int64_t GetTransactionCost(const CTransaction& tx)
{
return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
}

View File

@ -13,6 +13,8 @@
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
static const int WITNESS_SCALE_FACTOR = 4;
/** An outpoint - a combination of a transaction hash and an index n into its vout */ /** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint class COutPoint
{ {
@ -166,15 +168,30 @@ public:
// which has units satoshis-per-kilobyte. // which has units satoshis-per-kilobyte.
// If you'd pay more than 1/3 in fees // If you'd pay more than 1/3 in fees
// to spend something, then we consider it dust. // to spend something, then we consider it dust.
// A typical spendable txout is 34 bytes big, and will // A typical spendable non-segwit txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend: // need a CTxIn of at least 148 bytes to spend:
// so dust is a spendable txout less than // so dust is a spendable txout less than
// 546*minRelayTxFee/1000 (in satoshis) // 546*minRelayTxFee/1000 (in satoshis).
// A typical spendable segwit txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
// 294*minRelayTxFee/1000 (in satoshis).
if (scriptPubKey.IsUnspendable()) if (scriptPubKey.IsUnspendable())
return 0; return 0;
size_t nSize = GetSerializeSize(SER_DISK,0)+148u; size_t nSize = GetSerializeSize(SER_DISK, 0);
return 3*minRelayTxFee.GetFee(nSize); int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
// sum the sizes of the parts of a transaction input
// with 75% segwit discount applied to the script size.
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
} else {
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
}
return 3 * minRelayTxFee.GetFee(nSize);
} }
bool IsDust(const CFeeRate &minRelayTxFee) const bool IsDust(const CFeeRate &minRelayTxFee) const
@ -442,4 +459,7 @@ struct CMutableTransaction
uint256 GetHash() const; uint256 GetHash() const;
}; };
/** Compute the cost of a transaction, as defined by BIP 141 */
int64_t GetTransactionCost(const CTransaction &tx);
#endif // BITCOIN_PRIMITIVES_TRANSACTION_H #endif // BITCOIN_PRIMITIVES_TRANSACTION_H

View File

@ -101,6 +101,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("confirmations", confirmations)); result.push_back(Pair("confirmations", confirmations));
result.push_back(Pair("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS))); result.push_back(Pair("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)));
result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
result.push_back(Pair("cost", (int)::GetBlockCost(block)));
result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("height", blockindex->nHeight));
result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("version", block.nVersion));
result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion))); result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
@ -560,6 +561,7 @@ UniValue getblock(const UniValue& params, bool fHelp)
" \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
" \"size\" : n, (numeric) The block size\n" " \"size\" : n, (numeric) The block size\n"
" \"strippedsize\" : n, (numeric) The block size excluding witness data\n" " \"strippedsize\" : n, (numeric) The block size excluding witness data\n"
" \"cost\" : n (numeric) The block cost\n"
" \"height\" : n, (numeric) The block height or index\n" " \"height\" : n, (numeric) The block height or index\n"
" \"version\" : n, (numeric) The block version\n" " \"version\" : n, (numeric) The block version\n"
" \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n"

View File

@ -224,6 +224,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
"{\n" "{\n"
" \"blocks\": nnn, (numeric) The current block\n" " \"blocks\": nnn, (numeric) The current block\n"
" \"currentblocksize\": nnn, (numeric) The last block size\n" " \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblockcost\": nnn, (numeric) The last block cost\n"
" \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n" " \"errors\": \"...\" (string) Current errors\n"
@ -242,6 +243,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
obj.push_back(Pair("currentblockcost", (uint64_t)nLastBlockCost));
obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("errors", GetWarnings("statusbar")));
@ -349,13 +351,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
" {\n" " {\n"
" \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
" \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n"
" \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal\n" " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n"
" \"depends\" : [ (array) array of numbers \n" " \"depends\" : [ (array) array of numbers \n"
" n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
" ,...\n" " ,...\n"
" ],\n" " ],\n"
" \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
" \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n" " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
" \"cost\" : n, (numeric) total transaction size cost, as counted for purposes of block limits\n"
" \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
" }\n" " }\n"
" ,...\n" " ,...\n"
@ -372,8 +375,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
" ,...\n" " ,...\n"
" ],\n" " ],\n"
" \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n" " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n"
" \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" " \"sigoplimit\" : n, (numeric) cost limit of sigops in blocks\n"
" \"sizelimit\" : n, (numeric) limit of block size\n" " \"sizelimit\" : n, (numeric) limit of block size\n"
" \"costlimit\" : n, (numeric) limit of block cost\n"
" \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
" \"bits\" : \"xxx\", (string) compressed target of next block\n" " \"bits\" : \"xxx\", (string) compressed target of next block\n"
" \"height\" : n (numeric) The height of the next block\n" " \"height\" : n (numeric) The height of the next block\n"
@ -570,7 +574,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
int index_in_template = i - 1; int index_in_template = i - 1;
entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); entry.push_back(Pair("sigops", pblocktemplate->vTxSigOpsCost[index_in_template]));
entry.push_back(Pair("cost", GetTransactionCost(tx)));
transactions.push_back(entry); transactions.push_back(entry);
} }
@ -652,8 +657,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
result.push_back(Pair("mutable", aMutable)); result.push_back(Pair("mutable", aMutable));
result.push_back(Pair("noncerange", "00000000ffffffff")); result.push_back(Pair("noncerange", "00000000ffffffff"));
result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS_COST));
result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SERIALIZED_SIZE));
result.push_back(Pair("costlimit", (int64_t)MAX_BLOCK_COST));
result.push_back(Pair("curtime", pblock->GetBlockTime())); result.push_back(Pair("curtime", pblock->GetBlockTime()));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));

View File

@ -64,6 +64,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
entry.push_back(Pair("txid", tx.GetHash().GetHex())); entry.push_back(Pair("txid", tx.GetHash().GetHex()));
entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex()));
entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx)));
entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("version", tx.nVersion));
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
@ -150,6 +151,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp)
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n" " \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The serialized transaction size\n" " \"size\" : n, (numeric) The serialized transaction size\n"
" \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
" \"version\" : n, (numeric) The version\n" " \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n" " \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n" " \"vin\" : [ (array of json objects)\n"
@ -461,6 +463,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp)
" \"txid\" : \"id\", (string) The transaction id\n" " \"txid\" : \"id\", (string) The transaction id\n"
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The transaction size\n" " \"size\" : n, (numeric) The transaction size\n"
" \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
" \"version\" : n, (numeric) The version\n" " \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n" " \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n" " \"vin\" : [ (array of json objects)\n"

View File

@ -1470,3 +1470,50 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
return set_success(serror); return set_success(serror);
} }
size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness, int flags)
{
if (witversion == 0) {
if (witprogram.size() == 20)
return 1;
if (witprogram.size() == 32 && witness.stack.size() > 0) {
CScript subscript(witness.stack.back().begin(), witness.stack.back().end());
return subscript.GetSigOpCount(true);
}
}
// Future flags may be implemented here.
return 0;
}
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags)
{
static const CScriptWitness witnessEmpty;
if ((flags & SCRIPT_VERIFY_WITNESS) == 0) {
return 0;
}
assert((flags & SCRIPT_VERIFY_P2SH) != 0);
int witnessversion;
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags);
}
if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) {
CScript::const_iterator pc = scriptSig.begin();
vector<unsigned char> data;
while (pc < scriptSig.end()) {
opcodetype opcode;
scriptSig.GetOp(pc, opcode, data);
}
CScript subscript(data.begin(), data.end());
if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) {
return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags);
}
}
return 0;
}

View File

@ -156,4 +156,6 @@ public:
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL); bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL);
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
#endif // BITCOIN_SCRIPT_INTERPRETER_H #endif // BITCOIN_SCRIPT_INTERPRETER_H

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "policy/policy.h"
#include "txmempool.h" #include "txmempool.h"
#include "util.h" #include "util.h"
@ -336,7 +337,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx2.vout[0].nValue = 2 * COIN; tx2.vout[0].nValue = 2 * COIN;
pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).Priority(9.0).FromTx(tx2)); pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).Priority(9.0).FromTx(tx2));
uint64_t tx2Size = ::GetSerializeSize(tx2, SER_NETWORK, PROTOCOL_VERSION); uint64_t tx2Size = GetVirtualTransactionSize(tx2);
/* lowest fee */ /* lowest fee */
CMutableTransaction tx3 = CMutableTransaction(); CMutableTransaction tx3 = CMutableTransaction();
@ -384,7 +385,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx6.vout.resize(1); tx6.vout.resize(1);
tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx6.vout[0].nValue = 20 * COIN; tx6.vout[0].nValue = 20 * COIN;
uint64_t tx6Size = ::GetSerializeSize(tx6, SER_NETWORK, PROTOCOL_VERSION); uint64_t tx6Size = GetVirtualTransactionSize(tx6);
pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6)); pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
BOOST_CHECK_EQUAL(pool.size(), 6); BOOST_CHECK_EQUAL(pool.size(), 6);
@ -398,7 +399,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
tx7.vout.resize(1); tx7.vout.resize(1);
tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
tx7.vout[0].nValue = 10 * COIN; tx7.vout[0].nValue = 10 * COIN;
uint64_t tx7Size = ::GetSerializeSize(tx7, SER_NETWORK, PROTOCOL_VERSION); uint64_t tx7Size = GetVirtualTransactionSize(tx7);
/* set the fee to just below tx2's feerate when including ancestor */ /* set the fee to just below tx2's feerate when including ancestor */
CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1; CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
@ -467,12 +468,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
BOOST_CHECK(pool.exists(tx2.GetHash())); BOOST_CHECK(pool.exists(tx2.GetHash()));
BOOST_CHECK(pool.exists(tx3.GetHash())); BOOST_CHECK(pool.exists(tx3.GetHash()));
pool.TrimToSize(::GetSerializeSize(CTransaction(tx1), SER_NETWORK, PROTOCOL_VERSION)); // mempool is limited to tx1's size in memory usage, so nothing fits pool.TrimToSize(GetVirtualTransactionSize(tx1)); // mempool is limited to tx1's size in memory usage, so nothing fits
BOOST_CHECK(!pool.exists(tx1.GetHash())); BOOST_CHECK(!pool.exists(tx1.GetHash()));
BOOST_CHECK(!pool.exists(tx2.GetHash())); BOOST_CHECK(!pool.exists(tx2.GetHash()));
BOOST_CHECK(!pool.exists(tx3.GetHash())); BOOST_CHECK(!pool.exists(tx3.GetHash()));
CFeeRate maxFeeRateRemoved(25000, ::GetSerializeSize(CTransaction(tx3), SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(CTransaction(tx2), SER_NETWORK, PROTOCOL_VERSION)); CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3) + GetVirtualTransactionSize(tx2));
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
CMutableTransaction tx4 = CMutableTransaction(); CMutableTransaction tx4 = CMutableTransaction();

View File

@ -181,6 +181,9 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
// NOTE: These tests rely on CreateNewBlock doing its own self-validation! // NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{ {
// Disable size accounting (CPFP does not support it)
mapArgs["-blockmaxsize"] = strprintf("%u", MAX_BLOCK_SERIALIZED_SIZE);
const CChainParams& chainparams = Params(CBaseChainParams::MAIN); const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
CBlockTemplate *pblocktemplate; CBlockTemplate *pblocktemplate;
@ -264,7 +267,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash(); hash = tx.GetHash();
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
tx.vin[0].prevout.hash = hash; tx.vin[0].prevout.hash = hash;
} }
BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "policy/policy.h"
#include "policy/fees.h" #include "policy/fees.h"
#include "txmempool.h" #include "txmempool.h"
#include "uint256.h" #include "uint256.h"
@ -50,7 +51,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
tx.vin[0].scriptSig = garbage; tx.vin[0].scriptSig = garbage;
tx.vout.resize(1); tx.vout.resize(1);
tx.vout[0].nValue=0LL; tx.vout[0].nValue=0LL;
CFeeRate baseRate(basefee, ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx));
// Create a fake block // Create a fake block
std::vector<CTransaction> block; std::vector<CTransaction> block;

View File

@ -136,7 +136,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CTransaction &txn, CTxMemPool *po
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight, return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
hasNoDependencies, inChainValue, spendsCoinbase, sigOpCount, lp); hasNoDependencies, inChainValue, spendsCoinbase, sigOpCost, lp);
} }
void Shutdown(void* parg) void Shutdown(void* parg)

View File

@ -70,12 +70,12 @@ struct TestMemPoolEntryHelper
unsigned int nHeight; unsigned int nHeight;
bool hadNoDependencies; bool hadNoDependencies;
bool spendsCoinbase; bool spendsCoinbase;
unsigned int sigOpCount; unsigned int sigOpCost;
LockPoints lp; LockPoints lp;
TestMemPoolEntryHelper() : TestMemPoolEntryHelper() :
nFee(0), nTime(0), dPriority(0.0), nHeight(1), nFee(0), nTime(0), dPriority(0.0), nHeight(1),
hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1) { } hadNoDependencies(false), spendsCoinbase(false), sigOpCost(4) { }
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL); CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
CTxMemPoolEntry FromTx(CTransaction &tx, CTxMemPool *pool = NULL); CTxMemPoolEntry FromTx(CTransaction &tx, CTxMemPool *pool = NULL);
@ -87,6 +87,6 @@ struct TestMemPoolEntryHelper
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; } TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
TestMemPoolEntryHelper &SigOps(unsigned int _sigops) { sigOpCount = _sigops; return *this; } TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; }
}; };
#endif #endif

View File

@ -9,6 +9,7 @@
#include "consensus/consensus.h" #include "consensus/consensus.h"
#include "consensus/validation.h" #include "consensus/validation.h"
#include "main.h" #include "main.h"
#include "policy/policy.h"
#include "policy/fees.h" #include "policy/fees.h"
#include "streams.h" #include "streams.h"
#include "timedata.h" #include "timedata.h"
@ -22,17 +23,17 @@ using namespace std;
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight, int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool poolHasNoInputsOf, CAmount _inChainInputValue,
bool _spendsCoinbase, unsigned int _sigOps, LockPoints lp): bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp):
tx(std::make_shared<CTransaction>(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), tx(std::make_shared<CTransaction>(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), lockPoints(lp) spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
{ {
nTxSize = ::GetSerializeSize(_tx, SER_NETWORK, PROTOCOL_VERSION); nTxCost = GetTransactionCost(_tx);
nModSize = _tx.CalculateModifiedSize(nTxSize); nModSize = _tx.CalculateModifiedSize(GetTxSize());
nUsageSize = RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(tx); nUsageSize = RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(tx);
nCountWithDescendants = 1; nCountWithDescendants = 1;
nSizeWithDescendants = nTxSize; nSizeWithDescendants = GetTxSize();
nModFeesWithDescendants = nFee; nModFeesWithDescendants = nFee;
CAmount nValueIn = _tx.GetValueOut()+nFee; CAmount nValueIn = _tx.GetValueOut()+nFee;
assert(inChainInputValue <= nValueIn); assert(inChainInputValue <= nValueIn);
@ -40,9 +41,9 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
feeDelta = 0; feeDelta = 0;
nCountWithAncestors = 1; nCountWithAncestors = 1;
nSizeWithAncestors = nTxSize; nSizeWithAncestors = GetTxSize();
nModFeesWithAncestors = nFee; nModFeesWithAncestors = nFee;
nSigOpCountWithAncestors = sigOpCount; nSigOpCostWithAncestors = sigOpCost;
} }
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
@ -72,6 +73,11 @@ void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp)
lockPoints = lp; lockPoints = lp;
} }
size_t CTxMemPoolEntry::GetTxSize() const
{
return GetVirtualTransactionSize(nTxCost);
}
// Update the given tx for any in-mempool descendants. // Update the given tx for any in-mempool descendants.
// Assumes that setMemPoolChildren is correct for the given tx and all // Assumes that setMemPoolChildren is correct for the given tx and all
// descendants. // descendants.
@ -111,7 +117,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
modifyCount++; modifyCount++;
cachedDescendants[updateIt].insert(cit); cachedDescendants[updateIt].insert(cit);
// Update ancestor state for each descendant // Update ancestor state for each descendant
mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount())); mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));
} }
} }
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount)); mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
@ -247,13 +253,13 @@ void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncesto
int64_t updateCount = setAncestors.size(); int64_t updateCount = setAncestors.size();
int64_t updateSize = 0; int64_t updateSize = 0;
CAmount updateFee = 0; CAmount updateFee = 0;
int updateSigOps = 0; int64_t updateSigOpsCost = 0;
BOOST_FOREACH(txiter ancestorIt, setAncestors) { BOOST_FOREACH(txiter ancestorIt, setAncestors) {
updateSize += ancestorIt->GetTxSize(); updateSize += ancestorIt->GetTxSize();
updateFee += ancestorIt->GetModifiedFee(); updateFee += ancestorIt->GetModifiedFee();
updateSigOps += ancestorIt->GetSigOpCount(); updateSigOpsCost += ancestorIt->GetSigOpCost();
} }
mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOps)); mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOpsCost));
} }
void CTxMemPool::UpdateChildrenForRemoval(txiter it) void CTxMemPool::UpdateChildrenForRemoval(txiter it)
@ -282,7 +288,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
setDescendants.erase(removeIt); // don't update state for self setDescendants.erase(removeIt); // don't update state for self
int64_t modifySize = -((int64_t)removeIt->GetTxSize()); int64_t modifySize = -((int64_t)removeIt->GetTxSize());
CAmount modifyFee = -removeIt->GetModifiedFee(); CAmount modifyFee = -removeIt->GetModifiedFee();
int modifySigOps = -removeIt->GetSigOpCount(); int modifySigOps = -removeIt->GetSigOpCost();
BOOST_FOREACH(txiter dit, setDescendants) { BOOST_FOREACH(txiter dit, setDescendants) {
mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps)); mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps));
} }
@ -338,8 +344,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
nModFeesWithAncestors += modifyFee; nModFeesWithAncestors += modifyFee;
nCountWithAncestors += modifyCount; nCountWithAncestors += modifyCount;
assert(int64_t(nCountWithAncestors) > 0); assert(int64_t(nCountWithAncestors) > 0);
nSigOpCountWithAncestors += modifySigOps; nSigOpCostWithAncestors += modifySigOps;
assert(int(nSigOpCountWithAncestors) >= 0); assert(int(nSigOpCostWithAncestors) >= 0);
} }
CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) : CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) :
@ -666,7 +672,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
bool fDependsWait = false; bool fDependsWait = false;
setEntries setParentCheck; setEntries setParentCheck;
int64_t parentSizes = 0; int64_t parentSizes = 0;
unsigned int parentSigOpCount = 0; int64_t parentSigOpCost = 0;
BOOST_FOREACH(const CTxIn &txin, tx.vin) { BOOST_FOREACH(const CTxIn &txin, tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
@ -676,7 +682,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
fDependsWait = true; fDependsWait = true;
if (setParentCheck.insert(it2).second) { if (setParentCheck.insert(it2).second) {
parentSizes += it2->GetTxSize(); parentSizes += it2->GetTxSize();
parentSigOpCount += it2->GetSigOpCount(); parentSigOpCost += it2->GetSigOpCost();
} }
} else { } else {
const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash); const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash);
@ -698,17 +704,17 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
uint64_t nCountCheck = setAncestors.size() + 1; uint64_t nCountCheck = setAncestors.size() + 1;
uint64_t nSizeCheck = it->GetTxSize(); uint64_t nSizeCheck = it->GetTxSize();
CAmount nFeesCheck = it->GetModifiedFee(); CAmount nFeesCheck = it->GetModifiedFee();
unsigned int nSigOpCheck = it->GetSigOpCount(); int64_t nSigOpCheck = it->GetSigOpCost();
BOOST_FOREACH(txiter ancestorIt, setAncestors) { BOOST_FOREACH(txiter ancestorIt, setAncestors) {
nSizeCheck += ancestorIt->GetTxSize(); nSizeCheck += ancestorIt->GetTxSize();
nFeesCheck += ancestorIt->GetModifiedFee(); nFeesCheck += ancestorIt->GetModifiedFee();
nSigOpCheck += ancestorIt->GetSigOpCount(); nSigOpCheck += ancestorIt->GetSigOpCost();
} }
assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetCountWithAncestors() == nCountCheck);
assert(it->GetSizeWithAncestors() == nSizeCheck); assert(it->GetSizeWithAncestors() == nSizeCheck);
assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); assert(it->GetSigOpCostWithAncestors() == nSigOpCheck);
assert(it->GetModFeesWithAncestors() == nFeesCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck);
// Check children against mapNextTx // Check children against mapNextTx

View File

@ -78,7 +78,7 @@ class CTxMemPoolEntry
private: private:
std::shared_ptr<const CTransaction> tx; std::shared_ptr<const CTransaction> tx;
CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
size_t nTxSize; //!< ... and avoid recomputing tx size size_t nTxCost; //!< ... and avoid recomputing tx cost (also used for GetTxSize())
size_t nModSize; //!< ... and modified size for priority size_t nModSize; //!< ... and modified size for priority
size_t nUsageSize; //!< ... and total memory usage size_t nUsageSize; //!< ... and total memory usage
int64_t nTime; //!< Local time when entering the mempool int64_t nTime; //!< Local time when entering the mempool
@ -87,7 +87,7 @@ private:
bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool
CAmount inChainInputValue; //!< Sum of all txin values that are already in blockchain CAmount inChainInputValue; //!< Sum of all txin values that are already in blockchain
bool spendsCoinbase; //!< keep track of transactions that spend a coinbase bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
unsigned int sigOpCount; //!< Legacy sig ops plus P2SH sig op count int64_t sigOpCost; //!< Total sigop cost
int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block
LockPoints lockPoints; //!< Track the height and time at which tx was final LockPoints lockPoints; //!< Track the height and time at which tx was final
@ -104,13 +104,13 @@ private:
uint64_t nCountWithAncestors; uint64_t nCountWithAncestors;
uint64_t nSizeWithAncestors; uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors; CAmount nModFeesWithAncestors;
unsigned int nSigOpCountWithAncestors; int64_t nSigOpCostWithAncestors;
public: public:
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight, int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase, bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase,
unsigned int nSigOps, LockPoints lp); int64_t nSigOpsCost, LockPoints lp);
CTxMemPoolEntry(const CTxMemPoolEntry& other); CTxMemPoolEntry(const CTxMemPoolEntry& other);
const CTransaction& GetTx() const { return *this->tx; } const CTransaction& GetTx() const { return *this->tx; }
@ -121,11 +121,12 @@ public:
*/ */
double GetPriority(unsigned int currentHeight) const; double GetPriority(unsigned int currentHeight) const;
const CAmount& GetFee() const { return nFee; } const CAmount& GetFee() const { return nFee; }
size_t GetTxSize() const { return nTxSize; } size_t GetTxSize() const;
size_t GetTxCost() const { return nTxCost; }
int64_t GetTime() const { return nTime; } int64_t GetTime() const { return nTime; }
unsigned int GetHeight() const { return entryHeight; } unsigned int GetHeight() const { return entryHeight; }
bool WasClearAtEntry() const { return hadNoDependencies; } bool WasClearAtEntry() const { return hadNoDependencies; }
unsigned int GetSigOpCount() const { return sigOpCount; } int64_t GetSigOpCost() const { return sigOpCost; }
int64_t GetModifiedFee() const { return nFee + feeDelta; } int64_t GetModifiedFee() const { return nFee + feeDelta; }
size_t DynamicMemoryUsage() const { return nUsageSize; } size_t DynamicMemoryUsage() const { return nUsageSize; }
const LockPoints& GetLockPoints() const { return lockPoints; } const LockPoints& GetLockPoints() const { return lockPoints; }
@ -149,7 +150,7 @@ public:
uint64_t GetCountWithAncestors() const { return nCountWithAncestors; } uint64_t GetCountWithAncestors() const { return nCountWithAncestors; }
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
unsigned int GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; } int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; }
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
}; };
@ -172,18 +173,18 @@ struct update_descendant_state
struct update_ancestor_state struct update_ancestor_state
{ {
update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int _modifySigOps) : update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOps(_modifySigOps) modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
{} {}
void operator() (CTxMemPoolEntry &e) void operator() (CTxMemPoolEntry &e)
{ e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOps); } { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
private: private:
int64_t modifySize; int64_t modifySize;
CAmount modifyFee; CAmount modifyFee;
int64_t modifyCount; int64_t modifyCount;
int modifySigOps; int64_t modifySigOpsCost;
}; };
struct update_fee_delta struct update_fee_delta

View File

@ -2348,7 +2348,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nIn++; nIn++;
} }
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); unsigned int nBytes = GetVirtualTransactionSize(txNew);
// Remove scriptSigs if we used dummy signatures for fee calculation // Remove scriptSigs if we used dummy signatures for fee calculation
if (!sign) { if (!sign) {
@ -2360,7 +2360,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
*static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
// Limit size // Limit size
if (nBytes >= MAX_STANDARD_TX_SIZE) if (GetTransactionCost(txNew) >= MAX_STANDARD_TX_COST)
{ {
strFailReason = _("Transaction too large"); strFailReason = _("Transaction too large");
return false; return false;