public override void CheckTransaction(MempoolValidationContext context) { // Calculate in-mempool ancestors, up to a limit. context.SetAncestors = new TxMempool.SetEntries(); int nLimitAncestors = this.settings.LimitAncestors; int nLimitAncestorSize = this.settings.LimitAncestorSize * 1000; int nLimitDescendants = this.settings.LimitDescendants; int nLimitDescendantSize = this.settings.LimitDescendantSize * 1000; if (!this.mempool.CalculateMemPoolAncestors(context.Entry, context.SetAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, out string errString)) { this.logger.LogTrace("(-)FAIL_CHAIN_TOO_LONG]"); context.State.Fail(MempoolErrors.TooLongMempoolChain, errString).Throw(); } // A transaction, that spends outputs that would be replaced by it, is invalid. Now // that we have the set of all ancestors we can detect this // pathological case by making sure setConflicts and setAncestors don't // intersect. foreach (TxMempoolEntry ancestorIt in context.SetAncestors) { uint256 hashAncestor = ancestorIt.TransactionHash; if (context.SetConflicts.Contains(hashAncestor)) { this.logger.LogTrace("(-)[FAIL_BAD_TX_SPENDS_CONFLICTING]"); context.State.Fail(MempoolErrors.BadTxnsSpendsConflictingTx, $"{context.TransactionHash} spends conflicting transaction {hashAncestor}").Throw(); } } }
public override void CheckTransaction(MempoolValidationContext context) { Transaction transaction = context.Transaction; // Coinbase is only valid in a block, not as a loose transaction if (context.Transaction.IsCoinBase) { this.logger.LogTrace("(-)[FAIL_INVALID_COINBASE]"); context.State.Fail(MempoolErrors.Coinbase).Throw(); } // Coinstake is only valid in a block, not as a loose transaction if (transaction.IsCoinStake) { this.logger.LogTrace("(-)[FAIL_INVALID_COINSTAKE]"); context.State.Fail(MempoolErrors.Coinstake).Throw(); } this.checkPowTransactionRule.CheckTransaction(this.network, this.network.Consensus.Options, transaction); // we do not need the CheckPosTransactionRule, the checks for empty outputs are already in the MainNetOutputNotWhitelistedMempoolRule. CheckStandardTransaction(context); // ObsidianX behaves according do BIP 113. Since the adoption of BIP 113, the time-based nLockTime is compared to the 11 - block median time past // (the median timestamp of the 11 blocks preceding the block in which the transaction is mined), and not the block time itself. // The median time past tends to lag the current unix time by about one hour(give or take), but unlike block time it increases monotonically. // For transaction relay, nLockTime must be <= the current block's height (block-based) or <= the current median time past (if time based). // This ensures that the transaction can be included in the next block. if (!CheckFinalTransaction(this.chainIndexer, this.dateTimeProvider, context.Transaction)) { this.logger.LogTrace("(-)[FAIL_NONSTANDARD]"); context.State.Fail(MempoolErrors.NonFinal).Throw(); } }
public override void CheckTransaction(MempoolValidationContext context) { // We expect a reward claim transaction to have at least 2 outputs. bool federationPayment = !(context.Transaction.Outputs.Count < 2); // The OP_RETURN output that marks the transaction as cross-chain (and in particular a reward claiming transaction) must be present. if (context.Transaction.Outputs.All(o => o.ScriptPubKey != StraxCoinstakeRule.CirrusTransactionTag(((StraxBaseNetwork)(this.network)).CirrusRewardDummyAddress))) { federationPayment = false; } // At least one other output must be paying to the multisig. if (context.Transaction.Outputs.All(o => o.ScriptPubKey != ((StraxBaseNetwork)(this.network)).Federations.GetOnlyFederation().MultisigScript.PaymentScript)) { federationPayment = false; } // There must be no other spendable scriptPubKeys. if (context.Transaction.Outputs.Any(o => o.ScriptPubKey != ((StraxBaseNetwork)(this.network)).Federations.GetOnlyFederation().MultisigScript.PaymentScript&& !o.ScriptPubKey.IsUnspendable)) { federationPayment = false; } // We need to bypass the fee checking logic for correctly-formed transactions that pay Cirrus rewards to the federation. if (federationPayment) { return; } base.CheckTransaction(context); }
public override void CheckTransaction(MempoolValidationContext context) { Guard.Assert(context.View != null); context.LockPoints = new LockPoints(); // Do we already have it? if (context.View.HaveTransaction(context.TransactionHash)) { this.logger.LogTrace("(-)[INVALID_ALREADY_KNOWN]"); context.State.Invalid(MempoolErrors.AlreadyKnown).Throw(); } // Do all inputs exist? foreach (TxIn txin in context.Transaction.Inputs) { if (!context.View.HaveCoins(txin.PrevOut)) { // Assume this might be an orphan tx for which we just haven't seen parents yet context.State.MissingInputs = true; this.logger.LogTrace("(-)[FAIL_MISSING_INPUTS]"); context.State.Fail(MempoolErrors.MissingOrSpentInputs).Throw(); } } }
public override void CheckTransaction(MempoolValidationContext context) { if (context.Transaction.Inputs.Any(x => x.ScriptSig.IsSmartContractSpend()) || context.Transaction.Outputs.Any(x => x.ScriptPubKey.IsSmartContractSpend())) { this.Throw(); } }
public void CheckTransaction(MempoolValidationContext context) { if (context.Transaction.Inputs.Any(x => ContainsOpSpend(x.ScriptSig)) || context.Transaction.Outputs.Any(x => ContainsOpSpend(x.ScriptPubKey))) { Throw(); } }
public override void CheckTransaction(MempoolValidationContext context) { var mempoolRejectFee = this.mempool.GetMinFee(this.settings.MaxMempool * 1000000).GetFee(context.EntrySize); if (mempoolRejectFee > 0 && context.ModifiedFees < mempoolRejectFee) { this.logger.LogTrace("(-)[FAIL_MIN_FEE_NOT_MET]"); context.State.Fail(MempoolErrors.MinFeeNotMet, $" {context.Fees} < {mempoolRejectFee}").Throw(); } else if (this.settings.RelayPriority && context.ModifiedFees < context.MinRelayTxFee.GetFee(context.EntrySize) && !TxMempool.AllowFree(context.Entry.GetPriority(this.chainIndexer.Height + 1))) { this.logger.LogTrace("(-)[FAIL_INSUFFICIENT_PRIORITY]"); // Require that free transactions have sufficient priority to be mined in the next block. context.State.Fail(MempoolErrors.InsufficientPriority).Throw(); } if (context.State.AbsurdFee > 0 && context.Fees > context.State.AbsurdFee) { this.logger.LogTrace("(-)[INVALID_ABSURD_FEE]"); context.State.Invalid(MempoolErrors.AbsurdlyHighFee, $"{context.Fees} > {context.State.AbsurdFee}") .Throw(); } }
public override void CheckTransaction(MempoolValidationContext context) { Guard.Assert(context.View != null); context.LockPoints = new LockPoints(); // Do we already have it? if (context.View.HaveTransaction(context.TransactionHash)) { this.logger.LogTrace("(-)[INVALID_ALREADY_KNOWN]"); context.State.Invalid(MempoolErrors.AlreadyKnown).Throw(); } // Do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), // and only helps with filling in pfMissingInputs (to determine missing vs spent). foreach (TxIn txin in context.Transaction.Inputs) { if (!context.View.HaveCoins(txin.PrevOut)) { context.State.MissingInputs = true; this.logger.LogTrace("(-)[FAIL_MISSING_INPUTS]"); context.State.Fail(MempoolErrors.MissingOrSpentInputs).Throw(); } } }
public override void CheckTransaction(MempoolValidationContext context) { if (!context.Transaction.HasWitness) { this.logger.LogTrace($"(-)[FAIL_{nameof(X1RequireWitnessMempoolRule)}]".ToUpperInvariant()); X1ConsensusErrors.MissingWitness.Throw(); } }
public bool TestSequenceLocks(TestContext testContext, ChainedBlock chainedBlock, Transaction tx, Transaction.LockTimeFlags flags, LockPoints uselock = null) { var context = new MempoolValidationContext(tx, new MempoolValidationState(false)); context.View = new MempoolCoinView(testContext.cachedCoinView, testContext.mempool, testContext.mempoolLock, null); context.View.LoadViewAsync(tx).GetAwaiter().GetResult(); return(MempoolValidator.CheckSequenceLocks(testContext.network, chainedBlock, context, flags, uselock, false)); }
public bool TestSequenceLocks(TestContext testContext, ChainedHeader chainedHeader, Transaction tx, Transaction.LockTimeFlags flags, LockPoints uselock = null) { var context = new MempoolValidationContext(tx, new MempoolValidationState(false)); context.View = new MempoolCoinView(this.network, testContext.cachedCoinView, testContext.mempool, testContext.mempoolLock, null); testContext.mempoolLock.ReadAsync(() => context.View.LoadViewLocked(tx)).GetAwaiter().GetResult(); return(CreateMempoolEntryMempoolRule.CheckSequenceLocks(testContext.network, chainedHeader, context, flags, uselock, false)); }
public override void CheckTransaction(MempoolValidationContext context) { // Only accept BIP68 sequence locked transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. // Must keep pool.cs for this unless we change CheckSequenceLocks to take a // CoinsViewCache instead of create its own if (!CheckSequenceLocks(this.network, this.chainIndexer.Tip, context, MempoolValidator.StandardLocktimeVerifyFlags, context.LockPoints)) { this.logger.LogTrace("(-)[FAIL_BIP68_SEQLOCK]"); context.State.Fail(MempoolErrors.NonBIP68Final).Throw(); } // Check for non-standard pay-to-script-hash in inputs if (this.settings.RequireStandard && !AreInputsStandard(this.network, context.Transaction, context.View)) { this.logger.LogTrace("(-)[INVALID_NONSTANDARD_INPUTS]"); context.State.Invalid(MempoolErrors.NonstandardInputs).Throw(); } // Check for non-standard witness in P2WSH if (context.Transaction.HasWitness && this.settings.RequireStandard && !IsWitnessStandard(context.Transaction, context.View)) { this.logger.LogTrace("(-)[INVALID_NONSTANDARD_WITNESS]"); context.State.Invalid(MempoolErrors.NonstandardWitness).Throw(); } context.SigOpsCost = this.consensusRules.GetRule <CoinViewRule>() .GetTransactionSignatureOperationCost(context.Transaction, context.View.Set, new DeploymentFlags { ScriptFlags = ScriptVerify.Standard }); var nValueIn = context.View.GetValueIn(context.Transaction); context.ValueOut = context.Transaction.TotalOut; context.Fees = nValueIn - context.ValueOut; // nModifiedFees includes any fee deltas from PrioritiseTransaction var nModifiedFees = context.Fees; double priorityDummy = 0; this.mempool.ApplyDeltas(context.TransactionHash, ref priorityDummy, ref nModifiedFees); context.ModifiedFees = nModifiedFees; (var dPriority, var inChainInputValue) = context.View.GetPriority(context.Transaction, this.chainIndexer.Height); // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. var spendsCoinbase = context.View.SpendsCoinBase(context.Transaction); context.Entry = new TxMempoolEntry(context.Transaction, context.Fees, context.State.AcceptTime, dPriority, this.chainIndexer.Height, inChainInputValue, spendsCoinbase, context.SigOpsCost, context.LockPoints, this.network.Consensus.Options); context.EntrySize = (int)context.Entry.GetTxSize(); }
/// <inheritdoc /> public override void CheckFee(MempoolValidationContext context) { base.CheckFee(context); foreach (ISmartContractMempoolRule rule in feeTxRules) { rule.CheckTransaction(context); } }
/// <inheritdoc /> protected override void PreMempoolChecks(MempoolValidationContext context) { base.PreMempoolChecks(context); foreach (ISmartContractMempoolRule rule in this.preTxRules) { rule.CheckTransaction(context); } }
/// <summary> /// Validates transaction inputs against transaction data for a specific script verify flag. /// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) /// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it /// instead of being performed inline. /// </summary> /// <param name="ruleContext">Current mempool rule context.</param> /// <param name="context">Current validation context.</param> /// <param name="scriptVerify">Script verify flag.</param> /// <param name="txData">Transaction data.</param> /// <returns>Whether inputs are valid.</returns> private bool CheckInputs(MempoolValidationContext context, ScriptVerify scriptVerify, PrecomputedTransactionData txData) { Transaction tx = context.Transaction; if (!context.Transaction.IsCoinBase) { this.consensusRuleEngine.GetRule <CoinViewRule>().CheckInputs(context.Transaction, context.View.Set, this.chainIndexer.Height + 1); for (int iInput = 0; iInput < tx.Inputs.Count; iInput++) { TxIn input = tx.Inputs[iInput]; int iiIntput = iInput; TxOut txout = context.View.GetOutputFor(input); var checker = new TransactionChecker(tx, iiIntput, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.network); ctx.ScriptVerify = scriptVerify; if (ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)) { this.logger.LogTrace("(-)[SCRIPT_VERIFIED]:true"); return(true); } else { //TODO: //if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) //{ // // Check whether the failure was caused by a // // non-mandatory script verification check, such as // // non-standard DER encodings or non-null dummy // // arguments; if so, don't trigger DoS protection to // // avoid splitting the network between upgraded and // // non-upgraded nodes. // CScriptCheck check2(*coins, tx, i, // flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); // if (check2()) // return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); //} //// Failures of other flags indicate a transaction that is //// invalid in new blocks, e.g. a invalid P2SH. We DoS ban //// such nodes as they are not following the protocol. That //// said during an upgrade careful thought should be taken //// as to the correct behavior - we may want to continue //// peering with non-upgraded nodes even after soft-fork //// super-majority signaling has occurred. this.logger.LogTrace("(-)[FAIL_SCRIPT_VERIFY]"); context.State.Fail(MempoolErrors.MandatoryScriptVerifyFlagFailed, ctx.Error.ToString()).Throw(); } } } return(true); }
public override void CheckTransaction(MempoolValidationContext context) { context.SetConflicts = new List <uint256>(); foreach (var txin in context.Transaction.Inputs) { var itConflicting = this.mempool.MapNextTx.Find(f => f.OutPoint == txin.PrevOut); if (itConflicting != null) { var ptxConflicting = itConflicting.Transaction; if (!context.SetConflicts.Contains(ptxConflicting.GetHash())) { // Allow opt-out of transaction replacement by setting // nSequence >= maxint-1 on all inputs. // // maxint-1 is picked to still allow use of nLockTime by // non-replaceable transactions. All inputs rather than just one // is for the sake of multi-party protocols, where we don't // want a single party to be able to disable replacement. // // The opt-out ignores descendants as anyone relying on // first-seen mempool behavior should be checking all // unconfirmed ancestors anyway; doing otherwise is hopelessly // insecure. var replacementOptOut = true; if (this.settings.EnableReplacement) { foreach (var txiner in ptxConflicting.Inputs) { if (txiner.Sequence < Sequence.Final - 1) { replacementOptOut = false; break; } } } if (replacementOptOut) { this.logger.LogTrace( "New transaction '{0}' and existing mempool transaction '{1}' both consume the same PrevOut: '{2}-{3}'", context.Transaction.GetHash(), ptxConflicting.GetHash(), txin.PrevOut.Hash, txin.PrevOut.N); this.logger.LogTrace("New transaction = {0}", context.Transaction.ToString(this.network)); this.logger.LogTrace("Old transaction = {0}", ptxConflicting.ToString(this.network)); this.logger.LogTrace("(-)[INVALID_CONFLICT]"); context.State.Invalid(MempoolErrors.Conflict).Throw(); } context.SetConflicts.Add(ptxConflicting.GetHash()); } } } }
public void CheckTransaction(MempoolValidationContext context) { // If wanting to execute a contract, we must be able to get the sender. if (context.Transaction.Outputs.Any(x => x.ScriptPubKey.IsSmartContractExec())) { GetSenderResult result = this.senderRetriever.GetSender(context.Transaction, context.View); if (!result.Success) { new ConsensusError("cant-get-sender", "smart contract output without a P2PKH as the first input to the tx.").Throw(); } } }
public void CheckTxOutDustRule_TxOut_Is_OpReturn_Pass() { var rule = new CheckTxOutDustRule(this.network, this.txMempool, new MempoolSettings(this.nodeSettings), this.chainIndexer, this.loggerFactory); var transaction = CreateTransaction(Money.Coins(1), true); var mempoolValidationContext = new MempoolValidationContext(transaction, new MempoolValidationState(false)) { MinRelayTxFee = this.nodeSettings.MinRelayTxFeeRate, ValueOut = transaction.TotalOut }; rule.CheckTransaction(mempoolValidationContext); Assert.Null(mempoolValidationContext.State.Error); }
public override void CheckTransaction(MempoolValidationContext context) { // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than // merely non-standard transaction. if (context.SigOpsCost > this.network.Consensus.Options.MaxStandardTxSigopsCost) { this.logger.LogTrace("(-)[FAIL_TOO_MANY_SIGOPS]"); context.State.Fail(MempoolErrors.TooManySigops).Throw(); } }
public void CheckTxOutDustRule_Fail() { var rule = new CheckTxOutDustRule(this.network, this.txMempool, new MempoolSettings(this.nodeSettings), this.chainIndexer, this.loggerFactory); var transaction = CreateTransaction(Money.Coins(0.000001m)); var mempoolValidationContext = new MempoolValidationContext(transaction, new MempoolValidationState(false)) { MinRelayTxFee = this.nodeSettings.MinRelayTxFeeRate, ValueOut = transaction.TotalOut }; Assert.Throws <MempoolErrorException>(() => rule.CheckTransaction(mempoolValidationContext)); Assert.NotNull(mempoolValidationContext.State.Error); Assert.Equal(MempoolErrors.TransactionContainsDustTxOuts, mempoolValidationContext.State.Error); }
/// <inheritdoc/> public override void CheckTransaction(MempoolValidationContext context) { TxOut scTxOut = context.Transaction.TryGetSmartContractTxOut(); if (scTxOut == null) { // No SC output to validate. return; } ContractTxData txData = ContractTransactionChecker.GetContractTxData(this.callDataSerializer, scTxOut); SmartContractFormatLogic.Check(txData, context.Fees); }
public override void CheckTransaction(MempoolValidationContext context) { Debug.Assert(((X1Main)this.network).AbsoluteMinTxFee.HasValue); long consensusRejectFee = ((X1Main)this.network).AbsoluteMinTxFee.Value; if (context.Fees < consensusRejectFee) { this.logger.LogTrace("(-)[FAIL_ABSOLUTE_MIN_TX_FEE_NOT_MET]"); context.State.Fail(MempoolErrors.MinFeeNotMet, $" {context.Fees} < {consensusRejectFee}").Throw(); } // calling the base class here allows for customized behavior above the AbsoluteMinTxFee threshold. base.CheckTransaction(context); }
/// <inheritdoc/> public override void CheckTransaction(MempoolValidationContext context) { TxOut scTxOut = context.Transaction.TryGetSmartContractTxOut(); if (scTxOut == null) { // No SC output to validate. return; } ContractTxData txData = ContractTransactionChecker.GetContractTxData(this.callDataSerializer, scTxOut); // Delegate to full validation rule. The full validation rule will differ for PoA/PoS. this.contractTransactionFullValidationRule.CheckContractTransaction(txData, null, this.chainIndexer.Tip.Height); }
/// <inheritdoc /> public override void CheckTransaction(MempoolValidationContext context) { foreach (var txOut in context.Transaction.Outputs) { if (StandardTransactionPolicy.IsOpReturn(txOut.ScriptPubKey.ToBytes())) { continue; } if (txOut.IsDust(context.MinRelayTxFee)) { this.logger.LogTrace("(-)[TX_CONTAINS_DUST_TXOUTS]"); context.State.Fail(MempoolErrors.TransactionContainsDustTxOuts, $"{context.Transaction.GetHash()} contains a dust TxOut {txOut.ScriptPubKey.ToString()}.").Throw(); } } }
/// <inheritdoc/> public override void CheckTransaction(MempoolValidationContext context) { if (context.Transaction.IsCoinBase || context.Transaction.IsCoinStake) { return; } // This will raise a consensus error if there is a voting request and it can't be decoded. JoinFederationRequest request = JoinFederationRequestBuilder.Deconstruct(context.Transaction, this.encoder); if (request == null) { return; } if (this.federationManager.IsMultisigMember(request.PubKey)) { this.logger.LogTrace("(-)[INVALID_MULTISIG_VOTING]"); context.State.Fail(MempoolErrors.VotingRequestInvalidMultisig, $"{context.Transaction.GetHash()} has an invalid voting request for a multisig member.").Throw(); } // Check collateral amount. var collateralAmount = CollateralFederationMember.GetCollateralAmountForPubKey((PoANetwork)this.network, request.PubKey); if (request.CollateralAmount.ToDecimal(MoneyUnit.BTC) != collateralAmount) { this.logger.LogTrace("(-)[INVALID_COLLATERAL_REQUIREMENT]"); context.State.Fail(MempoolErrors.InvalidCollateralRequirement, $"{context.Transaction.GetHash()} has a voting request with an invalid colateral requirement.").Throw(); } if (!(this.federationManager is CollateralFederationManager federationManager)) { return; } // Prohibit re-use of collateral addresses. Script script = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(request.CollateralMainchainAddress); string collateralAddress = script.GetDestinationAddress(this.network).ToString(); CollateralFederationMember owner = federationManager.CollateralAddressOwner(this.votingManager, VoteKey.AddFederationMember, collateralAddress); if (owner != null && owner.PubKey != request.PubKey) { this.logger.LogTrace("(-)[INVALID_COLLATERAL_REUSE]"); context.State.Fail(MempoolErrors.VotingRequestInvalidCollateralReuse, $"{context.Transaction.GetHash()} has a voting request that's re-using collateral.").Throw(); } }
public override void CheckTransaction(MempoolValidationContext context) { var scriptVerifyFlags = ScriptVerify.Standard; if (!this.settings.RequireStandard) { // TODO: implement -promiscuousmempoolflags // scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); } // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. var txdata = new PrecomputedTransactionData(context.Transaction); if (!this.CheckInputs(context, scriptVerifyFlags, txdata)) { // TODO: Implement Witness Code //// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we //// need to turn both off, and compare against just turning off CLEANSTACK //// to see if the failure is specifically due to witness validation. //if (!tx.HasWitness() && CheckInputs(Trx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && // !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) //{ // // Only the witness is missing, so the transaction itself may be fine. // state.SetCorruptionPossible(); //} this.logger.LogTrace("(-)[FAIL_INPUTS_PREV_TXS]"); context.State.Fail(new MempoolError()).Throw(); } // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For // instance the STRICTENC flag was incorrectly allowing certain // CHECKSIG NOT scripts to pass, even though they were invalid. // // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. if (!this.CheckInputs(context, ScriptVerify.P2SH, txdata)) { this.logger.LogTrace("(-)[FAIL_SCRIPT_VERIFY]"); context.State.Fail(new MempoolError(), $"CheckInputs: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags {context.TransactionHash}").Throw(); } }
/// <summary> /// Validate the transaction is a standard transaction. Checks the version number, transaction size, input signature size, /// output script template, single output, & dust outputs. /// <seealso> /// <cref>https://github.com/bitcoin/bitcoin/blob/aa624b61c928295c27ffbb4d27be582f5aa31b56/src/policy/policy.cpp##L82-L144</cref> /// </seealso> /// </summary> /// <remarks>Note that, unfortunately, this does not constitute everything that can be non-standard about a transaction. /// For example, there may be script verification flags that are not consensus-mandatory but are part of the standardness checks. /// These verify flags are checked elsewhere.</remarks> /// <param name="context">Current validation context.</param> private void CheckStandardTransaction(MempoolValidationContext context) { Transaction tx = context.Transaction; if (tx.Version > this.network.Consensus.Options.MaxStandardVersion || tx.Version < 1) { this.logger.LogTrace("(-)[FAIL_TX_VERSION]"); context.State.Fail(MempoolErrors.Version).Throw(); } int dataOut = 0; foreach (TxOut txOut in tx.Outputs) { // this rule runs very early in the validation pipeline, check basics as well. if (txOut?.ScriptPubKey == null || txOut.ScriptPubKey.Length < 1) { this.logger.LogTrace("(-)[FAIL_EMPTY_SCRIPTPUBKEY]"); context.State.Fail(MempoolErrors.Scriptpubkey).Throw(); } // Output checking is already implemented in the MainNetOutputNotWhitelistedRule // and MainNetOutputNotWhitelistedMempoolRule. // OP_RETURN byte[] raw = txOut.ScriptPubKey.ToBytes(); if (raw[0] == (byte)OpcodeType.OP_RETURN) { dataOut++; } if (txOut.IsDust(this.nodeSettings.MinRelayTxFeeRate)) { this.logger.LogTrace("(-)[FAIL_DUST]"); context.State.Fail(MempoolErrors.Dust).Throw(); } } // Only one OP_RETURN txOut is permitted if (dataOut > 1) { this.logger.LogTrace("(-)[FAIL_MULTI_OPRETURN]"); context.State.Fail(MempoolErrors.MultiOpReturn).Throw(); } }
public override void CheckTransaction(MempoolValidationContext context) { if (context.Transaction.IsCoinStake || (context.Transaction.IsCoinBase && context.Transaction.Outputs[0].IsEmpty)) // also check the coinbase tx in PoW blocks { return; } foreach (var output in context.Transaction.Outputs) { if (XdsOutputNotWhitelistedRule.IsOutputWhitelisted(output)) { continue; } this.logger.LogTrace($"(-)[FAIL_{nameof(XdsOutputNotWhitelistedMempoolRule)}]".ToUpperInvariant()); context.State.Fail(new MempoolError(XdsConsensusErrors.OutputNotWhitelisted)).Throw(); } }
/// <seealso>https://github.com/bitcoin/bitcoin/blob/febf3a856bcfb8fef2cb4ddcb8d1e0cab8a22580/src/validation.cpp#L770</seealso> public override void CheckTransaction(MempoolValidationContext context) { // TODO: How should the RequireStandard setting interact with this? var scriptVerifyFlags = ScriptVerify.Standard; // Check against previous transactions. // This is done last to help prevent CPU exhaustion denial-of-service attacks. var txdata = new PrecomputedTransactionData(context.Transaction); if (!CheckInputs(context, scriptVerifyFlags, txdata)) { // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. if (!context.Transaction.HasWitness && CheckInputs(context, scriptVerifyFlags & ~(ScriptVerify.Witness | ScriptVerify.CleanStack), txdata) && !CheckInputs(context, scriptVerifyFlags & ~ScriptVerify.CleanStack, txdata)) { // Only the witness is missing, so the transaction itself may be fine. this.logger.LogTrace("(-)[FAIL_WITNESS_MUTATED]"); context.State.Fail(MempoolErrors.WitnessMutated).Throw(); } this.logger.LogTrace("(-)[FAIL_INPUTS_PREV_TXS]"); context.State.Fail(new MempoolError()).Throw(); } // Check again against just the consensus-critical mandatory script verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For instance the STRICTENC flag was incorrectly allowing certain // CHECKSIG NOT scripts to pass, even though they were invalid. // // There is a similar check during block creation to prevent creating invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. var flags = this.nodeDeployments.GetFlags(this.chainIndexer.Tip); if (!CheckInputs(context, flags.ScriptFlags, txdata)) { this.logger.LogTrace("(-)[FAIL_SCRIPT_VERIFY]"); context.State.Fail(new MempoolError(), $"CheckInputs: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags {context.TransactionHash}") .Throw(); } }
private void CheckMinGasLimit(MempoolValidationContext context) { Transaction transaction = context.Transaction; if (!transaction.IsSmartContractExecTransaction()) { return; } // We know it has passed SmartContractFormatRule so we can deserialize it easily. TxOut scTxOut = transaction.TryGetSmartContractTxOut(); Result <ContractTxData> callDataDeserializationResult = this.callDataSerializer.Deserialize(scTxOut.ScriptPubKey.ToBytes()); ContractTxData callData = callDataDeserializationResult.Value; if (callData.GasPrice < MinGasPrice) { context.State.Fail(MempoolErrors.InsufficientFee, $"Gas price {callData.GasPrice} is below required price: {MinGasPrice}").Throw(); } }