/** For each transaction being removed, update ancestors and any direct children. * If updateDescendants is true, then also update in-mempool descendants' * ancestor state. */ private void UpdateForRemoveFromMempool(SetEntries entriesToRemove, bool updateDescendants) { // For each entry, walk back all ancestors and decrement size associated with this // transaction var nNoLimit = long.MaxValue; if (updateDescendants) { // updateDescendants should be true whenever we're not recursively // removing a tx and all its descendants, eg when a transaction is // confirmed in a block. // Here we only update statistics and not data in mapLinks (which // we need to preserve until we're finished with all operations that // need to traverse the mempool). foreach (var removeIt in entriesToRemove) { SetEntries setDescendants = new SetEntries(); CalculateDescendants(removeIt, setDescendants); setDescendants.Remove(removeIt); // don't update state for self var modifySize = -removeIt.GetTxSize(); var modifyFee = -removeIt.ModifiedFee; var modifySigOps = -removeIt.SigOpCost; foreach (var dit in setDescendants) { dit.UpdateAncestorState(modifySize, modifyFee, -1, modifySigOps); } } } foreach (var entry in entriesToRemove) { SetEntries setAncestors = new SetEntries(); string dummy = string.Empty; // Since this is a tx that is already in the mempool, we can call CMPA // with fSearchForParents = false. If the mempool is in a consistent // state, then using true or false should both be correct, though false // should be a bit faster. // However, if we happen to be in the middle of processing a reorg, then // the mempool can be in an inconsistent state. In this case, the set // of ancestors reachable via mapLinks will be the same as the set of // ancestors whose packages include this transaction, because when we // add a new transaction to the mempool in addUnchecked(), we assume it // has no children, and in the case of a reorg where that assumption is // false, the in-mempool children aren't linked to the in-block tx's // until UpdateTransactionsFromBlock() is called. // So if we're being called during a reorg, ie before // UpdateTransactionsFromBlock() has been called, then mapLinks[] will // differ from the set of mempool parents we'd calculate by searching, // and it's important that we use the mapLinks[] notion of ancestor // transactions as the set of things to update for removal. CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, out dummy, false); // Note that UpdateAncestorsOf severs the child links that point to // removeIt in the entries for the parents of removeIt. UpdateAncestorsOf(false, entry, setAncestors); } // After updating all the ancestor sizes, we can now sever the link between each // transaction being removed and any mempool children (ie, update setMemPoolParents // for each direct child of a transaction being removed). foreach (var removeIt in entriesToRemove) { UpdateChildrenForRemoval(removeIt); } }
public bool AddUnchecked(uint256 hash, TxMempoolEntry entry, SetEntries setAncestors, bool validFeeEstimate = true) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. //LOCK(cs); this.MapTx.Add(entry); this.mapLinks.Add(entry, new TxLinks { Parents = new SetEntries(), Children = new SetEntries() }); // Update transaction for any feeDelta created by PrioritiseTransaction // TODO: refactor so that the fee delta is calculated before inserting // into mapTx. var pos = this.mapDeltas.TryGet(hash); if (pos != null) { if (pos.Amount != null) { entry.UpdateFeeDelta(pos.Amount.Satoshi); } } // Update cachedInnerUsage to include contained transaction's usage. // (When we update the entry for in-mempool parents, memory usage will be // further updated.) this.cachedInnerUsage += entry.DynamicMemoryUsage(); var tx = entry.Transaction; HashSet <uint256> setParentTransactions = new HashSet <uint256>(); foreach (var txInput in tx.Inputs) { this.MapNextTx.Add(new NextTxPair { OutPoint = txInput.PrevOut, Transaction = tx }); setParentTransactions.Add(txInput.PrevOut.Hash); } // Don't bother worrying about child transactions of this one. // Normal case of a new transaction arriving is that there can't be any // children, because such children would be orphans. // An exception to that is if a transaction enters that used to be in a block. // In that case, our disconnect block logic will call UpdateTransactionsFromBlock // to clean up the mess we're leaving here. // Update ancestors with information about this tx foreach (var phash in setParentTransactions) { var pit = this.MapTx.TryGet(phash); if (pit != null) { UpdateParent(entry, pit, true); } } UpdateAncestorsOf(true, entry, setAncestors); UpdateEntryForAncestors(entry, setAncestors); this.nTransactionsUpdated++; this.totalTxSize += entry.GetTxSize(); this.MinerPolicyEstimator.ProcessTransaction(entry, validFeeEstimate); this.vTxHashes.Add(entry, tx.GetWitHash()); //entry.vTxHashesIdx = vTxHashes.size() - 1; return(true); }
/** Try to calculate all in-mempool ancestors of entry. * (these are all calculated including the tx itself) * limitAncestorCount = max number of ancestorsUpdateTransactionsFromBlock * limitAncestorSize = max size of ancestors * limitDescendantCount = max number of descendants any ancestor can have * limitDescendantSize = max size of descendants any ancestor can have * errString = populated with error reason if any limits are hit * fSearchForParents = whether to search a tx's vin for in-mempool parents, or * look up parents from mapLinks. Must be true for entries not in the mempool */ public bool CalculateMemPoolAncestors(TxMempoolEntry entry, SetEntries setAncestors, long limitAncestorCount, long limitAncestorSize, long limitDescendantCount, long limitDescendantSize, out string errString, bool fSearchForParents = true) { errString = string.Empty; SetEntries parentHashes = new SetEntries(); var tx = entry.Transaction; if (fSearchForParents) { // Get parents of this transaction that are in the mempool // GetMemPoolParents() is only valid for entries in the mempool, so we // iterate mapTx to find parents. foreach (var txInput in tx.Inputs) { var piter = this.MapTx.TryGet(txInput.PrevOut.Hash); if (piter != null) { parentHashes.Add(piter); if (parentHashes.Count + 1 > limitAncestorCount) { errString = $"too many unconfirmed parents [limit: {limitAncestorCount}]"; return(false); } } } } else { // If we're not searching for parents, we require this to be an // entry in the mempool already. //var it = mapTx.Txids.TryGet(entry.TransactionHash); var memPoolParents = GetMemPoolParents(entry); foreach (var item in memPoolParents) { parentHashes.Add(item); } } var totalSizeWithAncestors = entry.GetTxSize(); while (parentHashes.Any()) { var stageit = parentHashes.First(); setAncestors.Add(stageit); parentHashes.Remove(stageit); totalSizeWithAncestors += stageit.GetTxSize(); if (stageit.SizeWithDescendants + entry.GetTxSize() > limitDescendantSize) { errString = $"exceeds descendant size limit for tx {stageit.TransactionHash} [limit: {limitDescendantSize}]"; return(false); } else if (stageit.CountWithDescendants + 1 > limitDescendantCount) { errString = $"too many descendants for tx {stageit.TransactionHash} [limit: {limitDescendantCount}]"; return(false); } else if (totalSizeWithAncestors > limitAncestorSize) { errString = $"exceeds ancestor size limit [limit: {limitAncestorSize}]"; return(false); } var setMemPoolParents = GetMemPoolParents(stageit); foreach (var phash in setMemPoolParents) { // If this is a new ancestor, add it. if (!setAncestors.Contains(phash)) { parentHashes.Add(phash); } if (parentHashes.Count + setAncestors.Count + 1 > limitAncestorCount) { errString = $"too many unconfirmed ancestors [limit: {limitAncestorCount}]"; return(false); } } } return(true); }
public bool Equals(SetEntries other) { return(this.SequenceEqual(other, this)); }