/// <summary> /// Creates a persistence entry from a memory pool transaction entry. /// </summary> /// <param name="tx">Memory pool transaction entry.</param> /// <returns>Persistence entry.</returns> public static MempoolPersistenceEntry FromTxMempoolEntry(TxMempoolEntry tx) { return(new MempoolPersistenceEntry { Tx = tx.Transaction, Time = tx.Time, FeeDelta = tx.feeDelta }); }
/** Sever link between specified transaction and direct children. */ private void UpdateChildrenForRemoval(TxMempoolEntry it) { var setMemPoolChildren = this.GetMemPoolChildren(it); foreach (var updateIt in setMemPoolChildren) { this.UpdateParent(updateIt, it, false); } }
private SetEntries GetMemPoolChildren(TxMempoolEntry entry) { Guard.NotNull(entry, nameof(entry)); Utilities.Guard.Assert(this.MapTx.ContainsKey(entry.TransactionHash)); var it = this.mapLinks.TryGet(entry); Utilities.Guard.Assert(it != null); return(it.Children); }
// addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of // addUnchecked can be used to have it call CalculateMemPoolAncestors(), and // then invoke the second version. public bool AddUnchecked(uint256 hash, TxMempoolEntry entry, bool validFeeEstimate = true) { //LOCK(cs); SetEntries setAncestors = new SetEntries(); long nNoLimit = long.MaxValue; string dummy; this.CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, out dummy); return(this.AddUnchecked(hash, entry, setAncestors, validFeeEstimate)); }
/// <summary> /// Gets transaction information for a specific transaction. /// </summary> /// <param name="hash">Hash of the transaction to query.</param> /// <returns>Transaction information.</returns> public TxMempoolInfo Info(uint256 hash) { TxMempoolEntry item = this.memPool.MapTx.TryGet(hash); return(item == null ? null : new TxMempoolInfo { Trx = item.Transaction, Time = item.Time, FeeRate = new FeeRate(item.Fee, (int)item.GetTxSize()), FeeDelta = item.ModifiedFee - item.Fee }); }
private void UpdateParent(TxMempoolEntry entry, TxMempoolEntry parent, bool add) { // todo: find how to take a memory size of SetEntries //SetEntries s; if (add && this.mapLinks[entry].Parents.Add(parent)) { this.cachedInnerUsage += parent.DynamicMemoryUsage(); } else if (!add && this.mapLinks[entry].Parents.Remove(parent)) { this.cachedInnerUsage -= parent.DynamicMemoryUsage(); } }
private void UpdateChild(TxMempoolEntry entry, TxMempoolEntry child, bool add) { // todo: find how to take a memory size of SetEntries //setEntries s; if (add && this.mapLinks[entry].Children.Add(child)) { this.cachedInnerUsage += child.DynamicMemoryUsage(); } else if (!add && this.mapLinks[entry].Children.Remove(child)) { this.cachedInnerUsage -= child.DynamicMemoryUsage(); } }
/** Set ancestor state for an entry */ void UpdateEntryForAncestors(TxMempoolEntry it, SetEntries setAncestors) { long updateCount = setAncestors.Count; long updateSize = 0; Money updateFee = 0; long updateSigOpsCost = 0; foreach (var ancestorIt in setAncestors) { updateSize += ancestorIt.GetTxSize(); updateFee += ancestorIt.ModifiedFee; updateSigOpsCost += ancestorIt.SigOpCost; } it.UpdateAncestorState(updateSize, updateFee, updateCount, updateSigOpsCost); }
/// <summary> /// Gets transaction information for a specific transaction. /// </summary> /// <param name="hash">Hash of the transaction to query.</param> /// <returns>Transaction information.</returns> public TxMempoolInfo Info(uint256 hash) { this.logger.LogTrace("({0}:'{1}')", nameof(hash), hash); TxMempoolEntry item = this.memPool.MapTx.TryGet(hash); var infoItem = item == null ? null : new TxMempoolInfo { Trx = item.Transaction, Time = item.Time, FeeRate = new FeeRate(item.Fee, (int)item.GetTxSize()), FeeDelta = item.ModifiedFee - item.Fee }; this.logger.LogTrace("(-):{0}.{1}='{2}'", nameof(TxMempoolInfo), nameof(TxMempoolInfo.Trx), infoItem?.Trx?.GetHash()); return(infoItem); }
/** Update ancestors of hash to add/remove it as a descendant transaction. */ private void UpdateAncestorsOf(bool add, TxMempoolEntry it, SetEntries setAncestors) { SetEntries parentIters = this.GetMemPoolParents(it); // add or remove this tx as a child of each parent foreach (var piter in parentIters) { this.UpdateChild(piter, it, add); } long updateCount = (add ? 1 : -1); long updateSize = updateCount * it.GetTxSize(); Money updateFee = updateCount * it.ModifiedFee; foreach (var ancestorIt in setAncestors) { ancestorIt.UpdateDescendantState(updateSize, updateFee, updateCount); } }
/// <summary> /// Copy constructor for a transaction memory pool entry. /// </summary> /// <param name="other">Entry to copy.</param> /// <exception cref="NotImplementedException"/> public TxMempoolEntry(TxMempoolEntry other) { throw new NotImplementedException(); }
/** 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 = this.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 = this.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 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) { this.UpdateParent(entry, pit, true); } } this.UpdateAncestorsOf(true, entry, setAncestors); this.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); }