/// <summary> /// Checks that are done before touching the memory pool. /// These checks don't need to run under the memory pool lock. /// </summary> /// <param name="context">Current validation context.</param> protected virtual void PreMempoolChecks(MempoolValidationContext context) { // TODO: fix this to use dedicated mempool rules. new CheckPowTransactionRule { Logger = this.logger }.CheckTransaction(this.network, this.network.Consensus.Options, context.Transaction); if (this.chainIndexer.Network.Consensus.IsProofOfStake) { new CheckPosTransactionRule { Logger = this.logger } }
/// <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.LogDebug($"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); }); }
/// <inheritdoc /> public abstract void CheckTransaction(MempoolValidationContext context);