Ejemplo n.º 1
0
        /// <summary>
        /// Removes top two stack item checks their equality, fails if not equal. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = "There was not enough items left in the stack to check.";
                return(false);
            }

            byte[] item1 = opData.Pop();
            byte[] item2 = opData.Pop();

            if (item1.IsEqualTo(item2))
            {
                error = null;
                return(true);
            }
            else
            {
                error = "Items were not equal.";
                return(false);
            }

            // ^^ this way we skip unnecessary OP instantiation and Push() and Pop() operations on IOpData

            //EqualOp eq = new EqualOp();
            //VerifyOp ver = new VerifyOp();
            //if (!eq.Run(opData, out error))
            //{
            //    return false;
            //}
            //return ver.Run(opData, out error);
        }
        /// <summary>
        /// Removes top three stack items and converts them to <see cref="long"/>s: value, Min, Max.
        /// Pushes 1 if value was in range, otherwise 0.
        /// Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 3)
            {
                error = "Invalid number of elements in stack.";
                return(false);
            }

            // Stack is: x then min then max so we pop max first
            if (!OpHelper.TryConvertByteArrayToInt(opData.Pop(), out long max, true))
            {
                error = "Couldn't convert to number.";
                return(false);
            }
            if (!OpHelper.TryConvertByteArrayToInt(opData.Pop(), out long min, true))
            {
                error = "Couldn't convert to number.";
                return(false);
            }
            if (!OpHelper.TryConvertByteArrayToInt(opData.Pop(), out long x, true))
            {
                error = "Couldn't convert to number.";
                return(false);
            }

            int c = (x >= min && x < max) ? 1 : 0;

            opData.Push(OpHelper.IntToByteArray(c));

            error = null;
            return(true);
        }
        /// <summary>
        /// Removes top three stack items and converts them to <see cref="long"/>s: value, Min, Max.
        /// Pushes 1 if value was in range, otherwise 0.
        /// Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 3)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            // Stack is: x then min then max => pop max first
            byte[][] numbers = opData.Pop(3);
            if (!TryConvertToLong(numbers[2], out long max, opData.StrictNumberEncoding))
            {
                error = "Invalid number format (max).";
                return(false);
            }
            if (!TryConvertToLong(numbers[1], out long min, opData.StrictNumberEncoding))
            {
                error = "Invalid number format (min).";
                return(false);
            }
            if (!TryConvertToLong(numbers[0], out long x, opData.StrictNumberEncoding))
            {
                error = "Invalid number format (x).";
                return(false);
            }

            opData.Push(x >= min && x < max);

            error = null;
            return(true);
        }
        /// <summary>
        /// Removes top two stack item and pushes the result of their equality check (true for equality and false otherwiwe)
        /// onto the stack. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            ReadOnlySpan <byte> item1 = opData.Pop();
            ReadOnlySpan <byte> item2 = opData.Pop();

            opData.Push(item1.SequenceEqual(item2));

            error = null;
            return(true);
        }
Ejemplo n.º 5
0
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = "Invalid number of elements in stack.";
                return(false);
            }

            byte[] pubBa = opData.Pop();
            byte[] sigBa = opData.Pop();

            // "fake" pass!
            opData.Push(new byte[] { 1 });

            error = null;
            return(true);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Removes top stack item and puts it in alt-stack. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = "There was not enough items left in the stack to copy to alt-stack.";
                return(false);
            }

            opData.AltPush(opData.Pop());

            error = null;
            return(true);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Removes the top stack item. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = "There was no item left in the stack to drop.";
                return(false);
            }

            opData.Pop(); // Throw away the top stack item

            error = null;
            return(true);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Removes (discards) top two stack items. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = "There was not enough items left in the stack to drop.";
                return(false);
            }

            opData.Pop(2); // Throw away the 2 items

            error = null;
            return(true);
        }
        /// <summary>
        /// Removes top two stack item checks their equality, only fails if not equal. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            ReadOnlySpan <byte> item1 = opData.Pop();
            ReadOnlySpan <byte> item2 = opData.Pop();

            if (item1.SequenceEqual(item2))
            {
                error = null;
                return(true);
            }
            else
            {
                error = "Top two stack items are not equal.";
                return(false);
            }
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Replaces top stack item with its hash digest. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = "Not enough items in stack.";
                return(false);
            }

            opData.Push(Hash.ComputeHash(opData.Pop()));

            error = null;
            return(true);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Removes top stack item and puts it in alt-stack. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            opData.AltPush(opData.Pop());

            error = null;
            return(true);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Removes (discards) top two stack items. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            // Throw away the 2 items
            _ = opData.Pop(2);

            error = null;
            return(true);
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Swaps the position of top 2 items on top of the stack. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[][] data = opData.Pop(2);
            opData.Push(new byte[2][] { data[1], data[0] });

            error = null;
            return(true);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Removes top two stack item and pushes (true for equality and false otherwiwe) onto the stack. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = "There was not enough items left in the stack to check.";
                return(false);
            }

            byte[] item1 = opData.Pop();
            byte[] item2 = opData.Pop();

            if (item1.IsEqualTo(item2))
            {
                opData.Push(new byte[] { 1 });
            }
            else
            {
                opData.Push(new byte[0]);
            }

            error = null;
            return(true);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Replaces top stack item with its hash digest. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            using Sha256 hash = new Sha256();
            opData.Push(hash.ComputeHash(opData.Pop()));

            error = null;
            return(true);
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Swaps the position of top 2 items on top of the stack. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = "There was not enough items left in the stack to swap.";
                return(false);
            }

            byte[][] data = opData.Pop(2);
            opData.Push(new byte[2][] { data[1], data[0] });

            error = null;
            return(true);
        }
        protected bool TrySetValues(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = "Not enough items left in the stack.";
                return(false);
            }

            // Stack is: a then b so we pop b first
            if (!OpHelper.TryConvertByteArrayToInt(opData.Pop(), out b, true))
            {
                error = "Invalid number format.";
                return(false);
            }

            if (!OpHelper.TryConvertByteArrayToInt(opData.Pop(), out a, true))
            {
                error = "Invalid number format.";
                return(false);
            }

            error = null;
            return(true);
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Swaps top two item pairs on top of the stack: x1 x2 x3 x4 -> x3 x4 x1 x2
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 4)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[][] data = opData.Pop(4);
            // x0 x1 x2 x3 -> x2 x3 x0 x1
            opData.Push(new byte[4][] { data[2], data[3], data[0], data[1] });

            error = null;
            return(true);
        }
Ejemplo n.º 19
0
        /// <summary>
        /// Rotates top 3 items on top of the stack to the left. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 3)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[][] data = opData.Pop(3);
            // (x0 x1 x2 -> x1 x2 x0)
            opData.Push(new byte[3][] { data[1], data[2], data[0] });

            error = null;
            return(true);
        }
        /// <summary>
        /// Removes the top two stack items and sets the two integer values based on them for usages of child classes.
        /// </summary>
        /// <param name="opData"><inheritdoc cref="IOperation.Run(IOpData, out string)" path="/param[@name='opData']"/></param>
        /// <param name="error"><inheritdoc cref="IOperation.Run(IOpData, out string)" path="/param[@name='error']"/></param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        protected bool TrySetValues(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            // Stack is: a then b => pop b first
            if (!TryConvertToLong(opData.Pop(), out b, opData.StrictNumberEncoding))
            {
                error = "Invalid number format.";
                return(false);
            }

            if (!TryConvertToLong(opData.Pop(), out a, opData.StrictNumberEncoding))
            {
                error = "Invalid number format.";
                return(false);
            }

            error = null;
            return(true);
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Rotates top 3 items on top of the stack to the left. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 3)
            {
                error = "There was not enough items left in the stack to rotate.";
                return(false);
            }

            byte[][] data = opData.Pop(3);
            // (x0 x1 x2 -> x1 x2 x0)
            opData.Push(new byte[3][] { data[1], data[2], data[0] });

            error = null;
            return(true);
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Swaps top two item pairs on top of the stack: x1 x2 x3 x4 -> x3 x4 x1 x2
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 4)
            {
                error = "There was not enough items left in the stack to swap.";
                return(false);
            }

            byte[][] data = opData.Pop(4);
            // x0 x1 x2 x3 -> x2 x3 x0 x1
            opData.Push(new byte[4][] { data[2], data[3], data[0], data[1] });

            error = null;
            return(true);
        }
        /// <summary>
        /// Runs all the operations in main or else list. Return value indicates success.
        /// </summary>
        /// <param name="opData">Stack to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            // Remove top stack item and convert it to bool
            // OP_IF: runs if True
            // OP_NOTIF: runs if false
            byte[] topItem = opData.Pop();

            if (!opData.CheckConditionalOpBool(topItem))
            {
                error = "True/False item popped by conditional OPs must be strict.";
                return(false);
            }

            if (IsNotZero(topItem) == runWithTrue)
            {
                foreach (var op in mainOps)
                {
                    if (!op.Run(opData, out error))
                    {
                        return(false);
                    }
                }
            }
            else if (elseOps != null)
            {
                foreach (var op in elseOps)
                {
                    if (!op.Run(opData, out error))
                    {
                        return(false);
                    }
                }
            }

            error = null;
            return(true);
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Removes top stack item only passes if it is true. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = "There was not enough items left in the stack.";
                return(false);
            }
            // Check the top stack value, only fail if False
            bool b = OpHelper.IsNotZero(opData.Pop());

            if (!b)
            {
                error = "Top stack item value was 'false'.";
                return(false);
            }

            error = null;
            return(true);
        }
Ejemplo n.º 25
0
        /// <summary>
        /// Removes top two stack items as public key and signature and calls
        /// <see cref="IOpData.Verify(Signature, PublicKey, System.ReadOnlySpan{byte})"/>.
        /// Return value indicates success.
        /// </summary>
        /// <param name="opData">Stack to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public bool ExtractAndVerify(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[][] values = opData.Pop(2);

            if (values[0].Length == 0)
            {
                error = null;
                return(false);
            }

            Signature sig;

            if (opData.IsStrictDerSig)
            {
                if (!Signature.TryReadStrict(values[0], out sig, out error))
                {
                    return(false);
                }
            }
            else
            {
                if (!Signature.TryReadLoose(values[0], out sig, out _))
                {
                    error = null;
                    return(false);
                }
            }

            if (!PublicKey.TryRead(values[1], out PublicKey pubK))
            {
                error = null;
                return(false);
            }

            error = null;
            return(opData.Verify(sig, pubK, values[0]));
        }
Ejemplo n.º 26
0
        /// <summary>
        /// Removes top stack item and only passes if its value is true. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            // Check the top stack value, only fail if False
            bool b = IsNotZero(opData.Pop());

            if (!b)
            {
                error = "Top stack item value was 'false'.";
                return(false);
            }

            error = null;
            return(true);
        }
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 1)
            {
                error = "Invalid number of elements in stack.";
                return(false);
            }

            // TODO: check what this part is
            // https://github.com/bitcoin/bitcoin/blob/a822a0e4f6317f98cde6f0d5abe952b4e8992ac9/src/script/interpreter.cpp#L478-L483
            // probably have to include it in whever we run these operations.

            // Remove top stack item and interpret it as bool
            // OP_IF: runs for True
            // OP_NOTIF: runs for false
            bool isRunnable = OpHelper.IsNotZero(opData.Pop()) & ShouldRunIfTopItemIs; // Note: this is a bitwise operation not logical

            if (isRunnable)
            {
                foreach (var op in mainOps)
                {
                    if (!op.Run(opData, out error))
                    {
                        return(false);
                    }
                }
            }
            else if (elseOps != null && elseOps.Length != 0)
            {
                foreach (var op in elseOps)
                {
                    if (!op.Run(opData, out error))
                    {
                        return(false);
                    }
                }
            }

            error = null;
            return(true);
        }
Ejemplo n.º 28
0
        /// <summary>
        /// Moves the nth item from top of the stack to the top. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            // At least 2 items is needed. 1 telling us the index and the other is the item to move
            if (opData.ItemCount < 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[] data = opData.Pop();
            // Initial test to reject any huge numbers
            if (data.Length > 4)
            {
                error = "'n' is too big.";
                return(false);
            }

            // TODO: set isStrict field based on BIP62

            if (!TryConvertToLong(data, out long n, true))
            {
                error = "Invalid number format.";
                return(false);
            }
            if (n < 0)
            {
                error = "'n' can not be negative.";
                return(false);
            }
            // 'n' is index so it can't be equal to ItemCount
            if (opData.ItemCount <= n)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            opData.Push(opData.PopAtIndex((int)n));

            error = null;
            return(true);
        }
Ejemplo n.º 29
0
        /// <summary>
        /// Copies the nth item from top of the stack to the top. Return value indicates success.
        /// </summary>
        /// <param name="opData">Data to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public override bool Run(IOpData opData, out string error)
        {
            if (opData.ItemCount < 2) // At least 2 items is needed. 1 telling us the index and the other to copy
            {
                error = "There was not enough items left in the stack to copy to top.";
                return(false);
            }

            byte[] data = opData.Pop();
            if (data.Length > 4) // Initial test to reject any huge numbers
            {
                error = "'n' is too big.";
                return(false);
            }

            if (!OpHelper.TryConvertByteArrayToInt(data, out long n, true)) // TODO: set isStrict field base don BIP62
            {
                error = "Invalid number format.";
                return(false);
            }

            if (n < 0)
            {
                error = "'n' can not be negative.";
                return(false);
            }
            if (opData.ItemCount <= n) // 'n' is index so it can't be equal to ItemCount
            {
                error = "There was not enough items left in the stack to copy to top.";
                return(false);
            }

            opData.Push(opData.PeekAtIndex((int)n));

            error = null;
            return(true);
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Removes all needed items from the stack as public keys and signatures and calls
        /// <see cref="IOpData.Verify(byte[][], byte[][], int, out string)"/>.
        /// Return value indicates success.
        /// </summary>
        /// <param name="opData">Stack to use</param>
        /// <param name="error">Error message (null if sucessful, otherwise will contain information about the failure)</param>
        /// <returns>True if operation was successful, false if otherwise</returns>
        public bool ExtractAndVerify(IOpData opData, out string error)
        {
            // A multi-sig stack is (extra item, usually OP_0) + (m*sig) + (OP_m) + (n*pub) + (OP_n)
            // both m and n values are needed and the extra item is also mandatory. but since both m and n can be zero
            // the key[] and sig[] can be empty so the smallest stack item count should be 3 items [OP_0 (m=0) (n=0)]
            if (opData.ItemCount < 3)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[] nBa = opData.Pop();
            if (!TryConvertToLong(nBa, out long n, opData.StrictNumberEncoding, maxDataLength: 4))
            {
                error = "Invalid number (n) format.";
                return(false);
            }

            if (n < 0 || n > Constants.MaxMultisigPubkeyCount)
            {
                error = "Invalid number of public keys in multi-sig.";
                return(false);
            }

            opData.OpCount += (int)n;
            if (opData.OpCount > Constants.MaxScriptOpCount)
            {
                error = "Number of OPs in this script exceeds the allowed number.";
                return(false);
            }

            // By knowing n we know the number of public keys and the "index" of m to be popped
            // eg. indes:item => n=3 => 3:m 2:pub1 1:pub2 0:pub3    n=2 => 2:m 1:pub1 0:pub2
            // The "remaining" ItemCount must also have the "garbage" item. Assuming m=0 there is no signatures so min count is:
            //      n=0 : GM(N)   n=1 : GMP(N)    n=2 : GMPP(N)    n=3 : GMPPP(N)
            if (opData.ItemCount < n + 2)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            byte[] mBa = opData.PopAtIndex((int)n);
            if (!TryConvertToLong(mBa, out long m, opData.StrictNumberEncoding, maxDataLength: 4))
            {
                error = "Invalid number (m) format.";
                return(false);
            }
            if (m < 0 || m > n)
            {
                error = "Invalid number of signatures in multi-sig.";
                return(false);
            }

            // Note that m and n are already popped (removed) from the stack so it looks like this:
            // (extra item, usually OP_0) + (m*sig) + (n*pub)
            if (opData.ItemCount < n + m + 1)
            {
                error = Err.OpNotEnoughItems;
                return(false);
            }

            if (n == 0)
            {
                if (opData.CheckMultiSigGarbage(opData.Pop()))
                {
                    error = null;
                    return(true);
                }
                else
                {
                    error = "The extra item should be OP_0.";
                    return(false);
                }
            }

            byte[][] allPubs = opData.Pop((int)n);
            byte[][] allSigs = opData.Pop((int)m);

            // Handle bitcoin-core bug before checking signatures (has to pop 1 extra item)
            byte[] garbage = opData.Pop();
            if (!opData.CheckMultiSigGarbage(garbage))
            {
                error = "The extra item should be OP_0.";
                return(false);
            }

            if (m == 0)
            {
                error = null;
                return(true);
            }

            return(opData.Verify(allSigs, allPubs, (int)m, out error));
        }