diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -47,18 +47,24 @@ explicit CTxMemPoolModifiedEntry(CTxMemPool::txiter entry) { iter = entry; nSizeWithAncestors = entry->GetSizeWithAncestors(); + nVirtualSizeWithAncestors = entry->GetVirtualSizeWithAncestors(); nModFeesWithAncestors = entry->GetModFeesWithAncestors(); nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors(); } Amount GetModifiedFee() const { return iter->GetModifiedFee(); } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } + uint64_t GetVirtualSizeWithAncestors() const { + return nVirtualSizeWithAncestors; + } 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; uint64_t nSizeWithAncestors; + uint64_t nVirtualSizeWithAncestors; Amount nModFeesWithAncestors; int64_t nSigOpCountWithAncestors; }; @@ -120,6 +126,7 @@ void operator()(CTxMemPoolModifiedEntry &e) { e.nModFeesWithAncestors -= iter->GetFee(); e.nSizeWithAncestors -= iter->GetTxSize(); + e.nVirtualSizeWithAncestors -= iter->GetTxVirtualSize(); e.nSigOpCountWithAncestors -= iter->GetSigOpCount(); } diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -322,6 +322,7 @@ if (mit == mapModifiedTx.end()) { CTxMemPoolModifiedEntry modEntry(desc); modEntry.nSizeWithAncestors -= it->GetTxSize(); + modEntry.nVirtualSizeWithAncestors -= it->GetTxVirtualSize(); modEntry.nModFeesWithAncestors -= it->GetModifiedFee(); modEntry.nSigOpCountWithAncestors -= it->GetSigOpCount(); mapModifiedTx.insert(modEntry); @@ -455,6 +456,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 @@ -45,6 +45,7 @@ Amount totalFee = Amount::zero(); size_t totalSize = CTransaction(parentOfAll).GetTotalSize(); + size_t totalVirtualSize = totalSize; int64_t totalSigOpCount = 0; // Generate 100 transactions @@ -58,6 +59,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 @@ -84,6 +87,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(); @@ -115,12 +121,18 @@ maxFees += randFee; minSize += CTransaction(tx).GetTotalSize(); maxSize += CTransaction(tx).GetTotalSize(); + minVirtualSize += GetVirtualTransactionSize( + CTransaction(tx).GetTotalSize(), randSigOpCount); + 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); @@ -132,6 +144,11 @@ BOOST_CHECK(latestEntry.GetSizeWithAncestors() >= minSize); BOOST_CHECK(latestEntry.GetSizeWithAncestors() <= maxSize); + BOOST_CHECK(latestEntry.GetVirtualSizeWithAncestors() >= + minVirtualSize); + BOOST_CHECK(latestEntry.GetVirtualSizeWithAncestors() <= + maxVirtualSize); + BOOST_CHECK(latestEntry.GetSigOpCountWithAncestors() >= minSigOpCount); BOOST_CHECK(latestEntry.GetSigOpCountWithAncestors() <= maxSigOpCount); @@ -141,6 +158,8 @@ BOOST_CHECK_EQUAL(parentEntry.GetCountWithDescendants(), testPool.mapTx.size()); BOOST_CHECK_EQUAL(parentEntry.GetSizeWithDescendants(), totalSize); + BOOST_CHECK_EQUAL(parentEntry.GetVirtualSizeWithDescendants(), + totalVirtualSize); BOOST_CHECK_EQUAL(parentEntry.GetModFeesWithDescendants(), totalFee); } } diff --git a/src/txmempool.h b/src/txmempool.h --- a/src/txmempool.h +++ b/src/txmempool.h @@ -94,13 +94,15 @@ uint64_t nCountWithDescendants; //!< ... and size uint64_t nSizeWithDescendants; - + //!< ... and virtual size + uint64_t nVirtualSizeWithDescendants; //!< ... and total fees (all including us) Amount nModFeesWithDescendants; // Analogous statistics for ancestor transactions uint64_t nCountWithAncestors; uint64_t nSizeWithAncestors; + uint64_t nVirtualSizeWithAncestors; Amount nModFeesWithAncestors; int64_t nSigOpCountWithAncestors; @@ -113,6 +115,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; } @@ -122,11 +125,12 @@ const LockPoints &GetLockPoints() const { return lockPoints; } // Adjusts the descendant state. - void UpdateDescendantState(int64_t modifySize, Amount modifyFee, - int64_t modifyCount); + void UpdateDescendantState(int64_t modifySize, int64_t modifyVirtualSize, + Amount modifyFee, int64_t modifyCount); // Adjusts the ancestor state - void UpdateAncestorState(int64_t modifySize, Amount modifyFee, - int64_t modifyCount, int modifySigOps); + void UpdateAncestorState(int64_t modifySize, int64_t modifyVirtualSize, + Amount modifyFee, int64_t modifyCount, + int modifySigOps); // Updates the fee delta used for mining priority score, and the // modified fees with descendants. void UpdateFeeDelta(Amount feeDelta); @@ -135,12 +139,18 @@ uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } + uint64_t GetVirtualSizeWithDescendants() const { + return nVirtualSizeWithDescendants; + } Amount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } uint64_t GetCountWithAncestors() const { return nCountWithAncestors; } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } + uint64_t GetVirtualSizeWithAncestors() const { + return nVirtualSizeWithAncestors; + } Amount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } int64_t GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; @@ -152,34 +162,39 @@ // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. struct update_descendant_state { - update_descendant_state(int64_t _modifySize, Amount _modifyFee, - int64_t _modifyCount) - : modifySize(_modifySize), modifyFee(_modifyFee), - modifyCount(_modifyCount) {} + update_descendant_state(int64_t _modifySize, int64_t _modifyVirtualSize, + Amount _modifyFee, int64_t _modifyCount) + : modifySize(_modifySize), modifyVirtualSize(_modifyVirtualSize), + modifyFee(_modifyFee), modifyCount(_modifyCount) {} void operator()(CTxMemPoolEntry &e) { - e.UpdateDescendantState(modifySize, modifyFee, modifyCount); + e.UpdateDescendantState(modifySize, modifyVirtualSize, modifyFee, + modifyCount); } private: int64_t modifySize; + int64_t modifyVirtualSize; Amount modifyFee; int64_t modifyCount; }; struct update_ancestor_state { - update_ancestor_state(int64_t _modifySize, Amount _modifyFee, - int64_t _modifyCount, int64_t _modifySigOpCount) - : modifySize(_modifySize), modifyFee(_modifyFee), - modifyCount(_modifyCount), modifySigOpCount(_modifySigOpCount) {} + update_ancestor_state(int64_t _modifySize, int64_t _modifyVirtualSize, + Amount _modifyFee, int64_t _modifyCount, + int64_t _modifySigOpCount) + : modifySize(_modifySize), modifyVirtualSize(_modifyVirtualSize), + modifyFee(_modifyFee), modifyCount(_modifyCount), + modifySigOpCount(_modifySigOpCount) {} void operator()(CTxMemPoolEntry &e) { - e.UpdateAncestorState(modifySize, modifyFee, modifyCount, - modifySigOpCount); + e.UpdateAncestorState(modifySize, modifyVirtualSize, modifyFee, + modifyCount, modifySigOpCount); } private: int64_t modifySize; + int64_t modifyVirtualSize; Amount modifyFee; int64_t modifyCount; int64_t modifySigOpCount; diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -37,16 +37,22 @@ nCountWithDescendants = 1; nSizeWithDescendants = GetTxSize(); + nVirtualSizeWithDescendants = GetTxVirtualSize(); nModFeesWithDescendants = nFee; feeDelta = Amount::zero(); nCountWithAncestors = 1; nSizeWithAncestors = GetTxSize(); + nVirtualSizeWithAncestors = GetTxVirtualSize(); nModFeesWithAncestors = nFee; nSigOpCountWithAncestors = sigOpCount; } +size_t CTxMemPoolEntry::GetTxVirtualSize() const { + return GetVirtualTransactionSize(nTxSize, sigOpCount); +} + void CTxMemPoolEntry::UpdateFeeDelta(Amount newFeeDelta) { nModFeesWithDescendants += newFeeDelta - feeDelta; nModFeesWithAncestors += newFeeDelta - feeDelta; @@ -88,23 +94,27 @@ // setAllDescendants now contains all in-mempool descendants of updateIt. // Update and add to cached descendant map int64_t modifySize = 0; + int64_t modifyVirtualSize = 0; int64_t modifyCount = 0; Amount modifyFee = Amount::zero(); for (txiter cit : setAllDescendants) { if (!setExclude.count(cit->GetTx().GetId())) { modifySize += cit->GetTxSize(); + modifyVirtualSize += cit->GetTxVirtualSize(); modifyFee += cit->GetModifiedFee(); modifyCount++; cachedDescendants[updateIt].insert(cit); // Update ancestor state for each descendant mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), + updateIt->GetTxVirtualSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount())); } } mapTx.modify(updateIt, - update_descendant_state(modifySize, modifyFee, modifyCount)); + update_descendant_state(modifySize, modifyVirtualSize, + modifyFee, modifyCount)); } // txidsToUpdate is the set of transaction hashes from a disconnected block @@ -250,10 +260,12 @@ } const int64_t updateCount = (add ? 1 : -1); const int64_t updateSize = updateCount * it->GetTxSize(); + const int64_t updateVirtualSize = updateCount * it->GetTxVirtualSize(); const Amount updateFee = updateCount * it->GetModifiedFee(); for (txiter ancestorIt : setAncestors) { - mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, - updateCount)); + mapTx.modify(ancestorIt, + update_descendant_state(updateSize, updateVirtualSize, + updateFee, updateCount)); } } @@ -261,16 +273,19 @@ const setEntries &setAncestors) { int64_t updateCount = setAncestors.size(); int64_t updateSize = 0; + int64_t updateVirtualSize = 0; int64_t updateSigOpsCount = 0; Amount updateFee = Amount::zero(); for (txiter ancestorIt : setAncestors) { updateSize += ancestorIt->GetTxSize(); + updateVirtualSize += ancestorIt->GetTxVirtualSize(); updateFee += ancestorIt->GetModifiedFee(); updateSigOpsCount += ancestorIt->GetSigOpCount(); } - mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, - updateSigOpsCount)); + mapTx.modify(it, + update_ancestor_state(updateSize, updateVirtualSize, updateFee, + updateCount, updateSigOpsCount)); } void CTxMemPool::UpdateChildrenForRemoval(txiter it) { @@ -296,11 +311,13 @@ CalculateDescendants(removeIt, setDescendants); setDescendants.erase(removeIt); // don't update state for self int64_t modifySize = -int64_t(removeIt->GetTxSize()); + int64_t modifyVirtualSize = -int64_t(removeIt->GetTxVirtualSize()); Amount modifyFee = -1 * removeIt->GetModifiedFee(); int modifySigOps = -removeIt->GetSigOpCount(); for (txiter dit : setDescendants) { - mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, - -1, modifySigOps)); + mapTx.modify( + dit, update_ancestor_state(modifySize, modifyVirtualSize, + modifyFee, -1, modifySigOps)); } } } @@ -341,20 +358,26 @@ } void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, + int64_t modifyVirtualSize, Amount modifyFee, int64_t modifyCount) { nSizeWithDescendants += modifySize; assert(int64_t(nSizeWithDescendants) > 0); + nVirtualSizeWithDescendants += modifyVirtualSize; + assert(int64_t(nVirtualSizeWithDescendants) > 0); nModFeesWithDescendants += modifyFee; nCountWithDescendants += modifyCount; assert(int64_t(nCountWithDescendants) > 0); } -void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, Amount modifyFee, - int64_t modifyCount, +void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, + int64_t modifyVirtualSize, + Amount modifyFee, int64_t modifyCount, int modifySigOps) { nSizeWithAncestors += modifySize; assert(int64_t(nSizeWithAncestors) > 0); + nVirtualSizeWithAncestors += modifyVirtualSize; + assert(int64_t(nVirtualSizeWithAncestors) > 0); nModFeesWithAncestors += modifyFee; nCountWithAncestors += modifyCount; assert(int64_t(nCountWithAncestors) > 0); @@ -729,17 +752,20 @@ nNoLimit, nNoLimit, dummy); uint64_t nCountCheck = setAncestors.size() + 1; uint64_t nSizeCheck = it->GetTxSize(); + uint64_t nVirtualSizeCheck = it->GetTxVirtualSize(); Amount nFeesCheck = it->GetModifiedFee(); int64_t nSigOpCheck = it->GetSigOpCount(); for (txiter ancestorIt : setAncestors) { nSizeCheck += ancestorIt->GetTxSize(); + nVirtualSizeCheck += ancestorIt->GetTxVirtualSize(); nFeesCheck += ancestorIt->GetModifiedFee(); nSigOpCheck += ancestorIt->GetSigOpCount(); } assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetSizeWithAncestors() == nSizeCheck); + assert(it->GetVirtualSizeWithAncestors() == nVirtualSizeCheck); assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck); @@ -747,6 +773,7 @@ CTxMemPool::setEntries setChildrenCheck; auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetId(), 0)); uint64_t child_sizes = 0; + uint64_t child_virtual_sizes = 0; for (; iter != mapNextTx.end() && iter->first->GetTxId() == it->GetTx().GetId(); ++iter) { @@ -755,6 +782,7 @@ assert(childit != mapTx.end()); if (setChildrenCheck.insert(childit).second) { child_sizes += childit->GetTxSize(); + child_virtual_sizes += childit->GetTxVirtualSize(); } } assert(setChildrenCheck == GetMemPoolChildren(it)); @@ -762,6 +790,8 @@ // children. Just a sanity check, not definitive that this calc is // correct... assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); + assert(it->GetVirtualSizeWithDescendants() >= + child_virtual_sizes + it->GetTxVirtualSize()); if (fDependsWait) { waitingOnDependants.push_back(&(*it)); @@ -927,7 +957,7 @@ nNoLimit, nNoLimit, dummy, false); for (txiter ancestorIt : setAncestors) { mapTx.modify(ancestorIt, - update_descendant_state(0, nFeeDelta, 0)); + update_descendant_state(0, 0, nFeeDelta, 0)); } // Now update all descendants' modified fees with ancestors @@ -936,7 +966,7 @@ setDescendants.erase(it); for (txiter descendantIt : setDescendants) { mapTx.modify(descendantIt, - update_ancestor_state(0, nFeeDelta, 0, 0)); + update_ancestor_state(0, 0, nFeeDelta, 0, 0)); } ++nTransactionsUpdated; } diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -697,7 +697,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");