diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py index 5087f0762..125d4eb27 100755 --- a/qa/rpc-tests/maxuploadtarget.py +++ b/qa/rpc-tests/maxuploadtarget.py @@ -97,7 +97,7 @@ class MaxUploadTest(BitcoinTestFramework): def setup_network(self): # Start a node with maxuploadtarget of 200 MB (/24h) 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): # Want to create a full block @@ -175,13 +175,13 @@ class MaxUploadTest(BitcoinTestFramework): getdata_request = msg_getdata() getdata_request.inv.append(CInv(2, big_old_block)) - max_bytes_per_day = 200*1024*1024 - daily_buffer = 144 * MAX_BLOCK_SIZE + max_bytes_per_day = 800*1024*1024 + daily_buffer = 144 * 4000000 max_bytes_available = max_bytes_per_day - daily_buffer success_count = max_bytes_available // old_block_size - # 144MB will be reserved for relaying new blocks, so expect this to - # succeed for ~70 tries. + # 576MB will be reserved for relaying new blocks, so expect this to + # succeed for ~235 tries. for i in range(success_count): test_nodes[0].send_message(getdata_request) test_nodes[0].sync_with_ping() @@ -198,9 +198,9 @@ class MaxUploadTest(BitcoinTestFramework): # Requesting the current block on test_nodes[1] should succeed indefinitely, # 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)] - for i in range(200): + for i in range(800): test_nodes[1].send_message(getdata_request) test_nodes[1].sync_with_ping() assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 424812a6d..e77aa6c72 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -195,7 +195,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput) uint256 txid(uint256S(strTxid)); 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 string strVout = vStrInputParts[1]; diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 7fd6a9cf5..5c4c3bd27 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -15,7 +15,7 @@ #include -#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) : nonce(GetRand(std::numeric_limits::max())), @@ -50,7 +50,7 @@ uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256& txhash) const { ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock) { if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty())) 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; assert(header.IsNull() && txn_available.empty()); diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index ad9cc2617..81f40593b 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -6,10 +6,16 @@ #ifndef BITCOIN_CONSENSUS_CONSENSUS_H #define BITCOIN_CONSENSUS_CONSENSUS_H -/** The maximum allowed size for a serialized block, in bytes (network rule) */ -static const unsigned int MAX_BLOCK_SIZE = 1000000; +#include + +/** 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) */ -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) */ static const int COINBASE_MATURITY = 100; diff --git a/src/init.cpp b/src/init.cpp index 6bce0a9b4..e924d504b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -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 += HelpMessageGroup(_("Block creation options:")); + strUsage += HelpMessageOpt("-blockmaxcost=", strprintf(_("Set maximum block cost (default: %d)"), DEFAULT_BLOCK_MAX_COST)); strUsage += HelpMessageOpt("-blockminsize=", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE)); strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); diff --git a/src/main.cpp b/src/main.cpp index c22faf6db..df758bc41 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -684,8 +684,8 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c // have been mined or received. // 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): - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + unsigned int sz = GetTransactionCost(tx); + if (sz >= MAX_STANDARD_TX_COST) { LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; @@ -1018,8 +1018,24 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in 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()) 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) - 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"); // Check for negative or overflow output values @@ -1239,8 +1255,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (fRequireStandard && !AreInputsStandard(tx, view)) return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); - unsigned int nSigOps = GetLegacySigOpCount(tx); - nSigOps += GetP2SHSigOpCount(tx, view); + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); CAmount nValueOut = tx.GetValueOut(); 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(); // 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 // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than // 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, - strprintf("%d", nSigOps)); + strprintf("%d", nSigOpsCost)); CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { @@ -2439,7 +2454,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector prevheights; CAmount nFees = 0; int nInputs = 0; - unsigned int nSigOps = 0; + int64_t nSigOpsCost = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; vPos.reserve(block.vtx.size()); @@ -2449,10 +2464,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin const CTransaction &tx = block.vtx[i]; 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()) { @@ -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__), REJECT_INVALID, "bad-txns-nonfinal"); } + } - if (fStrictPayToScriptHash) - { - // Add in sigops done by pay-to-script-hash inputs; - // this is to prevent a "rogue miner" from creating - // an incredibly-expensive-to-validate block. - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock(): too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - } + // GetTransactionSigOpCost counts 3 types of sigops: + // * legacy (always) + // * p2sh (when P2SH enabled in flags and excludes coinbase) + // * witness (when witness enabled in flags and excludes coinbase) + nSigOpsCost += GetTransactionSigOpCost(tx, view, flags); + if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST) + return state.DoS(100, error("ConnectBlock(): too many sigops"), + REJECT_INVALID, "bad-blk-sigops"); + if (!tx.IsCoinBase()) + { nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector 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 // transaction validation, as otherwise we may mark the header as invalid // 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 - 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"); // 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); } - 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"); 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; } @@ -4284,7 +4308,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB int nLoaded = 0; try { // 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(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -4303,7 +4327,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB continue; // read size blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE) continue; } catch (const std::exception&) { // no valid block header found; don't complain diff --git a/src/main.h b/src/main.h index 6dd4b1aa3..317470e3c 100644 --- a/src/main.h +++ b/src/main.h @@ -152,6 +152,7 @@ typedef boost::unordered_map BlockMap; extern BlockMap mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; +extern uint64_t nLastBlockCost; extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index dca4973cc..31332526a 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -155,7 +155,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch, std::ve if (nTransactions == 0) return uint256(); // 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(); // there can never be more hashes provided than one for every txid if (vHash.size() > nTransactions) diff --git a/src/miner.cpp b/src/miner.cpp index a7bf9ae84..cfc2dae56 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -45,6 +45,7 @@ using namespace std; uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; +uint64_t nLastBlockCost = 0; class ScoreCompare { @@ -75,15 +76,36 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam BlockAssembler::BlockAssembler(const CChainParams& _chainparams) : chainparams(_chainparams) { - // Largest block you're willing to create: - nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + // Block resource limits + // If neither -blockmaxsize or -blockmaxcost is given, limit to DEFAULT_BLOCK_MAX_* + // If only one is given, only restrict the specified resource. + // 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 // until there are no more or the block reaches this size: nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); 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() @@ -92,7 +114,8 @@ void BlockAssembler::resetBlock() // Reserve space for coinbase tx nBlockSize = 1000; - nBlockSigOps = 100; + nBlockCost = 4000; + nBlockSigOpsCost = 400; fIncludeWitness = false; // These counters do not include coinbase tx @@ -116,7 +139,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) // Add dummy coinbase tx as first transaction pblock->vtx.push_back(CTransaction()); 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); CBlockIndex* pindexPrev = chainActive.Tip(); @@ -144,11 +167,18 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()); 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; 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. CMutableTransaction coinbaseTx; @@ -167,7 +197,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nNonce = 0; - pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); + pblocktemplate->vTxSigOpsCost[0] = GetLegacySigOpCount(pblock->vtx[0]); CValidationState state; 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; - if (nBlockSigOps + packageSigOps >= MAX_BLOCK_SIGOPS) + if (nBlockSigOpsCost + packageSigOpsCost >= MAX_BLOCK_SIGOPS_COST) return false; return true; } @@ -223,26 +254,39 @@ bool BlockAssembler::TestPackageFinality(const CTxMemPool::setEntries& package) 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 // or if we've tried more than 50 times to fill remaining space // then flag that the block is finished - if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { + if (nBlockCost > nBlockMaxCost - 400 || lastFewTxs > 50) { blockFinished = true; 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. - if (nBlockSize > nBlockMaxSize - 1000) { + if (nBlockCost > nBlockMaxCost - 4000) { lastFewTxs++; } 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 // flag that the block is finished - if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + if (nBlockSigOpsCost > MAX_BLOCK_SIGOPS_COST - 8) { blockFinished = true; return false; } @@ -264,10 +308,13 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) { pblock->vtx.push_back(iter->GetTx()); pblocktemplate->vTxFees.push_back(iter->GetFee()); - pblocktemplate->vTxSigOps.push_back(iter->GetSigOpCount()); - nBlockSize += iter->GetTxSize(); + pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost()); + if (fNeedSizeAccounting) { + nBlockSize += ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION); + } + nBlockCost += iter->GetTxCost(); ++nBlockTx; - nBlockSigOps += iter->GetSigOpCount(); + nBlockSigOpsCost += iter->GetSigOpCost(); nFees += iter->GetFee(); inBlock.insert(iter); @@ -358,7 +405,7 @@ void BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& alread CTxMemPoolModifiedEntry modEntry(desc); modEntry.nSizeWithAncestors -= it->GetTxSize(); modEntry.nModFeesWithAncestors -= it->GetModifiedFee(); - modEntry.nSigOpCountWithAncestors -= it->GetSigOpCount(); + modEntry.nSigOpCostWithAncestors -= it->GetSigOpCost(); mapModifiedTx.insert(modEntry); } else { mapModifiedTx.modify(mit, update_for_parent_inclusion(it)); @@ -460,19 +507,19 @@ void BlockAssembler::addPackageTxs() uint64_t packageSize = iter->GetSizeWithAncestors(); CAmount packageFees = iter->GetModFeesWithAncestors(); - unsigned int packageSigOps = iter->GetSigOpCountWithAncestors(); + int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors(); if (fUsingModified) { packageSize = modit->nSizeWithAncestors; 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 return; } - if (!TestPackage(packageSize, packageSigOps)) { + if (!TestPackage(packageSize, packageSigOpsCost)) { if (fUsingModified) { // Since we always look at the best entry in mapModifiedTx, // we must erase failed entries so that we can consider the @@ -526,6 +573,8 @@ void BlockAssembler::addPriorityTxs() return; } + fNeedSizeAccounting = true; + // This vector will be sorted into a priority queue: vector vecPriority; TxCoinAgePriorityCompare pricomparer; diff --git a/src/miner.h b/src/miner.h index 8bfc1493d..b303a8fa3 100644 --- a/src/miner.h +++ b/src/miner.h @@ -28,7 +28,7 @@ struct CBlockTemplate { CBlock block; std::vector vTxFees; - std::vector vTxSigOps; + std::vector vTxSigOpsCost; std::vector vchCoinbaseCommitment; }; @@ -40,13 +40,13 @@ struct CTxMemPoolModifiedEntry { iter = entry; nSizeWithAncestors = entry->GetSizeWithAncestors(); nModFeesWithAncestors = entry->GetModFeesWithAncestors(); - nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors(); + nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors(); } CTxMemPool::txiter iter; uint64_t nSizeWithAncestors; CAmount nModFeesWithAncestors; - unsigned int nSigOpCountWithAncestors; + int64_t nSigOpCostWithAncestors; }; /** Comparator for CTxMemPool::txiter objects. @@ -124,7 +124,7 @@ struct update_for_parent_inclusion { e.nModFeesWithAncestors -= iter->GetFee(); e.nSizeWithAncestors -= iter->GetTxSize(); - e.nSigOpCountWithAncestors -= iter->GetSigOpCount(); + e.nSigOpCostWithAncestors -= iter->GetSigOpCost(); } CTxMemPool::txiter iter; @@ -141,12 +141,14 @@ private: // Configuration parameters for the block size bool fIncludeWitness; - unsigned int nBlockMaxSize, nBlockMinSize; + unsigned int nBlockMaxCost, nBlockMaxSize, nBlockMinSize; + bool fNeedSizeAccounting; // Information on the current status of the block + uint64_t nBlockCost; uint64_t nBlockSize; uint64_t nBlockTx; - unsigned int nBlockSigOps; + uint64_t nBlockSigOpsCost; CAmount nFees; CTxMemPool::setEntries inBlock; @@ -189,7 +191,7 @@ private: /** Remove confirmed (inBlock) entries from given set */ void onlyUnconfirmed(CTxMemPool::setEntries& testSet); /** 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 */ bool TestPackageFinality(const CTxMemPool::setEntries& package); /** Return true if given transaction from mapTx has already been evaluated, diff --git a/src/net.cpp b/src/net.cpp index dc83f19be..4cbc43e4d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2182,7 +2182,7 @@ void CNode::RecordBytesSent(uint64_t bytes) void CNode::SetMaxOutboundTarget(uint64_t limit) { LOCK(cs_totalBytesSent); - uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SIZE; + uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SERIALIZED_SIZE; nMaxOutboundLimit = limit; 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 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) return true; } diff --git a/src/net.h b/src/net.h index 7ed18b205..41315fc9b 100644 --- a/src/net.h +++ b/src/net.h @@ -45,8 +45,8 @@ static const int TIMEOUT_INTERVAL = 20 * 60; static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; -/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ -static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; +/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */ +static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000; /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** -listen default */ diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 67434a38f..f2148bfe1 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -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 // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) { + unsigned int sz = GetTransactionCost(tx); + if (sz >= MAX_STANDARD_TX_COST) { reason = "tx-size"; return false; } @@ -150,3 +150,13 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) 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)); +} diff --git a/src/policy/policy.h b/src/policy/policy.h index a6bcc777f..fefb562ff 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -19,12 +19,14 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ 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 */ -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 */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** 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 */ 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); +/** 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 diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 6fb33230a..df900388f 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -31,3 +31,12 @@ std::string CBlock::ToString() const } 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); +} diff --git a/src/primitives/block.h b/src/primitives/block.h index 29307aed5..e2a309e63 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -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 diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index b0230530e..7f10409c0 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -121,7 +121,7 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const // Providing any more cleanup incentive than making additional inputs free would // risk encouraging people to create junk outputs to redeem later. if (nTxSize == 0) - nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + nTxSize = (GetTransactionCost(*this) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR; for (std::vector::const_iterator it(vin.begin()); it != vin.end(); ++it) { 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"; 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); +} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 5a5824134..e87ad90f0 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -13,6 +13,8 @@ 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 */ class COutPoint { @@ -166,15 +168,30 @@ public: // which has units satoshis-per-kilobyte. // If you'd pay more than 1/3 in fees // 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: // 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()) return 0; - size_t nSize = GetSerializeSize(SER_DISK,0)+148u; - return 3*minRelayTxFee.GetFee(nSize); + size_t nSize = GetSerializeSize(SER_DISK, 0); + int witnessversion = 0; + std::vector 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 @@ -442,4 +459,7 @@ struct CMutableTransaction 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 diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f11af876f..43ba4edd7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -101,6 +101,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx 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("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("version", 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" " \"size\" : n, (numeric) The block size\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" " \"version\" : n, (numeric) The block version\n" " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 291314b8b..4c4e59978 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -224,6 +224,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) "{\n" " \"blocks\": nnn, (numeric) The current block\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" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" " \"errors\": \"...\" (string) Current errors\n" @@ -242,6 +243,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("blocks", (int)chainActive.Height())); 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("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); @@ -349,13 +351,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " {\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\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" " 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" " \"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" " }\n" " ,...\n" @@ -372,8 +375,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " ,...\n" " ],\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" + " \"costlimit\" : n, (numeric) limit of block cost\n" " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" " \"bits\" : \"xxx\", (string) compressed target of 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; 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); } @@ -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("mutable", aMutable)); result.push_back(Pair("noncerange", "00000000ffffffff")); - result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS_COST)); + 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("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 12e3eaa92..b36179943 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -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("hash", tx.GetWitnessHash().GetHex())); 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("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" " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\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" " \"locktime\" : ttt, (numeric) The lock time\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" " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\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" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 4deebd050..bc027e9f0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1470,3 +1470,50 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_success(serror); } + +size_t static WitnessSigOps(int witversion, const std::vector& 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 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 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; +} diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 864372942..bd2f21166 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -156,4 +156,6 @@ public: bool EvalScript(std::vector >& 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); +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); + #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index c8b43df26..033a50f94 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "policy/policy.h" #include "txmempool.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].nValue = 2 * COIN; 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 */ CMutableTransaction tx3 = CMutableTransaction(); @@ -384,7 +385,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) tx6.vout.resize(1); tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; 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)); BOOST_CHECK_EQUAL(pool.size(), 6); @@ -398,7 +399,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) tx7.vout.resize(1); tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; 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 */ 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(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(tx2.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); CMutableTransaction tx4 = CMutableTransaction(); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ca8d6d2e0..fd581db52 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -181,6 +181,9 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, // NOTE: These tests rely on CreateNewBlock doing its own self-validation! 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); CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CBlockTemplate *pblocktemplate; @@ -264,7 +267,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); 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 - 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; } BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 2b00e6f56..5c902387f 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "policy/policy.h" #include "policy/fees.h" #include "txmempool.h" #include "uint256.h" @@ -50,7 +51,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) tx.vin[0].scriptSig = garbage; tx.vout.resize(1); tx.vout[0].nValue=0LL; - CFeeRate baseRate(basefee, ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); + CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx)); // Create a fake block std::vector block; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 1f3b19880..856f9b842 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -136,7 +136,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CTransaction &txn, CTxMemPool *po CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight, - hasNoDependencies, inChainValue, spendsCoinbase, sigOpCount, lp); + hasNoDependencies, inChainValue, spendsCoinbase, sigOpCost, lp); } void Shutdown(void* parg) diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 78b87e710..bc0d2fe31 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -70,12 +70,12 @@ struct TestMemPoolEntryHelper unsigned int nHeight; bool hadNoDependencies; bool spendsCoinbase; - unsigned int sigOpCount; + unsigned int sigOpCost; LockPoints lp; TestMemPoolEntryHelper() : 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(CTransaction &tx, CTxMemPool *pool = NULL); @@ -87,6 +87,6 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; 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 diff --git a/src/txmempool.cpp b/src/txmempool.cpp index ead28546d..a48a6d946 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -9,6 +9,7 @@ #include "consensus/consensus.h" #include "consensus/validation.h" #include "main.h" +#include "policy/policy.h" #include "policy/fees.h" #include "streams.h" #include "timedata.h" @@ -22,17 +23,17 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, - bool _spendsCoinbase, unsigned int _sigOps, LockPoints lp): + bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp): tx(std::make_shared(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), - spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), lockPoints(lp) + spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp) { - nTxSize = ::GetSerializeSize(_tx, SER_NETWORK, PROTOCOL_VERSION); - nModSize = _tx.CalculateModifiedSize(nTxSize); + nTxCost = GetTransactionCost(_tx); + nModSize = _tx.CalculateModifiedSize(GetTxSize()); nUsageSize = RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(tx); nCountWithDescendants = 1; - nSizeWithDescendants = nTxSize; + nSizeWithDescendants = GetTxSize(); nModFeesWithDescendants = nFee; CAmount nValueIn = _tx.GetValueOut()+nFee; assert(inChainInputValue <= nValueIn); @@ -40,9 +41,9 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, feeDelta = 0; nCountWithAncestors = 1; - nSizeWithAncestors = nTxSize; + nSizeWithAncestors = GetTxSize(); nModFeesWithAncestors = nFee; - nSigOpCountWithAncestors = sigOpCount; + nSigOpCostWithAncestors = sigOpCost; } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) @@ -72,6 +73,11 @@ void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp) lockPoints = lp; } +size_t CTxMemPoolEntry::GetTxSize() const +{ + return GetVirtualTransactionSize(nTxCost); +} + // Update the given tx for any in-mempool descendants. // Assumes that setMemPoolChildren is correct for the given tx and all // descendants. @@ -111,7 +117,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan modifyCount++; cachedDescendants[updateIt].insert(cit); // 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)); @@ -247,13 +253,13 @@ void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncesto int64_t updateCount = setAncestors.size(); int64_t updateSize = 0; CAmount updateFee = 0; - int updateSigOps = 0; + int64_t updateSigOpsCost = 0; BOOST_FOREACH(txiter ancestorIt, setAncestors) { updateSize += ancestorIt->GetTxSize(); 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) @@ -282,7 +288,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b setDescendants.erase(removeIt); // don't update state for self int64_t modifySize = -((int64_t)removeIt->GetTxSize()); CAmount modifyFee = -removeIt->GetModifiedFee(); - int modifySigOps = -removeIt->GetSigOpCount(); + int modifySigOps = -removeIt->GetSigOpCost(); BOOST_FOREACH(txiter dit, setDescendants) { mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps)); } @@ -338,8 +344,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, nModFeesWithAncestors += modifyFee; nCountWithAncestors += modifyCount; assert(int64_t(nCountWithAncestors) > 0); - nSigOpCountWithAncestors += modifySigOps; - assert(int(nSigOpCountWithAncestors) >= 0); + nSigOpCostWithAncestors += modifySigOps; + assert(int(nSigOpCostWithAncestors) >= 0); } CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) : @@ -666,7 +672,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const bool fDependsWait = false; setEntries setParentCheck; int64_t parentSizes = 0; - unsigned int parentSigOpCount = 0; + int64_t parentSigOpCost = 0; BOOST_FOREACH(const CTxIn &txin, tx.vin) { // 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); @@ -676,7 +682,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const fDependsWait = true; if (setParentCheck.insert(it2).second) { parentSizes += it2->GetTxSize(); - parentSigOpCount += it2->GetSigOpCount(); + parentSigOpCost += it2->GetSigOpCost(); } } else { 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 nSizeCheck = it->GetTxSize(); CAmount nFeesCheck = it->GetModifiedFee(); - unsigned int nSigOpCheck = it->GetSigOpCount(); + int64_t nSigOpCheck = it->GetSigOpCost(); BOOST_FOREACH(txiter ancestorIt, setAncestors) { nSizeCheck += ancestorIt->GetTxSize(); nFeesCheck += ancestorIt->GetModifiedFee(); - nSigOpCheck += ancestorIt->GetSigOpCount(); + nSigOpCheck += ancestorIt->GetSigOpCost(); } assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetSizeWithAncestors() == nSizeCheck); - assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); + assert(it->GetSigOpCostWithAncestors() == nSigOpCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck); // Check children against mapNextTx diff --git a/src/txmempool.h b/src/txmempool.h index d6d0d72ff..e5a500e19 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -78,7 +78,7 @@ class CTxMemPoolEntry private: std::shared_ptr tx; 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 nUsageSize; //!< ... and total memory usage 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 CAmount inChainInputValue; //!< Sum of all txin values that are already in blockchain 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 LockPoints lockPoints; //!< Track the height and time at which tx was final @@ -104,13 +104,13 @@ private: uint64_t nCountWithAncestors; uint64_t nSizeWithAncestors; CAmount nModFeesWithAncestors; - unsigned int nSigOpCountWithAncestors; + int64_t nSigOpCostWithAncestors; public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase, - unsigned int nSigOps, LockPoints lp); + int64_t nSigOpsCost, LockPoints lp); CTxMemPoolEntry(const CTxMemPoolEntry& other); const CTransaction& GetTx() const { return *this->tx; } @@ -121,11 +121,12 @@ public: */ double GetPriority(unsigned int currentHeight) const; 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; } unsigned int GetHeight() const { return entryHeight; } bool WasClearAtEntry() const { return hadNoDependencies; } - unsigned int GetSigOpCount() const { return sigOpCount; } + int64_t GetSigOpCost() const { return sigOpCost; } int64_t GetModifiedFee() const { return nFee + feeDelta; } size_t DynamicMemoryUsage() const { return nUsageSize; } const LockPoints& GetLockPoints() const { return lockPoints; } @@ -149,7 +150,7 @@ public: uint64_t GetCountWithAncestors() const { return nCountWithAncestors; } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } 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 }; @@ -172,18 +173,18 @@ struct update_descendant_state struct update_ancestor_state { - update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int _modifySigOps) : - modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOps(_modifySigOps) + update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) : + modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost) {} void operator() (CTxMemPoolEntry &e) - { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOps); } + { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); } private: int64_t modifySize; CAmount modifyFee; int64_t modifyCount; - int modifySigOps; + int64_t modifySigOpsCost; }; struct update_fee_delta diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 723b2ecef..babde1a02 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2348,7 +2348,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt 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 if (!sign) { @@ -2360,7 +2360,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt *static_cast(&wtxNew) = CTransaction(txNew); // Limit size - if (nBytes >= MAX_STANDARD_TX_SIZE) + if (GetTransactionCost(txNew) >= MAX_STANDARD_TX_COST) { strFailReason = _("Transaction too large"); return false;