From dcf3e3215bc9a0b713787c081f28b95e9f3fad1f Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Fri, 16 Aug 2024 16:17:52 +0800 Subject: [PATCH 01/19] test RpcServer.Utilities --- src/Plugins/RpcServer/RpcServer.Utilities.cs | 4 +- .../UT_RpcServer.Utilities.cs | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs diff --git a/src/Plugins/RpcServer/RpcServer.Utilities.cs b/src/Plugins/RpcServer/RpcServer.Utilities.cs index f9b874c824..e08dfd28ee 100644 --- a/src/Plugins/RpcServer/RpcServer.Utilities.cs +++ b/src/Plugins/RpcServer/RpcServer.Utilities.cs @@ -18,7 +18,7 @@ namespace Neo.Plugins.RpcServer partial class RpcServer { [RpcMethod] - protected virtual JToken ListPlugins(JArray _params) + protected internal virtual JToken ListPlugins(JArray _params) { return new JArray(Plugin.Plugins .OrderBy(u => u.Name) @@ -34,7 +34,7 @@ protected virtual JToken ListPlugins(JArray _params) } [RpcMethod] - protected virtual JToken ValidateAddress(JArray _params) + protected internal virtual JToken ValidateAddress(JArray _params) { string address = Result.Ok_Or(() => _params[0].AsString(), RpcError.InvalidParams.WithData($"Invlid address format: {_params[0]}")); JObject json = new(); diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs new file mode 100644 index 0000000000..cd0c7bf1ff --- /dev/null +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RpcServer.Wallet.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 FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Json; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.UnitTests; +using Neo.UnitTests.Extensions; +using System; +using System.IO; +using System.Linq; + +namespace Neo.Plugins.RpcServer.Tests; + +public partial class UT_RpcServer +{ + [TestMethod] + public void TestListPlugins() + { + JArray resp = (JArray)_rpcServer.ListPlugins([]); + Assert.AreEqual(resp.Count, 0); + Plugins.Plugin.Plugins.Add(new RpcServerPlugin()); + resp = (JArray)_rpcServer.ListPlugins([]); + Assert.AreEqual(resp.Count, 2); + foreach (JObject p in resp) + Assert.AreEqual(p["name"], "RpcServer"); + } + + [TestMethod] + public void TestValidateAddress() + { + string validAddr = "NM7Aky765FG8NhhwtxjXRx7jEL1cnw7PBP"; + JObject resp = (JObject)_rpcServer.ValidateAddress([validAddr]); + Assert.AreEqual(resp["address"], validAddr); + Assert.AreEqual(resp["isvalid"], true); + string invalidAddr = "ANeo2toNeo3MigrationAddressxwPB2Hz"; + resp = (JObject)_rpcServer.ValidateAddress([invalidAddr]); + Assert.AreEqual(resp["address"], invalidAddr); + Assert.AreEqual(resp["isvalid"], false); + } +} From 3c51bd4403018cb8958ce3dc1593da1fcddceba7 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Fri, 16 Aug 2024 16:49:26 +0800 Subject: [PATCH 02/19] test invoke function and script --- .../UT_RpcServer.SmartContract.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs new file mode 100644 index 0000000000..98defbe104 --- /dev/null +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -0,0 +1,68 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RpcServer.Wallet.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 FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Json; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.UnitTests; +using Neo.UnitTests.Extensions; +using System; +using System.IO; +using System.Linq; + +namespace Neo.Plugins.RpcServer.Tests; + +public partial class UT_RpcServer +{ + static readonly string neoScriptHash = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"; + static readonly string NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; + static readonly JArray signers = [new JObject() + { + ["account"] = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", + ["scopes"] = "CalledByEntry", + }]; + + [TestMethod] + public void TestInvokeFunction() + { + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(neoScriptHash, "totalSupply", new JArray([]), signers, true)); + Assert.AreEqual(resp.Count, 7); + Assert.AreEqual(resp["script"], NeoTotalSupplyScript); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.IsTrue(resp.ContainsProperty("diagnostics")); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], neoScriptHash); + Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); + Assert.AreEqual(resp["stack"][0]["type"], "Integer"); + Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + } + + [TestMethod] + public void TestInvokeScript() + { + JObject resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTotalSupplyScript, signers, true)); + Assert.AreEqual(resp.Count, 7); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.IsTrue(resp.ContainsProperty("diagnostics")); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], neoScriptHash); + Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); + Assert.AreEqual(resp["stack"][0]["type"], "Integer"); + Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + } +} From 07e506dca3f29683c6a8c52ae6b81d3e8d6a52a2 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Fri, 16 Aug 2024 17:16:56 +0800 Subject: [PATCH 03/19] TestTraverseIterator --- .../UT_RpcServer.SmartContract.cs | 19 +++++++++++++++ .../UT_RpcServer.cs | 24 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 98defbe104..fc55577e01 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -65,4 +65,23 @@ public void TestInvokeScript() Assert.AreEqual(resp["stack"][0]["type"], "Integer"); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); } + + [TestMethod] + public void TestTraverseIterator() + { + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(neoScriptHash, "getAllCandidates", new JArray([]), signers, true)); + string sessionId = resp["session"].AsString(); + string iteratorId = resp["stack"][0]["id"].AsString(); + JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + Assert.AreEqual(respArray.Count, 0); + _rpcServer.TerminateSession([sessionId]); + try + { + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + } + catch (RpcException e) + { + Assert.AreEqual(e.Message, "Unknown session"); + } + } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index 2561171c81..952167c3f7 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -19,6 +19,7 @@ using Neo.Wallets; using Neo.Wallets.NEP6; using System; +using System.Net; using System.Text; namespace Neo.Plugins.RpcServer.Tests @@ -39,7 +40,28 @@ public void TestSetup() _memoryStore = new MemoryStore(); _memoryStoreProvider = new TestMemoryStoreProvider(_memoryStore); _neoSystem = new NeoSystem(TestProtocolSettings.SoleNode, _memoryStoreProvider); - _rpcServer = new RpcServer(_neoSystem, RpcServerSettings.Default); + _rpcServer = new RpcServer(_neoSystem, new RpcServerSettings + { + Network = 5195086u, + BindAddress = IPAddress.None, + SslCert = string.Empty, + SslCertPassword = string.Empty, + MaxGasInvoke = (long)new BigDecimal(10M, NativeContract.GAS.Decimals).Value, + MaxFee = (long)new BigDecimal(0.1M, NativeContract.GAS.Decimals).Value, + TrustedAuthorities = Array.Empty(), + EnableCors = true, + AllowOrigins = Array.Empty(), + KeepAliveTimeout = 60, + RequestHeadersTimeout = 15, + MaxIteratorResultItems = 100, + MaxStackSize = ushort.MaxValue, + DisabledMethods = Array.Empty(), + MaxConcurrentConnections = 40, + MaxRequestBodySize = 5 * 1024 * 1024, + SessionEnabled = true, + SessionExpirationTime = TimeSpan.FromSeconds(60), + FindStoragePageSize = 50 + }); _walletAccount = _wallet.Import("KxuRSsHgJMb3AMSN6B9P3JHNGMFtxmuimqgR9MmXPcv3CLLfusTd"); var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(_walletAccount.ScriptHash); var snapshot = _neoSystem.GetSnapshotCache(); From 14d409a33da06b2d9b6d554d7af2034006582e20 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Fri, 16 Aug 2024 17:26:48 +0800 Subject: [PATCH 04/19] TestGetUnclaimedGas --- .../UT_RpcServer.SmartContract.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index fc55577e01..99ec5ffb8d 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.AspNetCore.Http; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Json; @@ -19,6 +20,7 @@ using Neo.SmartContract.Native; using Neo.UnitTests; using Neo.UnitTests.Extensions; +using Neo.Wallets; using System; using System.IO; using System.Linq; @@ -84,4 +86,15 @@ public void TestTraverseIterator() Assert.AreEqual(e.Message, "Unknown session"); } } + + [TestMethod] + public void TestGetUnclaimedGas() + { + string address = Contract + .CreateSignatureRedeemScript(TestProtocolSettings.SoleNode.StandbyCommittee[0]) + .ToScriptHash().ToAddress(ProtocolSettings.Default.AddressVersion); + JObject resp = (JObject)_rpcServer.GetUnclaimedGas([address]); + Assert.AreEqual(resp["unclaimed"], "0"); + Assert.AreEqual(resp["address"], address); + } } From c3f7f4b4146ba0bb3c8e1c9bcefcca2c02c6a26f Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Sat, 17 Aug 2024 12:51:47 +0800 Subject: [PATCH 05/19] RpcServerSettings.Default with { SessionEnabled = true } --- .../UT_RpcServer.cs | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index 952167c3f7..4343acf6fe 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -40,28 +40,7 @@ public void TestSetup() _memoryStore = new MemoryStore(); _memoryStoreProvider = new TestMemoryStoreProvider(_memoryStore); _neoSystem = new NeoSystem(TestProtocolSettings.SoleNode, _memoryStoreProvider); - _rpcServer = new RpcServer(_neoSystem, new RpcServerSettings - { - Network = 5195086u, - BindAddress = IPAddress.None, - SslCert = string.Empty, - SslCertPassword = string.Empty, - MaxGasInvoke = (long)new BigDecimal(10M, NativeContract.GAS.Decimals).Value, - MaxFee = (long)new BigDecimal(0.1M, NativeContract.GAS.Decimals).Value, - TrustedAuthorities = Array.Empty(), - EnableCors = true, - AllowOrigins = Array.Empty(), - KeepAliveTimeout = 60, - RequestHeadersTimeout = 15, - MaxIteratorResultItems = 100, - MaxStackSize = ushort.MaxValue, - DisabledMethods = Array.Empty(), - MaxConcurrentConnections = 40, - MaxRequestBodySize = 5 * 1024 * 1024, - SessionEnabled = true, - SessionExpirationTime = TimeSpan.FromSeconds(60), - FindStoragePageSize = 50 - }); + _rpcServer = new RpcServer(_neoSystem, RpcServerSettings.Default with { SessionEnabled = true }); _walletAccount = _wallet.Import("KxuRSsHgJMb3AMSN6B9P3JHNGMFtxmuimqgR9MmXPcv3CLLfusTd"); var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(_walletAccount.ScriptHash); var snapshot = _neoSystem.GetSnapshotCache(); From e97576524a75a71e31488ace7e1edd191f6f7571 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Mon, 19 Aug 2024 11:28:46 +0800 Subject: [PATCH 06/19] test call with storage changes and events --- .../UT_RpcServer.SmartContract.cs | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 99ec5ffb8d..d4bdbf6d81 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -29,38 +29,77 @@ namespace Neo.Plugins.RpcServer.Tests; public partial class UT_RpcServer { - static readonly string neoScriptHash = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"; + static readonly string NeoScriptHash = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"; + static readonly string GasScriptHash = "0xd2a4cff31913016155e38e474a2c06d08be276cf"; static readonly string NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; - static readonly JArray signers = [new JObject() + static readonly UInt160 ValidatorScriptHash = Contract + .CreateSignatureRedeemScript(TestProtocolSettings.SoleNode.StandbyCommittee[0]) + .ToScriptHash(); + static readonly string ValidatorAddress = ValidatorScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); + static readonly UInt160 MultisigScriptHash = Contract + .CreateMultiSigRedeemScript(1, TestProtocolSettings.SoleNode.StandbyCommittee) + .ToScriptHash(); + static readonly string MultisigAddress = MultisigScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); + + static readonly JArray validatorSigner = [new JObject() { - ["account"] = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", + ["account"] = ValidatorScriptHash.ToString(), + ["scopes"] = "CalledByEntry", + }]; + static readonly JArray multisigSigner = [new JObject() + { + ["account"] = MultisigScriptHash.ToString(), ["scopes"] = "CalledByEntry", }]; [TestMethod] public void TestInvokeFunction() { - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(neoScriptHash, "totalSupply", new JArray([]), signers, true)); + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "totalSupply", new JArray([]), validatorSigner, true)); Assert.AreEqual(resp.Count, 7); Assert.AreEqual(resp["script"], NeoTotalSupplyScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], neoScriptHash); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); + Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 0); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); Assert.AreEqual(resp["stack"][0]["type"], "Integer"); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + + // This call triggers not only NEO but also unclaimed GAS + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "transfer", new JArray([ + new JObject() { ["type"] = "Hash160", ["value"] = MultisigScriptHash.ToString() }, + new JObject() { ["type"] = "Hash160", ["value"] = ValidatorScriptHash.ToString() }, + new JObject() { ["type"] = "Integer", ["value"] = "1" }, + new JObject() { ["type"] = "Any" }, + ]), multisigSigner, true)); + Assert.AreEqual(resp.Count, 7); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.IsTrue(resp.ContainsProperty("diagnostics")); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); + Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); + Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["exception"], null); + JArray notifications = (JArray)resp["notifications"]; + Assert.AreEqual(notifications.Count, 2); + Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); + Assert.AreEqual(notifications[0]["contract"].AsString(), NeoScriptHash); + Assert.AreEqual(((JArray)resp["notifications"])[0]["state"]["value"][2]["value"], "1"); + Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); + Assert.AreEqual(notifications[1]["contract"].AsString(), GasScriptHash); + Assert.AreEqual(((JArray)resp["notifications"])[1]["state"]["value"][2]["value"], "50000000"); } [TestMethod] public void TestInvokeScript() { - JObject resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTotalSupplyScript, signers, true)); + JObject resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTotalSupplyScript, validatorSigner, true)); Assert.AreEqual(resp.Count, 7); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], neoScriptHash); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); @@ -71,7 +110,7 @@ public void TestInvokeScript() [TestMethod] public void TestTraverseIterator() { - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(neoScriptHash, "getAllCandidates", new JArray([]), signers, true)); + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "getAllCandidates", new JArray([]), validatorSigner, true)); string sessionId = resp["session"].AsString(); string iteratorId = resp["stack"][0]["id"].AsString(); JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); @@ -90,11 +129,11 @@ public void TestTraverseIterator() [TestMethod] public void TestGetUnclaimedGas() { - string address = Contract - .CreateSignatureRedeemScript(TestProtocolSettings.SoleNode.StandbyCommittee[0]) - .ToScriptHash().ToAddress(ProtocolSettings.Default.AddressVersion); - JObject resp = (JObject)_rpcServer.GetUnclaimedGas([address]); + JObject resp = (JObject)_rpcServer.GetUnclaimedGas([MultisigAddress]); + Assert.AreEqual(resp["unclaimed"], "50000000"); + Assert.AreEqual(resp["address"], MultisigAddress); + resp = (JObject)_rpcServer.GetUnclaimedGas([ValidatorAddress]); Assert.AreEqual(resp["unclaimed"], "0"); - Assert.AreEqual(resp["address"], address); + Assert.AreEqual(resp["address"], ValidatorAddress); } } From 851b4b488a839d3d049909ae20c8d6cd0bee8a06 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Mon, 19 Aug 2024 14:25:53 +0800 Subject: [PATCH 07/19] use wallet in invokefunction --- .../UT_RpcServer.SmartContract.cs | 9 ++++++--- tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index d4bdbf6d81..50b850a1f2 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -55,8 +55,9 @@ public partial class UT_RpcServer [TestMethod] public void TestInvokeFunction() { + _rpcServer.wallet = _wallet; JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "totalSupply", new JArray([]), validatorSigner, true)); - Assert.AreEqual(resp.Count, 7); + Assert.AreEqual(resp.Count, 8); Assert.AreEqual(resp["script"], NeoTotalSupplyScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); @@ -67,6 +68,8 @@ public void TestInvokeFunction() Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); Assert.AreEqual(resp["stack"][0]["type"], "Integer"); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + Assert.IsTrue(resp.ContainsProperty("tx")); + _rpcServer.wallet = null; // This call triggers not only NEO but also unclaimed GAS resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "transfer", new JArray([ @@ -86,10 +89,10 @@ public void TestInvokeFunction() Assert.AreEqual(notifications.Count, 2); Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); Assert.AreEqual(notifications[0]["contract"].AsString(), NeoScriptHash); - Assert.AreEqual(((JArray)resp["notifications"])[0]["state"]["value"][2]["value"], "1"); + Assert.AreEqual(notifications[0]["state"]["value"][2]["value"], "1"); Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); Assert.AreEqual(notifications[1]["contract"].AsString(), GasScriptHash); - Assert.AreEqual(((JArray)resp["notifications"])[1]["state"]["value"][2]["value"], "50000000"); + Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); } [TestMethod] diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index 4343acf6fe..c07d534fd7 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -40,7 +40,7 @@ public void TestSetup() _memoryStore = new MemoryStore(); _memoryStoreProvider = new TestMemoryStoreProvider(_memoryStore); _neoSystem = new NeoSystem(TestProtocolSettings.SoleNode, _memoryStoreProvider); - _rpcServer = new RpcServer(_neoSystem, RpcServerSettings.Default with { SessionEnabled = true }); + _rpcServer = new RpcServer(_neoSystem, RpcServerSettings.Default with { SessionEnabled = true, Network = TestProtocolSettings.SoleNode.Network }); _walletAccount = _wallet.Import("KxuRSsHgJMb3AMSN6B9P3JHNGMFtxmuimqgR9MmXPcv3CLLfusTd"); var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(_walletAccount.ScriptHash); var snapshot = _neoSystem.GetSnapshotCache(); From 54ae25f4e64caa84f7e4d87d086269b88bba4752 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Mon, 19 Aug 2024 14:30:27 +0800 Subject: [PATCH 08/19] use invalid wallet --- .../UT_RpcServer.SmartContract.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 50b850a1f2..45081ed1d4 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -56,6 +56,7 @@ public partial class UT_RpcServer public void TestInvokeFunction() { _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "totalSupply", new JArray([]), validatorSigner, true)); Assert.AreEqual(resp.Count, 8); Assert.AreEqual(resp["script"], NeoTotalSupplyScript); @@ -69,7 +70,6 @@ public void TestInvokeFunction() Assert.AreEqual(resp["stack"][0]["type"], "Integer"); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); Assert.IsTrue(resp.ContainsProperty("tx")); - _rpcServer.wallet = null; // This call triggers not only NEO but also unclaimed GAS resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "transfer", new JArray([ @@ -84,7 +84,7 @@ public void TestInvokeFunction() Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); Assert.AreEqual(resp["state"], "HALT"); - Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash.ToString()} is not found"); JArray notifications = (JArray)resp["notifications"]; Assert.AreEqual(notifications.Count, 2); Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); @@ -93,6 +93,8 @@ public void TestInvokeFunction() Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); Assert.AreEqual(notifications[1]["contract"].AsString(), GasScriptHash); Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); + + _rpcServer.wallet = null; } [TestMethod] From eefa8c930b5fe7600827c48818f556161ea5d8e6 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 20 Aug 2024 10:29:15 +0800 Subject: [PATCH 09/19] invoke without signer --- .../UT_RpcServer.SmartContract.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 45081ed1d4..f9a74ad4d2 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -24,6 +24,7 @@ using System; using System.IO; using System.Linq; +using System.Text; namespace Neo.Plugins.RpcServer.Tests; @@ -32,6 +33,7 @@ public partial class UT_RpcServer static readonly string NeoScriptHash = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"; static readonly string GasScriptHash = "0xd2a4cff31913016155e38e474a2c06d08be276cf"; static readonly string NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; + static readonly string NeoTransferScript = "CxEMFPlu76Cuc\u002BbgteStE4ozsOWTNUdrDBQtYNweHko3YcnMFOes3ceblcI/lRTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I="; static readonly UInt160 ValidatorScriptHash = Contract .CreateSignatureRedeemScript(TestProtocolSettings.SoleNode.StandbyCommittee[0]) .ToScriptHash(); @@ -71,6 +73,16 @@ public void TestInvokeFunction() Assert.AreEqual(resp["stack"][0]["value"], "100000000"); Assert.IsTrue(resp.ContainsProperty("tx")); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "symbol")); + Assert.AreEqual(resp.Count, 6); + Assert.IsTrue(resp.ContainsProperty("script")); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); + Assert.AreEqual(resp["stack"][0]["type"], "ByteString"); + Assert.AreEqual(resp["stack"][0]["value"], Convert.ToBase64String(Encoding.UTF8.GetBytes("NEO"))); + // This call triggers not only NEO but also unclaimed GAS resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "transfer", new JArray([ new JObject() { ["type"] = "Hash160", ["value"] = MultisigScriptHash.ToString() }, @@ -79,6 +91,7 @@ public void TestInvokeFunction() new JObject() { ["type"] = "Any" }, ]), multisigSigner, true)); Assert.AreEqual(resp.Count, 7); + Assert.AreEqual(resp["script"], NeoTransferScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); @@ -110,6 +123,11 @@ public void TestInvokeScript() Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); Assert.AreEqual(resp["stack"][0]["type"], "Integer"); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + + resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTransferScript)); + Assert.AreEqual(resp.Count, 6); + Assert.AreEqual(resp["stack"][0]["type"], "Boolean"); + Assert.AreEqual(resp["stack"][0]["value"], false); } [TestMethod] From b53a6861c517a1ee880983c40c4beb872c8f3b89 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 20 Aug 2024 11:49:31 +0800 Subject: [PATCH 10/19] all cases for TraverseIterator --- .../RpcServer/RpcServer.SmartContract.cs | 4 +- .../UT_RpcServer.SmartContract.cs | 98 ++++++++++++++++--- .../UT_RpcServer.cs | 18 +++- 3 files changed, 106 insertions(+), 14 deletions(-) diff --git a/src/Plugins/RpcServer/RpcServer.SmartContract.cs b/src/Plugins/RpcServer/RpcServer.SmartContract.cs index 70edc4cedb..a3de939f4b 100644 --- a/src/Plugins/RpcServer/RpcServer.SmartContract.cs +++ b/src/Plugins/RpcServer/RpcServer.SmartContract.cs @@ -39,7 +39,7 @@ private void Initialize_SmartContract() timer = new(OnTimer, null, settings.SessionExpirationTime, settings.SessionExpirationTime); } - private void Dispose_SmartContract() + internal void Dispose_SmartContract() { timer?.Dispose(); Session[] toBeDestroyed; @@ -52,7 +52,7 @@ private void Dispose_SmartContract() session.Dispose(); } - private void OnTimer(object state) + internal void OnTimer(object state) { List<(Guid Id, Session Session)> toBeDestroyed = new(); lock (sessions) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index f9a74ad4d2..5fe8c0d7f3 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -13,9 +13,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests; @@ -25,13 +27,12 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; namespace Neo.Plugins.RpcServer.Tests; public partial class UT_RpcServer { - static readonly string NeoScriptHash = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"; - static readonly string GasScriptHash = "0xd2a4cff31913016155e38e474a2c06d08be276cf"; static readonly string NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; static readonly string NeoTransferScript = "CxEMFPlu76Cuc\u002BbgteStE4ozsOWTNUdrDBQtYNweHko3YcnMFOes3ceblcI/lRTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I="; static readonly UInt160 ValidatorScriptHash = Contract @@ -59,12 +60,12 @@ public void TestInvokeFunction() { _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "totalSupply", new JArray([]), validatorSigner, true)); + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "totalSupply", new JArray([]), validatorSigner, true)); Assert.AreEqual(resp.Count, 8); Assert.AreEqual(resp["script"], NeoTotalSupplyScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash.ToString()); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 0); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); @@ -73,7 +74,7 @@ public void TestInvokeFunction() Assert.AreEqual(resp["stack"][0]["value"], "100000000"); Assert.IsTrue(resp.ContainsProperty("tx")); - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "symbol")); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "symbol")); Assert.AreEqual(resp.Count, 6); Assert.IsTrue(resp.ContainsProperty("script")); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); @@ -84,7 +85,7 @@ public void TestInvokeFunction() Assert.AreEqual(resp["stack"][0]["value"], Convert.ToBase64String(Encoding.UTF8.GetBytes("NEO"))); // This call triggers not only NEO but also unclaimed GAS - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "transfer", new JArray([ + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "transfer", new JArray([ new JObject() { ["type"] = "Hash160", ["value"] = MultisigScriptHash.ToString() }, new JObject() { ["type"] = "Hash160", ["value"] = ValidatorScriptHash.ToString() }, new JObject() { ["type"] = "Integer", ["value"] = "1" }, @@ -94,17 +95,17 @@ public void TestInvokeFunction() Assert.AreEqual(resp["script"], NeoTransferScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash.ToString()); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash.ToString()} is not found"); JArray notifications = (JArray)resp["notifications"]; Assert.AreEqual(notifications.Count, 2); Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); - Assert.AreEqual(notifications[0]["contract"].AsString(), NeoScriptHash); + Assert.AreEqual(notifications[0]["contract"].AsString(), NeoScriptHash.ToString()); Assert.AreEqual(notifications[0]["state"]["value"][2]["value"], "1"); Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); - Assert.AreEqual(notifications[1]["contract"].AsString(), GasScriptHash); + Assert.AreEqual(notifications[1]["contract"].AsString(), GasScriptHash.ToString()); Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); _rpcServer.wallet = null; @@ -117,7 +118,7 @@ public void TestInvokeScript() Assert.AreEqual(resp.Count, 7); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash.ToString()); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); @@ -133,7 +134,8 @@ public void TestInvokeScript() [TestMethod] public void TestTraverseIterator() { - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash, "getAllCandidates", new JArray([]), validatorSigner, true)); + // GetAllCandidates that should return 0 candidates + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); string sessionId = resp["session"].AsString(); string iteratorId = resp["stack"][0]["id"].AsString(); JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); @@ -147,6 +149,80 @@ public void TestTraverseIterator() { Assert.AreEqual(e.Message, "Unknown session"); } + + // register candidate in snapshot + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "registerCandidate", + new JArray([new JObject() + { + ["type"] = "PublicKey", + ["value"] = TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString(), + }]), validatorSigner, true)); + Assert.AreEqual(resp["state"], "HALT"); + SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); + Transaction? tx = new Transaction + { + Nonce = 233, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + _neoSystem.Settings.MaxValidUntilBlockIncrement, + Signers = [new Signer() { Account = ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], + Attributes = Array.Empty(), + Script = Convert.FromBase64String(resp["script"].AsString()), + Witnesses = null, + }; + ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: _neoSystem.Settings, gas: 1200_0000_0000); + engine.SnapshotCache.Commit(); + + // GetAllCandidates that should return 1 candidate + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + sessionId = resp["session"].AsString(); + iteratorId = resp["stack"][0]["id"].AsString(); + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + Assert.AreEqual(respArray.Count, 1); + Assert.AreEqual(respArray[0]["type"], "Struct"); + JArray value = (JArray)respArray[0]["value"]; + Assert.AreEqual(value.Count, 2); + Assert.AreEqual(value[0]["type"], "ByteString"); + Assert.AreEqual(value[0]["value"], Convert.ToBase64String(TestProtocolSettings.SoleNode.StandbyCommittee[0].ToArray())); + Assert.AreEqual(value[1]["type"], "Integer"); + Assert.AreEqual(value[1]["value"], "0"); + + // No result when traversed again + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + Assert.AreEqual(respArray.Count, 0); + + // GetAllCandidates again + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + sessionId = resp["session"].AsString(); + iteratorId = resp["stack"][0]["id"].AsString(); + + // Insufficient result count limit + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 0]); + Assert.AreEqual(respArray.Count, 0); + + // Mocking session timeout + Thread.Sleep(_rpcServerSettings.SessionExpirationTime.Milliseconds + 1); + _rpcServer.OnTimer(new object()); + try + { + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + } + catch (RpcException e) + { + Assert.AreEqual(e.Message, "Unknown session"); + } + + // Mocking disposal + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + sessionId = resp["session"].AsString(); + iteratorId = resp["stack"][0]["id"].AsString(); + _rpcServer.Dispose_SmartContract(); + try + { + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + } + catch (RpcException e) + { + Assert.AreEqual(e.Message, "Unknown session"); + } } [TestMethod] diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index c07d534fd7..aaaf9d5705 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; @@ -19,7 +20,9 @@ using Neo.Wallets; using Neo.Wallets.NEP6; using System; +using System.Linq; using System.Net; +using System.Numerics; using System.Text; namespace Neo.Plugins.RpcServer.Tests @@ -28,19 +31,32 @@ namespace Neo.Plugins.RpcServer.Tests public partial class UT_RpcServer { private NeoSystem _neoSystem; + private RpcServerSettings _rpcServerSettings; private RpcServer _rpcServer; private TestMemoryStoreProvider _memoryStoreProvider; private MemoryStore _memoryStore; private readonly NEP6Wallet _wallet = TestUtils.GenerateTestWallet("123"); private WalletAccount _walletAccount; + static readonly UInt160 NeoScriptHash = UInt160.Parse("0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"); + static readonly UInt160 GasScriptHash = UInt160.Parse("0xd2a4cff31913016155e38e474a2c06d08be276cf"); + const byte NativePrefixAccount = 20; + const byte NativePrefixTotalSupply = 11; + [TestInitialize] public void TestSetup() { _memoryStore = new MemoryStore(); _memoryStoreProvider = new TestMemoryStoreProvider(_memoryStore); _neoSystem = new NeoSystem(TestProtocolSettings.SoleNode, _memoryStoreProvider); - _rpcServer = new RpcServer(_neoSystem, RpcServerSettings.Default with { SessionEnabled = true, Network = TestProtocolSettings.SoleNode.Network }); + _rpcServerSettings = RpcServerSettings.Default with + { + SessionEnabled = true, + SessionExpirationTime = TimeSpan.FromSeconds(1), + MaxGasInvoke = 1500_0000_0000, + Network = TestProtocolSettings.SoleNode.Network, + }; + _rpcServer = new RpcServer(_neoSystem, _rpcServerSettings); _walletAccount = _wallet.Import("KxuRSsHgJMb3AMSN6B9P3JHNGMFtxmuimqgR9MmXPcv3CLLfusTd"); var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(_walletAccount.ScriptHash); var snapshot = _neoSystem.GetSnapshotCache(); From c0b98920c1082f4595a2c170f2dde63f7bd15dbc Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 20 Aug 2024 12:54:30 +0800 Subject: [PATCH 11/19] traversing same session twice; not expired session --- .../UT_RpcServer.SmartContract.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 5fe8c0d7f3..2947cf6aa2 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -197,10 +197,18 @@ public void TestTraverseIterator() // Insufficient result count limit respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 0]); Assert.AreEqual(respArray.Count, 0); + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 1]); + Assert.AreEqual(respArray.Count, 1); + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 1]); + Assert.AreEqual(respArray.Count, 0); // Mocking session timeout Thread.Sleep(_rpcServerSettings.SessionExpirationTime.Milliseconds + 1); _rpcServer.OnTimer(new object()); + // build another session that did not expire + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + string notExpiredSessionId = resp["session"].AsString(); + string notExpiredIteratorId = resp["stack"][0]["id"].AsString(); try { respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); @@ -209,6 +217,8 @@ public void TestTraverseIterator() { Assert.AreEqual(e.Message, "Unknown session"); } + respArray = (JArray)_rpcServer.TraverseIterator([notExpiredSessionId, notExpiredIteratorId, 1]); + Assert.AreEqual(respArray.Count, 1); // Mocking disposal resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); From 50df712dd13c2ef63b267de4deb6fc64a81522fb Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 20 Aug 2024 13:01:55 +0800 Subject: [PATCH 12/19] cover OnTimer --- .../Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs | 4 ++-- tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 2947cf6aa2..c7bda0d6d4 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -203,12 +203,12 @@ public void TestTraverseIterator() Assert.AreEqual(respArray.Count, 0); // Mocking session timeout - Thread.Sleep(_rpcServerSettings.SessionExpirationTime.Milliseconds + 1); - _rpcServer.OnTimer(new object()); + Thread.Sleep((int)_rpcServerSettings.SessionExpirationTime.TotalMilliseconds + 1); // build another session that did not expire resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); string notExpiredSessionId = resp["session"].AsString(); string notExpiredIteratorId = resp["stack"][0]["id"].AsString(); + _rpcServer.OnTimer(new object()); try { respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index aaaf9d5705..e8ca5616e8 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -52,7 +52,7 @@ public void TestSetup() _rpcServerSettings = RpcServerSettings.Default with { SessionEnabled = true, - SessionExpirationTime = TimeSpan.FromSeconds(1), + SessionExpirationTime = TimeSpan.FromSeconds(0.3), MaxGasInvoke = 1500_0000_0000, Network = TestProtocolSettings.SoleNode.Network, }; From ac8992e9b50ec43ad94d781321802f698747e276 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 20 Aug 2024 13:29:44 +0800 Subject: [PATCH 13/19] test deserializing complex signers --- .../Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index c7bda0d6d4..94b9b6a983 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -48,6 +48,9 @@ public partial class UT_RpcServer { ["account"] = ValidatorScriptHash.ToString(), ["scopes"] = "CalledByEntry", + ["allowedcontracts"] = new JArray([NeoScriptHash.ToString(), GasScriptHash.ToString()]), + ["allowedgroups"] = new JArray([TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString()]), + ["rules"] = new JArray([new JObject() { ["action"] = "Allow", ["condition"] = new JObject { ["type"] = "CalledByEntry" } }]), }]; static readonly JArray multisigSigner = [new JObject() { From 60527f4f39c4dfdc05dd16c70c6203d0ed9c8616 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 21 Aug 2024 10:16:01 +0800 Subject: [PATCH 14/19] use Assert.ThrowsException --- .../UT_RpcServer.SmartContract.cs | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 94b9b6a983..1275c201fa 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -144,14 +144,7 @@ public void TestTraverseIterator() JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); Assert.AreEqual(respArray.Count, 0); _rpcServer.TerminateSession([sessionId]); - try - { - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); - } - catch (RpcException e) - { - Assert.AreEqual(e.Message, "Unknown session"); - } + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); // register candidate in snapshot resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "registerCandidate", @@ -212,14 +205,9 @@ public void TestTraverseIterator() string notExpiredSessionId = resp["session"].AsString(); string notExpiredIteratorId = resp["stack"][0]["id"].AsString(); _rpcServer.OnTimer(new object()); - try - { - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); - } - catch (RpcException e) - { - Assert.AreEqual(e.Message, "Unknown session"); - } + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); + // If you want to run the following line without exception, + // DO NOT BREAK IN THE DEBUGGER, because the session expires quickly respArray = (JArray)_rpcServer.TraverseIterator([notExpiredSessionId, notExpiredIteratorId, 1]); Assert.AreEqual(respArray.Count, 1); @@ -228,14 +216,7 @@ public void TestTraverseIterator() sessionId = resp["session"].AsString(); iteratorId = resp["stack"][0]["id"].AsString(); _rpcServer.Dispose_SmartContract(); - try - { - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); - } - catch (RpcException e) - { - Assert.AreEqual(e.Message, "Unknown session"); - } + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); } [TestMethod] From 87dbe590a76f23a749213edf2d6c220fb0ed2991 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 21 Aug 2024 10:38:49 +0800 Subject: [PATCH 15/19] TestSendFrom and TestSendMany --- .../UT_RpcServer.Wallet.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 2db71ae570..ce7ca370c1 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -14,6 +14,7 @@ using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads.Conditions; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests; @@ -212,6 +213,16 @@ public void TestSendFrom() var exception = Assert.ThrowsException(() => _rpcServer.SendFrom(paramsArray)); Assert.AreEqual(exception.HResult, RpcError.InvalidRequest.Code); TestUtilCloseWallet(); + + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendFrom(paramsArray); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], "CalledByEntry"); + _rpcServer.wallet = null; } [TestMethod] @@ -222,6 +233,16 @@ public void TestSendMany() var paramsArray = new JArray(from, to); var exception = Assert.ThrowsException(() => _rpcServer.SendMany(paramsArray), "Should throw RpcException for insufficient funds"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendMany(paramsArray); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], "CalledByEntry"); + _rpcServer.wallet = null; } [TestMethod] @@ -233,6 +254,16 @@ public void TestSendToAddress() var paramsArray = new JArray(assetId.ToString(), to, amount); var exception = Assert.ThrowsException(() => _rpcServer.SendToAddress(paramsArray), "Should throw RpcException for insufficient funds"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendToAddress(paramsArray); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], "CalledByEntry"); + _rpcServer.wallet = null; } [TestMethod] From 0295089cec5f071ad154bd8554e6e1c12e7bf0fa Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 21 Aug 2024 11:00:50 +0800 Subject: [PATCH 16/19] apply code review with `nameof` --- .../UT_RpcServer.SmartContract.cs | 61 ++++++++++--------- .../UT_RpcServer.Wallet.cs | 6 +- .../UT_RpcServer.cs | 2 - 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 1275c201fa..5659549bcf 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -17,6 +17,7 @@ using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads.Conditions; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -47,15 +48,15 @@ public partial class UT_RpcServer static readonly JArray validatorSigner = [new JObject() { ["account"] = ValidatorScriptHash.ToString(), - ["scopes"] = "CalledByEntry", - ["allowedcontracts"] = new JArray([NeoScriptHash.ToString(), GasScriptHash.ToString()]), + ["scopes"] = nameof(WitnessConditionType.CalledByEntry), + ["allowedcontracts"] = new JArray([NeoToken.NEO.Hash.ToString(), GasToken.GAS.Hash.ToString()]), ["allowedgroups"] = new JArray([TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString()]), - ["rules"] = new JArray([new JObject() { ["action"] = "Allow", ["condition"] = new JObject { ["type"] = "CalledByEntry" } }]), + ["rules"] = new JArray([new JObject() { ["action"] = nameof(WitnessRuleAction.Allow), ["condition"] = new JObject { ["type"] = nameof(WitnessConditionType.CalledByEntry) } }]), }]; static readonly JArray multisigSigner = [new JObject() { ["account"] = MultisigScriptHash.ToString(), - ["scopes"] = "CalledByEntry", + ["scopes"] = nameof(WitnessConditionType.CalledByEntry), }]; [TestMethod] @@ -63,52 +64,52 @@ public void TestInvokeFunction() { _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "totalSupply", new JArray([]), validatorSigner, true)); + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "totalSupply", new JArray([]), validatorSigner, true)); Assert.AreEqual(resp.Count, 8); Assert.AreEqual(resp["script"], NeoTotalSupplyScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash.ToString()); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 0); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); - Assert.AreEqual(resp["stack"][0]["type"], "Integer"); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); Assert.IsTrue(resp.ContainsProperty("tx")); - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "symbol")); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "symbol")); Assert.AreEqual(resp.Count, 6); Assert.IsTrue(resp.ContainsProperty("script")); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); - Assert.AreEqual(resp["stack"][0]["type"], "ByteString"); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.ByteString)); Assert.AreEqual(resp["stack"][0]["value"], Convert.ToBase64String(Encoding.UTF8.GetBytes("NEO"))); // This call triggers not only NEO but also unclaimed GAS - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "transfer", new JArray([ - new JObject() { ["type"] = "Hash160", ["value"] = MultisigScriptHash.ToString() }, - new JObject() { ["type"] = "Hash160", ["value"] = ValidatorScriptHash.ToString() }, - new JObject() { ["type"] = "Integer", ["value"] = "1" }, - new JObject() { ["type"] = "Any" }, + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "transfer", new JArray([ + new JObject() { ["type"] = nameof(ContractParameterType.Hash160), ["value"] = MultisigScriptHash.ToString() }, + new JObject() { ["type"] = nameof(ContractParameterType.Hash160), ["value"] = ValidatorScriptHash.ToString() }, + new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "1" }, + new JObject() { ["type"] = nameof(ContractParameterType.Any) }, ]), multisigSigner, true)); Assert.AreEqual(resp.Count, 7); Assert.AreEqual(resp["script"], NeoTransferScript); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash.ToString()); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash.ToString()} is not found"); JArray notifications = (JArray)resp["notifications"]; Assert.AreEqual(notifications.Count, 2); Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); - Assert.AreEqual(notifications[0]["contract"].AsString(), NeoScriptHash.ToString()); + Assert.AreEqual(notifications[0]["contract"].AsString(), NeoToken.NEO.Hash.ToString()); Assert.AreEqual(notifications[0]["state"]["value"][2]["value"], "1"); Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); - Assert.AreEqual(notifications[1]["contract"].AsString(), GasScriptHash.ToString()); + Assert.AreEqual(notifications[1]["contract"].AsString(), GasToken.GAS.Hash.ToString()); Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); _rpcServer.wallet = null; @@ -121,16 +122,16 @@ public void TestInvokeScript() Assert.AreEqual(resp.Count, 7); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoScriptHash.ToString()); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); Assert.AreEqual(resp["state"], "HALT"); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); - Assert.AreEqual(resp["stack"][0]["type"], "Integer"); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); Assert.AreEqual(resp["stack"][0]["value"], "100000000"); resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTransferScript)); Assert.AreEqual(resp.Count, 6); - Assert.AreEqual(resp["stack"][0]["type"], "Boolean"); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Boolean)); Assert.AreEqual(resp["stack"][0]["value"], false); } @@ -138,7 +139,7 @@ public void TestInvokeScript() public void TestTraverseIterator() { // GetAllCandidates that should return 0 candidates - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); string sessionId = resp["session"].AsString(); string iteratorId = resp["stack"][0]["id"].AsString(); JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); @@ -147,10 +148,10 @@ public void TestTraverseIterator() Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); // register candidate in snapshot - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "registerCandidate", + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "registerCandidate", new JArray([new JObject() { - ["type"] = "PublicKey", + ["type"] = nameof(ContractParameterType.PublicKey), ["value"] = TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString(), }]), validatorSigner, true)); Assert.AreEqual(resp["state"], "HALT"); @@ -168,17 +169,17 @@ public void TestTraverseIterator() engine.SnapshotCache.Commit(); // GetAllCandidates that should return 1 candidate - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); sessionId = resp["session"].AsString(); iteratorId = resp["stack"][0]["id"].AsString(); respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); Assert.AreEqual(respArray.Count, 1); - Assert.AreEqual(respArray[0]["type"], "Struct"); + Assert.AreEqual(respArray[0]["type"], nameof(Neo.VM.Types.Struct)); JArray value = (JArray)respArray[0]["value"]; Assert.AreEqual(value.Count, 2); - Assert.AreEqual(value[0]["type"], "ByteString"); + Assert.AreEqual(value[0]["type"], nameof(Neo.VM.Types.ByteString)); Assert.AreEqual(value[0]["value"], Convert.ToBase64String(TestProtocolSettings.SoleNode.StandbyCommittee[0].ToArray())); - Assert.AreEqual(value[1]["type"], "Integer"); + Assert.AreEqual(value[1]["type"], nameof(Neo.VM.Types.Integer)); Assert.AreEqual(value[1]["value"], "0"); // No result when traversed again @@ -186,7 +187,7 @@ public void TestTraverseIterator() Assert.AreEqual(respArray.Count, 0); // GetAllCandidates again - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); sessionId = resp["session"].AsString(); iteratorId = resp["stack"][0]["id"].AsString(); @@ -201,7 +202,7 @@ public void TestTraverseIterator() // Mocking session timeout Thread.Sleep((int)_rpcServerSettings.SessionExpirationTime.TotalMilliseconds + 1); // build another session that did not expire - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); string notExpiredSessionId = resp["session"].AsString(); string notExpiredIteratorId = resp["stack"][0]["id"].AsString(); _rpcServer.OnTimer(new object()); @@ -212,7 +213,7 @@ public void TestTraverseIterator() Assert.AreEqual(respArray.Count, 1); // Mocking disposal - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoScriptHash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); sessionId = resp["session"].AsString(); iteratorId = resp["stack"][0]["id"].AsString(); _rpcServer.Dispose_SmartContract(); diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index ce7ca370c1..3ca2c3d1c1 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -221,7 +221,7 @@ public void TestSendFrom() JArray signers = (JArray)resp["signers"]; Assert.AreEqual(signers.Count, 1); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], "CalledByEntry"); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessConditionType.CalledByEntry)); _rpcServer.wallet = null; } @@ -241,7 +241,7 @@ public void TestSendMany() JArray signers = (JArray)resp["signers"]; Assert.AreEqual(signers.Count, 1); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], "CalledByEntry"); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessConditionType.CalledByEntry)); _rpcServer.wallet = null; } @@ -262,7 +262,7 @@ public void TestSendToAddress() JArray signers = (JArray)resp["signers"]; Assert.AreEqual(signers.Count, 1); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], "CalledByEntry"); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessConditionType.CalledByEntry)); _rpcServer.wallet = null; } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index e8ca5616e8..b63380f803 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -38,8 +38,6 @@ public partial class UT_RpcServer private readonly NEP6Wallet _wallet = TestUtils.GenerateTestWallet("123"); private WalletAccount _walletAccount; - static readonly UInt160 NeoScriptHash = UInt160.Parse("0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"); - static readonly UInt160 GasScriptHash = UInt160.Parse("0xd2a4cff31913016155e38e474a2c06d08be276cf"); const byte NativePrefixAccount = 20; const byte NativePrefixTotalSupply = 11; From b8a28fba0c434eb67b909f601dd7d451d5388699 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 21 Aug 2024 11:26:21 +0800 Subject: [PATCH 17/19] test cancel transaction --- .../UT_RpcServer.SmartContract.cs | 4 ++-- .../UT_RpcServer.Utilities.cs | 2 +- .../UT_RpcServer.Wallet.cs | 20 ++++++++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 5659549bcf..bd802ac260 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -48,7 +48,7 @@ public partial class UT_RpcServer static readonly JArray validatorSigner = [new JObject() { ["account"] = ValidatorScriptHash.ToString(), - ["scopes"] = nameof(WitnessConditionType.CalledByEntry), + ["scopes"] = nameof(WitnessScope.CalledByEntry), ["allowedcontracts"] = new JArray([NeoToken.NEO.Hash.ToString(), GasToken.GAS.Hash.ToString()]), ["allowedgroups"] = new JArray([TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString()]), ["rules"] = new JArray([new JObject() { ["action"] = nameof(WitnessRuleAction.Allow), ["condition"] = new JObject { ["type"] = nameof(WitnessConditionType.CalledByEntry) } }]), @@ -56,7 +56,7 @@ public partial class UT_RpcServer static readonly JArray multisigSigner = [new JObject() { ["account"] = MultisigScriptHash.ToString(), - ["scopes"] = nameof(WitnessConditionType.CalledByEntry), + ["scopes"] = nameof(WitnessScope.CalledByEntry), }]; [TestMethod] diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs index cd0c7bf1ff..a0f2384ce5 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs @@ -36,7 +36,7 @@ public void TestListPlugins() resp = (JArray)_rpcServer.ListPlugins([]); Assert.AreEqual(resp.Count, 2); foreach (JObject p in resp) - Assert.AreEqual(p["name"], "RpcServer"); + Assert.AreEqual(p["name"], nameof(RpcServer)); } [TestMethod] diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 3ca2c3d1c1..25f065c6b9 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -221,7 +221,7 @@ public void TestSendFrom() JArray signers = (JArray)resp["signers"]; Assert.AreEqual(signers.Count, 1); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessConditionType.CalledByEntry)); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); _rpcServer.wallet = null; } @@ -241,7 +241,7 @@ public void TestSendMany() JArray signers = (JArray)resp["signers"]; Assert.AreEqual(signers.Count, 1); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessConditionType.CalledByEntry)); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); _rpcServer.wallet = null; } @@ -262,7 +262,7 @@ public void TestSendToAddress() JArray signers = (JArray)resp["signers"]; Assert.AreEqual(signers.Count, 1); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessConditionType.CalledByEntry)); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); _rpcServer.wallet = null; } @@ -364,6 +364,20 @@ public void TestCancelTransaction() exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(paramsArray), "Should throw RpcException for no opened wallet"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); TestUtilCloseWallet(); + + // Test valid cancel + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendFrom(new JArray(NativeContract.GAS.Hash.ToString(), _walletAccount.Address, _walletAccount.Address, "1")); + string txHash = resp["hash"].AsString(); + resp = (JObject)_rpcServer.CancelTransaction(new JArray(txHash, new JArray(ValidatorAddress), "1")); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.None)); + Assert.AreEqual(resp["attributes"][0]["type"], nameof(TransactionAttributeType.Conflicts)); + _rpcServer.wallet = null; } [TestMethod] From 468394369a8cf5949747312b09bbe4debb2c0e0a Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 21 Aug 2024 13:11:23 +0800 Subject: [PATCH 18/19] TestInvokeContractVerify --- src/Plugins/RpcServer/RpcServer.Wallet.cs | 2 +- .../UT_RpcServer.SmartContract.cs | 10 ++-- .../UT_RpcServer.Wallet.cs | 54 +++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 50f3c7a1e0..860337994d 100644 --- a/src/Plugins/RpcServer/RpcServer.Wallet.cs +++ b/src/Plugins/RpcServer/RpcServer.Wallet.cs @@ -494,7 +494,7 @@ private JObject GetVerificationResult(UInt160 scriptHash, ContractParameter[] ar { using var snapshot = system.GetSnapshotCache(); var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash).NotNull_Or(RpcError.UnknownContract); - var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, ContractBasicMethod.VerifyPCount).NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash)); + var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, args.Count()).NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash)); (md.ReturnType == ContractParameterType.Boolean).True_Or(RpcErrorFactory.InvalidContractVerification("The verify method doesn't return boolean value.")); Transaction tx = new() { diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index bd802ac260..f994137258 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -71,7 +71,7 @@ public void TestInvokeFunction() Assert.IsTrue(resp.ContainsProperty("diagnostics")); Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 0); - Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); @@ -82,7 +82,7 @@ public void TestInvokeFunction() Assert.AreEqual(resp.Count, 6); Assert.IsTrue(resp.ContainsProperty("script")); Assert.IsTrue(resp.ContainsProperty("gasconsumed")); - Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.ByteString)); @@ -101,7 +101,7 @@ public void TestInvokeFunction() Assert.IsTrue(resp.ContainsProperty("diagnostics")); Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); - Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash.ToString()} is not found"); JArray notifications = (JArray)resp["notifications"]; Assert.AreEqual(notifications.Count, 2); @@ -123,7 +123,7 @@ public void TestInvokeScript() Assert.IsTrue(resp.ContainsProperty("gasconsumed")); Assert.IsTrue(resp.ContainsProperty("diagnostics")); Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); - Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); Assert.AreEqual(resp["exception"], null); Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); @@ -154,7 +154,7 @@ public void TestTraverseIterator() ["type"] = nameof(ContractParameterType.PublicKey), ["value"] = TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString(), }]), validatorSigner, true)); - Assert.AreEqual(resp["state"], "HALT"); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); Transaction? tx = new Transaction { diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 25f065c6b9..827f6c232e 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -15,6 +15,7 @@ using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Network.P2P.Payloads.Conditions; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests; @@ -391,6 +392,59 @@ public void TestInvokeContractVerify() var invalidParamsArray = new JArray("invalid_script_hash"); exception = Assert.ThrowsException(() => _rpcServer.InvokeContractVerify(invalidParamsArray), "Should throw RpcException for invalid script hash"); Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); + + // deploy a contract with `Verify` method; + string _contractSourceCode = """ +using Neo;using Neo.SmartContract.Framework;using Neo.SmartContract.Framework.Services; +namespace ContractWithVerify{public class ContractWithVerify:SmartContract { + const byte PREFIX_OWNER = 0x20; + public static void _deploy(object data, bool update) { + if (update) return; + Storage.Put(Storage.CurrentContext, new byte[] { PREFIX_OWNER }, + ((Transaction)Runtime.ScriptContainer).Sender);} + public static bool Verify() => Runtime.CheckWitness((UInt160)Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_OWNER })); + public static bool Verify(byte prefix) => Runtime.CheckWitness((UInt160)Storage.Get(Storage.CurrentContext, new byte[] { prefix }));}} +"""; + string base64NefFile = "TkVGM05lby5Db21waWxlci5DU2hhcnAgMy43LjQrNjAzNGExODIxY2E3MDk0NjBlYzMxMzZjNzBjMmRjYzNiZWEuLi4AAAAAAGNXAAJ5JgQiGEEtUQgwE84MASDbMEGb9mfOQeY/GIRADAEg2zBBm/ZnzkGSXegxStgkCUrKABQoAzpB\u002BCfsjEBXAAERiEoQeNBBm/ZnzkGSXegxStgkCUrKABQoAzpB\u002BCfsjEDo2WhC"; + string manifest = """{"name":"ContractWithVerify","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"_deploy","parameters":[{"name":"data","type":"Any"},{"name":"update","type":"Boolean"}],"returntype":"Void","offset":0,"safe":false},{"name":"verify","parameters":[],"returntype":"Boolean","offset":31,"safe":false},{"name":"verify","parameters":[{"name":"prefix","type":"Integer"}],"returntype":"Boolean","offset":63,"safe":false}],"events":[]},"permissions":[],"trusts":[],"extra":{"nef":{"optimization":"All"}}}"""; + JObject deployResp = (JObject)_rpcServer.InvokeFunction(new JArray([ContractManagement.ContractManagement.Hash.ToString(), + "deploy", + new JArray([ + new JObject() { ["type"] = nameof(ContractParameterType.ByteArray), ["value"] = base64NefFile }, + new JObject() { ["type"] = nameof(ContractParameterType.String), ["value"] = manifest }, + ]), + validatorSigner])); + Assert.AreEqual(deployResp["state"], nameof(VM.VMState.HALT)); + UInt160 deployedScriptHash = new UInt160(Convert.FromBase64String(deployResp["notifications"][0]["state"]["value"][0]["value"].AsString())); + SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); + Transaction? tx = new Transaction + { + Nonce = 233, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + _neoSystem.Settings.MaxValidUntilBlockIncrement, + Signers = [new Signer() { Account = ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], + Attributes = Array.Empty(), + Script = Convert.FromBase64String(deployResp["script"].AsString()), + Witnesses = null, + }; + ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: _neoSystem.Settings, gas: 1200_0000_0000); + engine.SnapshotCache.Commit(); + + // invoke verify without signer; should return false + JObject resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString()]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), false); + // invoke verify with signer; should return true + resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([]), validatorSigner]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); + // invoke verify with wrong input value; should FAULT + resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "0" }]), validatorSigner]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.FAULT)); + Assert.AreEqual(resp["exception"], "Object reference not set to an instance of an object."); + // invoke verify with 1 param and signer; should return true + resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); } From a7197cb6d297b3449a67ccaff2c26d2346d92555 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 21 Aug 2024 13:31:19 +0800 Subject: [PATCH 19/19] improve error message --- src/Plugins/RpcServer/RpcErrorFactory.cs | 2 +- src/Plugins/RpcServer/RpcServer.Wallet.cs | 2 +- tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Plugins/RpcServer/RpcErrorFactory.cs b/src/Plugins/RpcServer/RpcErrorFactory.cs index 3d2ac7c9a5..4ab6f04cbf 100644 --- a/src/Plugins/RpcServer/RpcErrorFactory.cs +++ b/src/Plugins/RpcServer/RpcErrorFactory.cs @@ -33,7 +33,7 @@ public static RpcError NewCustomError(int code, string message, string data = nu public static RpcError BadRequest(string data) => RpcError.BadRequest.WithData(data); public static RpcError InsufficientFundsWallet(string data) => RpcError.InsufficientFundsWallet.WithData(data); public static RpcError VerificationFailed(string data) => RpcError.VerificationFailed.WithData(data); - public static RpcError InvalidContractVerification(UInt160 contractHash) => RpcError.InvalidContractVerification.WithData($"The smart contract {contractHash} haven't got verify method."); + public static RpcError InvalidContractVerification(UInt160 contractHash, int pcount) => RpcError.InvalidContractVerification.WithData($"The smart contract {contractHash} haven't got verify method with {pcount} input parameters."); public static RpcError InvalidContractVerification(string data) => RpcError.InvalidContractVerification.WithData(data); public static RpcError InvalidSignature(string data) => RpcError.InvalidSignature.WithData(data); public static RpcError OracleNotDesignatedNode(ECPoint oraclePub) => RpcError.OracleNotDesignatedNode.WithData($"{oraclePub} isn't an oracle node."); diff --git a/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 860337994d..25a7333aac 100644 --- a/src/Plugins/RpcServer/RpcServer.Wallet.cs +++ b/src/Plugins/RpcServer/RpcServer.Wallet.cs @@ -494,7 +494,7 @@ private JObject GetVerificationResult(UInt160 scriptHash, ContractParameter[] ar { using var snapshot = system.GetSnapshotCache(); var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash).NotNull_Or(RpcError.UnknownContract); - var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, args.Count()).NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash)); + var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, args.Count()).NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash, args.Count())); (md.ReturnType == ContractParameterType.Boolean).True_Or(RpcErrorFactory.InvalidContractVerification("The verify method doesn't return boolean value.")); Transaction tx = new() { diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 827f6c232e..0897a12804 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -445,6 +445,9 @@ public static void _deploy(object data, bool update) { resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]); Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); + // invoke verify with 2 param (which does not exist); should throw Exception + Assert.ThrowsException(() => _rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }, new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]), + $"Invalid contract verification function - The smart contract {deployedScriptHash} haven't got verify method with 2 input parameters."); }