From 0f9e4116fde057da64f8b43e39dbd19fa4cbe152 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 26 Mar 2019 16:44:39 +0800 Subject: [PATCH 1/6] Improve the performance of NeoVM --- src/neo-vm/ExecutionEngine.cs | 344 +++++++++++++++++++++++++++++----- src/neo-vm/HashComparer.cs | 8 +- src/neo-vm/StackItem.cs | 5 + src/neo-vm/Types/Boolean.cs | 3 +- src/neo-vm/Types/ByteArray.cs | 5 +- src/neo-vm/Types/Integer.cs | 11 +- src/neo-vm/Unsafe.cs | 78 ++++++++ src/neo-vm/neo-vm.csproj | 9 +- 8 files changed, 402 insertions(+), 61 deletions(-) create mode 100644 src/neo-vm/Unsafe.cs diff --git a/src/neo-vm/ExecutionEngine.cs b/src/neo-vm/ExecutionEngine.cs index 942fa820..4dc863b6 100644 --- a/src/neo-vm/ExecutionEngine.cs +++ b/src/neo-vm/ExecutionEngine.cs @@ -17,12 +17,12 @@ public class ExecutionEngine : IDisposable /// /// Max value for SHL and SHR /// - public virtual int Max_SHL_SHR => ushort.MaxValue; + public virtual int Max_SHL_SHR => 256; /// /// Min value for SHL and SHR /// - public virtual int Min_SHL_SHR => -ushort.MaxValue; + public virtual int Min_SHL_SHR => -256; /// /// The max size in bytes allowed size for BigInteger @@ -51,6 +51,8 @@ public class ExecutionEngine : IDisposable #endregion + private static readonly byte[] EmptyBytes = new byte[0]; + private int stackitem_count = 0; private bool is_stackitem_count_strict = true; @@ -256,7 +258,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) // Push value case OpCode.PUSH0: { - context.EvaluationStack.Push(new byte[0]); + context.EvaluationStack.Push(EmptyBytes); if (!CheckStackSize(true)) { @@ -614,26 +616,31 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) { byte[] x2 = context.EvaluationStack.Pop().GetByteArray(); byte[] x1 = context.EvaluationStack.Pop().GetByteArray(); + int length = x1.Length + x2.Length; - if (!CheckMaxItemSize(x1.Length + x2.Length)) + if (!CheckMaxItemSize(length)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x1.Concat(x2).ToArray()); + byte[] buffer = new byte[length]; + Unsafe.MemoryCopy(x1, 0, buffer, 0, x1.Length); + Unsafe.MemoryCopy(x2, 0, buffer, x1.Length, x2.Length); + + context.EvaluationStack.Push(buffer); CheckStackSize(true, -1); break; } case OpCode.SUBSTR: { int count = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (count < 0) { State = VMState.FAULT; return; } + int index = (int)context.EvaluationStack.Pop().GetBigInteger(); if (index < 0) { @@ -642,14 +649,23 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } byte[] x = context.EvaluationStack.Pop().GetByteArray(); - context.EvaluationStack.Push(x.Skip(index).Take(count).ToArray()); + if (index > x.Length) + { + State = VMState.FAULT; + return; + } + + if (index + count > x.Length) count = x.Length - index; + byte[] buffer = new byte[count]; + Unsafe.MemoryCopy(x, index, buffer, 0, count); + + context.EvaluationStack.Push(buffer); CheckStackSize(true, -2); break; } case OpCode.LEFT: { int count = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (count < 0) { State = VMState.FAULT; @@ -657,34 +673,57 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } byte[] x = context.EvaluationStack.Pop().GetByteArray(); - context.EvaluationStack.Push(x.Take(count).ToArray()); + + byte[] buffer; + if (count >= x.Length) + { + buffer = x; + } + else + { + buffer = new byte[count]; + Unsafe.MemoryCopy(x, 0, buffer, 0, count); + } + + context.EvaluationStack.Push(buffer); CheckStackSize(true, -1); break; } case OpCode.RIGHT: { int count = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (count < 0) { State = VMState.FAULT; return; } + byte[] x = context.EvaluationStack.Pop().GetByteArray(); - if (x.Length < count) + if (count > x.Length) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x.Skip(x.Length - count).ToArray()); + byte[] buffer; + if (count == x.Length) + { + buffer = x; + } + else + { + buffer = new byte[count]; + Unsafe.MemoryCopy(x, x.Length - count, buffer, 0, count); + } + + context.EvaluationStack.Push(buffer); CheckStackSize(true, -1); break; } case OpCode.SIZE: { - byte[] x = context.EvaluationStack.Pop().GetByteArray(); - context.EvaluationStack.Push(x.Length); + StackItem x = context.EvaluationStack.Pop(); + context.EvaluationStack.Push(x.GetByteLength()); break; } @@ -692,13 +731,31 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.INVERT: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(~x); break; } case OpCode.AND: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(x1 & x2); CheckStackSize(true, -1); break; @@ -706,7 +763,19 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.OR: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(x1 | x2); CheckStackSize(true, -1); break; @@ -714,7 +783,19 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.XOR: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(x1 ^ x2); CheckStackSize(true, -1); break; @@ -732,44 +813,74 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.INC: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigInteger(x) || !CheckBigInteger(x + 1)) + x += 1; + if (!CheckBigInteger(x)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x + 1); + context.EvaluationStack.Push(x); break; } case OpCode.DEC: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigInteger(x) || (x.Sign <= 0 && !CheckBigInteger(x - 1))) + x -= 1; + if (!CheckBigInteger(x)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x - 1); + context.EvaluationStack.Push(x); break; } case OpCode.SIGN: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(x.Sign); break; } case OpCode.NEGATE: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(-x); break; } case OpCode.ABS: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } + context.EvaluationStack.Push(BigInteger.Abs(x)); break; } @@ -783,70 +894,107 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.NZ: { BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - context.EvaluationStack.Push(x != BigInteger.Zero); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } + + context.EvaluationStack.Push(!x.IsZero); break; } case OpCode.ADD: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigInteger(x2) || !CheckBigInteger(x1) || !CheckBigInteger(x1 + x2)) + BigInteger result = x1 + x2; + if (!CheckBigInteger(result)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x1 + x2); + context.EvaluationStack.Push(result); CheckStackSize(true, -1); break; } case OpCode.SUB: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigInteger(x2) || !CheckBigInteger(x1) || !CheckBigInteger(x1 - x2)) + BigInteger result = x1 - x2; + if (!CheckBigInteger(result)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x1 - x2); + context.EvaluationStack.Push(result); CheckStackSize(true, -1); break; } case OpCode.MUL: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); - - int lx1 = x1.ToByteArray().Length; - - if (!CheckBigIntegerByteLength(lx1)) + if (!CheckBigInteger(x2)) { State = VMState.FAULT; return; } - int lx2 = x2.ToByteArray().Length; + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigIntegerByteLength(lx1 + lx2)) + BigInteger result = x1 * x2; + if (!CheckBigInteger(result)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x1 * x2); + context.EvaluationStack.Push(result); CheckStackSize(true, -1); break; } case OpCode.DIV: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigInteger(x2) || !CheckBigInteger(x1)) + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) { State = VMState.FAULT; return; @@ -859,9 +1007,14 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.MOD: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); - BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } - if (!CheckBigInteger(x2) || !CheckBigInteger(x1)) + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) { State = VMState.FAULT; return; @@ -874,7 +1027,6 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.SHL: { int shift = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckShift(shift)) { State = VMState.FAULT; @@ -882,15 +1034,13 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckBigInteger(x)) { State = VMState.FAULT; return; } - x = x << shift; - + x <<= shift; if (!CheckBigInteger(x)) { State = VMState.FAULT; @@ -904,7 +1054,6 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.SHR: { int shift = (int)context.EvaluationStack.Pop().GetBigInteger(); - if (!CheckShift(shift)) { State = VMState.FAULT; @@ -912,14 +1061,20 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } + x >>= shift; if (!CheckBigInteger(x)) { State = VMState.FAULT; return; } - context.EvaluationStack.Push(x >> shift); + context.EvaluationStack.Push(x); CheckStackSize(true, -1); break; } @@ -944,7 +1099,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.NUMEQUAL: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(x1 == x2); CheckStackSize(true, -1); @@ -953,7 +1119,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.NUMNOTEQUAL: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(x1 != x2); CheckStackSize(true, -1); @@ -962,7 +1139,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.LT: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(x1 < x2); CheckStackSize(true, -1); @@ -971,7 +1159,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.GT: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(x1 > x2); CheckStackSize(true, -1); @@ -980,7 +1179,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.LTE: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(x1 <= x2); CheckStackSize(true, -1); @@ -989,7 +1199,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.GTE: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(x1 >= x2); CheckStackSize(true, -1); @@ -998,7 +1219,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.MIN: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(BigInteger.Min(x1, x2)); CheckStackSize(true, -1); @@ -1007,7 +1239,18 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.MAX: { BigInteger x2 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x2)) + { + State = VMState.FAULT; + return; + } + BigInteger x1 = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x1)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(BigInteger.Max(x1, x2)); CheckStackSize(true, -1); @@ -1016,8 +1259,25 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) case OpCode.WITHIN: { BigInteger b = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(b)) + { + State = VMState.FAULT; + return; + } + BigInteger a = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(a)) + { + State = VMState.FAULT; + return; + } + BigInteger x = context.EvaluationStack.Pop().GetBigInteger(); + if (!CheckBigInteger(x)) + { + State = VMState.FAULT; + return; + } context.EvaluationStack.Push(a <= x && x < b); CheckStackSize(true, -2); @@ -1174,7 +1434,7 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context) } else { - context.EvaluationStack.Push(item.GetByteArray().Length); + context.EvaluationStack.Push(item.GetByteLength()); CheckStackSize(true, 0); } break; diff --git a/src/neo-vm/HashComparer.cs b/src/neo-vm/HashComparer.cs index f2cf6b13..aed8b118 100644 --- a/src/neo-vm/HashComparer.cs +++ b/src/neo-vm/HashComparer.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; namespace Neo.VM { @@ -8,12 +6,12 @@ internal class HashComparer : IEqualityComparer { public bool Equals(byte[] x, byte[] y) { - return x.SequenceEqual(y); + return Unsafe.Equals(x, y); } public int GetHashCode(byte[] obj) { - return BitConverter.ToInt32(obj, 0); + return Unsafe.ToInt32(obj, 0); } } } diff --git a/src/neo-vm/StackItem.cs b/src/neo-vm/StackItem.cs index 44760090..17e28adb 100644 --- a/src/neo-vm/StackItem.cs +++ b/src/neo-vm/StackItem.cs @@ -36,6 +36,11 @@ public virtual BigInteger GetBigInteger() public abstract byte[] GetByteArray(); + public virtual int GetByteLength() + { + return GetByteArray().Length; + } + public override int GetHashCode() { unchecked diff --git a/src/neo-vm/Types/Boolean.cs b/src/neo-vm/Types/Boolean.cs index 31154b41..8704634f 100644 --- a/src/neo-vm/Types/Boolean.cs +++ b/src/neo-vm/Types/Boolean.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Numerics; namespace Neo.VM.Types @@ -30,7 +29,7 @@ public override bool Equals(StackItem other) { return false; } - return GetByteArray().SequenceEqual(bytes_other); + return Unsafe.Equals(GetByteArray(), bytes_other); } public override BigInteger GetBigInteger() diff --git a/src/neo-vm/Types/ByteArray.cs b/src/neo-vm/Types/ByteArray.cs index 70de3243..15eeaa56 100644 --- a/src/neo-vm/Types/ByteArray.cs +++ b/src/neo-vm/Types/ByteArray.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; namespace Neo.VM.Types { @@ -25,14 +24,14 @@ public override bool Equals(StackItem other) { return false; } - return value.SequenceEqual(bytes_other); + return Unsafe.Equals(value, bytes_other); } public override bool GetBoolean() { if (value.Length > ExecutionEngine.MaxSizeForBigInteger) return true; - return value.Any(p => p != 0); + return Unsafe.NotZero(value); } public override byte[] GetByteArray() diff --git a/src/neo-vm/Types/Integer.cs b/src/neo-vm/Types/Integer.cs index 4171e082..7933a93e 100644 --- a/src/neo-vm/Types/Integer.cs +++ b/src/neo-vm/Types/Integer.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Numerics; namespace Neo.VM.Types @@ -27,7 +26,7 @@ public override bool Equals(StackItem other) { return false; } - return GetByteArray().SequenceEqual(bytes_other); + return Unsafe.Equals(GetByteArray(), bytes_other); } public override BigInteger GetBigInteger() @@ -44,5 +43,13 @@ public override byte[] GetByteArray() { return value.ToByteArray(); } + + private int _length = -1; + public override int GetByteLength() + { + if (_length == -1) + _length = value.ToByteArray().Length; + return _length; + } } } diff --git a/src/neo-vm/Unsafe.cs b/src/neo-vm/Unsafe.cs new file mode 100644 index 00000000..8034bdb6 --- /dev/null +++ b/src/neo-vm/Unsafe.cs @@ -0,0 +1,78 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Neo.VM +{ + internal static class Unsafe + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe public static bool Equals(byte[] x, byte[] y) + { + if (ReferenceEquals(x, y)) return true; + if (x is null || y is null) return false; + int len = x.Length; + if (len != y.Length) return false; + fixed (byte* xp = x, yp = y) + { + long* xlp = (long*)xp, ylp = (long*)yp; + for (; len >= 8; len -= 8) + { + if (*xlp != *ylp) return false; + xlp++; + ylp++; + } + byte* xbp = (byte*)xlp, ybp = (byte*)ylp; + for (; len > 0; len--) + { + if (*xbp != *ybp) return false; + xbp++; + ybp++; + } + } + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe public static void MemoryCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + fixed (byte* sp = &src[srcOffset], dp = &dst[dstOffset]) + { + Buffer.MemoryCopy(sp, dp, dst.Length - dstOffset, count); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe public static bool NotZero(byte[] x) + { + if (x is null) + throw new ArgumentNullException(nameof(x)); + int len = x.Length; + if (len == 0) return false; + fixed (byte* xp = x) + { + long* xlp = (long*)xp; + for (; len >= 8; len -= 8) + { + if (*xlp != 0) return true; + xlp++; + } + byte* xbp = (byte*)xlp; + for (; len > 0; len--) + { + if (*xbp != 0) return true; + xbp++; + } + } + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + unsafe public static int ToInt32(byte[] value, int startIndex) + { + fixed (byte* pbyte = &value[startIndex]) + { + return *(int*)pbyte; + } + } + } +} diff --git a/src/neo-vm/neo-vm.csproj b/src/neo-vm/neo-vm.csproj index a7bfbe5b..3d7ef1b7 100644 --- a/src/neo-vm/neo-vm.csproj +++ b/src/neo-vm/neo-vm.csproj @@ -1,7 +1,7 @@  - 2016-2017 The Neo Project + 2016-2019 The Neo Project Neo.VM Neo.VM 2.4.0 @@ -15,12 +15,7 @@ https://github.com/neo-project/neo-vm.git 1.6.0 Neo.VM - - - - RELEASE;NETSTANDARD1_6 - none - False + true From 81c3358a5b8c1611321527cd790465417e4a5cbc Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 26 Mar 2019 16:46:44 +0800 Subject: [PATCH 2/6] remove `CheckBigIntegerByteLength()` --- src/neo-vm/ExecutionEngine.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/neo-vm/ExecutionEngine.cs b/src/neo-vm/ExecutionEngine.cs index 4dc863b6..3af70d7f 100644 --- a/src/neo-vm/ExecutionEngine.cs +++ b/src/neo-vm/ExecutionEngine.cs @@ -121,14 +121,6 @@ public void AddBreakPoint(byte[] script_hash, uint position) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CheckBigInteger(BigInteger value) => value.ToByteArray().Length <= MaxSizeForBigInteger; - /// - /// Check if the BigInteger is allowed for numeric operations - /// - /// Value - /// Return True if are allowed, otherwise False - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CheckBigIntegerByteLength(int byteLength) => byteLength <= MaxSizeForBigInteger; - /// /// Check if the number is allowed from SHL and SHR /// From 8684e83c19aa3b028ad6504aa7ddbb770c1472c9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 26 Mar 2019 10:16:15 +0100 Subject: [PATCH 3/6] Add comment Add a comment in order to prevent the use in the future without check the length previously --- src/neo-vm/Unsafe.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/neo-vm/Unsafe.cs b/src/neo-vm/Unsafe.cs index 8034bdb6..15f7f078 100644 --- a/src/neo-vm/Unsafe.cs +++ b/src/neo-vm/Unsafe.cs @@ -66,6 +66,12 @@ unsafe public static bool NotZero(byte[] x) return false; } + /// + /// Convert byte array to int32 + /// + /// Value (must be a checked before this call) + /// Start index + /// Integer [MethodImpl(MethodImplOptions.AggressiveInlining)] unsafe public static int ToInt32(byte[] value, int startIndex) { From 4b7e91ecc500bb7acaf7de22c9554cc66fe3c780 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 26 Mar 2019 10:37:39 +0100 Subject: [PATCH 4/6] Update Unsafe.cs --- src/neo-vm/Unsafe.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo-vm/Unsafe.cs b/src/neo-vm/Unsafe.cs index 15f7f078..08dadeac 100644 --- a/src/neo-vm/Unsafe.cs +++ b/src/neo-vm/Unsafe.cs @@ -69,7 +69,7 @@ unsafe public static bool NotZero(byte[] x) /// /// Convert byte array to int32 /// - /// Value (must be a checked before this call) + /// Value (must be checked before this call) /// Start index /// Integer [MethodImpl(MethodImplOptions.AggressiveInlining)] From 39f8517ce526c0d8e2b1ef308c8b1431427e865d Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 27 Mar 2019 12:09:16 +0100 Subject: [PATCH 5/6] Some ut for performance PR (#100) * SHL and SHR * SIZE * Change category * Typo --- .../Tests/OpCodes/Numeric/SHL.json | 185 ++++++++ .../Tests/OpCodes/Numeric/SHR.json | 185 ++++++++ .../Tests/OpCodes/Splice/SIZE.json | 449 ++++++++++++++++++ tests/neo-vm.Tests/UtVMJson.cs | 1 + 4 files changed, 820 insertions(+) create mode 100644 tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHL.json create mode 100644 tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHR.json create mode 100644 tests/neo-vm.Tests/Tests/OpCodes/Splice/SIZE.json diff --git a/tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHL.json b/tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHL.json new file mode 100644 index 00000000..62ec5f29 --- /dev/null +++ b/tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHL.json @@ -0,0 +1,185 @@ +{ + "category": "Numeric", + "name": "SHL", + "tests": + [ + { + "name": "Exception - Above the limit 0 << 257", + "script": "0x0002010198", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x2C9BA6F08FFCCB999A3FB8636A485A0D12BD2E54", + "instructionPointer": 4, + "nextInstruction": "SHL", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0x0101" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x2C9BA6F08FFCCB999A3FB8636A485A0D12BD2E54", + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "Exception - Below the limit 0 << -257", + "script": "0x0002FFFE98", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x29351D375C5FEB0E1ED756CDEF8AB48260E8FCA9", + "instructionPointer": 4, + "nextInstruction": "SHL", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0xFFFE" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x29351D375C5FEB0E1ED756CDEF8AB48260E8FCA9", + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test 0 << 256", + "script": "0x0002000198", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x47DA2CD2329F2C882A4DB611D048D53F2CB07085", + "instructionPointer": 4, + "nextInstruction": "SHL", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0x0001" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHR.json b/tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHR.json new file mode 100644 index 00000000..7695f246 --- /dev/null +++ b/tests/neo-vm.Tests/Tests/OpCodes/Numeric/SHR.json @@ -0,0 +1,185 @@ +{ + "category": "Numeric", + "name": "SHR", + "tests": + [ + { + "name": "Exception - Above the limit 0 >> 257", + "script": "0x0002010199", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x630746B0F60CA8E6304D1B0E408F81F5DDED6D30", + "instructionPointer": 4, + "nextInstruction": "SHR", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0x0101" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x630746B0F60CA8E6304D1B0E408F81F5DDED6D30", + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "Exception - Below the limit 0 >> -257", + "script": "0x0002FFFE99", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x5A6B6669635678A89A431DF8DA8DA10040757401", + "instructionPointer": 4, + "nextInstruction": "SHR", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0xFFFE" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x5A6B6669635678A89A431DF8DA8DA10040757401", + "instructionPointer": 5, + "nextInstruction": "RET", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "Real test 0 >> 256", + "script": "0x0002000199", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x5BD16259EC43AD1842D577CE778357CC9A4D0756", + "instructionPointer": 4, + "nextInstruction": "SHR", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0x0001" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/neo-vm.Tests/Tests/OpCodes/Splice/SIZE.json b/tests/neo-vm.Tests/Tests/OpCodes/Splice/SIZE.json new file mode 100644 index 00000000..769b9d61 --- /dev/null +++ b/tests/neo-vm.Tests/Tests/OpCodes/Splice/SIZE.json @@ -0,0 +1,449 @@ +{ + "category": "Splice", + "name": "SIZE", + "tests": + [ + { + "name": "Without push", + "script": "0x82", + "steps": + [ + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x199D36C1A12C52C0BB6A4BBA08416B6A4882AB03", + "instructionPointer": 1, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "With byte array", + "script": "0x02000182", + "steps": + [ + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0xCE35C075D88F85C3D5941D02A25DA9B6A7ADC145", + "instructionPointer": 3, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "0x0001" + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "Integer", + "value": 2 + } + ] + } + } + ] + }, + { + "name": "With bool (true)", + "script": "0x009182", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0xA1381D2014709A7478ACCDD42EC1F872B6003019", + "instructionPointer": 2, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Boolean", + "value": true + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "With bool (false)", + "script": "0x00919182", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x571EE6AAE82CD7A7DEA9B72BFDB59CFDF02C2627", + "instructionPointer": 3, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "Integer", + "value": 0 + } + ] + } + } + ] + }, + { + "name": "With integer (1)", + "script": "0x008B82", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0xC7603DB4A074CFAB99E263650CCDFB55F61E5834", + "instructionPointer": 2, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Integer", + "value": 1 + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "Error - With Map", + "script": "0xC782", + "steps": + [ + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x6325A13B672E260A92B9A970FBCA307174BC5F88", + "instructionPointer": 1, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Map", + "value": + { + } + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x6325A13B672E260A92B9A970FBCA307174BC5F88", + "instructionPointer": 2, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "Error - With Array", + "script": "0x51C582", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0xFDF69EAC8E9D2F72E18A79DBD115C1D67C07AC66", + "instructionPointer": 2, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Array", + "value": + [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0xFDF69EAC8E9D2F72E18A79DBD115C1D67C07AC66", + "instructionPointer": 3, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "Error - With Struct", + "script": "0x51C682", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x2B5AAE9E01537EFE3070F1863CBBE6AFB4F1CABB", + "instructionPointer": 2, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Struct", + "value": + [ + { + "type": "Boolean", + "value": false + } + ] + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x2B5AAE9E01537EFE3070F1863CBBE6AFB4F1CABB", + "instructionPointer": 3, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "Error - With Interop", + "script": "0x682953797374656D2E457865637574696F6E456E67696E652E476574536372697074436F6E7461696E657282", + "message": "0x00", + "steps": + [ + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x767813581E20207A133CAB14CBF34AFF8D5CF476", + "instructionPointer": 43, + "nextInstruction": "SIZE", + "evaluationStack": + [ + { + "type": "Interop", + "value": "MessageProvider" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x767813581E20207A133CAB14CBF34AFF8D5CF476", + "instructionPointer": 44, + "nextInstruction": "RET" + } + ] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/neo-vm.Tests/UtVMJson.cs b/tests/neo-vm.Tests/UtVMJson.cs index 68e56542..ebb538f4 100644 --- a/tests/neo-vm.Tests/UtVMJson.cs +++ b/tests/neo-vm.Tests/UtVMJson.cs @@ -15,6 +15,7 @@ public class UtVMJson : VMJsonTestBase [InlineData("./Tests/OpCodes/Splice")] [InlineData("./Tests/OpCodes/Control")] [InlineData("./Tests/OpCodes/Push")] + [InlineData("./Tests/OpCodes/Numeric")] [InlineData("./Tests/OpCodes/Exceptions")] public void TestJson(string path) { From 55cb897170376a6772f5b61deb56aa72f3891729 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 27 Mar 2019 12:47:48 +0100 Subject: [PATCH 6/6] SUBSTR ut (#101) --- .../Tests/OpCodes/Splice/SUBSTR.json | 594 ++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 tests/neo-vm.Tests/Tests/OpCodes/Splice/SUBSTR.json diff --git a/tests/neo-vm.Tests/Tests/OpCodes/Splice/SUBSTR.json b/tests/neo-vm.Tests/Tests/OpCodes/Splice/SUBSTR.json new file mode 100644 index 00000000..046d0798 --- /dev/null +++ b/tests/neo-vm.Tests/Tests/OpCodes/Splice/SUBSTR.json @@ -0,0 +1,594 @@ +{ + "category": "Splice", + "name": "SUBSTR", + "tests": + [ + { + "name": "Without 3 items", + "script": "0x52537F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x477766E630E8A26363CDFEAFDC15F884F24521FA", + "instructionPointer": 2, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 2 + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x477766E630E8A26363CDFEAFDC15F884F24521FA", + "instructionPointer": 3, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "With negative count", + "script": "0x00004F7F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0xE52CA90A76F6DB5E03960BD34B28FE2EB232A4CF", + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + { + "type": "Integer", + "value": -1 + }, + { + "type": "ByteArray", + "value": "" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0xE52CA90A76F6DB5E03960BD34B28FE2EB232A4CF", + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "With map as string", + "script": "0xC700007F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x1D3F6C449E647B28807E07676B0912D2CFF8943C", + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + { + "type": "ByteArray", + "value": "" + }, + { + "type": "ByteArray", + "value": "" + }, + { + "type": "Map", + "value": + { + + } + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x1D3F6C449E647B28807E07676B0912D2CFF8943C", + "instructionPointer": 4, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "With map as count", + "script": "0x0000C77F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x1D0635D1DC475E73A9333B074947D775344709D9", + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + + { + "type": "Map", + "value": + { + + } + }, + { + "type": "ByteArray", + "value": "" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x1D0635D1DC475E73A9333B074947D775344709D9", + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": + [ + + { + "type": "ByteArray", + "value": "" + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "With map as index", + "script": "0x00C7007F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0xB475742AE49D3FA7F999DABF7AE163DF6967A774", + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + + { + "type": "ByteArray", + "value": "" + }, + { + "type": "Map", + "value": + { + + } + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0xB475742AE49D3FA7F999DABF7AE163DF6967A774", + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": + [ + + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "With negative index", + "script": "0x004F007F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x1D172F90690761A23FC3F9FAB26A25F3BB0B9766", + "instructionPointer": 3, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + + { + "type": "ByteArray", + "value": "" + }, + { + "type": "Integer", + "value": -1 + }, + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x1D172F90690761A23FC3F9FAB26A25F3BB0B9766", + "instructionPointer": 4, + "nextInstruction": "RET", + "evaluationStack": + [ + + { + "type": "ByteArray", + "value": "" + } + ] + } + ] + } + } + ] + }, + { + "name": "Overflow string index", + "script": "0x02000159527F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x2E60619059949C5EC62F43D9A8A20F8328A5F57E", + "instructionPointer": 5, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + + { + "type": "Integer", + "value": 2 + }, + { + "type": "Integer", + "value": 9 + }, + { + "type": "ByteArray", + "value": "0x0001" + } + ] + } + ] + } + }, + { + "actions": + [ + "StepInto" + ], + "result": + { + "state": "Fault", + "invocationStack": + [ + { + "scriptHash": "0x2E60619059949C5EC62F43D9A8A20F8328A5F57E", + "instructionPointer": 6, + "nextInstruction": "RET" + } + ] + } + } + ] + }, + { + "name": "Overflow string count", + "script": "0x0A0001020304050607080952597F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x1A22B972CE61B7A5664B0A0B545C38DEDD97C330", + "instructionPointer": 13, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + + { + "type": "Integer", + "value": 9 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "ByteArray", + "value": "0x00010203040506070809" + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "ByteArray", + "value": "0x0203040506070809" + } + ] + } + } + ] + }, + { + "name": "Real test", + "script": "0x0A0001020304050607080952517F", + "steps": + [ + { + "actions": + [ + "StepInto", + "StepInto", + "StepInto" + ], + "result": + { + "state": "Break", + "invocationStack": + [ + { + "scriptHash": "0x12F14CB19471D589199BA63A71FF89AFFA56F949", + "instructionPointer": 13, + "nextInstruction": "SUBSTR", + "evaluationStack": + [ + + { + "type": "Integer", + "value": 1 + }, + { + "type": "Integer", + "value": 2 + }, + { + "type": "ByteArray", + "value": "0x00010203040506070809" + } + ] + } + ] + } + }, + { + "actions": + [ + "Execute" + ], + "result": + { + "state": "Halt", + "resultStack": + [ + { + "type": "ByteArray", + "value": "0x02" + } + ] + } + } + ] + } + ] +} \ No newline at end of file