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 @@
-
+