/// <summary>
        /// Verifies transaction's signature.
        /// </summary>
        /// <param name="coin">UTXO that is spent in the transaction.</param>
        /// <param name="txTo">Transaction.</param>
        /// <param name="txToInN">Index of the transaction's input.</param>
        /// <param name="flagScriptVerify">Script verification flags.</param>
        /// <returns><c>true</c> if signature is valid.</returns>
        private bool VerifySignature(UnspentOutputs coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify)
        {
            TxIn input = txTo.Inputs[txToInN];

            if (input.PrevOut.N >= coin.Outputs.Length)
            {
                return(false);
            }

            if (input.PrevOut.Hash != coin.TransactionId)
            {
                return(false);
            }

            TxOut output = coin.Outputs[input.PrevOut.N];

            var txData  = new PrecomputedTransactionData(txTo);
            var checker = new TransactionChecker(txTo, txToInN, output.Value, txData);
            var ctx     = new ScriptEvaluationContext(this.chain.Network)
            {
                ScriptVerify = flagScriptVerify
            };

            bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker);

            return(res);
        }
Beispiel #2
0
        /// <summary>
        /// Verifies transaction's signature.
        /// </summary>
        /// <param name="coin">UTXO that is spent in the transaction.</param>
        /// <param name="txTo">Transaction.</param>
        /// <param name="txToInN">Index of the transaction's input.</param>
        /// <param name="flagScriptVerify">Script verification flags.</param>
        /// <returns><c>true</c> if signature is valid.</returns>
        private bool VerifySignature(UnspentOutputs coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify)
        {
            this.logger.LogTrace("({0}:'{1}/{2}',{3}:{4},{5}:{6})", nameof(coin), coin.TransactionId, coin.Height, nameof(txToInN), txToInN, nameof(flagScriptVerify), flagScriptVerify);

            TxIn input = txTo.Inputs[txToInN];

            if (input.PrevOut.N >= coin.Outputs.Length)
            {
                return(false);
            }

            if (input.PrevOut.Hash != coin.TransactionId)
            {
                return(false);
            }

            TxOut output = coin.Outputs[input.PrevOut.N];

            var txData  = new PrecomputedTransactionData(txTo);
            var checker = new TransactionChecker(txTo, txToInN, output.Value, txData);
            var ctx     = new ScriptEvaluationContext(this.chain.Network)
            {
                ScriptVerify = flagScriptVerify
            };

            bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker);

            this.logger.LogTrace("(-):{0}", res);
            return(res);
        }
Beispiel #3
0
        static void CheckTransaction(Transaction tx, SegWitCoin kernelCoin)
        {
            var txData = new PrecomputedTransactionData(tx);

            for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
            {
                TxIn input = tx.Inputs[inputIndex];

                TxOut spentOut = new TxOut(kernelCoin.UtxoValue, kernelCoin.SegWitAddress.GetScriptPubKey());

                var checker = new TransactionChecker(tx, inputIndex, spentOut, txData);
                var ctx     = new ScriptEvaluationContext
                {
                    ScriptVerify = ScriptVerify.Mandatory | ScriptVerify.DerSig | ScriptVerify.CheckLockTimeVerify |
                                   ScriptVerify.Witness /* todo | ScriptVerify.CheckColdStakeVerify*/
                };
                bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, spentOut.ScriptPubKey, checker);

                if (verifyScriptResult == false)
                {
                    throw new InvalidOperationException(
                              $"Verify script for transaction '{tx.GetHash()}' input {inputIndex} failed, ScriptSig = '{input.ScriptSig}', ScriptPubKey = '{spentOut.ScriptPubKey}', script evaluation error = '{ctx.Error}'");
                }
            }
        }
        /// <inheritdoc/>
        public bool VerifySignature(UnspentOutputs coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify)
        {
            TxIn input = txTo.Inputs[txToInN];

            if (input.PrevOut.N >= coin.Outputs.Length)
            {
                this.logger.LogTrace("(-)[OUTPUT_INCORRECT_LENGTH]");
                return(false);
            }

            if (input.PrevOut.Hash != coin.TransactionId)
            {
                this.logger.LogTrace("(-)[INCORRECT_TX]");
                return(false);
            }

            TxOut output = coin.Outputs[input.PrevOut.N];

            if (output == null)
            {
                this.logger.LogTrace("(-)[OUTPUT_NOT_FOUND]");
                return(false);
            }

            var txData  = new PrecomputedTransactionData(txTo);
            var checker = new TransactionChecker(txTo, txToInN, output.Value, txData);
            var ctx     = new ScriptEvaluationContext(this.chain.Network)
            {
                ScriptVerify = flagScriptVerify
            };

            bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker);

            return(res);
        }
Beispiel #5
0
        /// <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);
        }
Beispiel #6
0
        public TransactionChecker(Transaction tx, int index, Money amount,
                                  PrecomputedTransactionData precomputedTransactionData)
        {
            if (tx == null)
            {
                throw new ArgumentNullException(nameof(tx));
            }

            Transaction = tx;
            Index       = index;
            Amount      = amount;
            PrecomputedTransactionData = precomputedTransactionData;
        }
Beispiel #7
0
        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();
            }
        }
Beispiel #8
0
        /// <summary>
        ///     Verify that an input may be validly spent as part of the given transaction in the given block.
        /// </summary>
        /// <param name="tx">Transaction to check.</param>
        /// <param name="inputIndexCopy">Index of the input to check.</param>
        /// <param name="txout">Output the input is spending.</param>
        /// <param name="txData">Transaction data for the transaction being checked.</param>
        /// <param name="input">Input to check.</param>
        /// <param name="flags">Deployment flags</param>
        /// <returns>Whether the input is valid.</returns>
        protected virtual bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout,
                                          PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)
        {
            var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
            var ctx     = new ScriptEvaluationContext(this.Parent.Network);

            ctx.ScriptVerify = flags.ScriptFlags;
            var verifyScriptResult = ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker);

            if (verifyScriptResult == false)
            {
                this.Logger.LogDebug(
                    "Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'",
                    tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error);
            }

            return(verifyScriptResult);
        }
        /// <inheritdoc/>
        public bool VerifySignature(UnspentOutput coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify)
        {
            Guard.NotNull(coin, nameof(coin));
            Guard.NotNull(txTo, nameof(txTo));

            if (txToInN < 0 || txToInN >= txTo.Inputs.Count)
            {
                return(false);
            }

            TxIn input = txTo.Inputs[txToInN];

            //if (input.PrevOut.N >= coin.Outputs.Length)
            //{
            //    this.logger.LogTrace("(-)[OUTPUT_INCORRECT_LENGTH]");
            //    return false;
            //}

            //if (input.PrevOut.Hash != coin.TransactionId)
            if (input.PrevOut.Hash != coin.OutPoint.Hash)
            {
                this.logger.LogTrace("(-)[INCORRECT_TX]");
                return(false);
            }

            TxOut output = coin.Coins.TxOut;//.Outputs[input.PrevOut.N];

            if (output == null)
            {
                this.logger.LogTrace("(-)[OUTPUT_NOT_FOUND]");
                return(false);
            }

            var txData  = new PrecomputedTransactionData(txTo);
            var checker = new TransactionChecker(txTo, txToInN, output.Value, txData);
            var ctx     = new ScriptEvaluationContext(this.chainIndexer.Network)
            {
                ScriptVerify = flagScriptVerify
            };

            bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker);

            return(res);
        }
Beispiel #10
0
        /// <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 bool VerifySignature(UnspentOutputs txFrom, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify)
        {
            var input = txTo.Inputs[txToInN];

            if (input.PrevOut.N >= txFrom._Outputs.Length)
            {
                return(false);
            }

            if (input.PrevOut.Hash != txFrom.TransactionId)
            {
                return(false);
            }

            var output = txFrom._Outputs[input.PrevOut.N];

            var txData  = new PrecomputedTransactionData(txTo);
            var checker = new TransactionChecker(txTo, txToInN, output.Value, txData);
            var ctx     = new ScriptEvaluationContext {
                ScriptVerify = flagScriptVerify
            };

            return(ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker));
        }
        public bool CheckInput(Func <Transaction, int, TxOut, PrecomputedTransactionData, TxIn, DeploymentFlags, bool> baseCheckInput, Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)
        {
            if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall())
            {
                return(input.ScriptSig.IsSmartContractSpend());
            }

            return(baseCheckInput(tx, inputIndexCopy, txout, txData, input, flags));
        }
 /// <inheritdoc/>
 protected override bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)
 {
     return(this.logic.CheckInput(base.CheckInput, tx, inputIndexCopy, txout, txData, input, flags));
 }
        /// <inheritdoc />
        public override async Task RunAsync(RuleContext context)
        {
            Block            block = context.ValidationContext.BlockToValidate;
            ChainedHeader    index = context.ValidationContext.ChainedHeaderToValidate;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = (context as UtxoRuleContext).UnspentOutputSet;

            long  sigOpsCost    = 0;
            Money fees          = Money.Zero;
            var   inputsToCheck = new List <(Transaction tx, int inputIndexCopy, TxOut txOut, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)>();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                Transaction tx = block.Transactions[txIndex];

                if (!context.SkipValidation)
                {
                    if (!tx.IsCoinBase && !view.HaveInputs(tx))
                    {
                        this.Logger.LogTrace("Transaction '{0}' has not inputs", tx.GetHash());
                        this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                        ConsensusErrors.BadTransactionMissingInput.Throw();
                    }

                    if (!this.IsTxFinal(tx, context))
                    {
                        this.Logger.LogTrace("Transaction '{0}' is not final", tx.GetHash());
                        this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                        ConsensusErrors.BadTransactionNonFinal.Throw();
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    if (!tx.IsCoinBase)
                    {
                        this.CheckInputs(tx, view, index.Height);

                        if (!tx.IsCoinStake)
                        {
                            fees += view.GetValueIn(tx) - tx.TotalOut;
                        }

                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            TxIn input = tx.Inputs[inputIndex];

                            inputsToCheck.Add((
                                                  tx: tx,
                                                  inputIndexCopy: inputIndex,
                                                  txOut: view.GetOutputFor(input),
                                                  txData,
                                                  input: input,
                                                  flags
                                                  ));
                        }
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                // Start the Parallel loop on a thread so its result can be awaited rather than blocking
                Task <ParallelLoopResult> checkInputsInParallel = Task.Run(() =>
                {
                    return(Parallel.ForEach(inputsToCheck, (input, state) =>
                    {
                        if (state.ShouldExitCurrentIteration)
                        {
                            return;
                        }

                        if (!this.CheckInput(input.tx, input.inputIndexCopy, input.txOut, input.txData, input.input, input.flags))
                        {
                            state.Stop();
                        }
                    }));
                });

                ParallelLoopResult loopResult = await checkInputsInParallel.ConfigureAwait(false);

                if (!loopResult.IsCompleted)
                {
                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");

                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }
        }
        /// <inheritdoc />
        public async override Task RunAsync(RuleContext context)
        {
            this.Logger.LogTrace("()");

            this.blockTxsProcessed = new List <Transaction>();
            NBitcoin.Block   block = context.ValidationContext.Block;
            ChainedHeader    index = context.ValidationContext.ChainedHeader;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = ((UtxoRuleContext)context).UnspentOutputSet;

            this.Parent.PerformanceCounter.AddProcessedBlocks(1);

            // Start state from previous block's root
            this.ContractCoinviewRule.OriginalStateRoot.SyncToRoot(((SmartContractBlockHeader)context.ConsensusTip.Header).HashStateRoot.ToBytes());
            IContractState trackedState = this.ContractCoinviewRule.OriginalStateRoot.StartTracking();

            this.receipts = new List <Receipt>();

            this.refundCounter = 1;
            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                this.Parent.PerformanceCounter.AddProcessedTransactions(1);
                Transaction tx = block.Transactions[txIndex];
                if (!context.SkipValidation)
                {
                    if (!this.IsProtocolTransaction(tx))
                    {
                        if (!view.HaveInputs(tx))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                            ConsensusErrors.BadTransactionMissingInput.Throw();
                        }

                        var prevheights = new int[tx.Inputs.Count];
                        // Check that transaction is BIP68 final.
                        // BIP68 lock checks (as opposed to nLockTime checks) must
                        // be in ConnectBlock because they require the UTXO set.
                        for (int j = 0; j < tx.Inputs.Count; j++)
                        {
                            prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                        }

                        if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                            ConsensusErrors.BadTransactionNonFinal.Throw();
                        }
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    if (!this.IsProtocolTransaction(tx))
                    {
                        this.CheckInputs(tx, view, index.Height);
                        fees += view.GetValueIn(tx) - tx.TotalOut;
                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            this.Parent.PerformanceCounter.AddProcessedInputs(1);
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() =>
                            {
                                if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall())
                                {
                                    return(input.ScriptSig.IsSmartContractSpend());
                                }

                                var checker      = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
                                var ctx          = new ScriptEvaluationContext(this.Parent.Network);
                                ctx.ScriptVerify = flags.ScriptFlags;
                                return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker));
                            });

                            checkInput.Start();
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);

                this.blockTxsProcessed.Add(tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                foreach (Task <bool> checkInput in checkInputs)
                {
                    if (await checkInput.ConfigureAwait(false))
                    {
                        continue;
                    }

                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }

            if (new uint256(this.ContractCoinviewRule.OriginalStateRoot.Root) != ((SmartContractBlockHeader)block.Header).HashStateRoot)
            {
                SmartContractConsensusErrors.UnequalStateRoots.Throw();
            }

            ValidateAndStoreReceipts(((SmartContractBlockHeader)block.Header).ReceiptRoot);

            this.ContractCoinviewRule.OriginalStateRoot.Commit();

            this.Logger.LogTrace("(-)");
        }
        /// <inheritdoc />
        public override async Task RunAsync(RuleContext context)
        {
            Block            block = context.ValidationContext.BlockToValidate;
            ChainedHeader    index = context.ValidationContext.ChainedHeaderToValidate;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = (context as UtxoRuleContext).UnspentOutputSet;

            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                Transaction tx = block.Transactions[txIndex];

                if (!context.SkipValidation)
                {
                    if (!this.IsProtocolTransaction(tx))
                    {
                        if (!view.HaveInputs(tx))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                            ConsensusErrors.BadTransactionMissingInput.Throw();
                        }

                        var prevheights = new int[tx.Inputs.Count];
                        // Check that transaction is BIP68 final.
                        // BIP68 lock checks (as opposed to nLockTime checks) must
                        // be in ConnectBlock because they require the UTXO set.
                        for (int j = 0; j < tx.Inputs.Count; j++)
                        {
                            prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                        }

                        if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                            ConsensusErrors.BadTransactionNonFinal.Throw();
                        }
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    if (!this.IsProtocolTransaction(tx))
                    {
                        this.CheckInputs(tx, view, index.Height);
                        fees += view.GetValueIn(tx) - tx.TotalOut;
                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() => this.CheckInput(tx, inputIndexCopy, txout, txData, input, flags));
                            checkInput.Start();
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                foreach (Task <bool> checkInput in checkInputs)
                {
                    if (await checkInput.ConfigureAwait(false))
                    {
                        continue;
                    }

                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }
        }
Beispiel #17
0
            public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData)
            {
                if (sigversion == HashVersion.WitnessV0)
                {
                    if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney)
                    {
                        throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput));
                    }
                    uint256 hashPrevouts = uint256.Zero;
                    uint256 hashSequence = uint256.Zero;
                    uint256 hashOutputs  = uint256.Zero;

                    if ((nHashType & SigHash.AnyoneCanPay) == 0)
                    {
                        hashPrevouts = precomputedTransactionData == null?
                                       GetHashPrevouts() : precomputedTransactionData.HashPrevouts;
                    }

                    if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                    {
                        hashSequence = precomputedTransactionData == null?
                                       GetHashSequence() : precomputedTransactionData.HashSequence;
                    }

                    if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                    {
                        hashOutputs = precomputedTransactionData == null?
                                      GetHashOutputs() : precomputedTransactionData.HashOutputs;
                    }
                    else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count)
                    {
                        BitcoinStream ss = CreateHashWriter(sigversion);
                        ss.ReadWrite(this.Outputs[nIn]);
                        hashOutputs = GetHash(ss);
                    }

                    BitcoinStream sss = CreateHashWriter(sigversion);
                    // Version
                    sss.ReadWrite(this.Version);
                    // PoS Time
                    sss.ReadWrite(this.Time);
                    // Input prevouts/nSequence (none/all, depending on flags)
                    sss.ReadWrite(hashPrevouts);
                    sss.ReadWrite(hashSequence);
                    // The input being signed (replacing the scriptSig with scriptCode + amount)
                    // The prevout may already be contained in hashPrevout, and the nSequence
                    // may already be contain in hashSequence.
                    sss.ReadWrite(Inputs[nIn].PrevOut);
                    sss.ReadWrite(scriptCode);
                    sss.ReadWrite(spentOutput.Value.Satoshi);
                    sss.ReadWrite(Inputs[nIn].Sequence);
                    // Outputs (none/one/all, depending on flags)
                    sss.ReadWrite(hashOutputs);
                    // Locktime
                    sss.ReadWriteStruct(LockTime);
                    // Sighash type
                    sss.ReadWrite((uint)nHashType);

                    return(GetHash(sss));
                }

                bool fAnyoneCanPay = (nHashType & SigHash.AnyoneCanPay) != 0;
                bool fHashSingle   = ((byte)nHashType & 0x1f) == (byte)SigHash.Single;
                bool fHashNone     = ((byte)nHashType & 0x1f) == (byte)SigHash.None;

                if (nIn >= Inputs.Count)
                {
                    return(uint256.One);
                }
                if (fHashSingle)
                {
                    if (nIn >= Outputs.Count)
                    {
                        return(uint256.One);
                    }
                }

                var stream = CreateHashWriter(sigversion);

                stream.ReadWrite(Version);
                stream.ReadWrite(Time);
                uint nInputs = (uint)(fAnyoneCanPay ? 1 : Inputs.Count);

                stream.ReadWriteAsVarInt(ref nInputs);
                for (int nInput = 0; nInput < nInputs; nInput++)
                {
                    if (fAnyoneCanPay)
                    {
                        nInput = nIn;
                    }
                    stream.ReadWrite(Inputs[nInput].PrevOut);
                    if (nInput != nIn)
                    {
                        stream.ReadWrite(Script.Empty);
                    }
                    else
                    {
                        WriteScriptCode(stream, scriptCode);
                    }

                    if (nInput != nIn && (fHashSingle || fHashNone))
                    {
                        stream.ReadWrite((uint)0);
                    }
                    else
                    {
                        stream.ReadWrite(Inputs[nInput].Sequence);
                    }
                }

                uint nOutputs = (uint)(fHashNone ? 0 : (fHashSingle ? nIn + 1 : Outputs.Count));

                stream.ReadWriteAsVarInt(ref nOutputs);
                for (int nOutput = 0; nOutput < nOutputs; nOutput++)
                {
                    if (fHashSingle && nOutput != nIn)
                    {
                        this.Outputs.CreateNewTxOut().ReadWrite(stream);
                    }
                    else
                    {
                        Outputs[nOutput].ReadWrite(stream);
                    }
                }

                stream.ReadWriteStruct(LockTime);
                stream.ReadWrite((uint)nHashType);
                return(GetHash(stream));
            }
Beispiel #18
0
        /*
         *  if (nIn >= txTo.vin.size())
         *  {
         *      printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
         *      return 1;
         *  }
         *  CTransaction txTmp(txTo);
         *
         *  // In case concatenating two scripts ends up with two codeseparators,
         *  // or an extra one at the end, this prevents all those possible incompatibilities.
         *  scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
         *
         *  // Blank out other inputs' signatures
         *  for (unsigned int i = 0; i < txTmp.vin.size(); i++)
         *      txTmp.vin[i].scriptSig = CScript();
         *  txTmp.vin[nIn].scriptSig = scriptCode;
         *
         *  // Blank out some of the outputs
         *  if ((nHashType & 0x1f) == SIGHASH_NONE)
         *  {
         *      // Wildcard payee
         *      txTmp.vout.clear();
         *
         *      // Let the others update at will
         *      for (unsigned int i = 0; i < txTmp.vin.size(); i++)
         *          if (i != nIn)
         *              txTmp.vin[i].nSequence = 0;
         *  }
         *  else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
         *  {
         *      // Only lock-in the txout payee at same index as txin
         *      unsigned int nOut = nIn;
         *      if (nOut >= txTmp.vout.size())
         *      {
         *          printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut);
         *          return 1;
         *      }
         *      txTmp.vout.resize(nOut+1);
         *      for (unsigned int i = 0; i < nOut; i++)
         *          txTmp.vout[i].SetNull();
         *
         *      // Let the others update at will
         *      for (unsigned int i = 0; i < txTmp.vin.size(); i++)
         *          if (i != nIn)
         *              txTmp.vin[i].nSequence = 0;
         *  }
         *
         *  // Blank out other inputs completely, not recommended for open transactions
         *  if (nHashType & SIGHASH_ANYONECANPAY)
         *  {
         *      txTmp.vin[0] = txTmp.vin[nIn];
         *      txTmp.vin.resize(1);
         *  }
         *
         *  // Serialize and hash
         *  CHashWriter ss(SER_GETHASH, 0);
         *  ss << txTmp << nHashType;
         *  return ss.GetHash();
         */
        public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, Money amount,
                                                 HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData)
        {
            if (nIn >= Inputs.Count)
            {
                //Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n");
                return(uint256.One);
            }

            var scriptCopy = new Script(scriptCode.ToBytes());

            scriptCopy = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR);

            var hashType = nHashType & (SigHash)31;

            // Check for invalid use of SIGHASH_SINGLE
            //if (hashType == SigHash.Single)
            //{
            //    if (nIn >= Outputs.Count)
            //    {
            //        //Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n");
            //        return uint256.One;
            //    }
            //}

            var txCopy = (SolarCoinTransaction)GetConsensusFactory().CreateTransaction();

            txCopy.FromBytes(this.ToBytes());
            //Set all TxIn script to empty string
            foreach (var txin in txCopy.Inputs)
            {
                txin.ScriptSig = new Script();
            }

            //Copy subscript into the txin script you are checking
            txCopy.Inputs[nIn].ScriptSig = scriptCopy;

            if (hashType == SigHash.None)
            {
                //The output of txCopy is set to a vector of zero size.
                txCopy.Outputs.Clear();

                //All other inputs aside from the current input in txCopy have their nSequence index set to zero
                foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn))
                {
                    input.Sequence = 0;
                }
            }
            else if (hashType == SigHash.Single)
            {
                if (nIn >= Outputs.Count)
                {
                    //Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n");
                    return(uint256.One);
                }
                //The output of txCopy is resized to the size of the current input index+1.
                txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1));
                //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1.
                for (var i = 0; i < nIn; i++)
                {
                    //if (i == nIn)
                    //    continue;
                    txCopy.Outputs[i] = new TxOut();
                }

                //All other txCopy inputs aside from the current input are set to have an nSequence index of zero.
                foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn))
                {
                    input.Sequence = 0;
                }
            }

            if ((nHashType & SigHash.AnyoneCanPay) != 0)
            {
                //The txCopy input vector is resized to a length of one.
                var script = txCopy.Inputs[nIn];
                txCopy.Inputs.Clear();
                txCopy.Inputs.Add(script);
                //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.
                //txCopy.Inputs[0].ScriptSig = scriptCopy;
            }

            var previousType = txCopy.NType;

            txCopy.NType = (uint)PrimaryActions.SER_GETHASH;
            //Serialize TxCopy, append 4 byte hashtypecode
            var stream = CreateHashWriter(sigversion);

            txCopy.ReadWrite(stream);
            stream.ReadWrite((uint)nHashType);
            var txHash = GetHash(stream);

            txCopy.NType = previousType;

            return(txHash);
        }
Beispiel #19
0
        public virtual void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler)
        {
            Block            block = context.BlockResult.Block;
            ChainedBlock     index = context.BlockResult.ChainedBlock;
            ConsensusFlags   flags = context.Flags;
            UnspentOutputSet view  = context.Set;

            this.PerformanceCounter.AddProcessedBlocks(1);
            taskScheduler = taskScheduler ?? TaskScheduler.Default;
            if (flags.EnforceBIP30)
            {
                foreach (var tx in block.Transactions)
                {
                    var coins = view.AccessCoins(tx.GetHash());
                    if (coins != null && !coins.IsPrunable)
                    {
                        ConsensusErrors.BadTransactionBIP30.Throw();
                    }
                }
            }
            long  nSigOpsCost = 0;
            Money nFees       = Money.Zero;
            List <Task <bool> > checkInputs = new List <Task <bool> >();

            for (int i = 0; i < block.Transactions.Count; i++)
            {
                this.PerformanceCounter.AddProcessedTransactions(1);
                var tx = block.Transactions[i];
                if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake)))
                {
                    int[] prevheights;

                    if (!view.HaveInputs(tx))
                    {
                        ConsensusErrors.BadTransactionMissingInput.Throw();
                    }

                    prevheights = new int[tx.Inputs.Count];
                    // Check that transaction is BIP68 final
                    // BIP68 lock checks (as opposed to nLockTime checks) must
                    // be in ConnectBlock because they require the UTXO set
                    for (var j = 0; j < tx.Inputs.Count; j++)
                    {
                        prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                    }

                    if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                    {
                        ConsensusErrors.BadTransactionNonFinal.Throw();
                    }
                }
                // GetTransactionSigOpCost counts 3 types of sigops:
                // * legacy (always)
                // * p2sh (when P2SH enabled in flags and excludes coinbase)
                // * witness (when witness enabled in flags and excludes coinbase)
                nSigOpsCost += this.GetTransactionSigOpCost(tx, view, flags);
                if (nSigOpsCost > this.consensusOptions.MAX_BLOCK_SIGOPS_COST)
                {
                    ConsensusErrors.BadBlockSigOps.Throw();
                }

                if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake)))
                {
                    this.CheckInputs(tx, view, index.Height);
                    nFees += view.GetValueIn(tx) - tx.TotalOut;
                    int ii      = i;
                    var localTx = tx;
                    PrecomputedTransactionData txData = new PrecomputedTransactionData(tx);
                    for (int iInput = 0; iInput < tx.Inputs.Count; iInput++)
                    {
                        this.PerformanceCounter.AddProcessedInputs(1);
                        var input      = tx.Inputs[iInput];
                        int iiIntput   = iInput;
                        var txout      = view.GetOutputFor(input);
                        var checkInput = new Task <bool>(() =>
                        {
                            if (this.UseConsensusLib)
                            {
                                Script.BitcoinConsensusError error;
                                return(Script.VerifyScriptConsensus(txout.ScriptPubKey, tx, (uint)iiIntput, flags.ScriptFlags, out error));
                            }
                            else
                            {
                                var checker      = new TransactionChecker(tx, iiIntput, txout.Value, txData);
                                var ctx          = new ScriptEvaluationContext();
                                ctx.ScriptVerify = flags.ScriptFlags;
                                return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker));
                            }
                        });
                        checkInput.Start(taskScheduler);
                        checkInputs.Add(checkInput);
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            this.CheckBlockReward(context, nFees, index, block);

            var passed = checkInputs.All(c => c.GetAwaiter().GetResult());

            if (!passed)
            {
                ConsensusErrors.BadTransactionScriptError.Throw();
            }
        }
        /// <summary>
        /// Validates transaction inputs against transaction data for a specific set of script verify flags.
        /// Check whether all inputs of this transaction are valid (no double spends, scripts & signatures, amounts)
        /// This does not modify the UTXO set.
        /// </summary>
        /// <seealso>https://github.com/bitcoin/bitcoin/blob/febf3a856bcfb8fef2cb4ddcb8d1e0cab8a22580/src/validation.cpp#L1259</seealso>
        /// <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 (tx.IsCoinBase)
            {
                return(true);
            }

            // TODO: The original code does not appear to do these checks here. Reevaluate if this needs to be done, or perhaps moved to another rule/method.
            this.consensusRuleEngine.GetRule <CoinViewRule>().CheckInputs(context.Transaction, context.View.Set, this.chainIndexer.Height + 1);

            // TODO: Original code has the concept of a script execution cache. This might be worth looking into for performance improvements. Signature checks are expensive.

            for (int iInput = 0; iInput < tx.Inputs.Count; iInput++)
            {
                TxIn  input   = tx.Inputs[iInput];
                int   iiInput = iInput;
                TxOut txout   = context.View.GetOutputFor(input);

                var checker = new TransactionChecker(tx, iiInput, txout.Value, txData);
                var ctx     = new ScriptEvaluationContext(this.network)
                {
                    ScriptVerify = scriptVerify
                };
                if (!ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker))
                {
                    if ((scriptVerify & ScriptVerify.StandardNotMandatory) == ScriptVerify.StandardNotMandatory)
                    {
                        // 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.

                        // TODO: Investigate whether the checker and context can be reused instead of recreated. Probably not.
                        checker = new TransactionChecker(tx, iiInput, txout.Value, txData);
                        ctx     = new ScriptEvaluationContext(this.network)
                        {
                            ScriptVerify = (scriptVerify & ~ScriptVerify.StandardNotMandatory)
                        };

                        if (ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker))
                        {
                            this.logger.LogTrace("(-)[FAIL_NON_MANDATORY_SCRIPT_VERIFY]");
                            this.logger.LogDebug("Failed non-mandatory script verify: Transaction = {0}, scriptVerify = {1}, ctx.scriptVerify = {2}",
                                                 tx.ToHex(), scriptVerify, ctx.ScriptVerify);

                            // TODO: Check what this actually means in Core's logic. If it is on testnet/regtest and RequireStandard is false, is the transaction still rejected?
                            context.State.Fail(MempoolErrors.NonMandatoryScriptVerifyFlagFailed, ctx.Error.ToString()).Throw();
                        }
                    }

                    // Failures of other flags indicate a transaction that is invalid in new blocks, e.g. an 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.

                    // Further comment from Bitcoin Core:
                    // MANDATORY flag failures correspond to
                    // ValidationInvalidReason::CONSENSUS. Because CONSENSUS
                    // failures are the most serious case of validation
                    // failures, we may need to consider using
                    // RECENT_CONSENSUS_CHANGE for any script failure that
                    // could be due to non-upgraded nodes which we may want to
                    // support, to avoid splitting the network (but this
                    // depends on the details of how net_processing handles
                    // such errors).

                    this.logger.LogTrace("(-)[FAIL_MANDATORY_SCRIPT_VERIFY]");
                    context.State.Fail(MempoolErrors.MandatoryScriptVerifyFlagFailed, ctx.Error.ToString()).Throw();
                }
            }

            return(true);
        }
Beispiel #21
0
        public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, Money amount,
                                                 HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData)
        {
            if (nIn >= Inputs.Count && (uint)nIn != NOT_AN_INPUT) // second condition is always true, NBitcoin restricts us to transparent txs only, left to match the original code
            {
                throw new InvalidOperationException("Input index is out of range");
            }

            if (fOverwintered && (nVersionGroupId == OVERWINTER_VERSION_GROUP_ID || nVersionGroupId == SAPLING_VERSION_GROUP_ID))
            {
                uint256 hashPrevouts        = uint256.Zero;
                uint256 hashSequence        = uint256.Zero;
                uint256 hashOutputs         = uint256.Zero;
                uint256 hashJoinSplits      = uint256.Zero;
                uint256 hashShieldedSpends  = uint256.Zero;
                uint256 hashShieldedOutputs = uint256.Zero;

                if ((nHashType & SigHash.AnyoneCanPay) == 0)
                {
                    hashPrevouts = precomputedTransactionData == null?
                                   GetHashPrevouts() : precomputedTransactionData.HashPrevouts;
                }

                if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                {
                    hashSequence = precomputedTransactionData == null?
                                   GetHashSequence() : precomputedTransactionData.HashSequence;
                }

                if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                {
                    hashOutputs = precomputedTransactionData == null?
                                  GetHashOutputs() : precomputedTransactionData.HashOutputs;
                }
                else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count)
                {
                    using (var ss = new BLAKE2bWriter(ZCASH_OUTPUTS_HASH_PERSONALIZATION))
                    {
                        ss.ReadWrite(Outputs[nIn]);
                        hashOutputs = ss.GetHash();
                    }
                }

                if (vjoinsplit.Any())
                {
                    hashJoinSplits = GetJoinSplitsHash();
                }

                if (vShieldedSpend.Any())
                {
                    hashShieldedSpends = GetShieldedSpendsHash();
                }

                if (vShieldedOutput.Any())
                {
                    hashShieldedOutputs = GetShieldedOutputsHash();
                }

                var branchId =
                    nBranchId.HasValue ? nBranchId.Value :
                    Version == OVERWINTER_VERSION ? OVERWINTER_BRANCH_ID :
                    Version == SAPLING_VERSION ? SAPLING_BRANCH_ID :
                    0;

                var branchIdData = BitConverter.IsLittleEndian ?
                                   BitConverter.GetBytes(branchId) :
                                   BitConverter.GetBytes(branchId).Reverse().ToArray();

                var personal = Encoding.ASCII.GetBytes("ZcashSigHash")
                               .Concat(branchIdData)
                               .ToArray();

                using (var ss = new BLAKE2bWriter(personal))
                {
                    // Version
                    var nVersion = Version;
                    ss.ReadWriteVersionEncoded(ref nVersion, ref fOverwintered);
                    ss.ReadWrite(nVersionGroupId);
                    // Input prevouts/nSequence (none/all, depending on flags)
                    ss.ReadWrite(hashPrevouts);
                    ss.ReadWrite(hashSequence);
                    // Outputs (none/one/all, depending on flags)
                    ss.ReadWrite(hashOutputs);
                    // JoinSplits
                    ss.ReadWrite(hashJoinSplits);

                    if (nVersionGroupId == SAPLING_VERSION_GROUP_ID)
                    {
                        // Spend descriptions
                        ss.ReadWrite(hashShieldedSpends);
                        // Output descriptions
                        ss.ReadWrite(hashShieldedOutputs);
                    }

                    // Locktime
                    ss.ReadWriteStruct(LockTime);
                    // Expiry height
                    ss.ReadWrite(nExpiryHeight);

                    if (nVersionGroupId == SAPLING_VERSION_GROUP_ID)
                    {
                        // Sapling value balance
                        ss.ReadWrite(valueBalance);
                    }

                    // Sighash type
                    ss.ReadWrite((uint)nHashType);


                    // If this hash is for a transparent input signature
                    // (i.e. not for txTo.joinSplitSig):
                    if ((uint)nIn != NOT_AN_INPUT) // always true, NBitcoin restricts us to transparent txs only, left to match the original code
                    {
                        // The input being signed (replacing the scriptSig with scriptCode + amount)
                        // The prevout may already be contained in hashPrevout, and the nSequence
                        // may already be contained in hashSequence.
                        ss.ReadWrite(Inputs[nIn].PrevOut);
                        ss.ReadWrite(scriptCode);
                        ss.ReadWrite(amount.Satoshi);
                        ss.ReadWrite(Inputs[nIn].Sequence.Value);
                    }

                    return(ss.GetHash());
                }
            }
            else
            {
                // Check for invalid use of SIGHASH_SINGLE
                if (((uint)nHashType & 0x1f) == (uint)SigHash.Single)
                {
                    if (nIn >= Outputs.Count)
                    {
                        throw new InvalidOperationException("No matching output for SIGHASH_SINGLE");
                    }
                }

                var scriptCopy = new Script(scriptCode.ToOps().Where(op => op.Code != OpcodeType.OP_CODESEPARATOR));
                var txCopy     = new ZcashTransaction(this.ToHex());

                //Set all TxIn script to empty string
                foreach (var txin in txCopy.Inputs)
                {
                    txin.ScriptSig = new Script();
                }

                //Copy subscript into the txin script you are checking
                txCopy.Inputs[nIn].ScriptSig = scriptCopy;

                if (nHashType == SigHash.None)
                {
                    //The output of txCopy is set to a vector of zero size.
                    txCopy.Outputs.Clear();

                    //All other inputs aside from the current input in txCopy have their nSequence index set to zero
                    foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn))
                    {
                        input.Sequence = 0;
                    }
                }
                else if (nHashType == SigHash.Single)
                {
                    //The output of txCopy is resized to the size of the current input index+1.
                    txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1));

                    //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1.
                    for (var i = 0; i < txCopy.Outputs.Count; i++)
                    {
                        if (i == nIn)
                        {
                            continue;
                        }
                        txCopy.Outputs[i] = new TxOut();
                    }

                    //All other txCopy inputs aside from the current input are set to have an nSequence index of zero.
                    foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn))
                    {
                        input.Sequence = 0;
                    }
                }

                if ((nHashType & SigHash.AnyoneCanPay) != 0)
                {
                    //The txCopy input vector is resized to a length of one.
                    var script = txCopy.Inputs[nIn];
                    txCopy.Inputs.Clear();
                    txCopy.Inputs.Add(script);
                    //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.
                    txCopy.Inputs[0].ScriptSig = scriptCopy;
                }

                // clean JS signature
                // see https://github.com/zcash/zcash/blob/e868f8247faea8cc74aef69262d93bdeacc82c53/src/script/interpreter.cpp#L1053
                txCopy.joinSplitSig = new byte[64];

                //Serialize TxCopy, append 4 byte hashtypecode
                using (var hs = CreateSignatureHashStream())
                {
                    BitcoinStream stream = new BitcoinStream(hs, true);
                    stream.Type = SerializationType.Hash;
                    stream.TransactionOptions = sigversion == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness;
                    txCopy.ReadWrite(stream);
                    stream.ReadWrite((uint)nHashType);
                    return(hs.GetHash());
                }
            }
        }
Beispiel #22
0
        /// <inheritdoc />
        public virtual void ExecuteBlock(RuleContext context, TaskScheduler taskScheduler = null)
        {
            this.logger.LogTrace("()");

            Block            block = context.BlockValidationContext.Block;
            ChainedBlock     index = context.BlockValidationContext.ChainedBlock;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = context.Set;

            this.PerformanceCounter.AddProcessedBlocks(1);
            taskScheduler = taskScheduler ?? TaskScheduler.Default;

            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                this.PerformanceCounter.AddProcessedTransactions(1);
                Transaction tx = block.Transactions[txIndex];
                if (!context.SkipValidation)
                {
                    if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake)))
                    {
                        int[] prevheights;

                        if (!view.HaveInputs(tx))
                        {
                            this.logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                            ConsensusErrors.BadTransactionMissingInput.Throw();
                        }

                        prevheights = new int[tx.Inputs.Count];
                        // Check that transaction is BIP68 final.
                        // BIP68 lock checks (as opposed to nLockTime checks) must
                        // be in ConnectBlock because they require the UTXO set.
                        for (int j = 0; j < tx.Inputs.Count; j++)
                        {
                            prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                        }

                        if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                        {
                            this.logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                            ConsensusErrors.BadTransactionNonFinal.Throw();
                        }
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    // TODO: Simplify this condition.
                    if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake)))
                    {
                        this.CheckInputs(tx, view, index.Height);
                        fees += view.GetValueIn(tx) - tx.TotalOut;
                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            this.PerformanceCounter.AddProcessedInputs(1);
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() =>
                            {
                                var checker      = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
                                var ctx          = new ScriptEvaluationContext();
                                ctx.ScriptVerify = flags.ScriptFlags;
                                return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker));
                            });
                            checkInput.Start(taskScheduler);
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                bool passed = checkInputs.All(c => c.GetAwaiter().GetResult());
                if (!passed)
                {
                    this.logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }

            this.logger.LogTrace("(-)");
        }
        /// <inheritdoc />
        public override async Task RunAsync(RuleContext context)
        {
            this.Logger.LogTrace("()");

            Block            block = context.BlockValidationContext.Block;
            ChainedHeader    index = context.BlockValidationContext.ChainedHeader;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = context.Set;

            this.Parent.PerformanceCounter.AddProcessedBlocks(1);

            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                this.Parent.PerformanceCounter.AddProcessedTransactions(1);
                Transaction tx = block.Transactions[txIndex];
                if (!context.SkipValidation)
                {
                    // TODO: Simplify this condition.
                    if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake)))
                    {
                        if (!view.HaveInputs(tx))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                            ConsensusErrors.BadTransactionMissingInput.Throw();
                        }

                        var prevheights = new int[tx.Inputs.Count];
                        // Check that transaction is BIP68 final.
                        // BIP68 lock checks (as opposed to nLockTime checks) must
                        // be in ConnectBlock because they require the UTXO set.
                        for (int j = 0; j < tx.Inputs.Count; j++)
                        {
                            prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                        }

                        if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                            ConsensusErrors.BadTransactionNonFinal.Throw();
                        }
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.PowConsensusOptions.MaxBlockSigopsCost)
                    {
                        this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    // TODO: Simplify this condition.
                    if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake)))
                    {
                        this.CheckInputs(tx, view, index.Height);
                        fees += view.GetValueIn(tx) - tx.TotalOut;
                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            this.Parent.PerformanceCounter.AddProcessedInputs(1);
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() =>
                            {
                                var checker             = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
                                var ctx                 = new ScriptEvaluationContext(this.Parent.Network);
                                ctx.ScriptVerify        = flags.ScriptFlags;
                                bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker);

                                if (verifyScriptResult == false)
                                {
                                    this.Logger.LogTrace("Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'", tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error);
                                }

                                return(verifyScriptResult);
                            });
                            checkInput.Start();
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                foreach (Task <bool> checkInput in checkInputs)
                {
                    if (await checkInput.ConfigureAwait(false))
                    {
                        continue;
                    }

                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }

            this.Logger.LogTrace("(-)");
        }
        /// <inheritdoc />
        public override async Task RunAsync(RuleContext context)
        {
            Block            block = context.ValidationContext.BlockToValidate;
            ChainedHeader    index = context.ValidationContext.ChainedHeaderToValidate;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = (context as UtxoRuleContext).UnspentOutputSet;

            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                Transaction tx = block.Transactions[txIndex];

                if (!context.SkipValidation)
                {
                    if (!tx.IsCoinBase && !view.HaveInputs(tx))
                    {
                        this.Logger.LogTrace("Transaction '{0}' has not inputs", tx.GetHash());
                        this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                        ConsensusErrors.BadTransactionMissingInput.Throw();
                    }

                    if (!this.IsTxFinal(tx, context))
                    {
                        this.Logger.LogTrace("Transaction '{0}' is not final", tx.GetHash());
                        this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                        ConsensusErrors.BadTransactionNonFinal.Throw();
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    if (!tx.IsCoinBase)
                    {
                        this.CheckInputs(tx, view, index.Height);

                        if (!tx.IsCoinStake)
                        {
                            fees += view.GetValueIn(tx) - tx.TotalOut;
                        }

                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() => this.CheckInput(tx, inputIndexCopy, txout, txData, input, flags));
                            checkInput.Start();
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                foreach (Task <bool> checkInput in checkInputs)
                {
                    if (await checkInput.ConfigureAwait(false))
                    {
                        continue;
                    }

                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }
        }
        /// <inheritdoc/>
        protected override bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)
        {
            if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall())
            {
                return(input.ScriptSig.IsSmartContractSpend());
            }

            var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
            var ctx     = new ScriptEvaluationContext(this.Parent.Network);

            ctx.ScriptVerify = flags.ScriptFlags;
            return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker));
        }
Beispiel #26
0
        public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData)
        {
            uint nForkHashType = (uint)nHashType;

            if (UsesForkId(nHashType))
            {
                nForkHashType |= ForkId << 8;
            }

            if ((SupportSegwit && sigversion == HashVersion.Witness) || UsesForkId(nHashType))
            {
                if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney)
                {
                    throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput));
                }
                uint256 hashPrevouts = uint256.Zero;
                uint256 hashSequence = uint256.Zero;
                uint256 hashOutputs  = uint256.Zero;

                if ((nHashType & SigHash.AnyoneCanPay) == 0)
                {
                    hashPrevouts = precomputedTransactionData == null?
                                   GetHashPrevouts() : precomputedTransactionData.HashPrevouts;
                }

                if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                {
                    hashSequence = precomputedTransactionData == null?
                                   GetHashSequence() : precomputedTransactionData.HashSequence;
                }

                if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                {
                    hashOutputs = precomputedTransactionData == null?
                                  GetHashOutputs() : precomputedTransactionData.HashOutputs;
                }
                else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count)
                {
                    BitcoinStream ss = CreateHashWriter(sigversion);
                    ss.ReadWrite(this.Outputs[nIn]);
                    hashOutputs = GetHash(ss);
                }

                BitcoinStream sss = CreateHashWriter(sigversion);
                // Version
                sss.ReadWrite(this.Version);
                // Input prevouts/nSequence (none/all, depending on flags)
                sss.ReadWrite(hashPrevouts);
                sss.ReadWrite(hashSequence);
                // The input being signed (replacing the scriptSig with scriptCode + amount)
                // The prevout may already be contained in hashPrevout, and the nSequence
                // may already be contain in hashSequence.
                sss.ReadWrite(Inputs[nIn].PrevOut);
                sss.ReadWrite(scriptCode);
                sss.ReadWrite(spentOutput.Value.Satoshi);
                sss.ReadWrite((uint)Inputs[nIn].Sequence);
                // Outputs (none/one/all, depending on flags)
                sss.ReadWrite(hashOutputs);
                // Locktime
                sss.ReadWriteStruct(LockTime);
                // Sighash type
                sss.ReadWrite((uint)nForkHashType);

                return(GetHash(sss));
            }



            if (nIn >= Inputs.Count)
            {
                return(uint256.One);
            }

            var hashType = nHashType & (SigHash)31;

            // Check for invalid use of SIGHASH_SINGLE
            if (hashType == SigHash.Single)
            {
                if (nIn >= Outputs.Count)
                {
                    return(uint256.One);
                }
            }

            var scriptCopy = scriptCode.Clone();

            scriptCode = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR);

            var txCopy = GetConsensusFactory().CreateTransaction();

            txCopy.FromBytes(this.ToBytes());
            //Set all TxIn script to empty string
            foreach (var txin in txCopy.Inputs)
            {
                txin.ScriptSig = new Script();
            }
            //Copy subscript into the txin script you are checking
            txCopy.Inputs[nIn].ScriptSig = scriptCopy;

            if (hashType == SigHash.None)
            {
                //The output of txCopy is set to a vector of zero size.
                txCopy.Outputs.Clear();

                //All other inputs aside from the current input in txCopy have their nSequence index set to zero
                foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn))
                {
                    input.Sequence = 0;
                }
            }
            else if (hashType == SigHash.Single)
            {
                //The output of txCopy is resized to the size of the current input index+1.
                txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1));
                //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1.
                for (var i = 0; i < txCopy.Outputs.Count; i++)
                {
                    if (i == nIn)
                    {
                        continue;
                    }
                    txCopy.Outputs[i] = new TxOut();
                }
                //All other txCopy inputs aside from the current input are set to have an nSequence index of zero.
                foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn))
                {
                    input.Sequence = 0;
                }
            }


            if ((nHashType & SigHash.AnyoneCanPay) != 0)
            {
                //The txCopy input vector is resized to a length of one.
                var script = txCopy.Inputs[nIn];
                txCopy.Inputs.Clear();
                txCopy.Inputs.Add(script);
                //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.
                txCopy.Inputs[0].ScriptSig = scriptCopy;
            }


            //Serialize TxCopy, append 4 byte hashtypecode
            var stream = CreateHashWriter(sigversion);

            txCopy.ReadWrite(stream);
            stream.ReadWrite((uint)nForkHashType);
            return(GetHash(stream));
        }
Beispiel #27
0
        protected override bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)
        {
            if (txout.ScriptPubKey.IsScriptType(ScriptType.P2SH))
            {
                // federation output is p2sh

                IList <Op> ops = input.ScriptSig.ToOps();
                if (ops.Count > 5 && ops[0].PushData.Length == 0) // first op is zero, federation is at least 3 participants, last is redeem script
                {
                    Script redeemScript = new Script(ops.Last().PushData);

                    foreach (Op innerOp in redeemScript.ToOps())
                    {
                        if (innerOp.Code == OpcodeType.OP_NOP9)
                        {
                            // federation overrides OP_NOP9 to push fed pub keys to the stack and the
                            // needed signatures to satisfy the federation multisig, to avoid changing
                            // the script engine for now we ignore such outputs and consider them valid
                            // (as was with the opcode OP_NOP9) a malicious node may cause blockcore nodes
                            // to accept invalid blocks however as long as blockcore nodes are minority nodes
                            // its an acceptable risk, users not part of a federation should not really be effected.

                            return(true);
                        }
                    }
                }
            }

            this.AllowSpend(txout, tx);

            return(base.CheckInput(tx, inputIndexCopy, txout, txData, input, flags));
        }