diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -53,8 +53,10 @@ Amount GetModifiedFee() const { return iter->GetModifiedFee(); } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } + uint64_t GetVirtualSizeWithAncestors() const; Amount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } size_t GetTxSize() const { return iter->GetTxSize(); } + size_t GetTxVirtualSize() const { return iter->GetTxVirtualSize(); } const CTransaction &GetTx() const { return iter->GetTx(); } CTxMemPool::txiter iter; diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -58,6 +58,11 @@ return nNewTime - nOldTime; } +uint64_t CTxMemPoolModifiedEntry::GetVirtualSizeWithAncestors() const { + return GetVirtualTransactionSize(nSizeWithAncestors, + nSigOpCountWithAncestors); +} + BlockAssembler::Options::Options() : nExcessiveBlockSize(DEFAULT_MAX_BLOCK_SIZE), nMaxGeneratedBlockSize(DEFAULT_MAX_GENERATED_BLOCK_SIZE), @@ -455,6 +460,9 @@ return; } + // The following must not use virtual size since TestPackage relies on + // having an accurate call to + // GetMaxBlockSigOpsCount(blockSizeWithPackage). if (!TestPackage(packageSize, packageSigOps)) { if (fUsingModified) { // Since we always look at the best entry in mapModifiedTx, we diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -44,6 +44,7 @@ Amount totalFee = Amount::zero(); size_t totalSize = CTransaction(parentOfAll).GetTotalSize(); + size_t totalVirtualSize = totalSize; int64_t totalSigOpCount = 0; // Generate 100 transactions @@ -57,6 +58,8 @@ Amount maxFees = Amount::zero(); uint64_t minSize = std::numeric_limits::max(); uint64_t maxSize = 0; + uint64_t minVirtualSize = std::numeric_limits::max(); + uint64_t maxVirtualSize = 0; int64_t minSigOpCount = std::numeric_limits::max(); int64_t maxSigOpCount = 0; // Consume random inputs, but make sure we don't consume more than @@ -83,6 +86,9 @@ maxFees += parent.GetModFeesWithAncestors(); minSize = std::min(minSize, parent.GetSizeWithAncestors()); maxSize += parent.GetSizeWithAncestors(); + minVirtualSize = + std::min(minSize, parent.GetVirtualSizeWithAncestors()); + maxVirtualSize += parent.GetVirtualSizeWithAncestors(); minSigOpCount = std::min(minSigOpCount, parent.GetSigOpCountWithAncestors()); maxSigOpCount += parent.GetSigOpCountWithAncestors(); @@ -114,16 +120,39 @@ maxFees += randFee; minSize += CTransaction(tx).GetTotalSize(); maxSize += CTransaction(tx).GetTotalSize(); + // virtualsize is a nonlinear function of its arguments, so we can't + // make as strong guarantees about its range; but assuming virtualsize + // is monotonically increasing in each argument, we can say the + // following: + minVirtualSize += 0; + maxVirtualSize += GetVirtualTransactionSize( + CTransaction(tx).GetTotalSize(), randSigOpCount); minSigOpCount += randSigOpCount; maxSigOpCount += randSigOpCount; // Calculate overall values totalFee += randFee; totalSize += CTransaction(tx).GetTotalSize(); + totalVirtualSize += GetVirtualTransactionSize( + CTransaction(tx).GetTotalSize(), randSigOpCount); totalSigOpCount += randSigOpCount; CTxMemPoolEntry parentEntry = *testPool.mapTx.find(parentOfAllId); CTxMemPoolEntry latestEntry = *testPool.mapTx.find(curId); + // Based on size/sigops ranges we can compute more strict bounds for the + // virtual size ranges/totals, assuming virtualsize is monotonic in each + // argument. + uint64_t minVirtualSize_strict = + GetVirtualTransactionSize(minSize, minSigOpCount); + uint64_t maxVirtualSize_strict = + GetVirtualTransactionSize(maxSize, maxSigOpCount); + uint64_t totalVirtualSize_strict = + GetVirtualTransactionSize(totalSize, totalSigOpCount); + // these are as-good or better than the earlier estimations. + BOOST_CHECK(minVirtualSize_strict >= minVirtualSize); + BOOST_CHECK(maxVirtualSize_strict <= maxVirtualSize); + BOOST_CHECK(totalVirtualSize_strict <= totalVirtualSize); + // Ensure values are within the expected ranges BOOST_CHECK(latestEntry.GetCountWithAncestors() >= minAncestors); BOOST_CHECK(latestEntry.GetCountWithAncestors() <= maxAncestors); @@ -131,6 +160,11 @@ BOOST_CHECK(latestEntry.GetSizeWithAncestors() >= minSize); BOOST_CHECK(latestEntry.GetSizeWithAncestors() <= maxSize); + BOOST_CHECK(latestEntry.GetVirtualSizeWithAncestors() >= + minVirtualSize_strict); + BOOST_CHECK(latestEntry.GetVirtualSizeWithAncestors() <= + maxVirtualSize_strict); + BOOST_CHECK(latestEntry.GetSigOpCountWithAncestors() >= minSigOpCount); BOOST_CHECK(latestEntry.GetSigOpCountWithAncestors() <= maxSigOpCount); @@ -140,6 +174,8 @@ BOOST_CHECK_EQUAL(parentEntry.GetCountWithDescendants(), testPool.mapTx.size()); BOOST_CHECK_EQUAL(parentEntry.GetSizeWithDescendants(), totalSize); + BOOST_CHECK_EQUAL(parentEntry.GetVirtualSizeWithDescendants(), + totalVirtualSize_strict); BOOST_CHECK_EQUAL(parentEntry.GetModFeesWithDescendants(), totalFee); BOOST_CHECK_EQUAL(parentEntry.GetSigOpCountWithDescendants(), totalSigOpCount); diff --git a/src/txmempool.h b/src/txmempool.h --- a/src/txmempool.h +++ b/src/txmempool.h @@ -114,6 +114,7 @@ CTransactionRef GetSharedTx() const { return this->tx; } const Amount GetFee() const { return nFee; } size_t GetTxSize() const { return nTxSize; } + size_t GetTxVirtualSize() const; int64_t GetTime() const { return nTime; } unsigned int GetHeight() const { return entryHeight; } @@ -136,6 +137,7 @@ uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } + uint64_t GetVirtualSizeWithDescendants() const; Amount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } int64_t GetSigOpCountWithDescendants() const { return nSigOpCountWithDescendants; @@ -145,6 +147,7 @@ uint64_t GetCountWithAncestors() const { return nCountWithAncestors; } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } + uint64_t GetVirtualSizeWithAncestors() const; Amount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } int64_t GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -46,6 +46,24 @@ nSigOpCountWithAncestors = sigOpCount; } +size_t CTxMemPoolEntry::GetTxVirtualSize() const { + return GetVirtualTransactionSize(nTxSize, sigOpCount); +} + +size_t CTxMemPoolEntry::GetVirtualSizeWithDescendants() const { + // note this is distinct from the sum of descendants' individual virtual + // sizes, and may be smaller. + return GetVirtualTransactionSize(nSizeWithDescendants, + nSigOpCountWithDescendants); +} + +size_t CTxMemPoolEntry::GetVirtualSizeWithAncestors() const { + // note this is distinct from the sum of descendants' individual virtual + // sizes, and may be smaller. + return GetVirtualTransactionSize(nSizeWithAncestors, + nSigOpCountWithAncestors); +} + void CTxMemPoolEntry::UpdateFeeDelta(Amount newFeeDelta) { nModFeesWithDescendants += newFeeDelta - feeDelta; nModFeesWithAncestors += newFeeDelta - feeDelta; diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -696,7 +696,9 @@ } // No transactions are allowed below minRelayTxFee except from - // disconnected blocks + // disconnected blocks. + // Do not change this to use virtualsize without coordinating a network + // policy upgrade. if (!bypass_limits && nModifiedFees < minRelayTxFee.GetFee(nSize)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met");