/// <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); }
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); }
/// <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); }
/// <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); }
/// <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); } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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])); }
/// <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); }
/// <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); }
/// <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); }
/// <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)); }