diff --git a/neo-cli/CLI/MainService.Contracts.cs b/neo-cli/CLI/MainService.Contracts.cs index 1d411108f..ab12639ce 100644 --- a/neo-cli/CLI/MainService.Contracts.cs +++ b/neo-cli/CLI/MainService.Contracts.cs @@ -1,6 +1,7 @@ using Neo.ConsoleService; using Neo.IO.Json; using Neo.Network.P2P.Payloads; +using Neo.SmartContract; using Neo.SmartContract.Native; using System; using System.Linq; @@ -35,11 +36,82 @@ private void OnDeployCommand(string filePath, string manifestPath = null) UInt160 hash = SmartContract.Helper.GetContractHash(tx.Sender, nef.CheckSum, manifest.Name); Console.WriteLine($"Contract hash: {hash}"); - Console.WriteLine($"Gas: {new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals)}"); - Console.WriteLine(); + Console.WriteLine($"Gas consumed: {new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals)}"); + Console.WriteLine($"Network fee: {new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}"); + Console.WriteLine($"Total fee: {new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ReadUserInput("Relay tx? (no|yes)").IsYes()) // Add this in case just want to get hash but not relay + { + return; + } SignAndSendTx(NeoSystem.StoreView, tx); } + /// + /// Process "update" command + /// + /// File path + /// Manifest path + [ConsoleCommand("update", Category = "Contract Commands")] + private void OnUpdateCommand(UInt160 scriptHash, string filePath, string manifestPath, UInt160 sender, UInt160[] signerAccounts = null) + { + Signer[] signers = Array.Empty(); + + if (NoWallet()) return; + if (!NoWallet() && sender != null) + { + if (signerAccounts == null) + signerAccounts = new UInt160[1] { sender }; + else if (signerAccounts.Contains(sender) && signerAccounts[0] != sender) + { + var signersList = signerAccounts.ToList(); + signersList.Remove(sender); + signerAccounts = signersList.Prepend(sender).ToArray(); + } + else if (!signerAccounts.Contains(sender)) + { + signerAccounts = signerAccounts.Prepend(sender).ToArray(); + } + signers = signerAccounts.Select(p => new Signer() { Account = p, Scopes = WitnessScope.CalledByEntry }).ToArray(); + } + + Transaction tx = new Transaction + { + Signers = signers, + Attributes = Array.Empty(), + Witnesses = Array.Empty() + }; + + try + { + byte[] script = LoadUpdateScript(scriptHash, filePath, manifestPath, out var nef, out var manifest); + tx = CurrentWallet.MakeTransaction(NeoSystem.StoreView, script, sender, signers); + } + catch (InvalidOperationException e) + { + Console.WriteLine("Error: " + GetExceptionMessage(e)); + return; + } + + ContractState contract = NativeContract.ContractManagement.GetContract(NeoSystem.StoreView, scriptHash); + if (contract == null) + { + Console.WriteLine($"Can't upgrade, contract hash not exist: {scriptHash}"); + } + else + { + Console.WriteLine($"Contract hash: {scriptHash}"); + Console.WriteLine($"Updated times: {contract.UpdateCounter}"); + Console.WriteLine($"Gas consumed: {new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals)}"); + Console.WriteLine($"Network fee: {new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}"); + Console.WriteLine($"Total fee: {new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ReadUserInput("Relay tx? (no|yes)").IsYes()) // Add this in case just want to get hash but not relay + { + return; + } + SignAndSendTx(NeoSystem.StoreView, tx); + } + } + /// /// Process "invoke" command /// @@ -90,7 +162,9 @@ private void OnInvokeCommand(UInt160 scriptHash, string operation, JArray contra Console.WriteLine("Error: " + GetExceptionMessage(e)); return; } - if (!ReadUserInput("Relay tx(no|yes)").IsYes()) + Console.WriteLine($"Network fee: {new BigDecimal((BigInteger)tx.NetworkFee, NativeContract.GAS.Decimals)}"); + Console.WriteLine($"Total fee: {new BigDecimal((BigInteger)(tx.SystemFee + tx.NetworkFee), NativeContract.GAS.Decimals)} GAS"); + if (!ReadUserInput("Relay tx? (no|yes)").IsYes()) { return; } diff --git a/neo-cli/CLI/MainService.cs b/neo-cli/CLI/MainService.cs index 7a9b217a8..87dc2f024 100644 --- a/neo-cli/CLI/MainService.cs +++ b/neo-cli/CLI/MainService.cs @@ -279,6 +279,60 @@ private byte[] LoadDeploymentScript(string nefFilePath, string manifestFilePath, } } + private byte[] LoadUpdateScript(UInt160 scriptHash, string nefFilePath, string manifestFilePath, out NefFile nef, out ContractManifest manifest) + { + 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)); + } + + manifest = ContractManifest.Parse(File.ReadAllBytes(manifestFilePath)); + + // Read nef + + info = new FileInfo(nefFilePath); + if (!info.Exists || info.Length >= Transaction.MaxTransactionSize) + { + throw new ArgumentException(nameof(nefFilePath)); + } + + using (var stream = new BinaryReader(File.OpenRead(nefFilePath), Utility.StrictUTF8, false)) + { + nef = stream.ReadSerializable(); + } + + // Basic script checks + + Script script = new Script(nef.Script); + for (var i = 0; i < script.Length;) + { + // Check bad opcodes + + Instruction inst = script.GetInstruction(i); + if (inst is null || !Enum.IsDefined(typeof(OpCode), inst.OpCode)) + { + throw new FormatException($"OpCode not found at {i}-{((byte)inst.OpCode).ToString("x2")}"); + } + i += inst.Size; + } + + // Build script + + using (ScriptBuilder sb = new ScriptBuilder()) + { + sb.EmitDynamicCall(scriptHash, "update", nef.ToArray(), manifest.ToJson().ToString()); + return sb.ToArray(); + } + } + public override void OnStart(string[] args) { base.OnStart(args); diff --git a/neo-cli/neo-cli.csproj b/neo-cli/neo-cli.csproj index 52d94287e..46accbdee 100644 --- a/neo-cli/neo-cli.csproj +++ b/neo-cli/neo-cli.csproj @@ -23,7 +23,7 @@ - +