diff --git a/neo-cli/Services/ConsoleServiceBase.cs b/neo-cli/Services/ConsoleServiceBase.cs index 1ebc62b59..589ea99ae 100644 --- a/neo-cli/Services/ConsoleServiceBase.cs +++ b/neo-cli/Services/ConsoleServiceBase.cs @@ -348,11 +348,7 @@ private void RunConsole() } catch (Exception ex) { -#if DEBUG Console.WriteLine($"error: {ex.Message}"); -#else - Console.WriteLine("error"); -#endif } } diff --git a/neo-cli/Shell/MainService.cs b/neo-cli/Shell/MainService.cs index 54a29fc99..7523166de 100644 --- a/neo-cli/Shell/MainService.cs +++ b/neo-cli/Shell/MainService.cs @@ -140,10 +140,9 @@ private bool OnBroadcastCommand(string[] args) private bool OnDeployCommand(string[] args) { if (NoWallet()) return true; - byte[] script = LoadDeploymentScript( - /* filePath */ args[1], - /* hasStorage */ args[2].ToBool(), - /* isPayable */ args[3].ToBool(), + byte[] script = LoadDeploymentScript( + /* filePath */ args[1], + /* manifest */ args.Length == 2 ? "" : args[2], /* scriptHash */ out var scriptHash); Transaction tx; @@ -219,9 +218,26 @@ private bool OnInvokeCommand(string[] args) return SignAndSendTx(tx); } - private byte[] LoadDeploymentScript(string nefFilePath, bool hasStorage, bool isPayable, out UInt160 scriptHash) + private byte[] LoadDeploymentScript(string nefFilePath, string manifestFilePath, out UInt160 scriptHash) { - var info = new FileInfo(nefFilePath); + if (string.IsNullOrEmpty(manifestFilePath)) + { + manifestFilePath = Path.ChangeExtension(nefFilePath, ".manifest.json"); + } + + // Read manifest + + var info = new FileInfo(manifestFilePath); + if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) + { + throw new ArgumentException(nameof(manifestFilePath)); + } + + var manifest = ContractManifest.Parse(File.ReadAllText(manifestFilePath)); + + // Read nef + + info = new FileInfo(nefFilePath); if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) { throw new ArgumentException(nameof(nefFilePath)); @@ -232,14 +248,48 @@ private byte[] LoadDeploymentScript(string nefFilePath, bool hasStorage, bool is { file = stream.ReadSerializable(); } - scriptHash = file.ScriptHash; - ContractFeatures properties = ContractFeatures.NoProperty; - if (hasStorage) properties |= ContractFeatures.HasStorage; - if (isPayable) properties |= ContractFeatures.Payable; + // Basic script checks + + using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) + { + var context = engine.LoadScript(file.Script); + + while (context.InstructionPointer <= context.Script.Length) + { + // Check bad opcodes + + var ci = context.CurrentInstruction; + + if (ci == null || !Enum.IsDefined(typeof(OpCode), ci.OpCode)) + { + throw new FormatException($"OpCode not found at {context.InstructionPointer}-{((byte)ci.OpCode).ToString("x2")}"); + } + + switch (ci.OpCode) + { + case OpCode.SYSCALL: + { + // Check bad syscalls (NEO2) + + if (!InteropService.SupportedMethods().ContainsKey(ci.TokenU32)) + { + throw new FormatException($"Syscall not found {ci.TokenU32.ToString("x2")}. Are you using a NEO2 smartContract?"); + } + break; + } + } + + context.InstructionPointer += ci.Size; + } + } + + // Build script + + scriptHash = file.ScriptHash; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitSysCall(InteropService.Neo_Contract_Create, file.Script, properties); + sb.EmitSysCall(InteropService.Neo_Contract_Create, file.Script, manifest.ToJson().ToString()); return sb.ToArray(); } } @@ -572,7 +622,7 @@ private bool OnHelpCommand(string[] args) "\tsend
\n" + "\tsign \n" + "Contract Commands:\n" + - "\tdeploy [manifestFile]\n" + "\tinvoke [optionally quoted params separated by space]\n" + "Node Commands:\n" + "\tshow state\n" +