//https://en.bitcoin.it/wiki/OP_CHECKSIG public uint256 SignatureHash(Transaction txTo, int nIn, SigHash nHashType) { if(nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return uint256.One; } // Check for invalid use of SIGHASH_SINGLE if(nHashType == SigHash.Single) { if(nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return uint256.One; } } var scriptCopy = new Script(_Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes()); //Set all TxIn script to empty string foreach(var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; var hashType = nHashType & (SigHash)31; if(hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } else if(hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for(var i = 0 ; i < txCopy.Outputs.Count ; i++) { if(i == nIn) continue; txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } if((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var ms = new MemoryStream(); var bitcoinStream = new BitcoinStream(ms, true); txCopy.ReadWrite(bitcoinStream); bitcoinStream.ReadWrite((uint)nHashType); var hashed = ms.ToArray(); return Hashes.Hash256(hashed); }
//https://en.bitcoin.it/wiki/OP_CHECKSIG public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (sigversion == HashVersion.Witness) { if (amount == null) { throw new ArgumentException("The amount of the output being signed must be provided", "amount"); } uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts(txTo) : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence(txTo) : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs(txTo) : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < txTo.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(txTo.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(txTo.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(txTo.Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(amount.Satoshi); sss.ReadWrite((uint)txTo.Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(txTo.LockTime); // Sighash type sss.ReadWrite((uint)nHashType); return(GetHash(sss)); } if (nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return(uint256.One); } // Check for invalid use of SIGHASH_SINGLE if (nHashType == SigHash.Single) { if (nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return(uint256.One); } } var scriptCopy = new Script(scriptCode._Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; var hashType = nHashType & (SigHash)31; if (hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); return(GetHash(stream)); }
//https://en.bitcoin.it/wiki/OP_CHECKSIG public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount = null, HashVersion sigversion = HashVersion.Original) { if(sigversion == HashVersion.Witness) { if(amount == null) throw new ArgumentException("The amount of the output being signed must be provided", "amount"); uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if((nHashType & SigHash.AnyoneCanPay) == 0) { BitcoinStream ss = CreateHashWriter(sigversion); foreach(var input in txTo.Inputs) { ss.ReadWrite(input.PrevOut); } hashPrevouts = GetHash(ss); // TODO: cache this value for all signatures in a transaction } if((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { BitcoinStream ss = CreateHashWriter(sigversion); foreach(var input in txTo.Inputs) { ss.ReadWrite((uint)input.Sequence); } hashSequence = GetHash(ss); // TODO: cache this value for all signatures in a transaction } if(((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { BitcoinStream ss = CreateHashWriter(sigversion); foreach(var txout in txTo.Outputs) { ss.ReadWrite(txout); } hashOutputs = GetHash(ss); // TODO: cache this value for all signatures in a transaction } else if(((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < txTo.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(txTo.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(txTo.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(txTo.Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(amount.Satoshi); sss.ReadWrite((uint)txTo.Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(txTo.LockTime); // Sighash type sss.ReadWrite((uint)nHashType); return GetHash(sss); } if(nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return uint256.One; } // Check for invalid use of SIGHASH_SINGLE if(nHashType == SigHash.Single) { if(nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return uint256.One; } } var scriptCopy = new Script(scriptCode._Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes()); //Set all TxIn script to empty string foreach(var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; var hashType = nHashType & (SigHash)31; if(hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } else if(hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for(var i = 0 ; i < txCopy.Outputs.Count ; i++) { if(i == nIn) continue; txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } if((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); return GetHash(stream); }
//https://en.bitcoin.it/wiki/OP_CHECKSIG public uint256 SignatureHash(Transaction txTo, int nIn, SigHash nHashType) { if (nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return(1); } // Check for invalid use of SIGHASH_SINGLE if (nHashType == SigHash.Single) { if (nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return(1); } } var scriptCopy = new Script(_Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (((int)nHashType & 31) == (int)SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero for (int i = 0; i < txCopy.Inputs.Count; i++) { if (i == nIn) { continue; } txCopy.Inputs[i].Sequence = 0; } } if (((int)nHashType & 31) == (int)SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. var remainingOut = txCopy.Outputs.Take(nIn + 1).ToArray(); txCopy.Outputs.Clear(); txCopy.Outputs.AddRange(remainingOut); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (int i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } for (int i = 0; i < txCopy.Inputs.Count; i++) { //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. if (i == nIn) { continue; } txCopy.Inputs[i].Sequence = 0; } } if (((int)nHashType & (int)SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode MemoryStream ms = new MemoryStream(); BitcoinStream bitcoinStream = new BitcoinStream(ms, true); txCopy.ReadWrite(bitcoinStream); bitcoinStream.ReadWrite((uint)nHashType); var hashed = ms.ToArray(); return(Hashes.Hash256(hashed)); }
public bool EvalScript(Script s, Transaction txTo, int nIn) { var script = s.CreateReader(); int pend = (int)script.Inner.Length; int pbegincodehash = 0; Stack <bool> vfExec = new Stack <bool>(); Stack <byte[]> altstack = new Stack <byte[]>(); Op opcode = null; if (s.Length > 10000) { return(false); } int nOpCount = 0; try { while ((opcode = script.Read()) != null) { bool fExec = vfExec.All(o => o); //!count(vfExec.begin(), vfExec.end(), false); // // Read instruction // if (opcode.PushData != null && opcode.PushData.Length > 520) { return(false); } // Note how OP_RESERVED does not count towards the opcode limit. if (opcode.Code > OpcodeType.OP_16 && ++nOpCount > 201) { return(false); } if (opcode.Code == OpcodeType.OP_CAT || opcode.Code == OpcodeType.OP_SUBSTR || opcode.Code == OpcodeType.OP_LEFT || opcode.Code == OpcodeType.OP_RIGHT || opcode.Code == OpcodeType.OP_INVERT || opcode.Code == OpcodeType.OP_AND || opcode.Code == OpcodeType.OP_OR || opcode.Code == OpcodeType.OP_XOR || opcode.Code == OpcodeType.OP_2MUL || opcode.Code == OpcodeType.OP_2DIV || opcode.Code == OpcodeType.OP_MUL || opcode.Code == OpcodeType.OP_DIV || opcode.Code == OpcodeType.OP_MOD || opcode.Code == OpcodeType.OP_LSHIFT || opcode.Code == OpcodeType.OP_RSHIFT) { return(false); // Disabled opcodes. } if (fExec && opcode.PushData != null) { _Stack.Push(opcode.PushData); } else if (fExec || (OpcodeType.OP_IF <= opcode.Code && opcode.Code <= OpcodeType.OP_ENDIF)) { switch (opcode.Code) { // // 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.Code - (int)(OpcodeType.OP_1 - 1)); _Stack.Push(Utils.BigIntegerToBytes(bn)); } 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: { // <expression> if [statements] [else [statements]] endif bool fValue = false; if (fExec) { if (_Stack.Count < 1) { return(false); } var vch = top(_Stack, -1); fValue = CastToBool(vch); if (opcode.Code == OpcodeType.OP_NOTIF) { fValue = !fValue; } _Stack.Pop(); } vfExec.Push(fValue); } break; case OpcodeType.OP_ELSE: { if (vfExec.Count == 0) { return(false); } var v = vfExec.Pop(); vfExec.Push(!v); //vfExec.Peek() = !vfExec.Peek(); } break; case OpcodeType.OP_ENDIF: { if (vfExec.Count == 0) { return(false); } vfExec.Pop(); } break; case OpcodeType.OP_VERIFY: { // (true -- ) or // (false -- false) and return if (_Stack.Count < 1) { return(false); } bool fValue = CastToBool(top(_Stack, -1)); if (fValue) { _Stack.Pop(); } else { return(false); } } break; case OpcodeType.OP_RETURN: { return(false); } // // Stack ops // case OpcodeType.OP_TOALTSTACK: { if (_Stack.Count < 1) { return(false); } altstack.Push(top(_Stack, -1)); _Stack.Pop(); } break; case OpcodeType.OP_FROMALTSTACK: { if (altstack.Count < 1) { return(false); } _Stack.Push(top(altstack, -1)); altstack.Pop(); } break; case OpcodeType.OP_2DROP: { // (x1 x2 -- ) if (_Stack.Count < 2) { return(false); } _Stack.Pop(); _Stack.Pop(); } break; case OpcodeType.OP_2DUP: { // (x1 x2 -- x1 x2 x1 x2) if (_Stack.Count < 2) { return(false); } var vch1 = top(_Stack, -2); var vch2 = top(_Stack, -1); _Stack.Push(vch1); _Stack.Push(vch2); } break; case OpcodeType.OP_3DUP: { // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) if (_Stack.Count < 3) { return(false); } var vch1 = top(_Stack, -3); var vch2 = top(_Stack, -2); var vch3 = top(_Stack, -1); _Stack.Push(vch1); _Stack.Push(vch2); _Stack.Push(vch3); } break; case OpcodeType.OP_2OVER: { // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) if (_Stack.Count < 4) { return(false); } var vch1 = top(_Stack, -4); var vch2 = top(_Stack, -3); _Stack.Push(vch1); _Stack.Push(vch2); } break; case OpcodeType.OP_2ROT: { // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) if (_Stack.Count < 6) { return(false); } var vch1 = top(_Stack, -6); var vch2 = top(_Stack, -5); erase(ref _Stack, _Stack.Count - 6, _Stack.Count - 4); _Stack.Push(vch1); _Stack.Push(vch2); } break; case OpcodeType.OP_2SWAP: { // (x1 x2 x3 x4 -- x3 x4 x1 x2) if (_Stack.Count < 4) { return(false); } swap(ref _Stack, -4, -2); swap(ref _Stack, -3, -1); } break; case OpcodeType.OP_IFDUP: { // (x - 0 | x x) if (_Stack.Count < 1) { return(false); } var vch = top(_Stack, -1); if (CastToBool(vch)) { _Stack.Push(vch); } } break; case OpcodeType.OP_DEPTH: { // -- stacksize BigInteger bn = new BigInteger(_Stack.Count); _Stack.Push(Utils.BigIntegerToBytes(bn)); } break; case OpcodeType.OP_DROP: { // (x -- ) if (_Stack.Count < 1) { return(false); } _Stack.Pop(); } break; case OpcodeType.OP_DUP: { // (x -- x x) if (_Stack.Count < 1) { return(false); } var vch = top(_Stack, -1); _Stack.Push(vch); } break; case OpcodeType.OP_NIP: { // (x1 x2 -- x2) if (_Stack.Count < 2) { return(false); } erase(ref _Stack, _Stack.Count - 2); } break; case OpcodeType.OP_OVER: { // (x1 x2 -- x1 x2 x1) if (_Stack.Count < 2) { return(false); } var vch = top(_Stack, -2); _Stack.Push(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) if (_Stack.Count < 2) { return(false); } int n = (int)CastToBigNum(top(_Stack, -1)); _Stack.Pop(); if (n < 0 || n >= _Stack.Count) { return(false); } var vch = top(_Stack, -n - 1); if (opcode.Code == OpcodeType.OP_ROLL) { erase(ref _Stack, _Stack.Count - n - 1); } _Stack.Push(vch); } break; case OpcodeType.OP_ROT: { // (x1 x2 x3 -- x2 x3 x1) // x2 x1 x3 after first swap // x2 x3 x1 after second swap if (_Stack.Count < 3) { return(false); } swap(ref _Stack, -3, -2); swap(ref _Stack, -2, -1); } break; case OpcodeType.OP_SWAP: { // (x1 x2 -- x2 x1) if (_Stack.Count < 2) { return(false); } swap(ref _Stack, -2, -1); } break; case OpcodeType.OP_TUCK: { // (x1 x2 -- x2 x1 x2) if (_Stack.Count < 2) { return(false); } var vch = top(_Stack, -1); insert(ref _Stack, _Stack.Count - 2, vch); } break; case OpcodeType.OP_SIZE: { // (in -- in size) if (_Stack.Count < 1) { return(false); } BigInteger bn = new BigInteger(top(_Stack, -1).Length); _Stack.Push(Utils.BigIntegerToBytes(bn)); } break; // // Bitwise logic // case OpcodeType.OP_EQUAL: case OpcodeType.OP_EQUALVERIFY: //case OpcodeType.OP_NOTEQUAL: // use OpcodeType.OP_NUMNOTEQUAL { // (x1 x2 - bool) if (_Stack.Count < 2) { return(false); } var vch1 = top(_Stack, -2); var vch2 = top(_Stack, -1); bool fEqual = Utils.ArrayEqual(vch1, vch2); // OpcodeType.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; _Stack.Pop(); _Stack.Pop(); _Stack.Push(fEqual ? vchTrue : vchFalse); if (opcode.Code == OpcodeType.OP_EQUALVERIFY) { if (fEqual) { _Stack.Pop(); } 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); } var bn = CastToBigNum(top(_Stack, -1)); switch (opcode.Code) { case OpcodeType.OP_1ADD: bn += BigInteger.One; break; case OpcodeType.OP_1SUB: bn -= BigInteger.One; break; case OpcodeType.OP_NEGATE: bn = -bn; break; case OpcodeType.OP_ABS: if (bn < BigInteger.Zero) { bn = -bn; } break; case OpcodeType.OP_NOT: bn = CastToBigNum(bn == BigInteger.Zero); break; case OpcodeType.OP_0NOTEQUAL: bn = CastToBigNum(bn != BigInteger.Zero); break; default: throw new NotSupportedException("invalid opcode"); } _Stack.Pop(); _Stack.Push(Utils.BigIntegerToBytes(bn)); } 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); } var bn1 = CastToBigNum(top(_Stack, -2)); var bn2 = CastToBigNum(top(_Stack, -1)); BigInteger bn; switch (opcode.Code) { case OpcodeType.OP_ADD: bn = bn1 + bn2; break; case OpcodeType.OP_SUB: bn = bn1 - bn2; break; case OpcodeType.OP_BOOLAND: bn = CastToBigNum(bn1 != BigInteger.Zero && bn2 != BigInteger.Zero); break; case OpcodeType.OP_BOOLOR: bn = CastToBigNum(bn1 != BigInteger.Zero || bn2 != BigInteger.Zero); break; case OpcodeType.OP_NUMEQUAL: bn = CastToBigNum(bn1 == bn2); break; case OpcodeType.OP_NUMEQUALVERIFY: bn = CastToBigNum(bn1 == bn2); break; case OpcodeType.OP_NUMNOTEQUAL: bn = CastToBigNum(bn1 != bn2); break; case OpcodeType.OP_LESSTHAN: bn = CastToBigNum(bn1 < bn2); break; case OpcodeType.OP_GREATERTHAN: bn = CastToBigNum(bn1 > bn2); break; case OpcodeType.OP_LESSTHANOREQUAL: bn = CastToBigNum(bn1 <= bn2); break; case OpcodeType.OP_GREATERTHANOREQUAL: bn = CastToBigNum(bn1 >= bn2); break; case OpcodeType.OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; case OpcodeType.OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; default: throw new NotSupportedException("invalid opcode"); } _Stack.Pop(); _Stack.Pop(); _Stack.Push(Utils.BigIntegerToBytes(bn)); if (opcode.Code == OpcodeType.OP_NUMEQUALVERIFY) { if (CastToBool(top(_Stack, -1))) { _Stack.Pop(); } else { return(false); } } } break; case OpcodeType.OP_WITHIN: { // (x min max -- out) if (_Stack.Count < 3) { return(false); } var bn1 = CastToBigNum(top(_Stack, -3)); var bn2 = CastToBigNum(top(_Stack, -2)); var bn3 = CastToBigNum(top(_Stack, -1)); bool fValue = (bn2 <= bn1 && bn1 < bn3); _Stack.Pop(); _Stack.Pop(); _Stack.Pop(); _Stack.Push(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); } var vch = top(_Stack, -1); byte[] vchHash = null; //((opcode == OpcodeType.OP_RIPEMD160 || opcode == OpcodeType.OP_SHA1 || opcode == OpcodeType.OP_HASH160) ? 20 : 32); if (opcode.Code == OpcodeType.OP_RIPEMD160) { vchHash = Hashes.RIPEMD160(vch, vch.Length); } else if (opcode.Code == OpcodeType.OP_SHA1) { vchHash = Hashes.SHA1(vch, vch.Length); } else if (opcode.Code == OpcodeType.OP_SHA256) { vchHash = Hashes.SHA256(vch, vch.Length); } else if (opcode.Code == OpcodeType.OP_HASH160) { vchHash = Hashes.Hash160(vch, vch.Length).ToBytes(); } else if (opcode.Code == OpcodeType.OP_HASH256) { vchHash = Hashes.Hash256(vch, vch.Length).ToBytes(); } _Stack.Pop(); _Stack.Push(vchHash); } break; case OpcodeType.OP_CODESEPARATOR: { // Hash starts after the code separator pbegincodehash = (int)script.Inner.Position; } break; case OpcodeType.OP_CHECKSIG: case OpcodeType.OP_CHECKSIGVERIFY: { // (sig pubkey -- bool) if (_Stack.Count < 2) { return(false); } var vchSig = top(_Stack, -2); var vchPubKey = top(_Stack, -1); ////// debug print //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); // Subset of script starting at the most recent codeseparator var scriptCode = new Script(s._Script.Skip(pbegincodehash).ToArray()); // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(vchSig); bool fSuccess = IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey) && CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn); _Stack.Pop(); _Stack.Pop(); _Stack.Push(fSuccess ? vchTrue : vchFalse); if (opcode.Code == OpcodeType.OP_CHECKSIGVERIFY) { if (fSuccess) { _Stack.Pop(); } 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 ((int)_Stack.Count < i) { return(false); } int nKeysCount = (int)CastToBigNum(top(_Stack, -i)); if (nKeysCount < 0 || nKeysCount > 20) { return(false); } nOpCount += nKeysCount; if (nOpCount > 201) { return(false); } int ikey = ++i; i += nKeysCount; if ((int)_Stack.Count < i) { return(false); } int nSigsCount = (int)CastToBigNum(top(_Stack, -i)); if (nSigsCount < 0 || nSigsCount > nKeysCount) { return(false); } int isig = ++i; i += nSigsCount; if ((int)_Stack.Count < i) { return(false); } // Subset of script starting at the most recent codeseparator Script scriptCode = new Script(s._Script.Skip(pbegincodehash).ToArray()); // Drop the signatures, since there's no way for a signature to sign itself for (int k = 0; k < nSigsCount; k++) { var vchSig = top(_Stack, -isig - k); scriptCode.FindAndDelete(vchSig); } bool fSuccess = true; while (fSuccess && nSigsCount > 0) { var vchSig = top(_Stack, -isig); var vchPubKey = top(_Stack, -ikey); // Check signature bool fOk = IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey) && CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn); 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) { _Stack.Pop(); } // 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 (((ScriptVerify & ScriptVerify.NullDummy) != 0) && top(_Stack, -1).Length != 0) { return(Utils.error("CHECKMULTISIG dummy argument not null")); } _Stack.Pop(); _Stack.Push(fSuccess ? vchTrue : vchFalse); if (opcode.Code == OpcodeType.OP_CHECKMULTISIGVERIFY) { if (fSuccess) { _Stack.Pop(); } else { return(false); } } } break; default: return(false); } } // Size limits if (_Stack.Count + altstack.Count > 1000) { return(false); } } } catch (Exception ex) { Utils.error("Error in EvalScript " + ex.Message + " on opcode " + opcode); return(false); } if (vfExec.Count != 0) { return(false); } return(true); }