diff --git a/src/neo-vm/ExecutionContext.cs b/src/neo-vm/ExecutionContext.cs index 80795b30..0a38c304 100644 --- a/src/neo-vm/ExecutionContext.cs +++ b/src/neo-vm/ExecutionContext.cs @@ -1,16 +1,17 @@ using System; -using System.Collections.Generic; using System.IO; namespace Neo.VM { public class ExecutionContext : IDisposable { - private ExecutionEngine engine; public readonly byte[] Script; - public readonly bool PushOnly; + internal readonly int RVCount; internal readonly BinaryReader OpReader; - internal readonly HashSet BreakPoints; + private readonly ICrypto crypto; + + public RandomAccessStack EvaluationStack { get; } = new RandomAccessStack(); + public RandomAccessStack AltStack { get; } = new RandomAccessStack(); public int InstructionPointer { @@ -32,26 +33,17 @@ public byte[] ScriptHash get { if (_script_hash == null) - _script_hash = engine.Crypto.Hash160(Script); + _script_hash = crypto.Hash160(Script); return _script_hash; } } - internal ExecutionContext(ExecutionEngine engine, byte[] script, bool push_only, HashSet break_points = null) + internal ExecutionContext(ExecutionEngine engine, byte[] script, int rvcount) { - this.engine = engine; this.Script = script; - this.PushOnly = push_only; + this.RVCount = rvcount; this.OpReader = new BinaryReader(new MemoryStream(script, false)); - this.BreakPoints = break_points ?? new HashSet(); - } - - public ExecutionContext Clone() - { - return new ExecutionContext(engine, Script, PushOnly, BreakPoints) - { - InstructionPointer = InstructionPointer - }; + this.crypto = engine.Crypto; } public void Dispose() diff --git a/src/neo-vm/ExecutionEngine.cs b/src/neo-vm/ExecutionEngine.cs index a13b05f0..d1683c77 100644 --- a/src/neo-vm/ExecutionEngine.cs +++ b/src/neo-vm/ExecutionEngine.cs @@ -13,13 +13,13 @@ namespace Neo.VM public class ExecutionEngine : IDisposable { private readonly IScriptTable table; + private readonly Dictionary> break_points = new Dictionary>(new HashComparer()); public IScriptContainer ScriptContainer { get; } public ICrypto Crypto { get; } public InteropService Service { get; } public RandomAccessStack InvocationStack { get; } = new RandomAccessStack(); - public RandomAccessStack EvaluationStack { get; } = new RandomAccessStack(); - public RandomAccessStack AltStack { get; } = new RandomAccessStack(); + public RandomAccessStack ResultStack { get; } = new RandomAccessStack(); public ExecutionContext CurrentContext => InvocationStack.Peek(); public ExecutionContext CallingContext => InvocationStack.Count > 1 ? InvocationStack.Peek(1) : null; public ExecutionContext EntryContext => InvocationStack.Peek(InvocationStack.Count - 1); @@ -33,12 +33,17 @@ public ExecutionEngine(IScriptContainer container, ICrypto crypto, IScriptTable this.Service = service ?? new InteropService(); } - public void AddBreakPoint(uint position) + public void AddBreakPoint(byte[] script_hash, uint position) { - CurrentContext.BreakPoints.Add(position); + if (!break_points.TryGetValue(script_hash, out HashSet hashset)) + { + hashset = new HashSet(); + break_points.Add(script_hash, hashset); + } + hashset.Add(position); } - public void Dispose() + public virtual void Dispose() { while (InvocationStack.Count > 0) InvocationStack.Pop().Dispose(); @@ -53,28 +58,23 @@ public void Execute() private void ExecuteOp(OpCode opcode, ExecutionContext context) { - 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)); + context.EvaluationStack.Push(context.OpReader.ReadBytes((byte)opcode)); else switch (opcode) { // Push value case OpCode.PUSH0: - EvaluationStack.Push(new byte[0]); + context.EvaluationStack.Push(new byte[0]); break; case OpCode.PUSHDATA1: - EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadByte())); + context.EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadByte())); break; case OpCode.PUSHDATA2: - EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadUInt16())); + context.EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadUInt16())); break; case OpCode.PUSHDATA4: - EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadInt32())); + context.EvaluationStack.Push(context.OpReader.ReadBytes(context.OpReader.ReadInt32())); break; case OpCode.PUSHM1: case OpCode.PUSH1: @@ -93,7 +93,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.PUSH14: case OpCode.PUSH15: case OpCode.PUSH16: - EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + 1); + context.EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + 1); break; // Control @@ -113,7 +113,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) bool fValue = true; if (opcode > OpCode.JMP) { - fValue = EvaluationStack.Pop().GetBoolean(); + fValue = context.EvaluationStack.Pop().GetBoolean(); if (opcode == OpCode.JMPIFNOT) fValue = !fValue; } @@ -122,12 +122,35 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } break; case OpCode.CALL: - InvocationStack.Push(context.Clone()); - context.InstructionPointer += 2; - ExecuteOp(OpCode.JMP, CurrentContext); + { + ExecutionContext context_call = LoadScript(context.Script); + context.EvaluationStack.CopyTo(context_call.EvaluationStack); + context_call.InstructionPointer = context.InstructionPointer; + context.EvaluationStack.Clear(); + context.InstructionPointer += 2; + ExecuteOp(OpCode.JMP, context_call); + } break; case OpCode.RET: - InvocationStack.Pop().Dispose(); + using (ExecutionContext context_pop = InvocationStack.Pop()) + { + int rvcount = context_pop.RVCount; + if (rvcount == -1) rvcount = context_pop.EvaluationStack.Count; + if (rvcount > 0) + { + if (context_pop.EvaluationStack.Count < rvcount) + { + State |= VMState.FAULT; + return; + } + RandomAccessStack stack_eval; + if (InvocationStack.Count == 0) + stack_eval = ResultStack; + else + stack_eval = CurrentContext.EvaluationStack; + context_pop.EvaluationStack.CopyTo(stack_eval, rvcount); + } + } if (InvocationStack.Count == 0) State |= VMState.HALT; break; @@ -143,7 +166,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) byte[] script_hash = context.OpReader.ReadBytes(20); if (script_hash.All(p => p == 0)) { - script_hash = EvaluationStack.Pop().GetByteArray(); + script_hash = context.EvaluationStack.Pop().GetByteArray(); } byte[] script = table.GetScript(script_hash); @@ -152,9 +175,14 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) State |= VMState.FAULT; return; } + + ExecutionContext context_new = LoadScript(script); + context.EvaluationStack.CopyTo(context_new.EvaluationStack); + if (opcode == OpCode.TAILCALL) - InvocationStack.Pop().Dispose(); - LoadScript(script); + InvocationStack.Remove(1).Dispose(); + else + context.EvaluationStack.Clear(); } break; case OpCode.SYSCALL: @@ -164,389 +192,362 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) // Stack ops case OpCode.DUPFROMALTSTACK: - EvaluationStack.Push(AltStack.Peek()); + context.EvaluationStack.Push(context.AltStack.Peek()); break; case OpCode.TOALTSTACK: - AltStack.Push(EvaluationStack.Pop()); + context.AltStack.Push(context.EvaluationStack.Pop()); break; case OpCode.FROMALTSTACK: - EvaluationStack.Push(AltStack.Pop()); + context.EvaluationStack.Push(context.AltStack.Pop()); break; case OpCode.XDROP: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } - EvaluationStack.Remove(n); + context.EvaluationStack.Remove(n); } break; case OpCode.XSWAP: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); + int n = (int)context.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); + StackItem xn = context.EvaluationStack.Peek(n); + context.EvaluationStack.Set(n, context.EvaluationStack.Peek()); + context.EvaluationStack.Set(0, xn); } break; case OpCode.XTUCK: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); if (n <= 0) { State |= VMState.FAULT; return; } - EvaluationStack.Insert(n, EvaluationStack.Peek()); + context.EvaluationStack.Insert(n, context.EvaluationStack.Peek()); } break; case OpCode.DEPTH: - EvaluationStack.Push(EvaluationStack.Count); + context.EvaluationStack.Push(context.EvaluationStack.Count); break; case OpCode.DROP: - EvaluationStack.Pop(); + context.EvaluationStack.Pop(); break; case OpCode.DUP: - EvaluationStack.Push(EvaluationStack.Peek()); + context.EvaluationStack.Push(context.EvaluationStack.Peek()); break; case OpCode.NIP: - { - StackItem x2 = EvaluationStack.Pop(); - EvaluationStack.Pop(); - EvaluationStack.Push(x2); - } + context.EvaluationStack.Remove(1); break; case OpCode.OVER: - { - StackItem x2 = EvaluationStack.Pop(); - StackItem x1 = EvaluationStack.Peek(); - EvaluationStack.Push(x2); - EvaluationStack.Push(x1); - } + context.EvaluationStack.Push(context.EvaluationStack.Peek(1)); break; case OpCode.PICK: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } - EvaluationStack.Push(EvaluationStack.Peek(n)); + context.EvaluationStack.Push(context.EvaluationStack.Peek(n)); } break; case OpCode.ROLL: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); if (n < 0) { State |= VMState.FAULT; return; } if (n == 0) break; - EvaluationStack.Push(EvaluationStack.Remove(n)); + context.EvaluationStack.Push(context.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); - } + context.EvaluationStack.Push(context.EvaluationStack.Remove(2)); break; case OpCode.SWAP: - { - StackItem x2 = EvaluationStack.Pop(); - StackItem x1 = EvaluationStack.Pop(); - EvaluationStack.Push(x2); - EvaluationStack.Push(x1); - } + context.EvaluationStack.Push(context.EvaluationStack.Remove(1)); break; case OpCode.TUCK: - { - StackItem x2 = EvaluationStack.Pop(); - StackItem x1 = EvaluationStack.Pop(); - EvaluationStack.Push(x2); - EvaluationStack.Push(x1); - EvaluationStack.Push(x2); - } + context.EvaluationStack.Insert(2, context.EvaluationStack.Peek()); break; case OpCode.CAT: { - byte[] x2 = EvaluationStack.Pop().GetByteArray(); - byte[] x1 = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(x1.Concat(x2).ToArray()); + byte[] x2 = context.EvaluationStack.Pop().GetByteArray(); + byte[] x1 = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(x1.Concat(x2).ToArray()); } break; case OpCode.SUBSTR: { - int count = (int)EvaluationStack.Pop().GetBigInteger(); + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); if (count < 0) { State |= VMState.FAULT; return; } - int index = (int)EvaluationStack.Pop().GetBigInteger(); + int index = (int)context.EvaluationStack.Pop().GetBigInteger(); if (index < 0) { State |= VMState.FAULT; return; } - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(x.Skip(index).Take(count).ToArray()); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(x.Skip(index).Take(count).ToArray()); } break; case OpCode.LEFT: { - int count = (int)EvaluationStack.Pop().GetBigInteger(); + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); if (count < 0) { State |= VMState.FAULT; return; } - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(x.Take(count).ToArray()); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(x.Take(count).ToArray()); } break; case OpCode.RIGHT: { - int count = (int)EvaluationStack.Pop().GetBigInteger(); + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); if (count < 0) { State |= VMState.FAULT; return; } - byte[] x = EvaluationStack.Pop().GetByteArray(); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); if (x.Length < count) { State |= VMState.FAULT; return; } - EvaluationStack.Push(x.Skip(x.Length - count).ToArray()); + context.EvaluationStack.Push(x.Skip(x.Length - count).ToArray()); } break; case OpCode.SIZE: { - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(x.Length); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(x.Length); } break; // Bitwise logic case OpCode.INVERT: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(~x); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(~x); } break; case OpCode.AND: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 & x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 & x2); } break; case OpCode.OR: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 | x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 | x2); } break; case OpCode.XOR: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 ^ x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 ^ x2); } break; case OpCode.EQUAL: { - StackItem x2 = EvaluationStack.Pop(); - StackItem x1 = EvaluationStack.Pop(); - EvaluationStack.Push(x1.Equals(x2)); + StackItem x2 = context.EvaluationStack.Pop(); + StackItem x1 = context.EvaluationStack.Pop(); + context.EvaluationStack.Push(x1.Equals(x2)); } break; // Numeric case OpCode.INC: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x + 1); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x + 1); } break; case OpCode.DEC: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x - 1); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x - 1); } break; case OpCode.SIGN: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x.Sign); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x.Sign); } break; case OpCode.NEGATE: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(-x); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(-x); } break; case OpCode.ABS: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(BigInteger.Abs(x)); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(BigInteger.Abs(x)); } break; case OpCode.NOT: { - bool x = EvaluationStack.Pop().GetBoolean(); - EvaluationStack.Push(!x); + bool x = context.EvaluationStack.Pop().GetBoolean(); + context.EvaluationStack.Push(!x); } break; case OpCode.NZ: { - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x != BigInteger.Zero); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x != BigInteger.Zero); } break; case OpCode.ADD: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 + x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 + x2); } break; case OpCode.SUB: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 - x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 - x2); } break; case OpCode.MUL: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 * x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 * x2); } break; case OpCode.DIV: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 / x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 / x2); } break; case OpCode.MOD: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 % x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 % x2); } break; case OpCode.SHL: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x << n); + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x << n); } break; case OpCode.SHR: { - int n = (int)EvaluationStack.Pop().GetBigInteger(); - BigInteger x = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x >> n); + int n = (int)context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x >> n); } break; case OpCode.BOOLAND: { - bool x2 = EvaluationStack.Pop().GetBoolean(); - bool x1 = EvaluationStack.Pop().GetBoolean(); - EvaluationStack.Push(x1 && x2); + bool x2 = context.EvaluationStack.Pop().GetBoolean(); + bool x1 = context.EvaluationStack.Pop().GetBoolean(); + context.EvaluationStack.Push(x1 && x2); } break; case OpCode.BOOLOR: { - bool x2 = EvaluationStack.Pop().GetBoolean(); - bool x1 = EvaluationStack.Pop().GetBoolean(); - EvaluationStack.Push(x1 || x2); + bool x2 = context.EvaluationStack.Pop().GetBoolean(); + bool x1 = context.EvaluationStack.Pop().GetBoolean(); + context.EvaluationStack.Push(x1 || x2); } break; case OpCode.NUMEQUAL: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 == x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 == x2); } break; case OpCode.NUMNOTEQUAL: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 != x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 != x2); } break; case OpCode.LT: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 < x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 < x2); } break; case OpCode.GT: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 > x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 > x2); } break; case OpCode.LTE: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 <= x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 <= x2); } break; case OpCode.GTE: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(x1 >= x2); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(x1 >= x2); } break; case OpCode.MIN: { - BigInteger x2 = EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = EvaluationStack.Pop().GetBigInteger(); - EvaluationStack.Push(BigInteger.Min(x1, x2)); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.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)); + BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + context.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); + BigInteger b = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger a = context.EvaluationStack.Pop().GetBigInteger(); + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + context.EvaluationStack.Push(a <= x && x < b); } break; @@ -554,55 +555,55 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.SHA1: using (SHA1 sha = SHA1.Create()) { - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(sha.ComputeHash(x)); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(sha.ComputeHash(x)); } break; case OpCode.SHA256: using (SHA256 sha = SHA256.Create()) { - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(sha.ComputeHash(x)); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(sha.ComputeHash(x)); } break; case OpCode.HASH160: { - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(Crypto.Hash160(x)); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(Crypto.Hash160(x)); } break; case OpCode.HASH256: { - byte[] x = EvaluationStack.Pop().GetByteArray(); - EvaluationStack.Push(Crypto.Hash256(x)); + byte[] x = context.EvaluationStack.Pop().GetByteArray(); + context.EvaluationStack.Push(Crypto.Hash256(x)); } break; case OpCode.CHECKSIG: { - byte[] pubkey = EvaluationStack.Pop().GetByteArray(); - byte[] signature = EvaluationStack.Pop().GetByteArray(); + byte[] pubkey = context.EvaluationStack.Pop().GetByteArray(); + byte[] signature = context.EvaluationStack.Pop().GetByteArray(); try { - EvaluationStack.Push(Crypto.VerifySignature(ScriptContainer.GetMessage(), signature, pubkey)); + context.EvaluationStack.Push(Crypto.VerifySignature(ScriptContainer.GetMessage(), signature, pubkey)); } catch (ArgumentException) { - EvaluationStack.Push(false); + context.EvaluationStack.Push(false); } } break; case OpCode.VERIFY: { - byte[] pubkey = EvaluationStack.Pop().GetByteArray(); - byte[] signature = EvaluationStack.Pop().GetByteArray(); - byte[] message = EvaluationStack.Pop().GetByteArray(); + byte[] pubkey = context.EvaluationStack.Pop().GetByteArray(); + byte[] signature = context.EvaluationStack.Pop().GetByteArray(); + byte[] message = context.EvaluationStack.Pop().GetByteArray(); try { - EvaluationStack.Push(Crypto.VerifySignature(message, signature, pubkey)); + context.EvaluationStack.Push(Crypto.VerifySignature(message, signature, pubkey)); } catch (ArgumentException) { - EvaluationStack.Push(false); + context.EvaluationStack.Push(false); } } break; @@ -610,7 +611,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) { int n; byte[][] pubkeys; - StackItem item = EvaluationStack.Pop(); + StackItem item = context.EvaluationStack.Pop(); if (item is VMArray array1) { pubkeys = array1.Select(p => p.GetByteArray()).ToArray(); @@ -624,18 +625,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) else { n = (int)item.GetBigInteger(); - if (n < 1 || n > EvaluationStack.Count) + if (n < 1 || n > context.EvaluationStack.Count) { State |= VMState.FAULT; return; } pubkeys = new byte[n][]; for (int i = 0; i < n; i++) - pubkeys[i] = EvaluationStack.Pop().GetByteArray(); + pubkeys[i] = context.EvaluationStack.Pop().GetByteArray(); } int m; byte[][] signatures; - item = EvaluationStack.Pop(); + item = context.EvaluationStack.Pop(); if (item is VMArray array2) { signatures = array2.Select(p => p.GetByteArray()).ToArray(); @@ -649,14 +650,14 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) else { m = (int)item.GetBigInteger(); - if (m < 1 || m > n || m > EvaluationStack.Count) + if (m < 1 || m > n || m > context.EvaluationStack.Count) { State |= VMState.FAULT; return; } signatures = new byte[m][]; for (int i = 0; i < m; i++) - signatures[i] = EvaluationStack.Pop().GetByteArray(); + signatures[i] = context.EvaluationStack.Pop().GetByteArray(); } byte[] message = ScriptContainer.GetMessage(); bool fSuccess = true; @@ -675,42 +676,42 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) { fSuccess = false; } - EvaluationStack.Push(fSuccess); + context.EvaluationStack.Push(fSuccess); } break; // Array case OpCode.ARRAYSIZE: { - StackItem item = EvaluationStack.Pop(); + StackItem item = context.EvaluationStack.Pop(); if (item is ICollection collection) - EvaluationStack.Push(collection.Count); + context.EvaluationStack.Push(collection.Count); else - EvaluationStack.Push(item.GetByteArray().Length); + context.EvaluationStack.Push(item.GetByteArray().Length); } break; case OpCode.PACK: { - int size = (int)EvaluationStack.Pop().GetBigInteger(); - if (size < 0 || size > EvaluationStack.Count) + int size = (int)context.EvaluationStack.Pop().GetBigInteger(); + if (size < 0 || size > context.EvaluationStack.Count) { State |= VMState.FAULT; return; } List items = new List(size); for (int i = 0; i < size; i++) - items.Add(EvaluationStack.Pop()); - EvaluationStack.Push(items); + items.Add(context.EvaluationStack.Pop()); + context.EvaluationStack.Push(items); } break; case OpCode.UNPACK: { - StackItem item = EvaluationStack.Pop(); + StackItem item = context.EvaluationStack.Pop(); if (item is VMArray array) { for (int i = array.Count - 1; i >= 0; i--) - EvaluationStack.Push(array[i]); - EvaluationStack.Push(array.Count); + context.EvaluationStack.Push(array[i]); + context.EvaluationStack.Push(array.Count); } else { @@ -721,13 +722,13 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) break; case OpCode.PICKITEM: { - StackItem key = EvaluationStack.Pop(); + StackItem key = context.EvaluationStack.Pop(); if (key is ICollection) { State |= VMState.FAULT; return; } - switch (EvaluationStack.Pop()) + switch (context.EvaluationStack.Pop()) { case VMArray array: int index = (int)key.GetBigInteger(); @@ -736,12 +737,12 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) State |= VMState.FAULT; return; } - EvaluationStack.Push(array[index]); + context.EvaluationStack.Push(array[index]); break; case Map map: if (map.TryGetValue(key, out StackItem value)) { - EvaluationStack.Push(value); + context.EvaluationStack.Push(value); } else { @@ -757,15 +758,15 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) break; case OpCode.SETITEM: { - StackItem value = EvaluationStack.Pop(); + StackItem value = context.EvaluationStack.Pop(); if (value is Struct s) value = s.Clone(); - StackItem key = EvaluationStack.Pop(); + StackItem key = context.EvaluationStack.Pop(); if (key is ICollection) { State |= VMState.FAULT; return; } - switch (EvaluationStack.Pop()) + switch (context.EvaluationStack.Pop()) { case VMArray array: int index = (int)key.GetBigInteger(); @@ -787,37 +788,37 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) break; case OpCode.NEWARRAY: { - int count = (int)EvaluationStack.Pop().GetBigInteger(); + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); List items = new List(count); for (var i = 0; i < count; i++) { items.Add(false); } - EvaluationStack.Push(new Types.Array(items)); + context.EvaluationStack.Push(new Types.Array(items)); } break; case OpCode.NEWSTRUCT: { - int count = (int)EvaluationStack.Pop().GetBigInteger(); + int count = (int)context.EvaluationStack.Pop().GetBigInteger(); List items = new List(count); for (var i = 0; i < count; i++) { items.Add(false); } - EvaluationStack.Push(new VM.Types.Struct(items)); + context.EvaluationStack.Push(new VM.Types.Struct(items)); } break; case OpCode.NEWMAP: - EvaluationStack.Push(new Map()); + context.EvaluationStack.Push(new Map()); break; case OpCode.APPEND: { - StackItem newItem = EvaluationStack.Pop(); + StackItem newItem = context.EvaluationStack.Pop(); if (newItem is Types.Struct s) { newItem = s.Clone(); } - StackItem arrItem = EvaluationStack.Pop(); + StackItem arrItem = context.EvaluationStack.Pop(); if (arrItem is VMArray array) { array.Add(newItem); @@ -831,7 +832,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) break; case OpCode.REVERSE: { - StackItem arrItem = EvaluationStack.Pop(); + StackItem arrItem = context.EvaluationStack.Pop(); if (arrItem is VMArray array) { array.Reverse(); @@ -845,13 +846,13 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) break; case OpCode.REMOVE: { - StackItem key = EvaluationStack.Pop(); + StackItem key = context.EvaluationStack.Pop(); if (key is ICollection) { State |= VMState.FAULT; return; } - switch (EvaluationStack.Pop()) + switch (context.EvaluationStack.Pop()) { case VMArray array: int index = (int)key.GetBigInteger(); @@ -873,13 +874,13 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) break; case OpCode.HASKEY: { - StackItem key = EvaluationStack.Pop(); + StackItem key = context.EvaluationStack.Pop(); if (key is ICollection) { State |= VMState.FAULT; return; } - switch (EvaluationStack.Pop()) + switch (context.EvaluationStack.Pop()) { case VMArray array: int index = (int)key.GetBigInteger(); @@ -888,10 +889,10 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) State |= VMState.FAULT; return; } - EvaluationStack.Push(index < array.Count); + context.EvaluationStack.Push(index < array.Count); break; case Map map: - EvaluationStack.Push(map.ContainsKey(key)); + context.EvaluationStack.Push(map.ContainsKey(key)); break; default: State |= VMState.FAULT; @@ -900,10 +901,10 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } break; case OpCode.KEYS: - switch (EvaluationStack.Pop()) + switch (context.EvaluationStack.Pop()) { case Map map: - EvaluationStack.Push(new VMArray(map.Keys)); + context.EvaluationStack.Push(new VMArray(map.Keys)); break; default: State |= VMState.FAULT; @@ -913,7 +914,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.VALUES: { ICollection values; - switch (EvaluationStack.Pop()) + switch (context.EvaluationStack.Pop()) { case VMArray array: values = array; @@ -931,7 +932,72 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) newArray.Add(s.Clone()); else newArray.Add(item); - EvaluationStack.Push(new VMArray(newArray)); + context.EvaluationStack.Push(new VMArray(newArray)); + } + break; + + // Stack isolation + case OpCode.CALL_I: + { + int rvcount = context.OpReader.ReadByte(); + int pcount = context.OpReader.ReadByte(); + if (context.EvaluationStack.Count < pcount) + { + State |= VMState.FAULT; + return; + } + ExecutionContext context_call = LoadScript(context.Script, rvcount); + context.EvaluationStack.CopyTo(context_call.EvaluationStack, pcount); + context_call.InstructionPointer = context.InstructionPointer; + for (int i = 0; i < pcount; i++) + context.EvaluationStack.Pop(); + context.InstructionPointer += 2; + ExecuteOp(OpCode.JMP, context_call); + } + break; + case OpCode.CALL_E: + case OpCode.CALL_ED: + case OpCode.CALL_ET: + case OpCode.CALL_EDT: + { + if (table == null) + { + State |= VMState.FAULT; + return; + } + int rvcount = context.OpReader.ReadByte(); + int pcount = context.OpReader.ReadByte(); + if (context.EvaluationStack.Count < pcount) + { + State |= VMState.FAULT; + return; + } + if (opcode == OpCode.CALL_ET || opcode == OpCode.CALL_EDT) + { + if (context.RVCount != rvcount) + { + State |= VMState.FAULT; + return; + } + } + byte[] script_hash; + if (opcode == OpCode.CALL_ED || opcode == OpCode.CALL_EDT) + script_hash = context.EvaluationStack.Pop().GetByteArray(); + else + script_hash = context.OpReader.ReadBytes(20); + byte[] script = table.GetScript(script_hash); + if (script == null) + { + State |= VMState.FAULT; + return; + } + ExecutionContext context_new = LoadScript(script, rvcount); + context.EvaluationStack.CopyTo(context_new.EvaluationStack, pcount); + if (opcode == OpCode.CALL_ET || opcode == OpCode.CALL_EDT) + InvocationStack.Remove(1).Dispose(); + else + for (int i = 0; i < pcount; i++) + context.EvaluationStack.Pop(); } break; @@ -940,7 +1006,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) State |= VMState.FAULT; return; case OpCode.THROWIFNOT: - if (!EvaluationStack.Pop().GetBoolean()) + if (!context.EvaluationStack.Pop().GetBoolean()) { State |= VMState.FAULT; return; @@ -953,20 +1019,27 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } if (!State.HasFlag(VMState.FAULT) && InvocationStack.Count > 0) { - if (CurrentContext.BreakPoints.Contains((uint)CurrentContext.InstructionPointer)) + if (break_points.TryGetValue(CurrentContext.ScriptHash, out HashSet hashset) && hashset.Contains((uint)CurrentContext.InstructionPointer)) State |= VMState.BREAK; } } - public void LoadScript(byte[] script, bool push_only = false) + public ExecutionContext LoadScript(byte[] script, int rvcount = -1) { - InvocationStack.Push(new ExecutionContext(this, script, push_only)); + ExecutionContext context = new ExecutionContext(this, script, rvcount); + InvocationStack.Push(context); + return context; } - public bool RemoveBreakPoint(uint position) + public bool RemoveBreakPoint(byte[] script_hash, uint position) { - if (InvocationStack.Count == 0) return false; - return CurrentContext.BreakPoints.Remove(position); + if (!break_points.TryGetValue(script_hash, out HashSet hashset)) + return false; + if (!hashset.Remove(position)) + return false; + if (hashset.Count == 0) + break_points.Remove(script_hash); + return true; } public void StepInto() diff --git a/src/neo-vm/HashComparer.cs b/src/neo-vm/HashComparer.cs new file mode 100644 index 00000000..f2cf6b13 --- /dev/null +++ b/src/neo-vm/HashComparer.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.VM +{ + internal class HashComparer : IEqualityComparer + { + public bool Equals(byte[] x, byte[] y) + { + return x.SequenceEqual(y); + } + + public int GetHashCode(byte[] obj) + { + return BitConverter.ToInt32(obj, 0); + } + } +} diff --git a/src/neo-vm/InteropService.cs b/src/neo-vm/InteropService.cs index d21c74b1..efd963ad 100644 --- a/src/neo-vm/InteropService.cs +++ b/src/neo-vm/InteropService.cs @@ -28,25 +28,25 @@ internal bool Invoke(string method, ExecutionEngine engine) private static bool GetScriptContainer(ExecutionEngine engine) { - engine.EvaluationStack.Push(StackItem.FromInterface(engine.ScriptContainer)); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(engine.ScriptContainer)); return true; } private static bool GetExecutingScriptHash(ExecutionEngine engine) { - engine.EvaluationStack.Push(engine.CurrentContext.ScriptHash); + engine.CurrentContext.EvaluationStack.Push(engine.CurrentContext.ScriptHash); return true; } private static bool GetCallingScriptHash(ExecutionEngine engine) { - engine.EvaluationStack.Push(engine.CallingContext.ScriptHash); + engine.CurrentContext.EvaluationStack.Push(engine.CallingContext.ScriptHash); return true; } private static bool GetEntryScriptHash(ExecutionEngine engine) { - engine.EvaluationStack.Push(engine.EntryContext.ScriptHash); + engine.CurrentContext.EvaluationStack.Push(engine.EntryContext.ScriptHash); return true; } } diff --git a/src/neo-vm/OpCode.cs b/src/neo-vm/OpCode.cs index 6bdf6181..86176316 100644 --- a/src/neo-vm/OpCode.cs +++ b/src/neo-vm/OpCode.cs @@ -136,6 +136,15 @@ public enum OpCode : byte KEYS = 0xCC, VALUES = 0xCD, + + // Stack isolation + CALL_I = 0xE0, + CALL_E = 0xE1, + CALL_ED = 0xE2, + CALL_ET = 0xE3, + CALL_EDT = 0xE4, + + // Exceptions THROW = 0xF0, THROWIFNOT = 0xF1 diff --git a/src/neo-vm/RandomAccessStack.cs b/src/neo-vm/RandomAccessStack.cs index 13b26a97..e48ed21b 100644 --- a/src/neo-vm/RandomAccessStack.cs +++ b/src/neo-vm/RandomAccessStack.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Neo.VM { @@ -15,6 +16,15 @@ public void Clear() list.Clear(); } + public void CopyTo(RandomAccessStack stack, int count = -1) + { + if (count == 0) return; + if (count == -1) + stack.list.AddRange(list); + else + stack.list.AddRange(list.Skip(list.Count - count)); + } + public IEnumerator GetEnumerator() { return list.GetEnumerator(); @@ -34,7 +44,10 @@ public void Insert(int index, T item) public T Peek(int index = 0) { if (index >= list.Count) throw new InvalidOperationException(); - return list[list.Count - 1 - index]; + if (index < 0) index += list.Count; + if (index < 0) throw new InvalidOperationException(); + index = list.Count - index - 1; + return list[index]; } public T Pop() @@ -50,15 +63,21 @@ public void Push(T item) public T Remove(int index) { if (index >= list.Count) throw new InvalidOperationException(); - T item = list[list.Count - index - 1]; - list.RemoveAt(list.Count - index - 1); + if (index < 0) index += list.Count; + if (index < 0) throw new InvalidOperationException(); + index = list.Count - index - 1; + T item = list[index]; + list.RemoveAt(index); return item; } public void Set(int index, T item) { if (index >= list.Count) throw new InvalidOperationException(); - list[list.Count - index - 1] = item; + if (index < 0) index += list.Count; + if (index < 0) throw new InvalidOperationException(); + index = list.Count - index - 1; + list[index] = item; } } } diff --git a/src/neo-vm/neo-vm.csproj b/src/neo-vm/neo-vm.csproj index 8592eb7b..cb03084c 100644 --- a/src/neo-vm/neo-vm.csproj +++ b/src/neo-vm/neo-vm.csproj @@ -4,7 +4,7 @@ 2016-2017 The Neo Project Neo.VM Neo.VM - 2.2.1 + 2.3.0 The Neo Project netstandard1.6;net461 Neo.VM