X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FScriptCode.cs;h=ab0db3fe04b795a77ee2235f977dbb762da1528a;hb=1dcac5faa2b1477034f82466ffb16170fa2e9bb6;hp=8f53feb0114e18040c15088ffe808ea62d5fcd32;hpb=94c2d4610c72f0303a89f45d0dc7b4c9f4be4c30;p=NovacoinLibrary.git diff --git a/Novacoin/ScriptCode.cs b/Novacoin/ScriptCode.cs index 8f53feb..ab0db3f 100644 --- a/Novacoin/ScriptCode.cs +++ b/Novacoin/ScriptCode.cs @@ -1,16 +1,32 @@ +/** + * Novacoin classes library + * Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com) + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; -using System.Text; - using System.Numerics; - -// using Org.BouncyCastle.Math; +using System.Text; namespace Novacoin { /// - /// Script opcodes + /// Script instructions /// public enum instruction { @@ -210,344 +226,82 @@ namespace Novacoin } /// - /// Get the name of supplied opcode + /// Get the name of instruction /// - /// Opcode - /// Opcode name + /// Instruction + /// Instruction name public static string GetOpName(instruction opcode) { - switch (opcode) - { - // push value - case instruction.OP_0: - return "OP_0"; - case instruction.OP_PUSHDATA1: - return "OP_PUSHDATA1"; - case instruction.OP_PUSHDATA2: - return "OP_PUSHDATA2"; - case instruction.OP_PUSHDATA4: - return "OP_PUSHDATA4"; - case instruction.OP_1NEGATE: - return "OP_1NEGATE"; - case instruction.OP_RESERVED: - return "OP_RESERVED"; - case instruction.OP_1: - return "OP_1"; - case instruction.OP_2: - return "OP_2"; - case instruction.OP_3: - return "OP_3"; - case instruction.OP_4: - return "OP_4"; - case instruction.OP_5: - return "OP_5"; - case instruction.OP_6: - return "OP_6"; - case instruction.OP_7: - return "OP_7"; - case instruction.OP_8: - return "OP_8"; - case instruction.OP_9: - return "OP_9"; - case instruction.OP_10: - return "OP_10"; - case instruction.OP_11: - return "OP_11"; - case instruction.OP_12: - return "OP_12"; - case instruction.OP_13: - return "OP_13"; - case instruction.OP_14: - return "OP_14"; - case instruction.OP_15: - return "OP_15"; - case instruction.OP_16: - return "OP_16"; - - // control - case instruction.OP_NOP: - return "OP_NOP"; - case instruction.OP_VER: - return "OP_VER"; - case instruction.OP_IF: - return "OP_IF"; - case instruction.OP_NOTIF: - return "OP_NOTIF"; - case instruction.OP_VERIF: - return "OP_VERIF"; - case instruction.OP_VERNOTIF: - return "OP_VERNOTIF"; - case instruction.OP_ELSE: - return "OP_ELSE"; - case instruction.OP_ENDIF: - return "OP_ENDIF"; - case instruction.OP_VERIFY: - return "OP_VERIFY"; - case instruction.OP_RETURN: - return "OP_RETURN"; - - // stack ops - case instruction.OP_TOALTSTACK: - return "OP_TOALTSTACK"; - case instruction.OP_FROMALTSTACK: - return "OP_FROMALTSTACK"; - case instruction.OP_2DROP: - return "OP_2DROP"; - case instruction.OP_2DUP: - return "OP_2DUP"; - case instruction.OP_3DUP: - return "OP_3DUP"; - case instruction.OP_2OVER: - return "OP_2OVER"; - case instruction.OP_2ROT: - return "OP_2ROT"; - case instruction.OP_2SWAP: - return "OP_2SWAP"; - case instruction.OP_IFDUP: - return "OP_IFDUP"; - case instruction.OP_DEPTH: - return "OP_DEPTH"; - case instruction.OP_DROP: - return "OP_DROP"; - case instruction.OP_DUP: - return "OP_DUP"; - case instruction.OP_NIP: - return "OP_NIP"; - case instruction.OP_OVER: - return "OP_OVER"; - case instruction.OP_PICK: - return "OP_PICK"; - case instruction.OP_ROLL: - return "OP_ROLL"; - case instruction.OP_ROT: - return "OP_ROT"; - case instruction.OP_SWAP: - return "OP_SWAP"; - case instruction.OP_TUCK: - return "OP_TUCK"; - - // splice ops - case instruction.OP_CAT: - return "OP_CAT"; - case instruction.OP_SUBSTR: - return "OP_SUBSTR"; - case instruction.OP_LEFT: - return "OP_LEFT"; - case instruction.OP_RIGHT: - return "OP_RIGHT"; - case instruction.OP_SIZE: - return "OP_SIZE"; - - // bit logic - case instruction.OP_INVERT: - return "OP_INVERT"; - case instruction.OP_AND: - return "OP_AND"; - case instruction.OP_OR: - return "OP_OR"; - case instruction.OP_XOR: - return "OP_XOR"; - case instruction.OP_EQUAL: - return "OP_EQUAL"; - case instruction.OP_EQUALVERIFY: - return "OP_EQUALVERIFY"; - case instruction.OP_RESERVED1: - return "OP_RESERVED1"; - case instruction.OP_RESERVED2: - return "OP_RESERVED2"; - - // numeric - case instruction.OP_1ADD: - return "OP_1ADD"; - case instruction.OP_1SUB: - return "OP_1SUB"; - case instruction.OP_2MUL: - return "OP_2MUL"; - case instruction.OP_2DIV: - return "OP_2DIV"; - case instruction.OP_NEGATE: - return "OP_NEGATE"; - case instruction.OP_ABS: - return "OP_ABS"; - case instruction.OP_NOT: - return "OP_NOT"; - case instruction.OP_0NOTEQUAL: - return "OP_0NOTEQUAL"; - case instruction.OP_ADD: - return "OP_ADD"; - case instruction.OP_SUB: - return "OP_SUB"; - case instruction.OP_MUL: - return "OP_MUL"; - case instruction.OP_DIV: - return "OP_DIV"; - case instruction.OP_MOD: - return "OP_MOD"; - case instruction.OP_LSHIFT: - return "OP_LSHIFT"; - case instruction.OP_RSHIFT: - return "OP_RSHIFT"; - case instruction.OP_BOOLAND: - return "OP_BOOLAND"; - case instruction.OP_BOOLOR: - return "OP_BOOLOR"; - case instruction.OP_NUMEQUAL: - return "OP_NUMEQUAL"; - case instruction.OP_NUMEQUALVERIFY: - return "OP_NUMEQUALVERIFY"; - case instruction.OP_NUMNOTEQUAL: - return "OP_NUMNOTEQUAL"; - case instruction.OP_LESSTHAN: - return "OP_LESSTHAN"; - case instruction.OP_GREATERTHAN: - return "OP_GREATERTHAN"; - case instruction.OP_LESSTHANOREQUAL: - return "OP_LESSTHANOREQUAL"; - case instruction.OP_GREATERTHANOREQUAL: - return "OP_GREATERTHANOREQUAL"; - case instruction.OP_MIN: - return "OP_MIN"; - case instruction.OP_MAX: - return "OP_MAX"; - case instruction.OP_WITHIN: - return "OP_WITHIN"; - - // crypto - case instruction.OP_RIPEMD160: - return "OP_RIPEMD160"; - case instruction.OP_SHA1: - return "OP_SHA1"; - case instruction.OP_SHA256: - return "OP_SHA256"; - case instruction.OP_HASH160: - return "OP_HASH160"; - case instruction.OP_HASH256: - return "OP_HASH256"; - case instruction.OP_CODESEPARATOR: - return "OP_CODESEPARATOR"; - case instruction.OP_CHECKSIG: - return "OP_CHECKSIG"; - case instruction.OP_CHECKSIGVERIFY: - return "OP_CHECKSIGVERIFY"; - case instruction.OP_CHECKMULTISIG: - return "OP_CHECKMULTISIG"; - case instruction.OP_CHECKMULTISIGVERIFY: - return "OP_CHECKMULTISIGVERIFY"; - - // expansion - case instruction.OP_NOP1: - return "OP_NOP1"; - case instruction.OP_NOP2: - return "OP_NOP2"; - case instruction.OP_NOP3: - return "OP_NOP3"; - case instruction.OP_NOP4: - return "OP_NOP4"; - case instruction.OP_NOP5: - return "OP_NOP5"; - case instruction.OP_NOP6: - return "OP_NOP6"; - case instruction.OP_NOP7: - return "OP_NOP7"; - case instruction.OP_NOP8: - return "OP_NOP8"; - case instruction.OP_NOP9: - return "OP_NOP9"; - case instruction.OP_NOP10: - return "OP_NOP10"; - - // template matching params - case instruction.OP_SMALLINTEGER: - return "OP_SMALLINTEGER"; - case instruction.OP_PUBKEYHASH: - return "OP_PUBKEYHASH"; - case instruction.OP_PUBKEY: - return "OP_PUBKEY"; - case instruction.OP_PUBKEYS: - return "OP_PUBKEYS"; - case instruction.OP_SMALLDATA: - return "OP_SMALLDATA"; - - case instruction.OP_INVALIDOPCODE: - return "OP_INVALIDOPCODE"; - default: - return "OP_UNKNOWN"; - } + if (opcode == instruction.OP_0) // OP_0 and OP_FALSE are synonyms + return "OP_0"; + if (opcode == instruction.OP_1) // OP_1 and OP_TRUE are synonyms + return "OP_1"; + + return Enum.GetName(typeof(instruction), opcode); } /// - /// Get next opcode from passed list of bytes and extract push arguments if there are some. + /// Get next instruction from list of bytes and extract push arguments if there are some. /// /// ByteQueue reference. - /// Found opcode. + /// Found instruction. /// IEnumerable out param which is used to get the push arguments. /// Result of operation - public static bool GetOp(ref ByteQueue codeBytes, out instruction opcodeRet, out IEnumerable bytesRet) + public static bool GetOp(ref InstructionQueue codeBytes, out instruction opcodeRet, out byte[] bytesRet) { - bytesRet = new List(); - opcodeRet = instruction.OP_INVALIDOPCODE; - - instruction opcode; + bytesRet = new byte[0]; + instruction opcode = opcodeRet = instruction.OP_INVALIDOPCODE; - try - { - // Read instruction - opcode = (instruction)codeBytes.Get(); - } - catch (ByteQueueException) + // Read instruction + byte opVal = 0xff; + if (!codeBytes.TryGet(ref opVal)) { - // No instruction found there return false; } + opcode = (instruction)opVal; // Immediate operand if (opcode <= instruction.OP_PUSHDATA4) { - byte[] szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length + var szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length + int nSize = 0; try { if (opcode < instruction.OP_PUSHDATA1) { - // Zero value opcodes (OP_0, OP_FALSE) - szBytes[3] = (byte)opcode; + // Zero value instructions (OP_0, OP_FALSE) + nSize = (int) opcode; } else if (opcode == instruction.OP_PUSHDATA1) { // The next byte contains the number of bytes to be pushed onto the stack, // i.e. you have something like OP_PUSHDATA1 0x01 [0x5a] - szBytes[3] = (byte)codeBytes.Get(); + nSize = codeBytes.Get(); } else if (opcode == instruction.OP_PUSHDATA2) { // The next two bytes contain the number of bytes to be pushed onto the stack, - // i.e. now your operation will seem like this: OP_PUSHDATA2 0x00 0x01 [0x5a] - codeBytes.Get(2).CopyTo(szBytes, 2); + // i.e. now your operation will seem like this: OP_PUSHDATA2 0x01 0x00 [0x5a] + nSize = BitConverter.ToInt16(codeBytes.Get(2), 0); } else if (opcode == instruction.OP_PUSHDATA4) { // The next four bytes contain the number of bytes to be pushed onto the stack, - // OP_PUSHDATA4 0x00 0x00 0x00 0x01 [0x5a] - szBytes = codeBytes.Get(4); + // OP_PUSHDATA4 0x01 0x00 0x00 0x00 [0x5a] + nSize = BitConverter.ToInt32(codeBytes.Get(4), 0); } } - catch (ByteQueueException) + catch (InstructionQueueException) { // Unable to read operand length return false; } - int nSize = (int)Interop.BEBytesToUInt32(szBytes); - if (nSize > 0) { - // If nSize is greater than zero then there is some data available - try - { - // Read found number of bytes into list of OP_PUSHDATAn arguments. - bytesRet = codeBytes.GetEnumerable(nSize); - } - catch (ByteQueueException) + // Trying to read found number of bytes into list of OP_PUSHDATAn arguments. + if (!codeBytes.TryGet(nSize, ref bytesRet)) { // Unable to read data return false; @@ -567,16 +321,13 @@ namespace Novacoin /// /// Collection of value bytes. /// Formatted value. - public static string ValueString(IEnumerable bytes) + public static string ValueString(byte[] bytes) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); - if (bytes.Count() <= 4) + if (bytes.Length <= 4) { - byte[] valueBytes = new byte[4] { 0, 0, 0, 0 }; - bytes.ToArray().CopyTo(valueBytes, valueBytes.Length - bytes.Count()); - - sb.Append(Interop.BEBytesToUInt32(valueBytes)); + sb.Append(new BigInteger(bytes)); } else { @@ -591,12 +342,12 @@ namespace Novacoin /// /// List of stack items. /// Formatted value. - public static string StackString(IList> stackList) + public static string StackString(IList stackList) { - StringBuilder sb = new StringBuilder(); - foreach (IList bytesList in stackList) + var sb = new StringBuilder(); + foreach (var bytes in stackList) { - sb.Append(ValueString(bytesList)); + sb.Append(ValueString(bytes)); } return sb.ToString(); @@ -605,53 +356,46 @@ namespace Novacoin /// /// Decode instruction to integer value /// - /// Small integer opcode (OP_1_NEGATE and OP_0 - OP_16) + /// Small integer instruction (OP_1_NEGATE and OP_0 - OP_16) /// Small integer public static int DecodeOP_N(instruction opcode, bool AllowNegate = false) { - if (AllowNegate && opcode == instruction.OP_1NEGATE) - { - return -1; - } - - if (opcode == instruction.OP_0) - { - return 0; - } + // Only OP_n instructions are supported, throw exception otherwise. + Contract.Requires((opcode == instruction.OP_1NEGATE && AllowNegate) || (opcode >= instruction.OP_0 && opcode <= instruction.OP_16), "Invalid integer instruction."); - // Only OP_n opcodes are supported, throw exception otherwise. - if (opcode < instruction.OP_1 || opcode > instruction.OP_16) + switch (opcode) { - throw new ArgumentException("Invalid integer instruction."); + case instruction.OP_1NEGATE: + return -1; + case instruction.OP_0: + return 0; + default: + return (int)opcode - (int)(instruction.OP_1 - 1); } - - return (int)opcode - (int)(instruction.OP_1 - 1); } /// /// Converts integer into instruction /// /// Small integer from the range of -1 up to 16. - /// Corresponding opcode. + /// Corresponding instruction. public static instruction EncodeOP_N(int n, bool allowNegate = false) { - if (allowNegate && n == -1) - { - return instruction.OP_1NEGATE; - } + // The n value must be in the range of 1 to 16. + Contract.Requires((n == -1 && allowNegate) || (n >= 0 && n <= 16), "Invalid integer value."); - if (n == 0) + switch (n) { - return instruction.OP_0; + case -1: + return instruction.OP_1NEGATE; + case 0: + return instruction.OP_0; + default: + return (instruction.OP_1 + n - 1); } - - // The n value must be in the range of 0 to 16. - if (n < 0 || n > 16) - throw new ArgumentException("Invalid integer value."); - return (instruction.OP_1 + n - 1); } - public static int ScriptSigArgsExpected(txnouttype t, IList> solutions) + public static int ScriptSigArgsExpected(txnouttype t, IList solutions) { switch (t) { @@ -664,9 +408,9 @@ namespace Novacoin case txnouttype.TX_PUBKEYHASH: return 2; case txnouttype.TX_MULTISIG: - if (solutions.Count() < 1 || solutions.First().Count() < 1) + if (solutions.Count < 1 || solutions.First().Length < 1) return -1; - return solutions.First().First() + 1; + return solutions.First()[0] + 1; case txnouttype.TX_SCRIPTHASH: return 1; // doesn't include args needed by the script } @@ -681,7 +425,7 @@ namespace Novacoin /// Checking result public static bool IsStandard(CScript scriptPubKey, out txnouttype whichType) { - IList> solutions = new List>(); + IList solutions; if (!Solver(scriptPubKey, out whichType, out solutions)) { @@ -692,8 +436,8 @@ namespace Novacoin if (whichType == txnouttype.TX_MULTISIG) { // Additional verification of OP_CHECKMULTISIG arguments - byte m = solutions.First().First(); - byte n = solutions.Last().First(); + var m = solutions.First()[0]; + var n = solutions.Last()[0]; // Support up to x-of-3 multisig txns as standard if (n < 1 || n > 3) @@ -716,9 +460,11 @@ namespace Novacoin /// Output type /// Set of solutions /// Result - public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList> solutions) + public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList solutions) { - solutions = new List>(); + byte[] scriptBytes = scriptPubKey; + + solutions = new List(); // There are shortcuts for pay-to-script-hash and pay-to-pubkey-hash, which are more constrained than the other types. @@ -728,8 +474,8 @@ namespace Novacoin typeRet = txnouttype.TX_SCRIPTHASH; // Take 20 bytes with offset of 2 bytes - IEnumerable hashBytes = scriptPubKey.Bytes.Skip(2).Take(20); - solutions.Add(hashBytes); + var hashBytes = scriptBytes.Skip(2).Take(20); + solutions.Add(hashBytes.ToArray()); return true; } @@ -740,18 +486,18 @@ namespace Novacoin typeRet = txnouttype.TX_PUBKEYHASH; // Take 20 bytes with offset of 3 bytes - IEnumerable hashBytes = scriptPubKey.Bytes.Skip(3).Take(20); - solutions.Add(hashBytes); + var hashBytes = scriptBytes.Skip(3).Take(20); + solutions.Add(hashBytes.ToArray()); return true; } - List>> templateTuples = new List>>(); + var templateTuples = new List>(); // Sender provides pubkey, receiver adds signature // [ECDSA public key] OP_CHECKSIG templateTuples.Add( - new Tuple>( + new Tuple( txnouttype.TX_PUBKEY, new byte[] { (byte)instruction.OP_PUBKEY, @@ -761,9 +507,9 @@ namespace Novacoin // Sender provides N pubkeys, receivers provides M signatures // N [pubkey1] [pubkey2] ... [pubkeyN] M OP_CHECKMULTISIG - // Where N and M are small integer opcodes (OP1 ... OP_16) + // Where N and M are small integer instructions (OP1 ... OP_16) templateTuples.Add( - new Tuple>( + new Tuple( txnouttype.TX_MULTISIG, new byte[] { (byte)instruction.OP_SMALLINTEGER, @@ -776,7 +522,7 @@ namespace Novacoin // Data-carrying output // OP_RETURN [up to 80 bytes of data] templateTuples.Add( - new Tuple>( + new Tuple( txnouttype.TX_NULL_DATA, new byte[] { (byte)instruction.OP_RETURN, @@ -787,33 +533,33 @@ namespace Novacoin // Nonstandard tx output typeRet = txnouttype.TX_NONSTANDARD; - foreach (Tuple> templateTuple in templateTuples) + foreach (var templateTuple in templateTuples) { - CScript script1 = scriptPubKey; - CScript script2 = new CScript(templateTuple.Item2); + var script1 = scriptPubKey; + var script2 = new CScript(templateTuple.Item2); instruction opcode1, opcode2; // Compare - ByteQueue bq1 = script1.GetByteQUeue(); - ByteQueue bq2 = script2.GetByteQUeue(); + var bq1 = script1.GetInstructionQueue(); + var bq2 = script2.GetInstructionQueue(); - IEnumerable args1, args2; + byte[] args1, args2; - int last1 = script1.Bytes.Count() -1; - int last2 = script2.Bytes.Count() - 1; + int last1 = ((byte[])script1).Length - 1; + int last2 = ((byte[])script2).Length - 1; while (true) { - if (bq1.CurrentIndex == last1 && bq2.CurrentIndex == last2) + if (bq1.Index == last1 && bq2.Index == last2) { // Found a match typeRet = templateTuple.Item1; if (typeRet == txnouttype.TX_MULTISIG) { // Additional checks for TX_MULTISIG: - byte m = solutions.First().First(); - byte n = solutions.Last().First(); + var m = solutions.First().First(); + var n = solutions.Last().First(); if (m < 1 || n < 1 || m > n || solutions.Count - 2 != n) { @@ -832,7 +578,7 @@ namespace Novacoin break; } - // Template matching opcodes: + // Template matching instructions: if (opcode2 == instruction.OP_PUBKEYS) { while (args1.Count() >= 33 && args1.Count() <= 120) @@ -872,7 +618,7 @@ namespace Novacoin // Single-byte small integer pushed onto solutions try { - byte n = (byte)DecodeOP_N(opcode1); + var n = (byte)DecodeOP_N(opcode1); solutions.Add(new byte[] { n }); } catch (Exception) @@ -883,7 +629,7 @@ namespace Novacoin else if (opcode2 == instruction.OP_SMALLDATA) { // small pushdata, <= 80 bytes - if (args1.Count() > 80) + if (args1.Length > 80) { break; } @@ -902,27 +648,31 @@ namespace Novacoin return false; } - public static Hash256 SignatureHash(CScript scriptCode, CTransaction txTo, int nIn, int nHashType) + /// + /// Generation of SignatureHash. This method is responsible for removal of transaction metadata. It's necessary signature can't sign itself. + /// + /// Spending instructions + /// Instance of transaction + /// Input number + /// Hash type flag + /// + public static uint256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType) { - if (nIn >= txTo.vin.Length) - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("ERROR: SignatureHash() : nIn={0} out of range\n", nIn); - throw new ArgumentOutOfRangeException("nIn", sb.ToString()); - } + Contract.Requires(nIn < txTo.vin.Length, "nIn out of range."); - CTransaction txTmp = new CTransaction(txTo); + // Init a copy of transaction + var txTmp = new CTransaction(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.RemovePattern(new byte[] { (byte)instruction.OP_CODESEPARATOR }); + script.RemoveInstruction(instruction.OP_CODESEPARATOR); // Blank out other inputs' signatures for (int i = 0; i < txTmp.vin.Length; i++) { txTmp.vin[i].scriptSig = new CScript(); } - txTmp.vin[nIn].scriptSig = scriptCode; + txTmp.vin[nIn].scriptSig = script; // Blank out some of the outputs if ((nHashType & 0x1f) == (int)sigflag.SIGHASH_NONE) @@ -973,14 +723,22 @@ namespace Novacoin Array.Resize(ref txTmp.vin, 1); } - // Serialize and hash - List b = new List(); - b.AddRange(txTmp.Bytes); - b.AddRange(BitConverter.GetBytes(nHashType)); + // Concatenate and hash + var txBytes = (byte[])txTmp; + var nHashTypeBytes = BitConverter.GetBytes(nHashType); - return Hash256.Compute256(b); + return CryptoUtils.ComputeHash256(ref txBytes, ref nHashTypeBytes); } + // + // Script is a stack machine (like Forth) that evaluates a predicate + // returning a bool indicating valid or not. There are no loops. + // + + /// + /// Script machine exception + /// + [Serializable] public class StackMachineException : Exception { public StackMachineException() @@ -998,22 +756,15 @@ namespace Novacoin } } - - // - // Script is a stack machine (like Forth) that evaluates a predicate - // returning a bool indicating valid or not. There are no loops. - // - /// /// Remove last element from stack /// /// Stack reference - static void popstack(ref List> stack) + private static void popstack(ref List stack) { - int nCount = stack.Count; - if (nCount == 0) - throw new StackMachineException("popstack() : stack empty"); - stack.RemoveAt(nCount - 1); + Contract.Requires(stack.Count > 0, "Stack is empty."); + + stack.RemoveAt(stack.Count - 1); } /// @@ -1022,27 +773,12 @@ namespace Novacoin /// Stack reference /// Depth /// Byte sequence - static IEnumerable stacktop(ref List> stack, int nDepth) + private static byte[] stacktop(ref List stack, int nDepth) { - int nStackElement = stack.Count + nDepth; + Contract.Requires(nDepth < 0, "Positive or zero stack depth makes no sense."); + Contract.Requires(stack.Count + nDepth >= 0, "Value exceeds real stack depth."); - if (nDepth >= 0) - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("stacktop() : positive depth ({0}) has no sense.", nDepth); - - throw new StackMachineException(sb.ToString()); - } - - if (nStackElement < 0) - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("stacktop() : nDepth={0} exceeds real stack depth ({1})", nDepth, stack.Count); - - throw new StackMachineException(sb.ToString()); - } - - return stack[nStackElement]; + return stack[stack.Count + nDepth]; } /// @@ -1050,16 +786,14 @@ namespace Novacoin /// /// Some byte sequence /// - private static bool CastToBool(IEnumerable arg) + private static bool CastToBool(byte[] arg) { - byte[] value = arg.ToArray(); - - for (var i = 0; i < value.Length; i++) + for (var i = 0; i < arg.Length; i++) { - if (value[i] != 0) + if (arg[i] != 0) { // Can be negative zero - if (i == value.Length - 1 && value[i] == 0x80) + if (i == arg.Length - 1 && arg[i] == 0x80) { return false; } @@ -1076,877 +810,894 @@ namespace Novacoin /// /// /// - private static BigInteger CastToBigInteger(IEnumerable value) + private static BigInteger CastToBigInteger(byte[] value) { - if (value.Count() > 4) - { - throw new StackMachineException("CastToBigInteger() : overflow"); - } + Contract.Requires(value.Length <= 4, "Size limit failed."); - return new BigInteger(value.ToArray()); + return new BigInteger(value); } - static bool EvalScript(ref List> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType) + /// + /// Execution of script + /// + /// + /// Script to execute + /// Transaction instance + /// Input number + /// Signature checking flags + /// Hash type flag + /// + public static bool EvalScript(ref List stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType) { - instruction opcode; - - ByteQueue CodeQueue = script.GetByteQUeue(); - - List vfExec = new List(); - List> altStack = new List>(); + var scriptBytes = ((byte[])script); - byte[] falseBytes = new byte[0]; - byte[] trueBytes = new byte[] { 0x01 }; - - if (script.Bytes.Count() > 10000) + if (scriptBytes.Length > 10000) { return false; // Size limit failed } + var vfExec = new List(); + int nOpCount = 0; int nCodeHashBegin = 0; - IEnumerable pushArg; + var falseBytes = new byte[0]; + var trueBytes = new byte[] { 0x01 }; - while (GetOp(ref CodeQueue, out opcode, out pushArg)) // Read instructions + var CodeQueue = script.GetInstructionQueue(); + var altStack = new List(); + +#if !DEBUG + try { - bool fExec = vfExec.IndexOf(false) != -1; +#endif + instruction opcode; + byte[] pushArg; - if (pushArg.Count() > 520) + while (GetOp(ref CodeQueue, out opcode, out pushArg)) // Read instructions { - return false; // Script element size limit failed - } + bool fExec = vfExec.IndexOf(false) == -1; - if (opcode > instruction.OP_16 && ++nOpCount > 201) - { - return false; - } + if (pushArg.Length > 520) + { + return false; // Script element size limit failed + } - if (fExec && 0 <= opcode && opcode <= instruction.OP_PUSHDATA4) - { - stack.Add(pushArg); // Push argument to stack - } - else if (fExec || (instruction.OP_IF <= opcode && opcode <= instruction.OP_ENDIF)) - switch (opcode) + if (opcode > instruction.OP_16 && ++nOpCount > 201) { - // - // Disabled opcodes - // - case instruction.OP_CAT: - case instruction.OP_SUBSTR: - case instruction.OP_LEFT: - case instruction.OP_RIGHT: - case instruction.OP_INVERT: - case instruction.OP_AND: - case instruction.OP_OR: - case instruction.OP_XOR: - case instruction.OP_2MUL: - case instruction.OP_2DIV: - case instruction.OP_MUL: - case instruction.OP_DIV: - case instruction.OP_MOD: - case instruction.OP_LSHIFT: - case instruction.OP_RSHIFT: - return false; - - // - // Push integer instructions - // - case instruction.OP_1NEGATE: - case instruction.OP_1: - case instruction.OP_2: - case instruction.OP_3: - case instruction.OP_4: - case instruction.OP_5: - case instruction.OP_6: - case instruction.OP_7: - case instruction.OP_8: - case instruction.OP_9: - case instruction.OP_10: - case instruction.OP_11: - case instruction.OP_12: - case instruction.OP_13: - case instruction.OP_14: - case instruction.OP_15: - case instruction.OP_16: - { - // ( -- value) - BigInteger bn = DecodeOP_N(opcode); - stack.Add(bn.ToByteArray()); - } - break; + return false; + } - // - // Extension - // - case instruction.OP_NOP: - case instruction.OP_NOP1: - case instruction.OP_NOP2: - case instruction.OP_NOP3: - case instruction.OP_NOP4: - case instruction.OP_NOP5: - case instruction.OP_NOP6: - case instruction.OP_NOP7: - case instruction.OP_NOP8: - case instruction.OP_NOP9: - case instruction.OP_NOP10: - { - // Just do nothing - break; - } + if (fExec && 0 <= opcode && opcode <= instruction.OP_PUSHDATA4) + { + stack.Add(pushArg); // Push argument to stack + } + else if (fExec || (instruction.OP_IF <= opcode && opcode <= instruction.OP_ENDIF)) + switch (opcode) + { + // + // Disabled instructions + // + case instruction.OP_CAT: + case instruction.OP_SUBSTR: + case instruction.OP_LEFT: + case instruction.OP_RIGHT: + case instruction.OP_INVERT: + case instruction.OP_AND: + case instruction.OP_OR: + case instruction.OP_XOR: + case instruction.OP_2MUL: + case instruction.OP_2DIV: + case instruction.OP_MUL: + case instruction.OP_DIV: + case instruction.OP_MOD: + case instruction.OP_LSHIFT: + case instruction.OP_RSHIFT: + return false; - // - // Control - // - case instruction.OP_IF: - case instruction.OP_NOTIF: - { - // if [statements] [else [statements]] endif - bool fValue = false; - if (fExec) + // + // Push integer instructions + // + case instruction.OP_1NEGATE: + case instruction.OP_1: + case instruction.OP_2: + case instruction.OP_3: + case instruction.OP_4: + case instruction.OP_5: + case instruction.OP_6: + case instruction.OP_7: + case instruction.OP_8: + case instruction.OP_9: + case instruction.OP_10: + case instruction.OP_11: + case instruction.OP_12: + case instruction.OP_13: + case instruction.OP_14: + case instruction.OP_15: + case instruction.OP_16: { - if (stack.Count() < 1) - { - return false; - } - IEnumerable vch = stacktop(ref stack, -1); - fValue = CastToBool(vch); - if (opcode == instruction.OP_NOTIF) - { - fValue = !fValue; - } - popstack(ref stack); + // ( -- value) + BigInteger bn = DecodeOP_N(opcode, true); + stack.Add(bn.ToByteArray()); } - vfExec.Add(fValue); - } - break; + break; - case instruction.OP_ELSE: - { - int nExecCount = vfExec.Count(); - if (nExecCount == 0) + // + // Extension + // + case instruction.OP_NOP: + case instruction.OP_NOP1: + case instruction.OP_NOP2: + case instruction.OP_NOP3: + case instruction.OP_NOP4: + case instruction.OP_NOP5: + case instruction.OP_NOP6: + case instruction.OP_NOP7: + case instruction.OP_NOP8: + case instruction.OP_NOP9: + case instruction.OP_NOP10: { - return false; + // Just do nothing } - vfExec[nExecCount - 1] = !vfExec[nExecCount - 1]; - } - break; + break; - case instruction.OP_ENDIF: - { - int nExecCount = vfExec.Count(); - if (nExecCount == 0) + // + // Control + // + case instruction.OP_IF: + case instruction.OP_NOTIF: { - return false; + // if [statements] [else [statements]] endif + var fValue = false; + if (fExec) + { + if (stack.Count() < 1) + { + return false; + } + var vch = stacktop(ref stack, -1); + fValue = CastToBool(vch); + if (opcode == instruction.OP_NOTIF) + { + fValue = !fValue; + } + popstack(ref stack); + } + vfExec.Add(fValue); } - vfExec.RemoveAt(nExecCount - 1); - } - break; + break; - case instruction.OP_VERIFY: - { - // (true -- ) or - // (false -- false) and return - if (stack.Count() < 1) + case instruction.OP_ELSE: { - return false; + int nExecCount = vfExec.Count(); + if (nExecCount == 0) + { + return false; + } + vfExec[nExecCount - 1] = !vfExec[nExecCount - 1]; } + break; - bool fValue = CastToBool(stacktop(ref stack, -1)); - if (fValue) + case instruction.OP_ENDIF: { - popstack(ref stack); - } - else - { - return false; + int nExecCount = vfExec.Count(); + if (nExecCount == 0) + { + return false; + } + vfExec.RemoveAt(nExecCount - 1); } - } - break; - - case instruction.OP_RETURN: - { - return false; - } + break; - // - // Stack ops - // - case instruction.OP_TOALTSTACK: - { - if (stack.Count() < 1) + case instruction.OP_VERIFY: { - return false; - } - altStack.Add(stacktop(ref stack, -1)); - popstack(ref stack); - } - break; + // (true -- ) or + // (false -- false) and return + if (stack.Count() < 1) + { + return false; + } - case instruction.OP_FROMALTSTACK: - { - if (altStack.Count() < 1) - { - return false; + bool fValue = CastToBool(stacktop(ref stack, -1)); + if (fValue) + { + popstack(ref stack); + } + else + { + return false; + } } - stack.Add(stacktop(ref stack, -1)); - popstack(ref altStack); - } - break; + break; - case instruction.OP_2DROP: - { - // (x1 x2 -- ) - if (stack.Count() < 2) + case instruction.OP_RETURN: { return false; } - popstack(ref stack); - popstack(ref stack); - } - break; - case instruction.OP_2DUP: - { - // (x1 x2 -- x1 x2 x1 x2) - if (stack.Count() < 2) + // + // Stack ops + // + case instruction.OP_TOALTSTACK: { - return false; + if (stack.Count() < 1) + { + return false; + } + altStack.Add(stacktop(ref stack, -1)); + popstack(ref stack); } - IEnumerable vch1 = stacktop(ref stack, -2); - IEnumerable vch2 = stacktop(ref stack, -1); - stack.Add(vch1); - stack.Add(vch2); - } - break; + break; - case instruction.OP_3DUP: - { - // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) - if (stack.Count() < 3) + case instruction.OP_FROMALTSTACK: { - return false; + if (altStack.Count() < 1) + { + return false; + } + stack.Add(stacktop(ref stack, -1)); + popstack(ref altStack); } - IEnumerable vch1 = stacktop(ref stack, -3); - IEnumerable vch2 = stacktop(ref stack, -2); - IEnumerable vch3 = stacktop(ref stack, -1); - stack.Add(vch1); - stack.Add(vch2); - stack.Add(vch3); - } - break; + break; - case instruction.OP_2OVER: - { - // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) - if (stack.Count() < 4) + case instruction.OP_2DROP: { - return false; + // (x1 x2 -- ) + if (stack.Count() < 2) + { + return false; + } + popstack(ref stack); + popstack(ref stack); } - IEnumerable vch1 = stacktop(ref stack, -4); - IEnumerable vch2 = stacktop(ref stack, -3); - stack.Add(vch1); - stack.Add(vch2); - } - break; + break; - case instruction.OP_2ROT: - { - int nStackDepth = stack.Count(); - // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) - if (nStackDepth < 6) + case instruction.OP_2DUP: { - return false; + // (x1 x2 -- x1 x2 x1 x2) + if (stack.Count() < 2) + { + return false; + } + var vch1 = stacktop(ref stack, -2); + var vch2 = stacktop(ref stack, -1); + stack.Add(vch1); + stack.Add(vch2); } - IEnumerable vch1 = stacktop(ref stack, -6); - IEnumerable vch2 = stacktop(ref stack, -5); - stack.RemoveRange(nStackDepth - 6, 2); - stack.Add(vch1); - stack.Add(vch2); - } - break; + break; - case instruction.OP_2SWAP: - { - // (x1 x2 x3 x4 -- x3 x4 x1 x2) - int nStackDepth = stack.Count(); - if (nStackDepth < 4) + case instruction.OP_3DUP: { - return false; + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.Count() < 3) + { + return false; + } + var vch1 = stacktop(ref stack, -3); + var vch2 = stacktop(ref stack, -2); + var vch3 = stacktop(ref stack, -1); + stack.Add(vch1); + stack.Add(vch2); + stack.Add(vch3); } - stack.Swap(nStackDepth - 4, nStackDepth - 2); - stack.Swap(nStackDepth - 3, nStackDepth - 1); - } - break; + break; - case instruction.OP_IFDUP: - { - // (x - 0 | x x) - if (stack.Count() < 1) + case instruction.OP_2OVER: { - return false; + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.Count() < 4) + { + return false; + } + var vch1 = stacktop(ref stack, -4); + var vch2 = stacktop(ref stack, -3); + stack.Add(vch1); + stack.Add(vch2); } + break; - IEnumerable vch = stacktop(ref stack, -1); - - if (CastToBool(vch)) + case instruction.OP_2ROT: { - stack.Add(vch); + int nStackDepth = stack.Count(); + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (nStackDepth < 6) + { + return false; + } + var vch1 = stacktop(ref stack, -6); + var vch2 = stacktop(ref stack, -5); + stack.RemoveRange(nStackDepth - 6, 2); + stack.Add(vch1); + stack.Add(vch2); } - } - break; - - case instruction.OP_DEPTH: - { - // -- stacksize - BigInteger bn = new BigInteger((ushort)stack.Count()); - stack.Add(bn.ToByteArray()); - } - break; + break; - case instruction.OP_DROP: - { - // (x -- ) - if (stack.Count() < 1) + case instruction.OP_2SWAP: { - return false; + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + int nStackDepth = stack.Count; + if (nStackDepth < 4) + { + return false; + } + stack.Swap(nStackDepth - 4, nStackDepth - 2); + stack.Swap(nStackDepth - 3, nStackDepth - 1); } + break; - popstack(ref stack); - } - break; - - case instruction.OP_DUP: - { - // (x -- x x) - if (stack.Count() < 1) + case instruction.OP_IFDUP: { - return false; - } + // (x - 0 | x x) + if (stack.Count() < 1) + { + return false; + } - IEnumerable vch = stacktop(ref stack, -1); - stack.Add(vch); - } - break; + var vch = stacktop(ref stack, -1); - case instruction.OP_NIP: - { - // (x1 x2 -- x2) - int nStackDepth = stack.Count(); - if (nStackDepth < 2) - { - return false; + if (CastToBool(vch)) + { + stack.Add(vch); + } } + break; - stack.RemoveAt(nStackDepth - 2); - } - break; - - case instruction.OP_OVER: - { - // (x1 x2 -- x1 x2 x1) - if (stack.Count() < 2) + case instruction.OP_DEPTH: { - return false; + // -- stacksize + BigInteger bn = new BigInteger((ushort)stack.Count()); + stack.Add(bn.ToByteArray()); } + break; - IEnumerable vch = stacktop(ref stack, -2); - stack.Add(vch); - } - break; - - case instruction.OP_PICK: - case instruction.OP_ROLL: - { - // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) - // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) - - int nStackDepth = stack.Count(); - if (nStackDepth < 2) + case instruction.OP_DROP: { - return false; - } - - int n = (int)CastToBigInteger(stacktop(ref stack, -1)); - popstack(ref stack); + // (x -- ) + if (stack.Count() < 1) + { + return false; + } - if (n < 0 || n >= stack.Count()) - { - return false; + popstack(ref stack); } + break; - IEnumerable vch = stacktop(ref stack, -n - 1); - if (opcode == instruction.OP_ROLL) + case instruction.OP_DUP: { - stack.RemoveAt(nStackDepth - n - 1); - } + // (x -- x x) + if (stack.Count() < 1) + { + return false; + } - stack.Add(vch); - } - break; + var vch = stacktop(ref stack, -1); + stack.Add(vch); + } + break; - case instruction.OP_ROT: - { - // (x1 x2 x3 -- x2 x3 x1) - // x2 x1 x3 after first swap - // x2 x3 x1 after second swap - int nStackDepth = stack.Count(); - if (nStackDepth < 3) + case instruction.OP_NIP: { - return false; - } - stack.Swap(nStackDepth - 3, nStackDepth - 2); - stack.Swap(nStackDepth - 2, nStackDepth - 1); + // (x1 x2 -- x2) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } - } - break; + stack.RemoveAt(nStackDepth - 2); + } + break; - case instruction.OP_SWAP: - { - // (x1 x2 -- x2 x1) - int nStackDepth = stack.Count(); - if (nStackDepth < 2) + case instruction.OP_OVER: { - return false; + // (x1 x2 -- x1 x2 x1) + if (stack.Count() < 2) + { + return false; + } + + var vch = stacktop(ref stack, -2); + stack.Add(vch); } - stack.Swap(nStackDepth - 2, nStackDepth - 1); - } - break; + break; - case instruction.OP_TUCK: - { - // (x1 x2 -- x2 x1 x2) - int nStackDepth = stack.Count(); - if (nStackDepth < 2) + case instruction.OP_PICK: + case instruction.OP_ROLL: { - return false; - } - IEnumerable vch = stacktop(ref stack, -1); - stack.Insert(nStackDepth - 2, vch); - } - break; + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } - case instruction.OP_SIZE: - { - // (in -- in size) - if (stack.Count() < 1) - { - return false; - } + int n = (int)CastToBigInteger(stacktop(ref stack, -1)); + popstack(ref stack); - BigInteger bnSize = new BigInteger((ushort)stacktop(ref stack, -1).Count()); - stack.Add(bnSize.ToByteArray()); - } - break; + if (n < 0 || n >= stack.Count()) + { + return false; + } + var vch = stacktop(ref stack, -n - 1); + if (opcode == instruction.OP_ROLL) + { + stack.RemoveAt(nStackDepth - n - 1); + } - // - // Bitwise logic - // - case instruction.OP_EQUAL: - case instruction.OP_EQUALVERIFY: - //case instruction.OP_NOTEQUAL: // use OP_NUMNOTEQUAL - { - // (x1 x2 - bool) - if (stack.Count() < 2) - { - return false; + stack.Add(vch); } + break; - IEnumerable vch1 = stacktop(ref stack, -2); - IEnumerable vch2 = stacktop(ref stack, -1); - bool fEqual = (vch1 == vch2); - // OP_NOTEQUAL is disabled because it would be too easy to say - // something like n != 1 and have some wiseguy pass in 1 with extra - // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) - //if (opcode == instruction.OP_NOTEQUAL) - // fEqual = !fEqual; - popstack(ref stack); - popstack(ref stack); - stack.Add(fEqual ? trueBytes : falseBytes); - - if (opcode == instruction.OP_EQUALVERIFY) + case instruction.OP_ROT: { - if (fEqual) - { - popstack(ref stack); - } - else + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + int nStackDepth = stack.Count(); + if (nStackDepth < 3) { return false; } - } - } - break; + stack.Swap(nStackDepth - 3, nStackDepth - 2); + stack.Swap(nStackDepth - 2, nStackDepth - 1); - - // - // Numeric - // - case instruction.OP_1ADD: - case instruction.OP_1SUB: - case instruction.OP_NEGATE: - case instruction.OP_ABS: - case instruction.OP_NOT: - case instruction.OP_0NOTEQUAL: - { - // (in -- out) - if (stack.Count() < 1) - { - return false; } + break; - BigInteger bn = CastToBigInteger(stacktop(ref stack, -1)); - switch (opcode) + case instruction.OP_SWAP: { - case instruction.OP_1ADD: - bn = bn + 1; - break; - case instruction.OP_1SUB: - bn = bn - 1; - break; - case instruction.OP_NEGATE: - bn = -bn; - break; - case instruction.OP_ABS: - bn = BigInteger.Abs(bn); - break; - case instruction.OP_NOT: - bn = bn == 0 ? 1 : 0; - break; - case instruction.OP_0NOTEQUAL: - bn = bn != 0 ? 1 : 0; - break; - - default: - throw new StackMachineException("invalid instruction"); + // (x1 x2 -- x2 x1) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } + stack.Swap(nStackDepth - 2, nStackDepth - 1); } + break; - popstack(ref stack); - stack.Add(bn.ToByteArray()); - } - break; - - case instruction.OP_ADD: - case instruction.OP_SUB: - case instruction.OP_BOOLAND: - case instruction.OP_BOOLOR: - case instruction.OP_NUMEQUAL: - case instruction.OP_NUMEQUALVERIFY: - case instruction.OP_NUMNOTEQUAL: - case instruction.OP_LESSTHAN: - case instruction.OP_GREATERTHAN: - case instruction.OP_LESSTHANOREQUAL: - case instruction.OP_GREATERTHANOREQUAL: - case instruction.OP_MIN: - case instruction.OP_MAX: - { - // (x1 x2 -- out) - if (stack.Count() < 2) + case instruction.OP_TUCK: { - return false; + // (x1 x2 -- x2 x1 x2) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } + var vch = stacktop(ref stack, -1); + stack.Insert(nStackDepth - 2, vch); } + break; - BigInteger bn1 = CastToBigInteger(stacktop(ref stack, -2)); - BigInteger bn2 = CastToBigInteger(stacktop(ref stack, -1)); - BigInteger bn = 0; - switch (opcode) + case instruction.OP_SIZE: { - case instruction.OP_ADD: - bn = bn1 + bn2; - break; - case instruction.OP_SUB: - bn = bn1 - bn2; - break; - case instruction.OP_BOOLAND: - bn = (bn1 != 0 && bn2 != 0) ? 1 : 0; - break; - case instruction.OP_BOOLOR: - bn = (bn1 != 0 || bn2 != 0) ? 1 : 0; - break; - case instruction.OP_NUMEQUAL: - bn = (bn1 == bn2) ? 1 : 0; - break; - case instruction.OP_NUMEQUALVERIFY: - bn = (bn1 == bn2) ? 1 : 0; - break; - case instruction.OP_NUMNOTEQUAL: - bn = (bn1 != bn2) ? 1 : 0; - break; - case instruction.OP_LESSTHAN: - bn = (bn1 < bn2) ? 1 : 0; - break; - case instruction.OP_GREATERTHAN: - bn = (bn1 > bn2) ? 1 : 0; - break; - case instruction.OP_LESSTHANOREQUAL: - bn = (bn1 <= bn2) ? 1 : 0; - break; - case instruction.OP_GREATERTHANOREQUAL: - bn = (bn1 >= bn2) ? 1 : 0; - break; - case instruction.OP_MIN: - bn = (bn1 < bn2 ? bn1 : bn2); - break; - case instruction.OP_MAX: - bn = (bn1 > bn2 ? bn1 : bn2); - break; - - default: - throw new StackMachineException("invalid instruction"); + // (in -- in size) + if (stack.Count() < 1) + { + return false; + } + + var bnSize = new BigInteger((ushort)stacktop(ref stack, -1).Count()); + stack.Add(bnSize.ToByteArray()); } + break; - popstack(ref stack); - popstack(ref stack); - stack.Add(bn.ToByteArray()); - if (opcode == instruction.OP_NUMEQUALVERIFY) + // + // Bitwise logic + // + case instruction.OP_EQUAL: + case instruction.OP_EQUALVERIFY: + //case instruction.OP_NOTEQUAL: // use OP_NUMNOTEQUAL { - if (CastToBool(stacktop(ref stack, -1))) + // (x1 x2 - bool) + if (stack.Count() < 2) { - popstack(ref stack); + return false; } - else + + var vch1 = stacktop(ref stack, -2); + var vch2 = stacktop(ref stack, -1); + bool fEqual = (vch1.SequenceEqual(vch2)); + // OP_NOTEQUAL is disabled because it would be too easy to say + // something like n != 1 and have some wiseguy pass in 1 with extra + // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) + //if (opcode == instruction.OP_NOTEQUAL) + // fEqual = !fEqual; + popstack(ref stack); + popstack(ref stack); + stack.Add(fEqual ? trueBytes : falseBytes); + + if (opcode == instruction.OP_EQUALVERIFY) { - return false; + if (fEqual) + { + popstack(ref stack); + } + else + { + return false; + } } } - } - break; - - case instruction.OP_WITHIN: - { - // (x min max -- out) - if (stack.Count() < 3) - return false; - BigInteger bn1 = CastToBigInteger(stacktop(ref stack, -3)); - BigInteger bn2 = CastToBigInteger(stacktop(ref stack, -2)); - BigInteger bn3 = CastToBigInteger(stacktop(ref stack, -1)); - bool fValue = (bn2 <= bn1 && bn1 < bn3); - popstack(ref stack); - popstack(ref stack); - popstack(ref stack); - stack.Add(fValue ? trueBytes : falseBytes); - } - break; + break; - // - // Crypto - // - case instruction.OP_RIPEMD160: - case instruction.OP_SHA1: - case instruction.OP_SHA256: - case instruction.OP_HASH160: - case instruction.OP_HASH256: - { - // (in -- hash) - if (stack.Count() < 1) - return false; - IEnumerable vch = stacktop(ref stack, -1); - IEnumerable vchHash = null; - if (opcode == instruction.OP_RIPEMD160) - { - RIPEMD160 hash = RIPEMD160.Compute160(vch); - vchHash = hash.hashBytes; - } - else if (opcode == instruction.OP_SHA1) - { - SHA1 hash = SHA1.Compute1(vch); - vchHash = hash.hashBytes; - } - else if (opcode == instruction.OP_SHA256) - { - SHA256 hash = SHA256.Compute256(vch); - vchHash = hash.hashBytes; - } - else if (opcode == instruction.OP_HASH160) - { - Hash160 hash = Hash160.Compute160(vch); - vchHash = hash.hashBytes; - } - else if (opcode == instruction.OP_HASH256) + // + // Numeric + // + case instruction.OP_1ADD: + case instruction.OP_1SUB: + case instruction.OP_NEGATE: + case instruction.OP_ABS: + case instruction.OP_NOT: + case instruction.OP_0NOTEQUAL: { - Hash256 hash = Hash256.Compute256(vch); - vchHash = hash.hashBytes; - } - popstack(ref stack); - stack.Add(vchHash); - } - break; + // (in -- out) + if (stack.Count() < 1) + { + return false; + } - case instruction.OP_CODESEPARATOR: - { - // Hash starts after the code separator - nCodeHashBegin = CodeQueue.CurrentIndex; - } - break; + var bn = CastToBigInteger(stacktop(ref stack, -1)); + switch (opcode) + { + case instruction.OP_1ADD: + bn = bn + 1; + break; + case instruction.OP_1SUB: + bn = bn - 1; + break; + case instruction.OP_NEGATE: + bn = -bn; + break; + case instruction.OP_ABS: + bn = BigInteger.Abs(bn); + break; + case instruction.OP_NOT: + bn = bn == 0 ? 1 : 0; + break; + case instruction.OP_0NOTEQUAL: + bn = bn != 0 ? 1 : 0; + break; + } - case instruction.OP_CHECKSIG: - case instruction.OP_CHECKSIGVERIFY: - { - // (sig pubkey -- bool) - if (stack.Count() < 2) - { - return false; + popstack(ref stack); + stack.Add(bn.ToByteArray()); } + break; - IList sigBytes = stacktop(ref stack, -2).ToList(); - IList pubkeyBytes = stacktop(ref stack, -1).ToList(); + case instruction.OP_ADD: + case instruction.OP_SUB: + case instruction.OP_BOOLAND: + case instruction.OP_BOOLOR: + case instruction.OP_NUMEQUAL: + case instruction.OP_NUMEQUALVERIFY: + case instruction.OP_NUMNOTEQUAL: + case instruction.OP_LESSTHAN: + case instruction.OP_GREATERTHAN: + case instruction.OP_LESSTHANOREQUAL: + case instruction.OP_GREATERTHANOREQUAL: + case instruction.OP_MIN: + case instruction.OP_MAX: + { + // (x1 x2 -- out) + if (stack.Count() < 2) + { + return false; + } - // Subset of script starting at the most recent codeseparator - CScript scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin)); + var bn1 = CastToBigInteger(stacktop(ref stack, -2)); + var bn2 = CastToBigInteger(stacktop(ref stack, -1)); + BigInteger bn = 0; - // There's no way for a signature to sign itself - scriptCode.RemovePattern(sigBytes); + switch (opcode) + { + case instruction.OP_ADD: + bn = bn1 + bn2; + break; + case instruction.OP_SUB: + bn = bn1 - bn2; + break; + case instruction.OP_BOOLAND: + bn = (bn1 != 0 && bn2 != 0) ? 1 : 0; + break; + case instruction.OP_BOOLOR: + bn = (bn1 != 0 || bn2 != 0) ? 1 : 0; + break; + case instruction.OP_NUMEQUAL: + bn = (bn1 == bn2) ? 1 : 0; + break; + case instruction.OP_NUMEQUALVERIFY: + bn = (bn1 == bn2) ? 1 : 0; + break; + case instruction.OP_NUMNOTEQUAL: + bn = (bn1 != bn2) ? 1 : 0; + break; + case instruction.OP_LESSTHAN: + bn = (bn1 < bn2) ? 1 : 0; + break; + case instruction.OP_GREATERTHAN: + bn = (bn1 > bn2) ? 1 : 0; + break; + case instruction.OP_LESSTHANOREQUAL: + bn = (bn1 <= bn2) ? 1 : 0; + break; + case instruction.OP_GREATERTHANOREQUAL: + bn = (bn1 >= bn2) ? 1 : 0; + break; + case instruction.OP_MIN: + bn = (bn1 < bn2 ? bn1 : bn2); + break; + case instruction.OP_MAX: + bn = (bn1 > bn2 ? bn1 : bn2); + break; + } - bool fSuccess = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubkeyBytes.ToList(), flags) && CheckSig(sigBytes, pubkeyBytes, scriptCode, txTo, nIn, nHashType, flags); + popstack(ref stack); + popstack(ref stack); + stack.Add(bn.ToByteArray()); - popstack(ref stack); - popstack(ref stack); - stack.Add(fSuccess ? trueBytes : falseBytes); - if (opcode == instruction.OP_CHECKSIGVERIFY) - { - if (fSuccess) + if (opcode == instruction.OP_NUMEQUALVERIFY) { - popstack(ref stack); + if (CastToBool(stacktop(ref stack, -1))) + { + popstack(ref stack); + } + else + { + return false; + } } - else + } + break; + + case instruction.OP_WITHIN: + { + // (x min max -- out) + if (stack.Count() < 3) { return false; } - } - } - break; - case instruction.OP_CHECKMULTISIG: - case instruction.OP_CHECKMULTISIGVERIFY: - { - // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + var bn1 = CastToBigInteger(stacktop(ref stack, -3)); + var bn2 = CastToBigInteger(stacktop(ref stack, -2)); + var bn3 = CastToBigInteger(stacktop(ref stack, -1)); - int i = 1; - if (stack.Count() < i) - { - return false; - } + bool fValue = (bn2 <= bn1 && bn1 < bn3); - int nKeysCount = (int)CastToBigInteger(stacktop(ref stack, -i)); - if (nKeysCount < 0 || nKeysCount > 20) - { - return false; - } - nOpCount += nKeysCount; - if (nOpCount > 201) - { - return false; + popstack(ref stack); + popstack(ref stack); + popstack(ref stack); + + stack.Add(fValue ? trueBytes : falseBytes); } - int ikey = ++i; - i += nKeysCount; - if (stack.Count() < i) + break; + + // + // Crypto + // + case instruction.OP_RIPEMD160: + case instruction.OP_SHA1: + case instruction.OP_SHA256: + case instruction.OP_HASH160: + case instruction.OP_HASH256: { - return false; + // (in -- hash) + if (stack.Count() < 1) + { + return false; + } + byte[] hash = null; + var data = stacktop(ref stack, -1); + + switch (opcode) + { + case instruction.OP_HASH160: + hash = CryptoUtils.ComputeHash160(data); + break; + case instruction.OP_HASH256: + hash = CryptoUtils.ComputeHash256(data); + break; + case instruction.OP_SHA1: + hash = CryptoUtils.ComputeSha1(data); + break; + case instruction.OP_SHA256: + hash = CryptoUtils.ComputeSha256(data); + break; + case instruction.OP_RIPEMD160: + hash = CryptoUtils.ComputeRipeMD160(data); + break; + } + popstack(ref stack); + stack.Add(hash); } + break; - int nSigsCount = (int)CastToBigInteger(stacktop(ref stack, -i)); - if (nSigsCount < 0 || nSigsCount > nKeysCount) + case instruction.OP_CODESEPARATOR: { - return false; + // Hash starts after the code separator + nCodeHashBegin = CodeQueue.Index; } - int isig = ++i; - i += nSigsCount; - if (stack.Count() < i) + break; + + case instruction.OP_CHECKSIG: + case instruction.OP_CHECKSIGVERIFY: { - return false; - } + // (sig pubkey -- bool) + if (stack.Count() < 2) + { + return false; + } - // Subset of script starting at the most recent codeseparator - CScript scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin)); + var sigBytes = stacktop(ref stack, -2); + var pubkeyBytes = stacktop(ref stack, -1); - // There is no way for a signature to sign itself, so we need to drop the signatures - for (int k = 0; k < nSigsCount; k++) - { - IEnumerable vchSig = stacktop(ref stack, -isig - k); - scriptCode.RemovePattern(vchSig.ToList()); + // Subset of script starting at the most recent codeseparator + var scriptCode = new CScript(scriptBytes.Skip(nCodeHashBegin).ToArray()); + + // There's no way for a signature to sign itself + scriptCode.RemovePattern(sigBytes); + + bool fSuccess = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubkeyBytes, flags) && CheckSig(sigBytes, pubkeyBytes, scriptCode, txTo, nIn, nHashType, flags); + + popstack(ref stack); + popstack(ref stack); + + stack.Add(fSuccess ? trueBytes : falseBytes); + + if (opcode == instruction.OP_CHECKSIGVERIFY) + { + if (fSuccess) + { + popstack(ref stack); + } + else + { + return false; + } + } } + break; - bool fSuccess = true; - while (fSuccess && nSigsCount > 0) + case instruction.OP_CHECKMULTISIG: + case instruction.OP_CHECKMULTISIGVERIFY: { - IList sigBytes = stacktop(ref stack, -isig).ToList(); - IList pubKeyBytes = stacktop(ref stack, -ikey).ToList(); + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) - // Check signature - bool fOk = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubKeyBytes.ToList(), flags) && CheckSig(sigBytes, pubKeyBytes, scriptCode, txTo, nIn, nHashType, flags); + int i = 1; + if (stack.Count() < i) + { + return false; + } - if (fOk) + int nKeysCount = (int)CastToBigInteger(stacktop(ref stack, -i)); + if (nKeysCount < 0 || nKeysCount > 20) { - isig++; - nSigsCount--; + return false; + } + nOpCount += nKeysCount; + if (nOpCount > 201) + { + return false; + } + int ikey = ++i; + i += nKeysCount; + if (stack.Count() < i) + { + return false; } - ikey++; - nKeysCount--; - // If there are more signatures left than keys left, - // then too many signatures have failed - if (nSigsCount > nKeysCount) + int nSigsCount = (int)CastToBigInteger(stacktop(ref stack, -i)); + if (nSigsCount < 0 || nSigsCount > nKeysCount) { - fSuccess = false; + return false; + } + int isig = ++i; + i += nSigsCount; + if (stack.Count() < i) + { + return false; } - } - while (i-- > 1) - { - popstack(ref stack); - } + // Subset of script starting at the most recent codeseparator + var scriptCode = new CScript(scriptBytes.Skip(nCodeHashBegin).ToArray()); - // A bug causes CHECKMULTISIG to consume one extra argument - // whose contents were not checked in any way. - // - // Unfortunately this is a potential source of mutability, - // so optionally verify it is exactly equal to zero prior - // to removing it from the stack. - if (stack.Count() < 1) - { - return false; - } - if ((flags & (int)scriptflag.SCRIPT_VERIFY_NULLDUMMY) != 0 && stacktop(ref stack, -1).Count() != 0) - { - return false; // CHECKMULTISIG dummy argument not null - } - popstack(ref stack); + // There is no way for a signature to sign itself, so we need to drop the signatures + for (int k = 0; k < nSigsCount; k++) + { + var vchSig = stacktop(ref stack, -isig - k); + scriptCode.RemovePattern(vchSig); + } - stack.Add(fSuccess ? trueBytes : falseBytes); + bool fSuccess = true; + while (fSuccess && nSigsCount > 0) + { + var sigBytes = stacktop(ref stack, -isig); + var pubKeyBytes = stacktop(ref stack, -ikey); + + // Check signature + bool fOk = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubKeyBytes, flags) && CheckSig(sigBytes, pubKeyBytes, scriptCode, txTo, nIn, nHashType, flags); + + if (fOk) + { + isig++; + nSigsCount--; + } + ikey++; + nKeysCount--; + + // If there are more signatures left than keys left, + // then too many signatures have failed + if (nSigsCount > nKeysCount) + { + fSuccess = false; + } + } - if (opcode == instruction.OP_CHECKMULTISIGVERIFY) - { - if (fSuccess) + while (i-- > 1) { popstack(ref stack); } - else + + // A bug causes CHECKMULTISIG to consume one extra argument + // whose contents were not checked in any way. + // + // Unfortunately this is a potential source of mutability, + // so optionally verify it is exactly equal to zero prior + // to removing it from the stack. + if (stack.Count() < 1) { return false; } + if ((flags & (int)scriptflag.SCRIPT_VERIFY_NULLDUMMY) != 0 && stacktop(ref stack, -1).Count() != 0) + { + return false; // CHECKMULTISIG dummy argument not null + } + popstack(ref stack); + + stack.Add(fSuccess ? trueBytes : falseBytes); + + if (opcode == instruction.OP_CHECKMULTISIGVERIFY) + { + if (fSuccess) + { + popstack(ref stack); + } + else + { + return false; + } + } } - } - break; + break; - default: - return false; - } + default: + return false; + } - // Size limits - if (stack.Count() + altStack.Count() > 1000) - { - return false; + // Size limits + if (stack.Count() + altStack.Count() > 1000) + { + return false; + } } +#if !DEBUG } + catch (Exception) + { + // If there are any exceptions then just return false. + return false; + } +#endif - - if (vfExec.Count() == 0) + if (vfExec.Count() != 0) { + // Something went wrong with conditional instructions. return false; } @@ -1954,21 +1705,21 @@ namespace Novacoin } - static bool IsCanonicalPubKey(IList pubKeyBytes, int flags) + public static bool IsCanonicalPubKey(byte[] pubKeyBytes, int flags) { if ((flags & (int)scriptflag.SCRIPT_VERIFY_STRICTENC) == 0) return true; - if (pubKeyBytes.Count < 33) + if (pubKeyBytes.Length < 33) return false; // Non-canonical public key: too short if (pubKeyBytes[0] == 0x04) { - if (pubKeyBytes.Count != 65) + if (pubKeyBytes.Length != 65) return false; // Non-canonical public key: invalid length for uncompressed key } else if (pubKeyBytes[0] == 0x02 || pubKeyBytes[0] == 0x03) { - if (pubKeyBytes.Count != 33) + if (pubKeyBytes.Length != 33) return false; // Non-canonical public key: invalid length for compressed key } else @@ -1978,14 +1729,25 @@ namespace Novacoin return true; } - static bool IsCanonicalSignature(IList sigBytes, int flags) + public static bool IsCanonicalSignature(byte[] sigBytes, int flags) { // STUB return true; } - static bool CheckSig(IList vchSig, IList vchPubKey, CScript scriptCode, CTransaction txTo, int nIn, int nHashType, int flags) + /// + /// Check signature. + /// + /// Signature + /// Public key + /// Spending script + /// CTransaction instance + /// Input number + /// Hashing type flag + /// Signature checking flags + /// Checking result + public static bool CheckSig(byte[] sigBytes, byte[] pubkeyBytes, CScript script, CTransaction txTo, int nIn, int nHashType, int flags) { CPubKey pubkey; @@ -1993,7 +1755,7 @@ namespace Novacoin { // Trying to initialize the public key instance - pubkey = new CPubKey(vchPubKey); + pubkey = new CPubKey(pubkeyBytes); } catch (Exception) { @@ -2007,7 +1769,7 @@ namespace Novacoin return false; } - if (vchSig.Count == 0) + if (sigBytes.Length == 0) { return false; } @@ -2015,23 +1777,89 @@ namespace Novacoin // Hash type is one byte tacked on to the end of the signature if (nHashType == 0) { - nHashType = vchSig.Last(); + nHashType = sigBytes.Last(); } - else if (nHashType != vchSig.Last()) + else if (nHashType != sigBytes.Last()) { return false; } // Remove hash type - vchSig.RemoveAt(vchSig.Count - 1); + Array.Resize(ref sigBytes, sigBytes.Length - 1); + + var sighash = SignatureHash(script, txTo, nIn, nHashType); + + if (!pubkey.VerifySignature(sighash, sigBytes)) + { + return false; + } + + return true; + } + + /// + /// Evaluates the both scriptSig and scriptPubKey. + /// + /// + /// + /// Transaction + /// Input number + /// Script validation flags + /// Hash type flag + /// + public static bool VerifyScript(CScript scriptSig, CScript scriptPubKey, CTransaction txTo, int nIn, int flags, int nHashType) + { + var stack = new List(); + List stackCopy = null; - Hash256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + if (!EvalScript(ref stack, scriptSig, txTo, nIn, flags, nHashType)) + { + return false; + } - if (!pubkey.VerifySignature(sighash, vchSig)) + if ((flags & (int)scriptflag.SCRIPT_VERIFY_P2SH) != 0) + { + stackCopy = new List(stack); + } + + if (!EvalScript(ref stack, scriptPubKey, txTo, nIn, flags, nHashType)) { return false; } + if (stack.Count == 0 || CastToBool(stack.Last()) == false) + { + return false; + } + + // Additional validation for spend-to-script-hash transactions: + if ((flags & (int)scriptflag.SCRIPT_VERIFY_P2SH) != 0 && scriptPubKey.IsPayToScriptHash) + { + if (!scriptSig.IsPushOnly) // scriptSig must be literals-only + { + return false; + } + + // stackCopy cannot be empty here, because if it was the + // P2SH HASH <> EQUAL scriptPubKey would be evaluated with + // an empty stack and the EvalScript above would return false. + + if (stackCopy.Count == 0) + { + throw new StackMachineException("Fatal script validation error."); + } + + var pubKey2 = new CScript(stackCopy.Last()); + popstack(ref stackCopy); + + if (!EvalScript(ref stackCopy, pubKey2, txTo, nIn, flags, nHashType)) + return false; + if (stackCopy.Count == 0) + return false; + + return CastToBool(stackCopy.Last()); + } + return true; } };