/// <summary> /// Validates and then adds a transaction to memory pool. /// </summary> /// <param name="state">Validation state for creating the validation context.</param> /// <param name="tx">The transaction to validate.</param> /// <param name="vHashTxnToUncache">Not currently used</param> private async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx, List <uint256> vHashTxnToUncache) { var context = new MempoolValidationContext(tx, state); context.MinRelayTxFee = this.minRelayTxFee; // TODO: Convert these into rules too this.PreMempoolChecks(context); // Create the MemPoolCoinView and load relevant utxoset context.View = new MempoolCoinView(this.network, this.coinView, this.memPool, this.mempoolLock, this); // adding to the mem pool can only be done sequentially // use the sequential scheduler for that. await this.mempoolLock.WriteAsync(() => { context.View.LoadViewLocked(context.Transaction); // If the transaction already exists in the mempool, // we only record the state but do not throw an exception. // This is because the caller will check if the state is invalid // and if so return false, meaning that the transaction should not be relayed. if (this.memPool.Exists(context.TransactionHash)) { state.Invalid(MempoolErrors.InPool); this.logger.LogTrace("(-)[INVALID_TX_ALREADY_EXISTS]"); return; } foreach (IMempoolRule rule in this.mempoolRules) { rule.CheckTransaction(context); } // Remove conflicting transactions from the mempool foreach (TxMempoolEntry it in context.AllConflicting) { this.logger.LogInformation($"Replacing tx {it.TransactionHash} with {context.TransactionHash} for {context.ModifiedFees - context.ConflictingFees} BTC additional fees, {context.EntrySize - context.ConflictingSize} delta bytes"); } this.memPool.RemoveStaged(context.AllConflicting, false); // This transaction should only count for fee estimation if // the node is not behind and it is not dependent on any other // transactions in the mempool bool validForFeeEstimation = this.IsCurrentForFeeEstimation() && this.memPool.HasNoInputsOf(tx); // Store transaction in memory this.memPool.AddUnchecked(context.TransactionHash, context.Entry, context.SetAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed if (!state.OverrideMempoolLimit) { this.LimitMempoolSize(this.mempoolSettings.MaxMempool * 1000000, this.mempoolSettings.MempoolExpiry * 60 * 60); if (!this.memPool.Exists(context.TransactionHash)) { this.logger.LogTrace("(-)[FAIL_MEMPOOL_FULL]"); state.Fail(MempoolErrors.Full).Throw(); } } // do this here inside the exclusive scheduler for better accuracy // and to avoid springing more concurrent tasks later state.MempoolSize = this.memPool.Size; state.MempoolDynamicSize = this.memPool.DynamicMemoryUsage(); this.PerformanceCounter.SetMempoolSize(state.MempoolSize); this.PerformanceCounter.SetMempoolDynamicSize(state.MempoolDynamicSize); this.PerformanceCounter.AddHitCount(1); }); }
private async Task AcceptToMemoryPoolWorker(MempoolValidationState state, Transaction tx, List <uint256> vHashTxnToUncache) { var context = new MempoolValidationContext(tx, state); this.PreMempoolChecks(context); // create the MemPoolCoinView and load relevant utxoset context.View = new MempoolCoinView(this.coinView, this.memPool, this.mempoolScheduler, this); await context.View.LoadView(context.Transaction).ConfigureAwait(false); // adding to the mem pool can only be done sequentially // use the sequential scheduler for that. await this.mempoolScheduler.WriteAsync(() => { // is it already in the memory pool? if (this.memPool.Exists(context.TransactionHash)) { state.Invalid(MempoolErrors.InPool).Throw(); } // Check for conflicts with in-memory transactions this.CheckConflicts(context); this.CheckMempoolCoinView(context); this.CreateMempoolEntry(context, state.AcceptTime); this.CheckSigOps(context); this.CheckFee(context); this.CheckRateLimit(context, state.LimitFree); this.CheckAncestors(context); this.CheckReplacment(context); this.CheckAllInputs(context); // Remove conflicting transactions from the mempool foreach (var it in context.AllConflicting) { this.logger.LogInformation($"replacing tx {it.TransactionHash} with {context.TransactionHash} for {context.ModifiedFees - context.ConflictingFees} BTC additional fees, {context.EntrySize - context.ConflictingSize} delta bytes"); } this.memPool.RemoveStaged(context.AllConflicting, false); // This transaction should only count for fee estimation if // the node is not behind and it is not dependent on any other // transactions in the mempool bool validForFeeEstimation = this.IsCurrentForFeeEstimation() && this.memPool.HasNoInputsOf(tx); // Store transaction in memory this.memPool.AddUnchecked(context.TransactionHash, context.Entry, context.SetAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed if (!state.OverrideMempoolLimit) { this.LimitMempoolSize(this.nodeArgs.Mempool.MaxMempool * 1000000, this.nodeArgs.Mempool.MempoolExpiry * 60 * 60); if (!this.memPool.Exists(context.TransactionHash)) { state.Fail(MempoolErrors.Full).Throw(); } } // do this here inside the exclusive scheduler for better accuracy // and to avoid springing more concurrent tasks later state.MempoolSize = this.memPool.Size; state.MempoolDynamicSize = this.memPool.DynamicMemoryUsage(); this.PerformanceCounter.SetMempoolSize(state.MempoolSize); this.PerformanceCounter.SetMempoolDynamicSize(state.MempoolDynamicSize); this.PerformanceCounter.AddHitCount(1); }); }