X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FScriptCode.cs;h=ab0db3fe04b795a77ee2235f977dbb762da1528a;hb=1dcac5faa2b1477034f82466ffb16170fa2e9bb6;hp=c90bf87b871e45ce5d8e705f9068622d1a2657c8;hpb=5f1c284906c75b1c579357db468239d47c5b8707;p=NovacoinLibrary.git diff --git a/Novacoin/ScriptCode.cs b/Novacoin/ScriptCode.cs index c90bf87..ab0db3f 100644 --- a/Novacoin/ScriptCode.cs +++ b/Novacoin/ScriptCode.cs @@ -1,21 +1,24 @@ /** - * - * 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 . + * 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.Numerics; using System.Text; @@ -23,7 +26,7 @@ using System.Text; namespace Novacoin { /// - /// Script opcodes + /// Script instructions /// public enum instruction { @@ -223,10 +226,10 @@ 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) { if (opcode == instruction.OP_0) // OP_0 and OP_FALSE are synonyms @@ -238,78 +241,67 @@ namespace Novacoin } /// - /// 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 byte[] bytesRet) + public static bool GetOp(ref InstructionQueue codeBytes, out instruction opcodeRet, out byte[] bytesRet) { bytesRet = new byte[0]; - opcodeRet = instruction.OP_INVALIDOPCODE; + instruction opcode = opcodeRet = instruction.OP_INVALIDOPCODE; - instruction opcode; - - try + // Read instruction + byte opVal = 0xff; + if (!codeBytes.TryGet(ref opVal)) { - // Read instruction - opcode = (instruction)codeBytes.Get(); - } - catch (ByteQueueException) - { - // 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] = 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.Get(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; @@ -335,7 +327,7 @@ namespace Novacoin if (bytes.Length <= 4) { - sb.Append(Interop.BEBytesToUInt32(bytes)); + sb.Append(new BigInteger(bytes)); } else { @@ -364,50 +356,43 @@ 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; - } + // 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."); - if (opcode == instruction.OP_0) + switch (opcode) { - return 0; - } - - // Only OP_n opcodes are supported, throw exception otherwise. - if (opcode < instruction.OP_1 || opcode > instruction.OP_16) - { - 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) @@ -477,6 +462,8 @@ namespace Novacoin /// Result public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList solutions) { + 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. @@ -487,7 +474,7 @@ namespace Novacoin typeRet = txnouttype.TX_SCRIPTHASH; // Take 20 bytes with offset of 2 bytes - var hashBytes = scriptPubKey.Bytes.Skip(2).Take(20); + var hashBytes = scriptBytes.Skip(2).Take(20); solutions.Add(hashBytes.ToArray()); return true; @@ -499,7 +486,7 @@ namespace Novacoin typeRet = txnouttype.TX_PUBKEYHASH; // Take 20 bytes with offset of 3 bytes - var hashBytes = scriptPubKey.Bytes.Skip(3).Take(20); + var hashBytes = scriptBytes.Skip(3).Take(20); solutions.Add(hashBytes.ToArray()); return true; @@ -520,7 +507,7 @@ 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( txnouttype.TX_MULTISIG, @@ -554,17 +541,17 @@ namespace Novacoin instruction opcode1, opcode2; // Compare - var bq1 = script1.GetByteQUeue(); - var bq2 = script2.GetByteQUeue(); + var bq1 = script1.GetInstructionQueue(); + var bq2 = script2.GetInstructionQueue(); 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; @@ -591,7 +578,7 @@ namespace Novacoin break; } - // Template matching opcodes: + // Template matching instructions: if (opcode2 == instruction.OP_PUBKEYS) { while (args1.Count() >= 33 && args1.Count() <= 120) @@ -669,21 +656,16 @@ namespace Novacoin /// Input number /// Hash type flag /// - public static Hash256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType) + public static uint256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType) { - if (nIn >= txTo.vin.Length) - { - var 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."); // 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. - script.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++) @@ -742,10 +724,10 @@ namespace Novacoin } // Concatenate and hash - var txBytes = txTmp.Bytes; + var txBytes = (byte[])txTmp; var nHashTypeBytes = BitConverter.GetBytes(nHashType); - return Hash256.Compute256(ref txBytes, ref nHashTypeBytes); + return CryptoUtils.ComputeHash256(ref txBytes, ref nHashTypeBytes); } // @@ -756,6 +738,7 @@ namespace Novacoin /// /// Script machine exception /// + [Serializable] public class StackMachineException : Exception { public StackMachineException() @@ -779,10 +762,9 @@ namespace Novacoin /// Stack reference 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); } /// @@ -793,25 +775,10 @@ namespace Novacoin /// Byte sequence private static byte[] stacktop(ref List stack, int nDepth) { - int nStackElement = stack.Count + nDepth; - - if (nDepth >= 0) - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("stacktop() : positive depth ({0}) has no sense.", nDepth); + Contract.Requires(nDepth < 0, "Positive or zero stack depth makes no sense."); + Contract.Requires(stack.Count + nDepth >= 0, "Value exceeds real stack depth."); - 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]; } /// @@ -845,10 +812,7 @@ namespace Novacoin /// private static BigInteger CastToBigInteger(byte[] value) { - if (value.Length > 4) - { - throw new StackMachineException("CastToBigInteger() : overflow"); - } + Contract.Requires(value.Length <= 4, "Size limit failed."); return new BigInteger(value); } @@ -865,7 +829,9 @@ namespace Novacoin /// public static bool EvalScript(ref List stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType) { - if (script.Bytes.Count() > 10000) + var scriptBytes = ((byte[])script); + + if (scriptBytes.Length > 10000) { return false; // Size limit failed } @@ -878,11 +844,13 @@ namespace Novacoin var falseBytes = new byte[0]; var trueBytes = new byte[] { 0x01 }; - var CodeQueue = script.GetByteQUeue(); + var CodeQueue = script.GetInstructionQueue(); var altStack = new List(); +#if !DEBUG try { +#endif instruction opcode; byte[] pushArg; @@ -908,7 +876,7 @@ namespace Novacoin switch (opcode) { // - // Disabled opcodes + // Disabled instructions // case instruction.OP_CAT: case instruction.OP_SUBSTR: @@ -1524,36 +1492,36 @@ namespace Novacoin { return false; } - Hash hash = null; + byte[] hash = null; var data = stacktop(ref stack, -1); switch (opcode) { case instruction.OP_HASH160: - hash = Hash160.Compute160(data); + hash = CryptoUtils.ComputeHash160(data); break; case instruction.OP_HASH256: - hash = Hash256.Compute256(data); + hash = CryptoUtils.ComputeHash256(data); break; case instruction.OP_SHA1: - hash = SHA1.Compute1(data); + hash = CryptoUtils.ComputeSha1(data); break; case instruction.OP_SHA256: - hash = SHA256.Compute256(data); + hash = CryptoUtils.ComputeSha256(data); break; case instruction.OP_RIPEMD160: - hash = RIPEMD160.Compute160(data); + hash = CryptoUtils.ComputeRipeMD160(data); break; } popstack(ref stack); - stack.Add(hash.hashBytes); + stack.Add(hash); } break; case instruction.OP_CODESEPARATOR: { // Hash starts after the code separator - nCodeHashBegin = CodeQueue.CurrentIndex; + nCodeHashBegin = CodeQueue.Index; } break; @@ -1566,11 +1534,11 @@ namespace Novacoin return false; } - byte[] sigBytes = stacktop(ref stack, -2); - byte[] pubkeyBytes = stacktop(ref stack, -1); + var sigBytes = stacktop(ref stack, -2); + var pubkeyBytes = stacktop(ref stack, -1); // Subset of script starting at the most recent codeseparator - CScript scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin)); + var scriptCode = new CScript(scriptBytes.Skip(nCodeHashBegin).ToArray()); // There's no way for a signature to sign itself scriptCode.RemovePattern(sigBytes); @@ -1637,7 +1605,7 @@ namespace Novacoin } // Subset of script starting at the most recent codeseparator - CScript scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin)); + var scriptCode = new CScript(scriptBytes.Skip(nCodeHashBegin).ToArray()); // There is no way for a signature to sign itself, so we need to drop the signatures for (int k = 0; k < nSigsCount; k++) @@ -1718,12 +1686,14 @@ namespace Novacoin return false; } } +#if !DEBUG } catch (Exception) { // If there are any exceptions then just return false. return false; } +#endif if (vfExec.Count() != 0) {