From: CryptoManiac Date: Sat, 22 Aug 2015 03:33:17 +0000 (+0300) Subject: EvaluateScript and IsCanonicalPubKey implementation, IsCanonicalSignature/CheckSig... X-Git-Url: https://git.novaco.in/?p=NovacoinLibrary.git;a=commitdiff_plain;h=9e61ae67cd995673bf3a1c4ce2e8747e0fec0009 EvaluateScript and IsCanonicalPubKey implementation, IsCanonicalSignature/CheckSig stubs. --- diff --git a/Novacoin/WrappedList.cs b/Novacoin/ByteQueue.cs similarity index 75% rename from Novacoin/WrappedList.cs rename to Novacoin/ByteQueue.cs index b8c1656..be1b639 100644 --- a/Novacoin/WrappedList.cs +++ b/Novacoin/ByteQueue.cs @@ -6,18 +6,18 @@ using System.Threading.Tasks; namespace Novacoin { - public class WrappedListException : Exception + public class ByteQueueException : Exception { - public WrappedListException() + public ByteQueueException() { } - public WrappedListException(string message) + public ByteQueueException(string message) : base(message) { } - public WrappedListException(string message, Exception inner) + public ByteQueueException(string message, Exception inner) : base(message, inner) { } @@ -44,7 +44,7 @@ namespace Novacoin { if (Elements.Count <= Index) { - throw new WrappedListException("No elements left."); + throw new ByteQueueException("No elements left."); } return Elements[Index++]; @@ -59,7 +59,7 @@ namespace Novacoin { if (Elements.Count - Index < Count) { - throw new WrappedListException("Unable to read requested amount of data."); + throw new ByteQueueException("Unable to read requested amount of data."); } byte[] result = Elements.Skip(Index).Take(Count).ToArray(); @@ -72,7 +72,7 @@ namespace Novacoin { if (Elements.Count - Index < Count) { - throw new WrappedListException("Unable to read requested amount of data."); + throw new ByteQueueException("Unable to read requested amount of data."); } byte[] result = Elements.Skip(Index).Take(Count).ToArray(); @@ -80,11 +80,19 @@ namespace Novacoin return result; } + /// + /// Current index value + /// + public int CurrentIndex + { + get { return Index; } + } + public IEnumerable GetEnumerable(int Count) { if (Elements.Count - Index < Count) { - throw new WrappedListException("Unable to read requested amount of data."); + throw new ByteQueueException("Unable to read requested amount of data."); } IEnumerable result = Elements.Skip(Index).Take(Count); diff --git a/Novacoin/CScript.cs b/Novacoin/CScript.cs index 777793b..e50b2da 100644 --- a/Novacoin/CScript.cs +++ b/Novacoin/CScript.cs @@ -50,7 +50,7 @@ namespace Novacoin /// Return a new instance of ByteQueue object for current code bytes /// /// - public ByteQueue GetWrappedList() + public ByteQueue GetByteQUeue() { return new ByteQueue(codeBytes); } @@ -340,7 +340,7 @@ namespace Novacoin // This is a pay-to-script-hash scriptPubKey; // get the last item that the scriptSig // pushes onto the stack: - ByteQueue wScriptSig = scriptSig.GetWrappedList(); + ByteQueue wScriptSig = scriptSig.GetByteQUeue(); opcodetype opcode; // Current opcode IEnumerable pushArgs; // OP_PUSHDATAn argument diff --git a/Novacoin/Hash256.cs b/Novacoin/Hash256.cs index bad52d7..71de8a5 100644 --- a/Novacoin/Hash256.cs +++ b/Novacoin/Hash256.cs @@ -8,7 +8,7 @@ using System.Security.Cryptography; namespace Novacoin { /// - /// Representation of SHA-256 hash + /// Representation of Double SHA-256 hash /// public class Hash256 : Hash { diff --git a/Novacoin/IListExtensions.cs b/Novacoin/IListExtensions.cs new file mode 100644 index 0000000..a8ccfd2 --- /dev/null +++ b/Novacoin/IListExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using System.Diagnostics.Contracts; + +namespace Novacoin +{ + static class IListExtensions + { + public static void Swap( + this IList list, + int firstIndex, + int secondIndex + ) + { + Contract.Requires(list != null); + Contract.Requires(firstIndex >= 0 && firstIndex < list.Count); + Contract.Requires(secondIndex >= 0 && secondIndex < list.Count); + if (firstIndex == secondIndex) + { + return; + } + T temp = list[firstIndex]; + list[firstIndex] = list[secondIndex]; + list[secondIndex] = temp; + } + } +} diff --git a/Novacoin/Novacoin.csproj b/Novacoin/Novacoin.csproj index f2705ee..b8a93a9 100644 --- a/Novacoin/Novacoin.csproj +++ b/Novacoin/Novacoin.csproj @@ -41,6 +41,7 @@ + @@ -48,9 +49,12 @@ + + + - + diff --git a/Novacoin/RIPEMD160.cs b/Novacoin/RIPEMD160.cs new file mode 100644 index 0000000..28f5ebf --- /dev/null +++ b/Novacoin/RIPEMD160.cs @@ -0,0 +1,39 @@ +using System; +using System.Security.Cryptography; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +namespace Novacoin +{ + /// + /// Representation of RIPEMD-160 hash. + /// + public class RIPEMD160 : Hash + { + /// + /// Computes RIPEMD160 hash using managed library + /// + private static readonly RIPEMD160Managed _hasher160 = new RIPEMD160Managed(); + + // 20 bytes + public override int hashSize + { + get { return 20; } + } + + public RIPEMD160() : base() { } + public RIPEMD160(byte[] bytes, int offset = 0) : base(bytes, offset) { } + public RIPEMD160(IEnumerable bytes, int skip = 0) : base(bytes, skip) { } + public RIPEMD160(RIPEMD160 h) : base(h) { } + + public static RIPEMD160 Compute160(IEnumerable inputBytes) + { + byte[] dataBytes = inputBytes.ToArray(); + byte[] digest1 = _hasher160.ComputeHash(dataBytes, 0, dataBytes.Length); + + return new RIPEMD160(digest1); + } + } +} + diff --git a/Novacoin/SHA1.cs b/Novacoin/SHA1.cs new file mode 100644 index 0000000..6d60d1c --- /dev/null +++ b/Novacoin/SHA1.cs @@ -0,0 +1,41 @@ +using System; +using System.Text; +using System.Linq; +using System.Collections.Generic; + +using System.Security.Cryptography; + +namespace Novacoin +{ + /// + /// Representation of SHA-256 hash + /// + public class SHA1 : Hash + { + /// + /// Computes RIPEMD160 hash using managed library + /// + private static readonly SHA1Managed _hasher1 = new SHA1Managed(); + + // 32 bytes + public override int hashSize + { + get { return 20; } + } + + public SHA1() : base() { } + public SHA1(byte[] bytes, int offset = 0) : base(bytes, offset) { } + public SHA1(IEnumerable bytes, int skip = 0) : base(bytes, skip) { } + public SHA1(SHA1 h) : base(h) { } + + + public static SHA1 Compute1(IEnumerable inputBytes) + { + byte[] dataBytes = inputBytes.ToArray(); + byte[] digest1 = _hasher1.ComputeHash(dataBytes, 0, dataBytes.Length); + + return new SHA1(digest1); + } + } +} + diff --git a/Novacoin/SHA256.cs b/Novacoin/SHA256.cs new file mode 100644 index 0000000..b8b6086 --- /dev/null +++ b/Novacoin/SHA256.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; +using System.Linq; +using System.Collections.Generic; + +using System.Security.Cryptography; + +namespace Novacoin +{ + /// + /// Representation of SHA-256 hash + /// + public class SHA256 : Hash + { + // 32 bytes + public override int hashSize + { + get { return 32; } + } + + public SHA256() : base() { } + public SHA256(byte[] bytes, int offset = 0) : base(bytes, offset) { } + public SHA256(IEnumerable bytes, int skip = 0) : base(bytes, skip) { } + public SHA256(SHA256 h) : base(h) { } + + + public static SHA256 Compute256(IEnumerable inputBytes) + { + byte[] dataBytes = inputBytes.ToArray(); + byte[] digest1 = _hasher256.ComputeHash(dataBytes, 0, dataBytes.Length); + + return new SHA256(digest1); + } + } +} + diff --git a/Novacoin/ScriptCode.cs b/Novacoin/ScriptCode.cs index 45f8098..9936f72 100644 --- a/Novacoin/ScriptCode.cs +++ b/Novacoin/ScriptCode.cs @@ -2,8 +2,11 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; + using System.Numerics; +// using Org.BouncyCastle.Math; + namespace Novacoin { /// @@ -179,6 +182,17 @@ namespace Novacoin SIGHASH_ANYONECANPAY = 0x80, }; + /** Script verification flags */ + public enum scriptflag + { + SCRIPT_VERIFY_NONE = 0, + SCRIPT_VERIFY_P2SH = (1 << 0), // evaluate P2SH (BIP16) subscripts + SCRIPT_VERIFY_STRICTENC = (1 << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys + SCRIPT_VERIFY_LOW_S = (1 << 2), // enforce low S values in signatures (depends on STRICTENC) + SCRIPT_VERIFY_NOCACHE = (1 << 3), // do not store results in signature cache (but do query it) + SCRIPT_VERIFY_NULLDUMMY = (1 << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length + }; + public static class ScriptCode { public static string GetTxnOutputType(txnouttype t) @@ -476,7 +490,7 @@ namespace Novacoin // Read instruction opcode = (opcodetype)codeBytes.Get(); } - catch (WrappedListException) + catch (ByteQueueException) { // No instruction found there return false; @@ -513,7 +527,7 @@ namespace Novacoin szBytes = codeBytes.Get(4); } } - catch (WrappedListException) + catch (ByteQueueException) { // Unable to read operand length return false; @@ -529,7 +543,7 @@ namespace Novacoin // Read found number of bytes into list of OP_PUSHDATAn arguments. bytesRet = codeBytes.GetEnumerable(nSize); } - catch (WrappedListException) + catch (ByteQueueException) { // Unable to read data return false; @@ -748,8 +762,8 @@ namespace Novacoin opcodetype opcode1, opcode2; // Compare - ByteQueue wl1 = script1.GetWrappedList(); - ByteQueue wl2 = script2.GetWrappedList(); + ByteQueue wl1 = script1.GetByteQUeue(); + ByteQueue wl2 = script2.GetByteQUeue(); IEnumerable args1, args2; @@ -1028,10 +1042,916 @@ namespace Novacoin { if (value.Count() > 4) { - throw new StackMachineException("CastToBigNum() : overflow"); + throw new StackMachineException("CastToBigInteger() : overflow"); } return new BigInteger(value.ToArray()); } - }; + + static bool EvalScript(ref List> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType) + { + ByteQueue pc = script.GetByteQUeue(); + + ByteQueue pbegincodehash = script.GetByteQUeue(); + + opcodetype opcode; + IEnumerable vchPushValue; + + List vfExec = new List(); + List> altstack = new List>(); + + byte[] vchFalse = new byte[0]; + byte[] vchTrue = new byte[] { 0x01 }; + + if (script.Bytes.Count() > 10000) + { + return false; + } + + int nOpCount = 0; + + while (true) + { + bool fExec = false; + foreach (bool fValue in vfExec) + { + if (!fValue) + { + fExec = true; + break; + } + } + + // + // Read instruction + // + if (!GetOp(ref pc, out opcode, out vchPushValue)) + return false; + if (vchPushValue.Count() > 520) // Check against MAX_SCRIPT_ELEMENT_SIZE + return false; + if (opcode > opcodetype.OP_16 && ++nOpCount > 201) + return false; + + if (opcode == opcodetype.OP_CAT || + opcode == opcodetype.OP_SUBSTR || + opcode == opcodetype.OP_LEFT || + opcode == opcodetype.OP_RIGHT || + opcode == opcodetype.OP_INVERT || + opcode == opcodetype.OP_AND || + opcode == opcodetype.OP_OR || + opcode == opcodetype.OP_XOR || + opcode == opcodetype.OP_2MUL || + opcode == opcodetype.OP_2DIV || + opcode == opcodetype.OP_MUL || + opcode == opcodetype.OP_DIV || + opcode == opcodetype.OP_MOD || + opcode == opcodetype.OP_LSHIFT || + opcode == opcodetype.OP_RSHIFT) + return false; // Disabled opcodes. + + if (fExec && 0 <= opcode && opcode <= opcodetype.OP_PUSHDATA4) + { + stack.Add(vchPushValue); + } + else if (fExec || (opcodetype.OP_IF <= opcode && opcode <= opcodetype.OP_ENDIF)) + switch (opcode) + { + // + // Push value + // + case opcodetype.OP_1NEGATE: + case opcodetype.OP_1: + case opcodetype.OP_2: + case opcodetype.OP_3: + case opcodetype.OP_4: + case opcodetype.OP_5: + case opcodetype.OP_6: + case opcodetype.OP_7: + case opcodetype.OP_8: + case opcodetype.OP_9: + case opcodetype.OP_10: + case opcodetype.OP_11: + case opcodetype.OP_12: + case opcodetype.OP_13: + case opcodetype.OP_14: + case opcodetype.OP_15: + case opcodetype.OP_16: + { + // ( -- value) + BigInteger bn = new BigInteger((int)opcode - (int)(opcodetype.OP_1 - 1)); + stack.Add(bn.ToByteArray()); + } + break; + + + // + // Control + // + case opcodetype.OP_NOP: + case opcodetype.OP_NOP1: + case opcodetype.OP_NOP2: + case opcodetype.OP_NOP3: + case opcodetype.OP_NOP4: + case opcodetype.OP_NOP5: + case opcodetype.OP_NOP6: + case opcodetype.OP_NOP7: + case opcodetype.OP_NOP8: + case opcodetype.OP_NOP9: + case opcodetype.OP_NOP10: + break; + + case opcodetype.OP_IF: + case opcodetype.OP_NOTIF: + { + // if [statements] [else [statements]] endif + bool fValue = false; + if (fExec) + { + if (stack.Count() < 1) + { + return false; + } + IEnumerable vch = stacktop(ref stack, -1); + fValue = CastToBool(vch); + if (opcode == opcodetype.OP_NOTIF) + { + fValue = !fValue; + } + popstack(ref stack); + } + vfExec.Add(fValue); + } + break; + + case opcodetype.OP_ELSE: + { + int nExecCount = vfExec.Count(); + if (nExecCount == 0) + { + return false; + } + vfExec[nExecCount - 1] = !vfExec[nExecCount - 1]; + } + break; + + case opcodetype.OP_ENDIF: + { + int nExecCount = vfExec.Count(); + if (nExecCount == 0) + { + return false; + } + vfExec.RemoveAt(nExecCount - 1); + } + break; + + case opcodetype.OP_VERIFY: + { + // (true -- ) or + // (false -- false) and return + if (stack.Count() < 1) + { + return false; + } + + bool fValue = CastToBool(stacktop(ref stack, -1)); + if (fValue) + { + popstack(ref stack); + } + else + { + return false; + } + } + break; + + case opcodetype.OP_RETURN: + return false; + // + // Stack ops + // + case opcodetype.OP_TOALTSTACK: + { + if (stack.Count() < 1) + { + return false; + } + altstack.Add(stacktop(ref stack, -1)); + popstack(ref stack); + } + break; + + case opcodetype.OP_FROMALTSTACK: + { + if (altstack.Count() < 1) + { + return false; + } + stack.Add(stacktop(ref stack, -1)); + popstack(ref altstack); + } + break; + + case opcodetype.OP_2DROP: + { + // (x1 x2 -- ) + if (stack.Count() < 2) + { + return false; + } + popstack(ref stack); + popstack(ref stack); + } + break; + + case opcodetype.OP_2DUP: + { + // (x1 x2 -- x1 x2 x1 x2) + if (stack.Count() < 2) + { + return false; + } + IEnumerable vch1 = stacktop(ref stack, -2); + IEnumerable vch2 = stacktop(ref stack, -1); + stack.Add(vch1); + stack.Add(vch2); + } + break; + + case opcodetype.OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.Count() < 3) + { + return false; + } + 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; + + case opcodetype.OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.Count() < 4) + { + return false; + } + IEnumerable vch1 = stacktop(ref stack, -4); + IEnumerable vch2 = stacktop(ref stack, -3); + stack.Add(vch1); + stack.Add(vch2); + } + break; + + case opcodetype.OP_2ROT: + { + int nStackDepth = stack.Count(); + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (nStackDepth < 6) + { + return false; + } + IEnumerable vch1 = stacktop(ref stack, -6); + IEnumerable vch2 = stacktop(ref stack, -5); + stack.RemoveRange(nStackDepth - 6, 2); + stack.Add(vch1); + stack.Add(vch2); + } + break; + + case opcodetype.OP_2SWAP: + { + // (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; + + case opcodetype.OP_IFDUP: + { + // (x - 0 | x x) + if (stack.Count() < 1) + { + return false; + } + + IEnumerable vch = stacktop(ref stack, -1); + + if (CastToBool(vch)) + { + stack.Add(vch); + } + } + break; + + case opcodetype.OP_DEPTH: + { + // -- stacksize + BigInteger bn = new BigInteger((ushort)stack.Count()); + stack.Add(bn.ToByteArray()); + } + break; + + case opcodetype.OP_DROP: + { + // (x -- ) + if (stack.Count() < 1) + { + return false; + } + + popstack(ref stack); + } + break; + + case opcodetype.OP_DUP: + { + // (x -- x x) + if (stack.Count() < 1) + { + return false; + } + + IEnumerable vch = stacktop(ref stack, -1); + stack.Add(vch); + } + break; + + case opcodetype.OP_NIP: + { + // (x1 x2 -- x2) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } + + stack.RemoveAt(nStackDepth - 2); + } + break; + + case opcodetype.OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack.Count() < 2) + { + return false; + } + + IEnumerable vch = stacktop(ref stack, -2); + stack.Add(vch); + } + break; + + case opcodetype.OP_PICK: + case opcodetype.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) + { + return false; + } + + int n = (int)CastToBigInteger(stacktop(ref stack, -1)); + popstack(ref stack); + + if (n < 0 || n >= stack.Count()) + { + return false; + } + + IEnumerable vch = stacktop(ref stack, -n - 1); + if (opcode == opcodetype.OP_ROLL) + { + stack.RemoveAt(nStackDepth - n - 1); + } + + stack.Add(vch); + } + break; + + case opcodetype.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) + { + return false; + } + stack.Swap(nStackDepth - 3, nStackDepth - 2); + stack.Swap(nStackDepth - 2, nStackDepth - 1); + + } + break; + + case opcodetype.OP_SWAP: + { + // (x1 x2 -- x2 x1) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } + stack.Swap(nStackDepth - 2, nStackDepth - 1); + } + break; + + case opcodetype.OP_TUCK: + { + // (x1 x2 -- x2 x1 x2) + int nStackDepth = stack.Count(); + if (nStackDepth < 2) + { + return false; + } + IEnumerable vch = stacktop(ref stack, -1); + stack.Insert(nStackDepth - 2, vch); + } + break; + + + case opcodetype.OP_SIZE: + { + // (in -- in size) + if (stack.Count() < 1) + { + return false; + } + + BigInteger bnSize = new BigInteger((ushort)stacktop(ref stack, -1).Count()); + stack.Add(bnSize.ToByteArray()); + } + break; + + + // + // Bitwise logic + // + case opcodetype.OP_EQUAL: + case opcodetype.OP_EQUALVERIFY: + //case opcodetype.OP_NOTEQUAL: // use OP_NUMNOTEQUAL + { + // (x1 x2 - bool) + if (stack.Count() < 2) + { + return false; + } + + 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 == opcodetype.OP_NOTEQUAL) + // fEqual = !fEqual; + popstack(ref stack); + popstack(ref stack); + stack.Add(fEqual ? vchTrue : vchFalse); + + if (opcode == opcodetype.OP_EQUALVERIFY) + { + if (fEqual) + { + popstack(ref stack); + } + else + { + return false; + } + } + } + break; + + + // + // Numeric + // + case opcodetype.OP_1ADD: + case opcodetype.OP_1SUB: + case opcodetype.OP_NEGATE: + case opcodetype.OP_ABS: + case opcodetype.OP_NOT: + case opcodetype.OP_0NOTEQUAL: + { + // (in -- out) + if (stack.Count() < 1) + { + return false; + } + + BigInteger bn = CastToBigInteger(stacktop(ref stack, -1)); + switch (opcode) + { + case opcodetype.OP_1ADD: + bn = bn + 1; + break; + case opcodetype.OP_1SUB: + bn = bn - 1; + break; + case opcodetype.OP_NEGATE: + bn = -bn; + break; + case opcodetype.OP_ABS: + bn = BigInteger.Abs(bn); + break; + case opcodetype.OP_NOT: + bn = bn == 0 ? 1 : 0; + break; + case opcodetype.OP_0NOTEQUAL: + bn = bn != 0 ? 1 : 0; + break; + + default: + throw new StackMachineException("invalid opcode"); + break; + } + + popstack(ref stack); + stack.Add(bn.ToByteArray()); + } + break; + + case opcodetype.OP_ADD: + case opcodetype.OP_SUB: + case opcodetype.OP_BOOLAND: + case opcodetype.OP_BOOLOR: + case opcodetype.OP_NUMEQUAL: + case opcodetype.OP_NUMEQUALVERIFY: + case opcodetype.OP_NUMNOTEQUAL: + case opcodetype.OP_LESSTHAN: + case opcodetype.OP_GREATERTHAN: + case opcodetype.OP_LESSTHANOREQUAL: + case opcodetype.OP_GREATERTHANOREQUAL: + case opcodetype.OP_MIN: + case opcodetype.OP_MAX: + { + // (x1 x2 -- out) + if (stack.Count() < 2) + { + return false; + } + + BigInteger bn1 = CastToBigInteger(stacktop(ref stack, -2)); + BigInteger bn2 = CastToBigInteger(stacktop(ref stack, -1)); + BigInteger bn = 0; + + switch (opcode) + { + case opcodetype.OP_ADD: + bn = bn1 + bn2; + break; + case opcodetype.OP_SUB: + bn = bn1 - bn2; + break; + case opcodetype.OP_BOOLAND: + bn = (bn1 != 0 && bn2 != 0) ? 1 : 0; + break; + case opcodetype.OP_BOOLOR: + bn = (bn1 != 0 || bn2 != 0) ? 1 : 0; + break; + case opcodetype.OP_NUMEQUAL: + bn = (bn1 == bn2) ? 1 : 0; + break; + case opcodetype.OP_NUMEQUALVERIFY: + bn = (bn1 == bn2) ? 1 : 0; + break; + case opcodetype.OP_NUMNOTEQUAL: + bn = (bn1 != bn2) ? 1 : 0; + break; + case opcodetype.OP_LESSTHAN: + bn = (bn1 < bn2) ? 1 : 0; + break; + case opcodetype.OP_GREATERTHAN: + bn = (bn1 > bn2) ? 1 : 0; + break; + case opcodetype.OP_LESSTHANOREQUAL: + bn = (bn1 <= bn2) ? 1 : 0; + break; + case opcodetype.OP_GREATERTHANOREQUAL: + bn = (bn1 >= bn2) ? 1 : 0; + break; + case opcodetype.OP_MIN: + bn = (bn1 < bn2 ? bn1 : bn2); + break; + case opcodetype.OP_MAX: + bn = (bn1 > bn2 ? bn1 : bn2); + break; + + default: + throw new StackMachineException("invalid opcode"); + break; + } + + popstack(ref stack); + popstack(ref stack); + stack.Add(bn.ToByteArray()); + + if (opcode == opcodetype.OP_NUMEQUALVERIFY) + { + if (CastToBool(stacktop(ref stack, -1))) + { + popstack(ref stack); + } + else + { + return false; + } + } + } + break; + + case opcodetype.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 ? vchTrue : vchFalse); + } + break; + + + // + // Crypto + // + case opcodetype.OP_RIPEMD160: + case opcodetype.OP_SHA1: + case opcodetype.OP_SHA256: + case opcodetype.OP_HASH160: + case opcodetype.OP_HASH256: + { + // (in -- hash) + if (stack.Count() < 1) + return false; + IEnumerable vch = stacktop(ref stack, -1); + IEnumerable vchHash = null; + if (opcode == opcodetype.OP_RIPEMD160) + { + RIPEMD160 hash = RIPEMD160.Compute160(vch); + vchHash = hash.hashBytes; + } + else if (opcode == opcodetype.OP_SHA1) + { + SHA1 hash = SHA1.Compute1(vch); + vchHash = hash.hashBytes; + } + else if (opcode == opcodetype.OP_SHA256) + { + SHA256 hash = SHA256.Compute256(vch); + vchHash = hash.hashBytes; + } + else if (opcode == opcodetype.OP_HASH160) + { + Hash160 hash = Hash160.Compute160(vch); + vchHash = hash.hashBytes; + } + else if (opcode == opcodetype.OP_HASH256) + { + Hash256 hash = Hash256.Compute256(vch); + vchHash = hash.hashBytes; + } + popstack(ref stack); + stack.Add(vchHash); + } + break; + + case opcodetype.OP_CODESEPARATOR: + { + // Hash starts after the code separator + pbegincodehash = pc; + } + break; + + case opcodetype.OP_CHECKSIG: + case opcodetype.OP_CHECKSIGVERIFY: + { + // (sig pubkey -- bool) + if (stack.Count() < 2) + { + return false; + } + + IList sigBytes = stacktop(ref stack, -2).ToList(); + IList pubkeyBytes = stacktop(ref stack, -1).ToList(); + + // Subset of script starting at the most recent codeseparator + CScript scriptCode = new CScript(script.Bytes.Skip(pbegincodehash.CurrentIndex)); + + // There's no way for a signature to sign itself + scriptCode.RemovePattern(sigBytes); + + 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(fSuccess ? vchTrue : vchFalse); + if (opcode == opcodetype.OP_CHECKSIGVERIFY) + { + if (fSuccess) + { + popstack(ref stack); + } + else + { + return false; + } + } + } + break; + + case opcodetype.OP_CHECKMULTISIG: + case opcodetype.OP_CHECKMULTISIGVERIFY: + { + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + + int i = 1; + if (stack.Count() < i) + { + return false; + } + + int nKeysCount = (int)CastToBigInteger(stacktop(ref stack, -i)); + if (nKeysCount < 0 || nKeysCount > 20) + { + return false; + } + nOpCount += nKeysCount; + if (nOpCount > 201) + { + return false; + } + int ikey = ++i; + i += nKeysCount; + if (stack.Count() < i) + { + return false; + } + + int nSigsCount = (int)CastToBigInteger(stacktop(ref stack, -i)); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + { + return false; + } + int isig = ++i; + i += nSigsCount; + if (stack.Count() < i) + { + return false; + } + + // Subset of script starting at the most recent codeseparator + CScript scriptCode = new CScript(script.Bytes.Skip(pbegincodehash.CurrentIndex)); + + // 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()); + } + + bool fSuccess = true; + while (fSuccess && nSigsCount > 0) + { + IList sigBytes = stacktop(ref stack, -isig).ToList(); + IList pubKeyBytes = stacktop(ref stack, -ikey).ToList(); + + // Check signature + bool fOk = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubKeyBytes.ToList(), 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; + } + } + + while (i-- > 1) + { + popstack(ref stack); + } + + // 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 ? vchTrue : vchFalse); + + if (opcode == opcodetype.OP_CHECKMULTISIGVERIFY) + { + if (fSuccess) + { + popstack(ref stack); + } + else + { + return false; + } + } + } + break; + + default: + return false; + } + + // Size limits + if (stack.Count() + altstack.Count() > 1000) + { + return false; + } + } + + + if (vfExec.Count() == 0) + { + return false; + } + + return true; + } + + + static bool IsCanonicalPubKey(IList pubKeyBytes, int flags) + { + if ((flags & (int)scriptflag.SCRIPT_VERIFY_STRICTENC) == 0) + return true; + + if (pubKeyBytes.Count() < 33) + return false; // Non-canonical public key: too short + if (pubKeyBytes[0] == 0x04) + { + if (pubKeyBytes.Count() != 65) + return false; // Non-canonical public key: invalid length for uncompressed key + } + else if (pubKeyBytes[0] == 0x02 || pubKeyBytes[0] == 0x03) + { + if (pubKeyBytes.Count() != 33) + return false; // Non-canonical public key: invalid length for compressed key + } + else + { + return false; // Non-canonical public key: compressed nor uncompressed + } + return true; + } + + static bool IsCanonicalSignature(IList sigBytes, int flags) + { + // STUB + + return true; + } + + + static bool CheckSig(IList sigBytes, IList pubKeyBytes, CScript scriptCode, CTransaction txTo, int nIn, int nHashType, int flags) + { + // STUB + return true; + } + +}; }