private void ExecuteOp(OpCode opcode, ExecutionContext context) { LastOpCode = opcode; if (opcode > OpCode.PUSH16 && opcode != OpCode.RET && context.PushOnly) { State |= VMState.FAULT; return; } if (opcode >= OpCode.PUSHBYTES1 && opcode <= OpCode.PUSHBYTES75) { EvaluationStack.Push(context.OpReader.ReadBytes((byte)opcode)); } else { switch (opcode) { // Push value case OpCode.PUSH0: EvaluationStack.Push(new byte[0]); break; case OpCode.PUSHDATA1: EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadByte())); break; case OpCode.PUSHDATA2: EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadUInt16())); break; case OpCode.PUSHDATA4: EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadInt32())); break; case OpCode.PUSHM1: case OpCode.PUSH1: case OpCode.PUSH2: case OpCode.PUSH3: case OpCode.PUSH4: case OpCode.PUSH5: case OpCode.PUSH6: case OpCode.PUSH7: case OpCode.PUSH8: case OpCode.PUSH9: case OpCode.PUSH10: case OpCode.PUSH11: case OpCode.PUSH12: case OpCode.PUSH13: case OpCode.PUSH14: case OpCode.PUSH15: case OpCode.PUSH16: EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + 1); break; // Control case OpCode.NOP: break; case OpCode.JMP: case OpCode.JMPIF: case OpCode.JMPIFNOT: { int offset = context.OpReader.ReadInt16(); offset = context.InstructionPointer + offset - 3; if (offset < 0 || offset > context.Script.Length) { State |= VMState.FAULT; return; } bool fValue = true; if (opcode > OpCode.JMP) { fValue = EvaluationStack.Pop().GetBoolean(); if (opcode == OpCode.JMPIFNOT) { fValue = !fValue; } } if (fValue) { context.InstructionPointer = offset; } } break; case OpCode.CALL: InvocationStack.Push(context.Clone()); context.InstructionPointer += 2; ExecuteOp(OpCode.JMP, CurrentContext); break; case OpCode.RET: InvocationStack.Pop().Dispose(); if (InvocationStack.Count == 0) { State |= VMState.HALT; } break; case OpCode.APPCALL: case OpCode.TAILCALL: { if (table == null) { State |= VMState.FAULT; return; } byte[] script_hash = context.OpReader.ReadBytes(20); byte[] script = table.GetScript(script_hash); if (script == null) { State |= VMState.FAULT; return; } if (opcode == OpCode.TAILCALL) { InvocationStack.Pop().Dispose(); } LoadScript(script); } break; case OpCode.SYSCALL: if (!service.Invoke(Encoding.ASCII.GetString(context.OpReader.ReadVarBytes(252)), this)) { State |= VMState.FAULT; } break; // Stack ops case OpCode.DUPFROMALTSTACK: EvaluationStack.Push(AltStack.Peek()); break; case OpCode.TOALTSTACK: AltStack.Push(EvaluationStack.Pop()); break; case OpCode.FROMALTSTACK: EvaluationStack.Push(AltStack.Pop()); break; case OpCode.XDROP: { int n = (int)EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } EvaluationStack.Remove(n); } break; case OpCode.XSWAP: { int n = (int)EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } if (n == 0) { break; } StackItem xn = EvaluationStack.Peek(n); EvaluationStack.Set(n, EvaluationStack.Peek()); EvaluationStack.Set(0, xn); } break; case OpCode.XTUCK: { int n = (int)EvaluationStack.Pop().GetBigInteger(); if (n <= 0) { State |= VMState.FAULT; return; } EvaluationStack.Insert(n, EvaluationStack.Peek()); } break; case OpCode.DEPTH: EvaluationStack.Push(EvaluationStack.Count); break; case OpCode.DROP: EvaluationStack.Pop(); break; case OpCode.DUP: EvaluationStack.Push(EvaluationStack.Peek()); break; case OpCode.NIP: { StackItem x2 = EvaluationStack.Pop(); EvaluationStack.Pop(); EvaluationStack.Push(x2); } break; case OpCode.OVER: { StackItem x2 = EvaluationStack.Pop(); StackItem x1 = EvaluationStack.Peek(); EvaluationStack.Push(x2); EvaluationStack.Push(x1); } break; case OpCode.PICK: { int n = (int)EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } EvaluationStack.Push(EvaluationStack.Peek(n)); } break; case OpCode.ROLL: { int n = (int)EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } if (n == 0) { break; } EvaluationStack.Push(EvaluationStack.Remove(n)); } break; case OpCode.ROT: { StackItem x3 = EvaluationStack.Pop(); StackItem x2 = EvaluationStack.Pop(); StackItem x1 = EvaluationStack.Pop(); EvaluationStack.Push(x2); EvaluationStack.Push(x3); EvaluationStack.Push(x1); } break; case OpCode.SWAP: { StackItem x2 = EvaluationStack.Pop(); StackItem x1 = EvaluationStack.Pop(); EvaluationStack.Push(x2); EvaluationStack.Push(x1); } break; case OpCode.TUCK: { StackItem x2 = EvaluationStack.Pop(); StackItem x1 = EvaluationStack.Pop(); EvaluationStack.Push(x2); EvaluationStack.Push(x1); EvaluationStack.Push(x2); } break; case OpCode.CAT: { byte[] x2 = EvaluationStack.Pop().GetByteArray(); byte[] x1 = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(x1.Concat(x2).ToArray()); } break; case OpCode.SUBSTR: { int count = (int)EvaluationStack.Pop().GetBigInteger(); if (count < 0) { State |= VMState.FAULT; return; } int index = (int)EvaluationStack.Pop().GetBigInteger(); if (index < 0) { State |= VMState.FAULT; return; } byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(x.Skip(index).Take(count).ToArray()); } break; case OpCode.LEFT: { int count = (int)EvaluationStack.Pop().GetBigInteger(); if (count < 0) { State |= VMState.FAULT; return; } byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(x.Take(count).ToArray()); } break; case OpCode.RIGHT: { int count = (int)EvaluationStack.Pop().GetBigInteger(); if (count < 0) { State |= VMState.FAULT; return; } byte[] x = EvaluationStack.Pop().GetByteArray(); if (x.Length < count) { State |= VMState.FAULT; return; } EvaluationStack.Push(x.Skip(x.Length - count).ToArray()); } break; case OpCode.SIZE: { byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(x.Length); } break; // Bitwise logic case OpCode.INVERT: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(~x); } break; case OpCode.AND: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 & x2); } break; case OpCode.OR: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 | x2); } break; case OpCode.XOR: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 ^ x2); } break; case OpCode.EQUAL: { StackItem x2 = EvaluationStack.Pop(); StackItem x1 = EvaluationStack.Pop(); EvaluationStack.Push(x1.Equals(x2)); } break; // Numeric case OpCode.INC: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x + 1); } break; case OpCode.DEC: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x - 1); } break; case OpCode.SIGN: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x.Sign); } break; case OpCode.NEGATE: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(-x); } break; case OpCode.ABS: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(BigInteger.Abs(x)); } break; case OpCode.NOT: { bool x = EvaluationStack.Pop().GetBoolean(); EvaluationStack.Push(!x); } break; case OpCode.NZ: { BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x != BigInteger.Zero); } break; case OpCode.ADD: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 + x2); } break; case OpCode.SUB: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 - x2); } break; case OpCode.MUL: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 * x2); } break; case OpCode.DIV: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 / x2); } break; case OpCode.MOD: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 % x2); } break; case OpCode.SHL: { int n = (int)EvaluationStack.Pop().GetBigInteger(); BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x << n); } break; case OpCode.SHR: { int n = (int)EvaluationStack.Pop().GetBigInteger(); BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x >> n); } break; case OpCode.BOOLAND: { bool x2 = EvaluationStack.Pop().GetBoolean(); bool x1 = EvaluationStack.Pop().GetBoolean(); EvaluationStack.Push(x1 && x2); } break; case OpCode.BOOLOR: { bool x2 = EvaluationStack.Pop().GetBoolean(); bool x1 = EvaluationStack.Pop().GetBoolean(); EvaluationStack.Push(x1 || x2); } break; case OpCode.NUMEQUAL: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 == x2); } break; case OpCode.NUMNOTEQUAL: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 != x2); } break; case OpCode.LT: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 < x2); } break; case OpCode.GT: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 > x2); } break; case OpCode.LTE: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 <= x2); } break; case OpCode.GTE: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(x1 >= x2); } break; case OpCode.MIN: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(BigInteger.Min(x1, x2)); } break; case OpCode.MAX: { BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(BigInteger.Max(x1, x2)); } break; case OpCode.WITHIN: { BigInteger b = EvaluationStack.Pop().GetBigInteger(); BigInteger a = EvaluationStack.Pop().GetBigInteger(); BigInteger x = EvaluationStack.Pop().GetBigInteger(); EvaluationStack.Push(a <= x && x < b); } break; // Crypto case OpCode.SHA1: using (SHA1 sha = SHA1.Create()) { byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(sha.ComputeHash(x)); } break; case OpCode.SHA256: using (SHA256 sha = SHA256.Create()) { byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(sha.ComputeHash(x)); } break; case OpCode.HASH160: { byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(Crypto.Hash160(x)); } break; case OpCode.HASH256: { byte[] x = EvaluationStack.Pop().GetByteArray(); EvaluationStack.Push(Crypto.Hash256(x)); } break; case OpCode.CHECKSIG: { byte[] pubkey = EvaluationStack.Pop().GetByteArray(); byte[] signature = EvaluationStack.Pop().GetByteArray(); try { EvaluationStack.Push(Crypto.VerifySignature(ScriptContainer.GetMessage(), signature, pubkey)); } catch (ArgumentException) { EvaluationStack.Push(false); } } break; case OpCode.CHECKMULTISIG: { int n; byte[][] pubkeys; StackItem item = EvaluationStack.Pop(); if (item.IsArray) { pubkeys = item.GetArray().Select(p => p.GetByteArray()).ToArray(); n = pubkeys.Length; if (n == 0) { State |= VMState.FAULT; return; } } else { n = (int)item.GetBigInteger(); if (n < 1) { State |= VMState.FAULT; return; } pubkeys = new byte[n][]; for (int i = 0; i < n; i++) { pubkeys[i] = EvaluationStack.Pop().GetByteArray(); } } int m; byte[][] signatures; item = EvaluationStack.Pop(); if (item.IsArray) { signatures = item.GetArray().Select(p => p.GetByteArray()).ToArray(); m = signatures.Length; if (m == 0 || m > n) { State |= VMState.FAULT; return; } } else { m = (int)item.GetBigInteger(); if (m < 1 || m > n) { State |= VMState.FAULT; return; } signatures = new byte[m][]; for (int i = 0; i < m; i++) { signatures[i] = EvaluationStack.Pop().GetByteArray(); } } byte[] message = ScriptContainer.GetMessage(); bool fSuccess = true; try { for (int i = 0, j = 0; fSuccess && i < m && j < n;) { if (Crypto.VerifySignature(message, signatures[i], pubkeys[j])) { i++; } j++; if (m - i > n - j) { fSuccess = false; } } } catch (ArgumentException) { fSuccess = false; } EvaluationStack.Push(fSuccess); } break; // Array case OpCode.ARRAYSIZE: { StackItem item = EvaluationStack.Pop(); if (!item.IsArray) { EvaluationStack.Push(item.GetByteArray().Length); } else { EvaluationStack.Push(item.GetArray().Length); } } break; case OpCode.PACK: { int size = (int)EvaluationStack.Pop().GetBigInteger(); if (size < 0 || size > EvaluationStack.Count) { State |= VMState.FAULT; return; } StackItem[] items = new StackItem[size]; for (int i = 0; i < size; i++) { items[i] = EvaluationStack.Pop(); } EvaluationStack.Push(items); } break; case OpCode.UNPACK: { StackItem item = EvaluationStack.Pop(); if (!item.IsArray) { State |= VMState.FAULT; return; } StackItem[] items = item.GetArray(); for (int i = items.Length - 1; i >= 0; i--) { EvaluationStack.Push(items[i]); } EvaluationStack.Push(items.Length); } break; case OpCode.PICKITEM: { int index = (int)EvaluationStack.Pop().GetBigInteger(); if (index < 0) { State |= VMState.FAULT; return; } StackItem item = EvaluationStack.Pop(); if (!item.IsArray) { State |= VMState.FAULT; return; } StackItem[] items = item.GetArray(); if (index >= items.Length) { State |= VMState.FAULT; return; } EvaluationStack.Push(items[index]); } break; case OpCode.SETITEM: { StackItem newItem = EvaluationStack.Pop(); if (newItem.IsStruct) { newItem = (newItem as Types.Struct).Clone(); } int index = (int)EvaluationStack.Pop().GetBigInteger(); StackItem arrItem = EvaluationStack.Pop(); if (!arrItem.IsArray) { State |= VMState.FAULT; return; } StackItem[] items = arrItem.GetArray(); if (index < 0 || index >= items.Length) { State |= VMState.FAULT; return; } items[index] = newItem; } break; case OpCode.NEWARRAY: { int count = (int)EvaluationStack.Pop().GetBigInteger(); StackItem[] items = new StackItem[count]; for (var i = 0; i < count; i++) { items[i] = false; } EvaluationStack.Push(new Types.Array(items)); } break; case OpCode.NEWSTRUCT: { int count = (int)EvaluationStack.Pop().GetBigInteger(); StackItem[] items = new StackItem[count]; for (var i = 0; i < count; i++) { items[i] = false; } EvaluationStack.Push(new VM.Types.Struct(items)); } break; // Exceptions case OpCode.THROW: State |= VMState.FAULT; return; case OpCode.THROWIFNOT: if (!EvaluationStack.Pop().GetBoolean()) { State |= VMState.FAULT; return; } break; default: State |= VMState.FAULT; LastException = new Exception("Not a valid OpCode"); return; } } if (!State.HasFlag(VMState.FAULT) && InvocationStack.Count > 0) { if (CurrentContext.BreakPoints.Contains((uint)CurrentContext.InstructionPointer)) { State |= VMState.BREAK; } } }