/// <summary> /// Constructs a memory pool feature. /// </summary> /// <param name="connectionManager">Connection manager for managing node connections.</param> /// <param name="mempoolSignaled">Observes block signal notifications from signals.</param> /// <param name="blocksDisconnectedSignaled">Observes reorged headers signal notifications from signals.</param> /// <param name="mempoolBehavior">Memory pool node behavior for managing attached node messages.</param> /// <param name="mempoolManager">Memory pool manager for managing external access to memory pool.</param> /// <param name="loggerFactory">Logger factory for creating instance logger.</param> public MempoolFeature( IConnectionManager connectionManager, MempoolSignaled mempoolSignaled, BlocksDisconnectedSignaled blocksDisconnectedSignaled, MempoolBehavior mempoolBehavior, MempoolManager mempoolManager, ILoggerFactory loggerFactory, INodeStats nodeStats) { this.connectionManager = connectionManager; this.mempoolSignaled = mempoolSignaled; this.blocksDisconnectedSignaled = blocksDisconnectedSignaled; this.mempoolBehavior = mempoolBehavior; this.mempoolManager = mempoolManager; this.logger = loggerFactory.CreateLogger(GetType().FullName); nodeStats.RegisterStats(AddComponentStats, StatsType.Component, GetType().Name); }
/// <summary> /// Processes orphan transactions. /// Executed when receive a new transaction through MempoolBehavior. /// </summary> /// <param name="behavior">Memory pool behavior that received new transaction.</param> /// <param name="tx">The new transaction received.</param> public async Task ProcessesOrphansAsync(MempoolBehavior behavior, Transaction tx) { var workQueue = new Queue <OutPoint>(); var eraseQueue = new List <uint256>(); var trxHash = tx.GetHash(); for (var index = 0; index < tx.Outputs.Count; index++) { workQueue.Enqueue(new OutPoint(trxHash, index)); } // Recursively process any orphan transactions that depended on this one var setMisbehaving = new List <ulong>(); while (workQueue.Any()) { List <OrphanTx> itByPrev = null; lock (this.lockObject) { var prevOrphans = this.mapOrphanTransactionsByPrev.TryGet(workQueue.Dequeue()); if (prevOrphans != null) { // Create a copy of the list so we can manage it outside of the lock. itByPrev = prevOrphans.ToList(); } } if (itByPrev == null) { continue; } foreach (var mi in itByPrev) { var orphanTx = mi.Tx; var orphanHash = orphanTx.GetHash(); var fromPeer = mi.NodeId; if (setMisbehaving.Contains(fromPeer)) { continue; } // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get // anyone relaying LegitTxX banned) var stateDummy = new MempoolValidationState(true); if (await this.Validator.AcceptToMemoryPool(stateDummy, orphanTx)) { this.logger.LogInformation("accepted orphan tx {0}", orphanHash); behavior.RelayTransaction(orphanTx.GetHash()); this.signals.Publish(new TransactionReceived(orphanTx)); for (var index = 0; index < orphanTx.Outputs.Count; index++) { workQueue.Enqueue(new OutPoint(orphanHash, index)); } eraseQueue.Add(orphanHash); } else if (!stateDummy.MissingInputs) { var nDos = 0; if (stateDummy.IsInvalid && nDos > 0) { // Punish peer that gave us an invalid orphan tx //Misbehaving(fromPeer, nDos); setMisbehaving.Add(fromPeer); this.logger.LogInformation("invalid orphan tx {0}", orphanHash); } // Has inputs but not accepted to mempool // Probably non-standard or insufficient fee/priority this.logger.LogInformation("removed orphan tx {0}", orphanHash); eraseQueue.Add(orphanHash); if (!orphanTx.HasWitness && !stateDummy.CorruptionPossible) { // Do not use rejection cache for witness transactions or // witness-stripped transactions, as they can have been malleated. // See https://github.com/bitcoin/bitcoin/issues/8279 for details. AddToRecentRejects(orphanHash); } } // TODO: implement sanity checks. //this.memPool.Check(new MempoolCoinView(this.coinView, this.memPool, this.MempoolLock, this.Validator)); } } if (eraseQueue.Count > 0) { lock (this.lockObject) { foreach (var hash in eraseQueue) { EraseOrphanTxLock(hash); } } } }