From d56d836ef1017bdef9190e833dd5ef756e24dada Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 17 Jul 2024 18:41:13 -0400 Subject: [PATCH 1/6] Fixed Parsing Smart Contract Script Analysis --- src/Neo.CLI/CLI/MainService.Tools.cs | 78 ++++++++-------------- src/Neo.CLI/Tools/VMInstruction.cs | 98 ++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 49 deletions(-) create mode 100644 src/Neo.CLI/Tools/VMInstruction.cs diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index 66723d7df9..47d5f18f2f 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -13,7 +13,6 @@ using Neo.Cryptography.ECC; using Neo.IO; using Neo.SmartContract; -using Neo.VM; using Neo.Wallets; using System; using System.Collections.Generic; @@ -21,6 +20,7 @@ using System.Linq; using System.Numerics; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; namespace Neo.CLI @@ -56,7 +56,8 @@ private void OnParseCommand(string value) if (result != null) { - Console.WriteLine($"{pair.Key,-30}\t{result}"); + ConsoleHelper.Info("", "-----", pair.Key, "-----"); + ConsoleHelper.Info("", result, Environment.NewLine); any = true; } } @@ -417,62 +418,41 @@ private static string Base64Fixed(string str) [ParseFunction("Base64 Smart Contract Script Analysis")] private string? ScriptsToOpCode(string base64) { - Script script; try { - var scriptData = Convert.FromBase64String(base64); - script = new Script(scriptData.ToArray(), true); + var bytes = Convert.FromBase64String(base64); + var sb = new StringBuilder(); + + foreach (var instruct in new VMInstruction(bytes)) + { + if (instruct.OperandSize == 0) + sb.AppendFormat("{0:X04} {1}{2}", instruct.Position, instruct.OpCode, Environment.NewLine); + else + sb.AppendFormat("{0:X04} {1,-10}{2}{3}", instruct.Position, instruct.OpCode, DecodeOperand(instruct), Environment.NewLine); + } + + return sb.ToString(); } - catch (Exception) + catch { return null; } - return ScriptsToOpCode(script); } - private string ScriptsToOpCode(Script script) + private string DecodeOperand(VMInstruction instruction) { - //Initialize all InteropService - var dic = new Dictionary(); - ApplicationEngine.Services.ToList().ForEach(p => dic.Add(p.Value.Hash, p.Value.Name)); - - //Analyzing Scripts - var ip = 0; - Instruction instruction; - var result = new List(); - while (ip < script.Length && (instruction = script.GetInstruction(ip)) != null) - { - ip += instruction.Size; - - var op = instruction.OpCode; - - if (op.ToString().StartsWith("PUSHINT")) - { - var operand = instruction.Operand.ToArray(); - result.Add($"{op} {new BigInteger(operand)}"); - } - else if (op == OpCode.SYSCALL) - { - var operand = instruction.Operand.ToArray(); - result.Add($"{op} {dic[BitConverter.ToUInt32(operand)]}"); - } - else - { - if (!instruction.Operand.IsEmpty && instruction.Operand.Length > 0) - { - var operand = instruction.Operand.ToArray(); - var ascii = Encoding.Default.GetString(operand); - ascii = ascii.Any(p => p < '0' || p > 'z') ? operand.ToHexString() : ascii; - - result.Add($"{op} {(operand.Length == 20 ? new UInt160(operand).ToString() : ascii)}"); - } - else - { - result.Add($"{op}"); - } - } - } - return Environment.NewLine + string.Join("\r\n", result.ToArray()); + var operand = instruction.Operand[instruction.OperandPrefixSize..].ToArray(); + var asStr = Encoding.UTF8.GetString(operand); + return instruction.OpCode switch + { + VM.OpCode.PUSHINT8 => $"{Unsafe.As(ref operand[0])}", + VM.OpCode.PUSHINT16 => $"{Unsafe.As(ref operand[0])}", + VM.OpCode.PUSHINT32 => $"{Unsafe.As(ref operand[0])}", + VM.OpCode.PUSHINT64 => $"{Unsafe.As(ref operand[0])}", + VM.OpCode.PUSHINT128 or VM.OpCode.PUSHINT256 => $"{new BigInteger(operand)}", + VM.OpCode.SYSCALL => $"[{ApplicationEngine.Services[Unsafe.As(ref operand[0])].Name}]", + _ => asStr.All(a => char.IsAscii(a)) ? $"\"{asStr}\"" : Convert.ToHexString(operand), + }; } /// diff --git a/src/Neo.CLI/Tools/VMInstruction.cs b/src/Neo.CLI/Tools/VMInstruction.cs new file mode 100644 index 0000000000..88dfc39568 --- /dev/null +++ b/src/Neo.CLI/Tools/VMInstruction.cs @@ -0,0 +1,98 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// VMInstruction.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System; +using System.Buffers.Binary; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Neo.CLI +{ + using Neo.VM; + + [DebuggerDisplay("OpCode={OpCode}, OperandSize={OperandSize}")] + internal sealed class VMInstruction : IEnumerable + { + private const int OpCodeSize = 1; + + public int Position { get; private init; } + public OpCode OpCode { get; private init; } + public ReadOnlyMemory Operand { get; private init; } + public int OperandSize { get; private init; } + public int OperandPrefixSize { get; private init; } + + private static readonly int[] s_operandSizeTable = new int[256]; + private static readonly int[] s_operandSizePrefixTable = new int[256]; + + private readonly ReadOnlyMemory _script; + + public VMInstruction(ReadOnlyMemory script, int start = 0) + { + if (script.IsEmpty) + throw new Exception("Bad Script."); + + var opcode = (OpCode)script.Span[start]; + + if (Enum.IsDefined(opcode) == false) + throw new InvalidDataException($"Invalid opcode at Position: {start}."); + + OperandPrefixSize = s_operandSizePrefixTable[(int)opcode]; + OperandSize = OperandPrefixSize switch + { + 0 => s_operandSizeTable[(int)opcode], + 1 => script.Span[start + 1], + 2 => BinaryPrimitives.ReadUInt16LittleEndian(script.Span[(start + 1)..]), + 4 => unchecked((int)BinaryPrimitives.ReadUInt32LittleEndian(script.Span[(start + 1)..])), + _ => throw new InvalidDataException($"Invalid opcode prefix at Position: {start}."), + }; + + OperandSize += OperandPrefixSize; + + if (start + OperandSize + OpCodeSize > script.Length) + throw new IndexOutOfRangeException("Operand size exceeds end of script."); + + Operand = script.Slice(start + OpCodeSize, OperandSize); + + _script = script; + OpCode = opcode; + Position = start; + } + + static VMInstruction() + { + foreach (var field in typeof(OpCode).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + var attr = field.GetCustomAttribute(); + if (attr == null) continue; + + var index = (uint)(OpCode)field.GetValue(null)!; + s_operandSizeTable[index] = attr.Size; + s_operandSizePrefixTable[index] = attr.SizePrefix; + } + } + + public IEnumerator GetEnumerator() + { + var nip = Position + OperandSize + OpCodeSize; + yield return this; + + VMInstruction? instruct; + for (var ip = nip; ip < _script.Length; ip += instruct.OperandSize + OpCodeSize) + yield return instruct = new VMInstruction(_script, ip); + } + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); + } +} From 797d076561b851738b71ae28d09fdc850426789f Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Tue, 23 Jul 2024 05:59:00 -0400 Subject: [PATCH 2/6] Add more opcode outputs --- src/Neo.CLI/CLI/MainService.Tools.cs | 55 ++++++++++++++++++++++++---- src/Neo.CLI/Tools/VMInstruction.cs | 15 ++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index 47d5f18f2f..6e59261c63 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -422,13 +422,15 @@ private static string Base64Fixed(string str) { var bytes = Convert.FromBase64String(base64); var sb = new StringBuilder(); + var line = 0; foreach (var instruct in new VMInstruction(bytes)) { if (instruct.OperandSize == 0) - sb.AppendFormat("{0:X04} {1}{2}", instruct.Position, instruct.OpCode, Environment.NewLine); + sb.AppendFormat("L{0:D04}:{1:X04} {2}{3}", line, instruct.Position, instruct.OpCode, Environment.NewLine); else - sb.AppendFormat("{0:X04} {1,-10}{2}{3}", instruct.Position, instruct.OpCode, DecodeOperand(instruct), Environment.NewLine); + sb.AppendFormat("L{0:D04}:{1:X04} {2,-10}{3}{4}", line, instruct.Position, instruct.OpCode, DecodeOperand(instruct), Environment.NewLine); + line++; } return sb.ToString(); @@ -443,15 +445,52 @@ private string DecodeOperand(VMInstruction instruction) { var operand = instruction.Operand[instruction.OperandPrefixSize..].ToArray(); var asStr = Encoding.UTF8.GetString(operand); + var readable = asStr.All(char.IsAscii); return instruction.OpCode switch { - VM.OpCode.PUSHINT8 => $"{Unsafe.As(ref operand[0])}", - VM.OpCode.PUSHINT16 => $"{Unsafe.As(ref operand[0])}", - VM.OpCode.PUSHINT32 => $"{Unsafe.As(ref operand[0])}", - VM.OpCode.PUSHINT64 => $"{Unsafe.As(ref operand[0])}", - VM.OpCode.PUSHINT128 or VM.OpCode.PUSHINT256 => $"{new BigInteger(operand)}", + VM.OpCode.JMP or + VM.OpCode.JMPIF or + VM.OpCode.JMPIFNOT or + VM.OpCode.JMPEQ or + VM.OpCode.JMPNE or + VM.OpCode.JMPGT or + VM.OpCode.JMPLT or + VM.OpCode.CALL or + VM.OpCode.ENDTRY => $"[{checked(instruction.Position + instruction.AsToken()):X02}]", + VM.OpCode.PUSHA or + VM.OpCode.JMP_L or + VM.OpCode.JMPIF_L or + VM.OpCode.JMPIFNOT_L or + VM.OpCode.JMPEQ_L or + VM.OpCode.JMPNE_L or + VM.OpCode.JMPGT_L or + VM.OpCode.JMPLT_L or + VM.OpCode.CALL_L or + VM.OpCode.ENDTRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X04}]", + VM.OpCode.TRY or + VM.OpCode.INITSLOT => $"{instruction.AsToken()}, {instruction.AsToken(1)}", + VM.OpCode.TRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X04}, {checked(instruction.Position + instruction.AsToken()):X04}]", + VM.OpCode.NEWARRAY_T or + VM.OpCode.ISTYPE or + VM.OpCode.CONVERT => $"{instruction.AsToken():X02}", + VM.OpCode.STLOC or + VM.OpCode.LDLOC or + VM.OpCode.LDSFLD or + VM.OpCode.STSFLD or + VM.OpCode.LDARG or + VM.OpCode.STARG or + VM.OpCode.INITSSLOT => $"{instruction.AsToken()}", + VM.OpCode.PUSHINT8 => $"{instruction.AsToken()}", + VM.OpCode.PUSHINT16 => $"{instruction.AsToken()}", + VM.OpCode.PUSHINT32 => $"{instruction.AsToken()}", + VM.OpCode.PUSHINT64 => $"{instruction.AsToken()}", + VM.OpCode.PUSHINT128 or + VM.OpCode.PUSHINT256 => $"{new BigInteger(operand)}", VM.OpCode.SYSCALL => $"[{ApplicationEngine.Services[Unsafe.As(ref operand[0])].Name}]", - _ => asStr.All(a => char.IsAscii(a)) ? $"\"{asStr}\"" : Convert.ToHexString(operand), + VM.OpCode.PUSHDATA1 or + VM.OpCode.PUSHDATA2 or + VM.OpCode.PUSHDATA4 => readable ? $"{Convert.ToHexString(operand)} // {asStr}" : Convert.ToHexString(operand), + _ => readable ? $"\"{asStr}\"" : $"{Convert.ToHexString(operand)} // {asStr}", }; } diff --git a/src/Neo.CLI/Tools/VMInstruction.cs b/src/Neo.CLI/Tools/VMInstruction.cs index 88dfc39568..4949570de2 100644 --- a/src/Neo.CLI/Tools/VMInstruction.cs +++ b/src/Neo.CLI/Tools/VMInstruction.cs @@ -20,6 +20,7 @@ namespace Neo.CLI { using Neo.VM; + using System.Runtime.CompilerServices; [DebuggerDisplay("OpCode={OpCode}, OperandSize={OperandSize}")] internal sealed class VMInstruction : IEnumerable @@ -94,5 +95,19 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public T AsToken(uint index = 0) + where T : unmanaged + { + var size = Unsafe.SizeOf(); + + if (size > OperandSize) + throw new ArgumentOutOfRangeException(nameof(T), $"SizeOf {typeof(T).FullName} is too big for operand. OpCode: {OpCode}."); + if (size + index > OperandSize) + throw new ArgumentOutOfRangeException(nameof(index), $"SizeOf {typeof(T).FullName} + {index} is too big for operand. OpCode: {OpCode}."); + + var bytes = Operand[..OperandSize].ToArray(); + return Unsafe.As(ref bytes[index]); + } } } From a54d04ae8b4c11ed5735888bb7563d4daa2e5874 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Tue, 23 Jul 2024 06:34:07 -0400 Subject: [PATCH 3/6] Bug fixes --- src/Neo.CLI/CLI/MainService.Tools.cs | 13 +++++++------ src/Neo.CLI/Tools/VMInstruction.cs | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index 6e59261c63..744ca7f4aa 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -445,7 +445,7 @@ private string DecodeOperand(VMInstruction instruction) { var operand = instruction.Operand[instruction.OperandPrefixSize..].ToArray(); var asStr = Encoding.UTF8.GetString(operand); - var readable = asStr.All(char.IsAscii); + var readable = asStr.All(char.IsAsciiLetterOrDigit); return instruction.OpCode switch { VM.OpCode.JMP or @@ -456,7 +456,7 @@ VM.OpCode.JMPNE or VM.OpCode.JMPGT or VM.OpCode.JMPLT or VM.OpCode.CALL or - VM.OpCode.ENDTRY => $"[{checked(instruction.Position + instruction.AsToken()):X02}]", + VM.OpCode.ENDTRY => $"[{checked(instruction.Position + instruction.AsToken()):X08}]", VM.OpCode.PUSHA or VM.OpCode.JMP_L or VM.OpCode.JMPIF_L or @@ -466,10 +466,11 @@ VM.OpCode.JMPNE_L or VM.OpCode.JMPGT_L or VM.OpCode.JMPLT_L or VM.OpCode.CALL_L or - VM.OpCode.ENDTRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X04}]", - VM.OpCode.TRY or + VM.OpCode.ENDTRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X08}]", + VM.OpCode.TRY => $"[{instruction.AsToken():X02}, {instruction.AsToken(1):X02}]", VM.OpCode.INITSLOT => $"{instruction.AsToken()}, {instruction.AsToken(1)}", - VM.OpCode.TRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X04}, {checked(instruction.Position + instruction.AsToken()):X04}]", + VM.OpCode.TRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X08}, {checked(instruction.Position + instruction.AsToken()):X08}]", + VM.OpCode.CALLT => $"[{checked(instruction.Position + instruction.AsToken()):X08}]", VM.OpCode.NEWARRAY_T or VM.OpCode.ISTYPE or VM.OpCode.CONVERT => $"{instruction.AsToken():X02}", @@ -490,7 +491,7 @@ VM.OpCode.PUSHINT128 or VM.OpCode.PUSHDATA1 or VM.OpCode.PUSHDATA2 or VM.OpCode.PUSHDATA4 => readable ? $"{Convert.ToHexString(operand)} // {asStr}" : Convert.ToHexString(operand), - _ => readable ? $"\"{asStr}\"" : $"{Convert.ToHexString(operand)} // {asStr}", + _ => readable ? $"\"{asStr}\"" : $"{Convert.ToHexString(operand)}", }; } diff --git a/src/Neo.CLI/Tools/VMInstruction.cs b/src/Neo.CLI/Tools/VMInstruction.cs index 4949570de2..150dfd79bf 100644 --- a/src/Neo.CLI/Tools/VMInstruction.cs +++ b/src/Neo.CLI/Tools/VMInstruction.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.VM; using System; using System.Buffers.Binary; using System.Collections; @@ -19,7 +20,6 @@ namespace Neo.CLI { - using Neo.VM; using System.Runtime.CompilerServices; [DebuggerDisplay("OpCode={OpCode}, OperandSize={OperandSize}")] From 830ce47f85130d62bb61fa82373e39ca20b2cf82 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Mon, 29 Jul 2024 18:27:27 +0200 Subject: [PATCH 4/6] Move to ToString --- src/Neo.CLI/CLI/MainService.Tools.cs | 57 +------------------------- src/Neo.CLI/Tools/VMInstruction.cs | 61 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index 744ca7f4aa..fcfac3cd0a 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -20,7 +20,6 @@ using System.Linq; using System.Numerics; using System.Reflection; -using System.Runtime.CompilerServices; using System.Text; namespace Neo.CLI @@ -429,7 +428,7 @@ private static string Base64Fixed(string str) if (instruct.OperandSize == 0) sb.AppendFormat("L{0:D04}:{1:X04} {2}{3}", line, instruct.Position, instruct.OpCode, Environment.NewLine); else - sb.AppendFormat("L{0:D04}:{1:X04} {2,-10}{3}{4}", line, instruct.Position, instruct.OpCode, DecodeOperand(instruct), Environment.NewLine); + sb.AppendFormat("L{0:D04}:{1:X04} {2,-10}{3}{4}", line, instruct.Position, instruct.OpCode, instruct, Environment.NewLine); line++; } @@ -441,60 +440,6 @@ private static string Base64Fixed(string str) } } - private string DecodeOperand(VMInstruction instruction) - { - var operand = instruction.Operand[instruction.OperandPrefixSize..].ToArray(); - var asStr = Encoding.UTF8.GetString(operand); - var readable = asStr.All(char.IsAsciiLetterOrDigit); - return instruction.OpCode switch - { - VM.OpCode.JMP or - VM.OpCode.JMPIF or - VM.OpCode.JMPIFNOT or - VM.OpCode.JMPEQ or - VM.OpCode.JMPNE or - VM.OpCode.JMPGT or - VM.OpCode.JMPLT or - VM.OpCode.CALL or - VM.OpCode.ENDTRY => $"[{checked(instruction.Position + instruction.AsToken()):X08}]", - VM.OpCode.PUSHA or - VM.OpCode.JMP_L or - VM.OpCode.JMPIF_L or - VM.OpCode.JMPIFNOT_L or - VM.OpCode.JMPEQ_L or - VM.OpCode.JMPNE_L or - VM.OpCode.JMPGT_L or - VM.OpCode.JMPLT_L or - VM.OpCode.CALL_L or - VM.OpCode.ENDTRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X08}]", - VM.OpCode.TRY => $"[{instruction.AsToken():X02}, {instruction.AsToken(1):X02}]", - VM.OpCode.INITSLOT => $"{instruction.AsToken()}, {instruction.AsToken(1)}", - VM.OpCode.TRY_L => $"[{checked(instruction.Position + instruction.AsToken()):X08}, {checked(instruction.Position + instruction.AsToken()):X08}]", - VM.OpCode.CALLT => $"[{checked(instruction.Position + instruction.AsToken()):X08}]", - VM.OpCode.NEWARRAY_T or - VM.OpCode.ISTYPE or - VM.OpCode.CONVERT => $"{instruction.AsToken():X02}", - VM.OpCode.STLOC or - VM.OpCode.LDLOC or - VM.OpCode.LDSFLD or - VM.OpCode.STSFLD or - VM.OpCode.LDARG or - VM.OpCode.STARG or - VM.OpCode.INITSSLOT => $"{instruction.AsToken()}", - VM.OpCode.PUSHINT8 => $"{instruction.AsToken()}", - VM.OpCode.PUSHINT16 => $"{instruction.AsToken()}", - VM.OpCode.PUSHINT32 => $"{instruction.AsToken()}", - VM.OpCode.PUSHINT64 => $"{instruction.AsToken()}", - VM.OpCode.PUSHINT128 or - VM.OpCode.PUSHINT256 => $"{new BigInteger(operand)}", - VM.OpCode.SYSCALL => $"[{ApplicationEngine.Services[Unsafe.As(ref operand[0])].Name}]", - VM.OpCode.PUSHDATA1 or - VM.OpCode.PUSHDATA2 or - VM.OpCode.PUSHDATA4 => readable ? $"{Convert.ToHexString(operand)} // {asStr}" : Convert.ToHexString(operand), - _ => readable ? $"\"{asStr}\"" : $"{Convert.ToHexString(operand)}", - }; - } - /// /// Checks if the string is null or cannot be printed. /// diff --git a/src/Neo.CLI/Tools/VMInstruction.cs b/src/Neo.CLI/Tools/VMInstruction.cs index 150dfd79bf..9ac2a42e07 100644 --- a/src/Neo.CLI/Tools/VMInstruction.cs +++ b/src/Neo.CLI/Tools/VMInstruction.cs @@ -20,7 +20,11 @@ namespace Neo.CLI { + using Neo.SmartContract; + using System.Linq; + using System.Numerics; using System.Runtime.CompilerServices; + using System.Text; [DebuggerDisplay("OpCode={OpCode}, OperandSize={OperandSize}")] internal sealed class VMInstruction : IEnumerable @@ -96,6 +100,8 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public override string ToString() => DecodeOperand(); + public T AsToken(uint index = 0) where T : unmanaged { @@ -109,5 +115,60 @@ public T AsToken(uint index = 0) var bytes = Operand[..OperandSize].ToArray(); return Unsafe.As(ref bytes[index]); } + + public string DecodeOperand() + { + var operand = Operand[OperandPrefixSize..].ToArray(); + var asStr = Encoding.UTF8.GetString(operand); + var readable = asStr.All(char.IsAsciiLetterOrDigit); + + return OpCode switch + { + OpCode.JMP or + OpCode.JMPIF or + OpCode.JMPIFNOT or + OpCode.JMPEQ or + OpCode.JMPNE or + OpCode.JMPGT or + OpCode.JMPLT or + OpCode.CALL or + OpCode.ENDTRY => $"[{checked(Position + AsToken()):X08}]", + OpCode.JMP_L or + OpCode.JMPIF_L or + OpCode.PUSHA or + OpCode.JMPIFNOT_L or + OpCode.JMPEQ_L or + OpCode.JMPNE_L or + OpCode.JMPGT_L or + OpCode.JMPLT_L or + OpCode.CALL_L or + OpCode.ENDTRY_L => $"[{checked(Position + AsToken()):X08}]", + OpCode.TRY => $"[{AsToken():X02}, {AsToken(1):X02}]", + OpCode.INITSLOT => $"{AsToken()}, {AsToken(1)}", + OpCode.TRY_L => $"[{checked(Position + AsToken()):X08}, {checked(Position + AsToken()):X08}]", + OpCode.CALLT => $"[{checked(Position + AsToken()):X08}]", + OpCode.NEWARRAY_T or + OpCode.ISTYPE or + OpCode.CONVERT => $"{AsToken():X02}", + OpCode.STLOC or + OpCode.LDLOC or + OpCode.LDSFLD or + OpCode.STSFLD or + OpCode.LDARG or + OpCode.STARG or + OpCode.INITSSLOT => $"{AsToken()}", + OpCode.PUSHINT8 => $"{AsToken()}", + OpCode.PUSHINT16 => $"{AsToken()}", + OpCode.PUSHINT32 => $"{AsToken()}", + OpCode.PUSHINT64 => $"{AsToken()}", + OpCode.PUSHINT128 or + OpCode.PUSHINT256 => $"{new BigInteger(operand)}", + OpCode.SYSCALL => $"[{ApplicationEngine.Services[Unsafe.As(ref operand[0])].Name}]", + OpCode.PUSHDATA1 or + OpCode.PUSHDATA2 or + OpCode.PUSHDATA4 => readable ? $"{Convert.ToHexString(operand)} // {asStr}" : Convert.ToHexString(operand), + _ => readable ? $"\"{asStr}\"" : $"{Convert.ToHexString(operand)}", + }; + } } } From 6bd036ecfeba7d6fbfe7a95768bca568d709a0b5 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Mon, 29 Jul 2024 18:28:47 +0200 Subject: [PATCH 5/6] Reorder using --- src/Neo.CLI/Tools/VMInstruction.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Neo.CLI/Tools/VMInstruction.cs b/src/Neo.CLI/Tools/VMInstruction.cs index 9ac2a42e07..28f17ef1cf 100644 --- a/src/Neo.CLI/Tools/VMInstruction.cs +++ b/src/Neo.CLI/Tools/VMInstruction.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.SmartContract; using Neo.VM; using System; using System.Buffers.Binary; @@ -16,16 +17,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; +using System.Numerics; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; namespace Neo.CLI { - using Neo.SmartContract; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Text; - [DebuggerDisplay("OpCode={OpCode}, OperandSize={OperandSize}")] internal sealed class VMInstruction : IEnumerable { From 360d54cf623edfbe6a8113c8fa58c39df2cc1a7f Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Mon, 29 Jul 2024 18:35:18 +0200 Subject: [PATCH 6/6] Change ToString --- src/Neo.CLI/CLI/MainService.Tools.cs | 2 +- src/Neo.CLI/Tools/VMInstruction.cs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index fcfac3cd0a..7195186763 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -428,7 +428,7 @@ private static string Base64Fixed(string str) if (instruct.OperandSize == 0) sb.AppendFormat("L{0:D04}:{1:X04} {2}{3}", line, instruct.Position, instruct.OpCode, Environment.NewLine); else - sb.AppendFormat("L{0:D04}:{1:X04} {2,-10}{3}{4}", line, instruct.Position, instruct.OpCode, instruct, Environment.NewLine); + sb.AppendFormat("L{0:D04}:{1:X04} {2,-10}{3}{4}", line, instruct.Position, instruct.OpCode, instruct.DecodeOperand(), Environment.NewLine); line++; } diff --git a/src/Neo.CLI/Tools/VMInstruction.cs b/src/Neo.CLI/Tools/VMInstruction.cs index 28f17ef1cf..9a4ba6a3d4 100644 --- a/src/Neo.CLI/Tools/VMInstruction.cs +++ b/src/Neo.CLI/Tools/VMInstruction.cs @@ -99,7 +99,12 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public override string ToString() => DecodeOperand(); + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendFormat("{1:X04} {2,-10}{3}{4}", Position, OpCode, DecodeOperand()); + return sb.ToString(); + } public T AsToken(uint index = 0) where T : unmanaged