From 7356060fc94799245afa5708a1cd7ea893f17975 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Sun, 23 Aug 2015 01:21:45 +0300 Subject: [PATCH 1/1] Document SignatureHash and EvalScript methods, a little refactoring of hashing instructions handling code --- Novacoin/ScriptCode.cs | 117 +++++++++++++++++++++++++++++------------------ 1 files changed, 72 insertions(+), 45 deletions(-) diff --git a/Novacoin/ScriptCode.cs b/Novacoin/ScriptCode.cs index 8f53feb..04ca7c4 100644 --- a/Novacoin/ScriptCode.cs +++ b/Novacoin/ScriptCode.cs @@ -902,7 +902,15 @@ 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 Hash256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType) { if (nIn >= txTo.vin.Length) { @@ -911,18 +919,19 @@ namespace Novacoin throw new ArgumentOutOfRangeException("nIn", sb.ToString()); } + // Init a copy of transaction CTransaction 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.RemovePattern(new byte[] { (byte)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) @@ -981,6 +990,14 @@ namespace Novacoin return Hash256.Compute256(b); } + // + // 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 + /// public class StackMachineException : Exception { public StackMachineException() @@ -998,12 +1015,6 @@ 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 /// @@ -1086,6 +1097,16 @@ namespace Novacoin return new BigInteger(value.ToArray()); } + /// + /// Execution of script + /// + /// + /// Script to execute + /// Transaction instance + /// Input number + /// Signature checking flags + /// Hash type flag + /// static bool EvalScript(ref List> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType) { instruction opcode; @@ -1171,7 +1192,7 @@ namespace Novacoin case instruction.OP_16: { // ( -- value) - BigInteger bn = DecodeOP_N(opcode); + BigInteger bn = DecodeOP_N(opcode, true); stack.Add(bn.ToByteArray()); } break; @@ -1192,8 +1213,8 @@ namespace Novacoin case instruction.OP_NOP10: { // Just do nothing - break; } + break; // // Control @@ -1732,7 +1753,6 @@ namespace Novacoin } break; - // // Crypto // @@ -1744,36 +1764,32 @@ namespace Novacoin { // (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; + return false; } - else if (opcode == instruction.OP_HASH256) + Hash hash = null; + IEnumerable data = stacktop(ref stack, -1); + + switch (opcode) { - Hash256 hash = Hash256.Compute256(vch); - vchHash = hash.hashBytes; + case instruction.OP_HASH160: + hash = Hash160.Compute160(data); + break; + case instruction.OP_HASH256: + hash = Hash256.Compute256(data); + break; + case instruction.OP_SHA1: + hash = SHA1.Compute1(data); + break; + case instruction.OP_SHA256: + hash = SHA256.Compute256(data); + break; + case instruction.OP_RIPEMD160: + hash = RIPEMD160.Compute160(data); + break; } popstack(ref stack); - stack.Add(vchHash); + stack.Add(hash.hashBytes); } break; @@ -1985,7 +2001,18 @@ namespace Novacoin 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 + /// + static bool CheckSig(IList sigBytes, IList pubkeyBytes, CScript script, CTransaction txTo, int nIn, int nHashType, int flags) { CPubKey pubkey; @@ -1993,7 +2020,7 @@ namespace Novacoin { // Trying to initialize the public key instance - pubkey = new CPubKey(vchPubKey); + pubkey = new CPubKey(pubkeyBytes); } catch (Exception) { @@ -2007,7 +2034,7 @@ namespace Novacoin return false; } - if (vchSig.Count == 0) + if (sigBytes.Count == 0) { return false; } @@ -2015,19 +2042,19 @@ 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); + sigBytes.RemoveAt(sigBytes.Count - 1); - Hash256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + Hash256 sighash = SignatureHash(script, txTo, nIn, nHashType); - if (!pubkey.VerifySignature(sighash, vchSig)) + if (!pubkey.VerifySignature(sighash, sigBytes)) { return false; } -- 1.7.1