public bool Evaluate(IEnumerable <object> scriptCommands, byte[]?sigHash = null) { var commands = new Queue <object>(scriptCommands ?? throw new ArgumentNullException(nameof(scriptCommands))); var stack = new ScriptStack(); var altStack = new ScriptStack(); while (commands.Count > 0) { if (IsPayToScriptHash(commands)) { var redeemScript = DecodeRedeemScript((byte[])commands.Dequeue()); commands = new Queue <object>(redeemScript.Commands); } var command = commands.Dequeue(); if (command is byte[] bytes) { stack.Push(bytes); } else if (command is OpCode opCode) { var result = false; try { switch (GetOpertationType(opCode)) { case OpertationType.Stack: result = Evaluate(opCode, stack); break; case OpertationType.SigHash: result = Evaluate(opCode, stack, sigHash ?? throw new ArgumentNullException(nameof(sigHash))); break; case OpertationType.Commands: result = Evaluate(opCode, stack, commands); break; case OpertationType.AltStack: result = Evaluate(opCode, stack, altStack); break; } } catch (FormatException ex) { if (throwOnFailure) { throw new VerificationException("Bad format", ex); } } catch (InvalidOperationException ex) { if (throwOnFailure) { throw new VerificationException("Bad operation", ex); } } if (!result) { if (throwOnFailure) { throw new VerificationException("Script evaluation false"); } return(false); } } else { throw new InvalidOperationException("Invalid command type"); } } return(stack.Count > 0 && stack.Pop().Length > 0); }
bool Evaluate(OpCode opCode, ScriptStack stack) { switch (opCode) { case OpCode.OP_0: return(stack.Push(0)); case OpCode.OP_1NEGATE: return(stack.Push(-1)); case OpCode.OP_1: case OpCode.OP_2: case OpCode.OP_3: case OpCode.OP_4: case OpCode.OP_5: case OpCode.OP_6: case OpCode.OP_7: case OpCode.OP_8: case OpCode.OP_9: case OpCode.OP_10: case OpCode.OP_11: case OpCode.OP_12: case OpCode.OP_13: case OpCode.OP_14: case OpCode.OP_15: case OpCode.OP_16: return(stack.Push((int)opCode - (int)OpCode.OP_1 + 1)); case OpCode.OP_NOP: case OpCode.OP_NOP1: case OpCode.OP_NOP4: case OpCode.OP_NOP5: case OpCode.OP_NOP6: case OpCode.OP_NOP7: case OpCode.OP_NOP8: case OpCode.OP_NOP9: case OpCode.OP_NOP10: return(true); case OpCode.OP_DUP: return(stack.Push(stack.Peek())); case OpCode.OP_2DUP: var d1 = stack.Pop(); var d2 = stack.Peek(); stack.Push(d1); stack.Push(d2); return(stack.Push(d1)); case OpCode.OP_SWAP: var s1 = stack.Pop(); var s2 = stack.Pop(); stack.Push(s1); return(stack.Push(s2)); case OpCode.OP_VERIFY: return(stack.Pop().Length > 0); case OpCode.OP_EQUALVERIFY: return(stack.Pop().SequenceEqual(stack.Pop())); case OpCode.OP_HASH160: return(stack.Push(Cipher.Hash160(stack.Pop()))); case OpCode.OP_SHA1: return(stack.Push(Cipher.Sha1(stack.Pop()))); case OpCode.OP_ADD: return(stack.Push(stack.PopInt() + stack.PopInt())); case OpCode.OP_MUL: return(stack.Push(stack.PopInt() * stack.PopInt())); case OpCode.OP_EQUAL: return(stack.Push(stack.Pop().SequenceEqual(stack.Pop()) ? 1 : 0)); case OpCode.OP_NOT: return(stack.Push(stack.Pop().Length == 0 ? 1 : 0)); default: throw new InvalidOperationException("Unknown operation: " + opCode); } }