Esempio n. 1
0
        private bool VerifyP2wsh(ITransaction tx, int index, IRedeemScript redeem, ReadOnlySpan <byte> expectedHash,
                                 ulong amount, out string error)
        {
            ReadOnlySpan <byte> actualHash = sha256.ComputeHash(redeem.Data);

            if (!actualHash.SequenceEqual(expectedHash))
            {
                error = "Invalid hash.";
                return(false);
            }

            if (!redeem.TryEvaluate(out IOperation[] redeemOps, out int redeemOpCount, out error))
            {
                error = $"Script evaluation failed." +
                        $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                        $"{Environment.NewLine}More info: {error}";
                return(false);
            }

            // Note that there is no *Constants.WitnessScaleFactor here anymore
            TotalSigOpCount += redeem.CountSigOps(redeemOps);

            var stack = new OpData()
            {
                Tx        = tx,
                TxInIndex = index,

                ForceLowS            = ForceLowS,
                StrictNumberEncoding = StrictNumberEncoding,

                IsBip65Enabled  = consensus.IsBip65Enabled(BlockHeight),
                IsBip112Enabled = consensus.IsBip112Enabled(BlockHeight),
                IsStrictDerSig  = consensus.IsStrictDerSig(BlockHeight),
                IsBip147Enabled = consensus.IsBip147Enabled(BlockHeight),
            };

            for (int j = 0; j < tx.WitnessList[index].Items.Length - 1; j++)
            {
                if (!tx.WitnessList[index].Items[j].Run(stack, out error))
                {
                    error = $"Script evaluation failed." +
                            $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                            $"{Environment.NewLine}More info: {error}";
                    return(false);
                }
            }

            stack.AmountBeingSpent = amount;
            stack.IsSegWit         = true;
            stack.OpCount          = redeemOpCount;
            stack.ExecutingScript  = redeemOps;

            foreach (var op in redeemOps)
            {
                if (!op.Run(stack, out error))
                {
                    error = $"Script evaluation failed." +
                            $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                            $"{Environment.NewLine}More info: {error}";
                    return(false);
                }
            }

            // Stack has to only have 1 item left
            if (stack.ItemCount == 1 && IsNotZero(stack.Pop()))
            {
                error = null;
                return(true);
            }
            else
            {
                error = stack.ItemCount != 1 ?
                        "Stack has to have only 1 item after witness execution." :
                        "Top stack item is not true";
                return(false);
            }
        }
        /// <inheritdoc/>
        public bool Verify(ITransaction tx, out string error)
        {
            // If a tx is already in memory pool it must have been verified and be valid.
            // The SigOpCount property must be set by the caller (mempool dependency).
            if (mempool.Contains(tx))
            {
                TotalSigOpCount += tx.SigOpCount;
                TotalFee        += utxoDb.MarkSpentAndGetFee(tx.TxInList);
                if (!AnySegWit)
                {
                    AnySegWit = tx.WitnessList != null;
                }
                error = null;
                return(true);
            }

            if (tx.TxInList.Length == 0 || tx.TxOutList.Length == 0)
            {
                error = "Invalid number of inputs or outputs.";
                return(false);
            }

            ulong toSpend = 0;

            for (int i = 0; i < tx.TxInList.Length; i++)
            {
                TxIn  currentInput = tx.TxInList[i];
                IUtxo prevOutput   = utxoDb.Find(currentInput);
                if (prevOutput is null)
                {
                    // TODO: add a ToString() method to TxIn?
                    error = $"Input {currentInput.TxHash.ToBase16()}:{currentInput.Index} was not found.";
                    return(false);
                }
                toSpend += prevOutput.Amount;

                if (!prevOutput.PubScript.TryEvaluate(out IOperation[] pubOps, out int pubOpCount, out error))
                {
                    error = $"Invalid input transaction pubkey script." +
                            $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                            $"{Environment.NewLine}More info: {error}";
                    return(false);
                }

                if (!currentInput.SigScript.TryEvaluate(out IOperation[] signatureOps, out int signatureOpCount, out error))
                {
                    error = $"Invalid transaction signature script." +
                            $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                            $"{Environment.NewLine}More info: {error}";
                    return(false);
                }

                OpData stack = new OpData()
                {
                    Tx        = tx,
                    TxInIndex = i,

                    ForceLowS            = ForceLowS,
                    StrictNumberEncoding = StrictNumberEncoding,

                    IsBip65Enabled  = consensus.IsBip65Enabled(BlockHeight),
                    IsBip112Enabled = consensus.IsBip112Enabled(BlockHeight),
                    IsStrictDerSig  = consensus.IsStrictDerSig(BlockHeight),
                    IsBip147Enabled = consensus.IsBip147Enabled(BlockHeight),
                };

                PubkeyScriptSpecialType pubType = prevOutput.PubScript.GetSpecialType(consensus, BlockHeight);

                // TODO: optimize for specific pubScrTypes
                if (pubType == PubkeyScriptSpecialType.None || pubType == PubkeyScriptSpecialType.P2PKH)
                {
                    TotalSigOpCount += (prevOutput.PubScript.CountSigOps() + currentInput.SigScript.CountSigOps())
                                       * Constants.WitnessScaleFactor;

                    if ((tx.WitnessList != null && tx.WitnessList.Length != 0) &&
                        (tx.WitnessList[i].Items.Length != 0))
                    {
                        error = $"Unexpected witness." +
                                $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                $"{Environment.NewLine}More info: {error}";
                        return(false);
                    }

                    // Note that checking OP count is below max is done during Evaluate() and op.Run()
                    stack.ExecutingScript = signatureOps;
                    stack.OpCount         = signatureOpCount;
                    foreach (var op in signatureOps)
                    {
                        if (!op.Run(stack, out error))
                        {
                            error = $"Script evaluation failed." +
                                    $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                    $"{Environment.NewLine}More info: {error}";
                            return(false);
                        }
                    }

                    stack.ExecutingScript = pubOps;
                    stack.OpCount         = pubOpCount;
                    foreach (var op in pubOps)
                    {
                        if (!op.Run(stack, out error))
                        {
                            error = $"Script evaluation failed." +
                                    $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                    $"{Environment.NewLine}More info: {error}";
                            return(false);
                        }
                    }
                }
                else if (pubType == PubkeyScriptSpecialType.P2SH)
                {
                    if (signatureOps.Length == 0 || !(signatureOps[^ 1] is PushDataOp rdmPush))