diff --git a/.devcontainer/devcontainer.dockerfile b/.devcontainer/devcontainer.dockerfile index 5c72dc8cfb..d2ccf16581 100644 --- a/.devcontainer/devcontainer.dockerfile +++ b/.devcontainer/devcontainer.dockerfile @@ -1,3 +1,6 @@ -FROM mcr.microsoft.com/devcontainers/dotnet:9.0-noble +# https://github.com/dotnet/dotnet-docker/blob/main/README.sdk.md +# https://mcr.microsoft.com/en-us/artifact/mar/dotnet/sdk/tags <-- this shows all images +FROM mcr.microsoft.com/dotnet/sdk:9.0.101-noble + # Install the libleveldb-dev package RUN apt-get update && apt-get install -y libleveldb-dev diff --git a/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj b/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj index 5f7211e49a..0201ed1b72 100644 --- a/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj +++ b/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj @@ -9,7 +9,6 @@ - diff --git a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj index 32b3ec9aa0..109fab93e3 100644 --- a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj +++ b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj @@ -9,12 +9,10 @@ - - diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs index f1452f2e6c..a5748fdd5b 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Test.Types; +using Neo.VM.Types; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -18,12 +18,14 @@ namespace Neo.VM.Benchmark.OpCode /// /// A simple benchmark engine for . /// - public class BenchmarkEngine : TestEngine + public class BenchmarkEngine : ExecutionEngine { private readonly Dictionary _opcodeStats = new(); private readonly Dictionary> _breakPoints = new(); private long _gasConsumed = 0; + public BenchmarkEngine() : base(ComposeJumpTable()) { } + /// /// Add a breakpoint at the specified position of the specified script. The VM will break the execution when it reaches the breakpoint. /// @@ -187,5 +189,23 @@ private void PrintOpcodeStats() $"Avg Time: {kvp.Value.TotalTime.TotalMilliseconds * 1000 / kvp.Value.Count,10:F2} μs"); } } + + private static JumpTable ComposeJumpTable() + { + JumpTable jumpTable = new JumpTable(); + jumpTable[VM.OpCode.SYSCALL] = OnSysCall; + return jumpTable; + } + + private static void OnSysCall(ExecutionEngine engine, VM.Instruction instruction) + { + uint method = instruction.TokenU32; + if (method == 0x77777777) + engine.CurrentContext!.EvaluationStack.Push(StackItem.FromInterface(new object())); + else if (method == 0xaddeadde) + engine.JumpTable.ExecuteThrow(engine, "error"); + else + throw new Exception(); + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/Program.cs b/benchmarks/Neo.VM.Benchmarks/Program.cs index 09523ecd7c..ebee933ff8 100644 --- a/benchmarks/Neo.VM.Benchmarks/Program.cs +++ b/benchmarks/Neo.VM.Benchmarks/Program.cs @@ -59,7 +59,6 @@ } var methods = benchmarkType.GetMethods(BindingFlags.Public | BindingFlags.Instance); - foreach (var method in methods) { if (method.DeclaringType == benchmarkType && !method.GetCustomAttributes().Any()) diff --git a/src/Neo/VM/Helper.cs b/src/Neo/Extensions/SmartContract/ContractParameterExtensions.cs similarity index 91% rename from src/Neo/VM/Helper.cs rename to src/Neo/Extensions/SmartContract/ContractParameterExtensions.cs index 8a1bc2b01b..d60b61eaf2 100644 --- a/src/Neo/VM/Helper.cs +++ b/src/Neo/Extensions/SmartContract/ContractParameterExtensions.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2024 The Neo Project. // -// Helper.cs file belongs to the neo project and is free +// ContractParameterExtensions.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 @@ -10,8 +10,6 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.Extensions; -using Neo.Json; using Neo.SmartContract; using Neo.VM.Types; using System; @@ -20,12 +18,9 @@ using System.Numerics; using Array = Neo.VM.Types.Array; -namespace Neo.VM +namespace Neo.Extensions { - /// - /// A helper class related to NeoVM. - /// - public static class Helper + public static class ContractParameterExtensions { /// /// Converts the to a . @@ -46,7 +41,7 @@ private static StackItem ToStackItem(ContractParameter parameter, List<(StackIte { case ContractParameterType.Array: if (context is null) - context = new List<(StackItem, ContractParameter)>(); + context = []; else (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) @@ -57,7 +52,7 @@ private static StackItem ToStackItem(ContractParameter parameter, List<(StackIte break; case ContractParameterType.Map: if (context is null) - context = new List<(StackItem, ContractParameter)>(); + context = []; else (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) diff --git a/src/Neo/Extensions/SmartContract/GasTokenExtensions.cs b/src/Neo/Extensions/SmartContract/GasTokenExtensions.cs new file mode 100644 index 0000000000..07a1bcb2d9 --- /dev/null +++ b/src/Neo/Extensions/SmartContract/GasTokenExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// GasTokenExtensions.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 Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Neo.Extensions +{ + public static class GasTokenExtensions + { + public static IEnumerable<(UInt160 Address, BigInteger Balance)> GetAccounts(this GasToken gasToken, DataCache snapshot) + { + if (gasToken is null) + throw new ArgumentNullException(nameof(gasToken)); + + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + + var kb = new KeyBuilder(gasToken.Id, GasToken.Prefix_Account); + var prefixKey = kb.ToArray(); + + foreach (var (key, value) in snapshot.Find(prefixKey, SeekDirection.Forward)) + { + var keyBytes = key.ToArray(); + var addressHash = new UInt160(keyBytes.AsSpan(prefixKey.Length)); + yield return new(addressHash, value.GetInteroperable().Balance); + } + } + } +} diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index 3771278839..6097345d1f 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -449,6 +449,7 @@ protected internal Signer[] GetCurrentSigners() private static bool CheckItemType(StackItem item, ContractParameterType type) { StackItemType aType = item.Type; + if (aType == StackItemType.Any) return true; if (aType == StackItemType.Pointer) return false; switch (type) { @@ -459,7 +460,7 @@ private static bool CheckItemType(StackItem item, ContractParameterType type) case ContractParameterType.Integer: return aType == StackItemType.Integer; case ContractParameterType.ByteArray: - return aType is StackItemType.Any or StackItemType.ByteString or StackItemType.Buffer; + return aType is StackItemType.ByteString or StackItemType.Buffer; case ContractParameterType.String: { if (aType is StackItemType.ByteString or StackItemType.Buffer) @@ -474,27 +475,23 @@ private static bool CheckItemType(StackItem item, ContractParameterType type) return false; } case ContractParameterType.Hash160: - if (aType == StackItemType.Any) return true; if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; return item.GetSpan().Length == UInt160.Length; case ContractParameterType.Hash256: - if (aType == StackItemType.Any) return true; if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; return item.GetSpan().Length == UInt256.Length; case ContractParameterType.PublicKey: - if (aType == StackItemType.Any) return true; if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; return item.GetSpan().Length == 33; case ContractParameterType.Signature: - if (aType == StackItemType.Any) return true; if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false; return item.GetSpan().Length == 64; case ContractParameterType.Array: - return aType is StackItemType.Any or StackItemType.Array or StackItemType.Struct; + return aType is StackItemType.Array or StackItemType.Struct; case ContractParameterType.Map: - return aType is StackItemType.Any or StackItemType.Map; + return aType is StackItemType.Map; case ContractParameterType.InteropInterface: - return aType is StackItemType.Any or StackItemType.InteropInterface; + return aType is StackItemType.InteropInterface; default: return false; } diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs index f8f2332b4e..c4a0b209bb 100644 --- a/src/Neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -325,7 +325,11 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe state.CallingContext = currentContext; for (int i = args.Count - 1; i >= 0; i--) + { + if (!CheckItemType(args[i], method.Parameters[i].Type)) + throw new InvalidOperationException($"The type of the argument `{args[i]}` does not match the formal parameter."); context_new.EvaluationStack.Push(args[i]); + } return context_new; } diff --git a/src/Neo/SmartContract/Native/FungibleToken.cs b/src/Neo/SmartContract/Native/FungibleToken.cs index ab6ed8f9c0..2175f43dfd 100644 --- a/src/Neo/SmartContract/Native/FungibleToken.cs +++ b/src/Neo/SmartContract/Native/FungibleToken.cs @@ -51,7 +51,7 @@ public abstract class FungibleToken : NativeContract /// /// The prefix for storing account states. /// - protected const byte Prefix_Account = 20; + protected internal const byte Prefix_Account = 20; /// /// Initializes a new instance of the class. diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 6aa4a9eaa9..0882687b9f 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -161,7 +161,7 @@ public static bool TryParse(string str, out UInt160 result) var data = new byte[Length]; for (var i = 0; i < Length; i++) { - if (!byte.TryParse(str.AsSpan(i * 2 + startIndex, 2), NumberStyles.HexNumber, null, out data[Length - i - 1])) + if (!byte.TryParse(str.AsSpan(i * 2 + startIndex, 2), NumberStyles.AllowHexSpecifier, null, out data[Length - i - 1])) return false; } result = new(data); diff --git a/src/Plugins/RpcServer/RpcException.cs b/src/Plugins/RpcServer/RpcException.cs index ab47901b6d..17ce4cd76b 100644 --- a/src/Plugins/RpcServer/RpcException.cs +++ b/src/Plugins/RpcServer/RpcException.cs @@ -15,9 +15,17 @@ namespace Neo.Plugins.RpcServer { public class RpcException : Exception { + private readonly RpcError _rpcError; + public RpcException(RpcError error) : base(error.ErrorMessage) { HResult = error.Code; + _rpcError = error; + } + + public RpcError GetError() + { + return _rpcError; } } } diff --git a/src/Plugins/RpcServer/RpcServer.cs b/src/Plugins/RpcServer/RpcServer.cs index 0dc3d467bf..bb948b0ba0 100644 --- a/src/Plugins/RpcServer/RpcServer.cs +++ b/src/Plugins/RpcServer/RpcServer.cs @@ -274,7 +274,7 @@ public async Task ProcessAsync(HttpContext context) await context.Response.WriteAsync(response.ToString(), Encoding.UTF8); } - private async Task ProcessRequestAsync(HttpContext context, JObject request) + internal async Task ProcessRequestAsync(HttpContext context, JObject request) { if (!request.ContainsProperty("id")) return null; var @params = request["params"] ?? new JArray(); @@ -315,11 +315,13 @@ private async Task ProcessRequestAsync(HttpContext context, JObject req { if (param.ParameterType == typeof(UInt160)) { - args[i] = ParameterConverter.ConvertUInt160(jsonParameters[i], system.Settings.AddressVersion); + args[i] = ParameterConverter.ConvertUInt160(jsonParameters[i], + system.Settings.AddressVersion); } else { - args[i] = ParameterConverter.ConvertParameter(jsonParameters[i], param.ParameterType); + args[i] = ParameterConverter.ConvertParameter(jsonParameters[i], + param.ParameterType); } } catch (Exception e) when (e is not RpcException) @@ -333,7 +335,8 @@ private async Task ProcessRequestAsync(HttpContext context, JObject req { args[i] = param.DefaultValue; } - else if (param.ParameterType.IsValueType && Nullable.GetUnderlyingType(param.ParameterType) == null) + else if (param.ParameterType.IsValueType && + Nullable.GetUnderlyingType(param.ParameterType) == null) { throw new ArgumentException($"Required parameter '{param.Name}' is missing"); } @@ -366,11 +369,23 @@ private async Task ProcessRequestAsync(HttpContext context, JObject req catch (Exception ex) when (ex is not RpcException) { #if DEBUG - return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message, ex.StackTrace)); + return CreateErrorResponse(request["id"], + RpcErrorFactory.NewCustomError(ex.HResult, ex.Message, ex.StackTrace)); #else return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message)); #endif } + catch (RpcException ex) + { + +#if DEBUG + return CreateErrorResponse(request["id"], + RpcErrorFactory.NewCustomError(ex.HResult, ex.Message, ex.StackTrace)); +#else + return CreateErrorResponse(request["id"], ex.GetError()); +#endif + + } } public void RegisterMethods(object handler) diff --git a/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogStorageStore.cs b/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogStorageStore.cs index 5cb6fd2d6e..c3dd6601cb 100644 --- a/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogStorageStore.cs +++ b/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogStorageStore.cs @@ -10,13 +10,19 @@ // modifications are permitted. using Microsoft.AspNetCore.Authorization; +using Neo.Extensions; +using Neo.IO; +using Neo.Ledger; using Neo.Persistence; using Neo.Plugins.ApplicationLogs.Store; using Neo.Plugins.ApplicationLogs.Store.States; using Neo.Plugins.ApplicationsLogs.Tests.Setup; using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -39,6 +45,10 @@ public void Test_Put_Get_BlockState_Storage(TriggerType expectedAppTrigger, stri { using (var lss = new LogStorageStore(snapshot)) { + var ok = lss.TryGetBlockState(expectedHash, expectedAppTrigger, out var actualState); + Assert.False(ok); + Assert.Null(actualState); + // Put Block States in Storage for each Trigger lss.PutBlockState(expectedHash, expectedAppTrigger, BlockLogState.Create([expectedGuid])); // Commit Data to "Store" Storage for Lookup @@ -72,6 +82,10 @@ public void Test_Put_Get_TransactionEngineState_Storage(string expectedLogId, st { using (var lss = new LogStorageStore(snapshot)) { + var ok = lss.TryGetTransactionEngineState(expectedTxHash, out var actualState); + Assert.False(ok); + Assert.Null(actualState); + // Put Block States in Storage for each Trigger lss.PutTransactionEngineState(expectedTxHash, TransactionEngineLogState.Create([expectedGuid])); // Commit Data to "Store" Storage for Lookup @@ -104,6 +118,10 @@ public void Test_Put_Get_EngineState_Storage(string expectedScriptHashString, st { using (var lss = new LogStorageStore(snapshot)) { + var ok = lss.TryGetEngineState(Guid.NewGuid(), out var actualState); + Assert.False(ok); + Assert.Null(actualState); + expectedGuid = lss.PutEngineState(EngineLogState.Create(expectedScriptHash, expectedMessage)); snapshot.Commit(); } @@ -134,6 +152,10 @@ public void Test_Put_Get_NotifyState_Storage(string expectedScriptHashString, st { using (var lss = new LogStorageStore(snapshot)) { + var ok = lss.TryGetNotifyState(Guid.NewGuid(), out var actualState); + Assert.False(ok); + Assert.Null(actualState); + expectedGuid = lss.PutNotifyState(NotifyLogState.Create(expectedNotifyEventArgs, [expectedItemGuid])); snapshot.Commit(); } @@ -152,5 +174,142 @@ public void Test_Put_Get_NotifyState_Storage(string expectedScriptHashString, st Assert.Equal(expectedItemGuid, actualState.StackItemIds[0]); } } + + [Fact] + public void Test_StackItemState() + { + using var store = new MemoryStore(); + using var snapshot = store.GetSnapshot(); + using var lss = new LogStorageStore(snapshot); + + var ok = lss.TryGetStackItemState(Guid.NewGuid(), out var actualState); + Assert.False(ok); + Assert.Equal(StackItem.Null, actualState); + + var id1 = lss.PutStackItemState(new Integer(1)); + var id2 = lss.PutStackItemState(new Integer(2)); + + snapshot.Commit(); + + using var snapshot2 = store.GetSnapshot(); + using var lss2 = new LogStorageStore(snapshot2); + ok = lss2.TryGetStackItemState(id1, out var actualState1); + Assert.True(ok); + Assert.Equal(new Integer(1), actualState1); + + ok = lss2.TryGetStackItemState(id2, out var actualState2); + Assert.True(ok); + Assert.Equal(new Integer(2), actualState2); + } + + [Fact] + public void Test_TransactionState() + { + using var store = new MemoryStore(); + using var snapshot = store.GetSnapshot(); + using var lss = new LogStorageStore(snapshot); + + // random 32 bytes + var bytes = new byte[32]; + Random.Shared.NextBytes(bytes); + + var hash = new UInt256(bytes); + var ok = lss.TryGetTransactionState(hash, out var actualState); + Assert.False(ok); + Assert.Null(actualState); + + var guid = Guid.NewGuid(); + lss.PutTransactionState(hash, TransactionLogState.Create([guid])); + snapshot.Commit(); + + using var snapshot2 = store.GetSnapshot(); + using var lss2 = new LogStorageStore(snapshot2); + ok = lss2.TryGetTransactionState(hash, out actualState); + Assert.True(ok); + Assert.Equal(TransactionLogState.Create([guid]), actualState); + } + + [Fact] + public void Test_ExecutionState() + { + using var store = new MemoryStore(); + using var snapshot = store.GetSnapshot(); + using var lss = new LogStorageStore(snapshot); + + var ok = lss.TryGetExecutionState(Guid.NewGuid(), out var actualState); + Assert.False(ok); + Assert.Null(actualState); + + // ExecutionLogState.Serialize + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream); + writer.Write((byte)VMState.HALT); + writer.WriteVarString("Test"); + writer.Write(100ul); + writer.Write(1u); + writer.WriteVarBytes(Guid.NewGuid().ToByteArray()); + writer.Flush(); + + var bytes = stream.ToArray(); + var state = new ExecutionLogState(); + + var reader = new MemoryReader(bytes); + state.Deserialize(ref reader); + + var guid = lss.PutExecutionState(state); + snapshot.Commit(); + + using var snapshot2 = store.GetSnapshot(); + using var lss2 = new LogStorageStore(snapshot2); + ok = lss2.TryGetExecutionState(guid, out actualState); + Assert.True(ok); + Assert.Equal(state, actualState); + } + + [Fact] + public void Test_ContractState() + { + using var store = new MemoryStore(); + using var snapshot = store.GetSnapshot(); + using var lss = new LogStorageStore(snapshot); + + var guid = Guid.NewGuid(); + var scriptHash = UInt160.Parse("0x0000000000000000000000000000000000000000"); + var timestamp = 100ul; + var index = 1u; + + var ok = lss.TryGetContractState(scriptHash, timestamp, index, out var actualState); + Assert.False(ok); + Assert.Null(actualState); + + // random 32 bytes + var bytes = new byte[32]; + Random.Shared.NextBytes(bytes); + + // ContractLogState.Serialize + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream); + writer.Write(new UInt256(bytes)); + writer.Write((byte)TriggerType.All); + writer.Write(scriptHash); + writer.WriteVarString("Test"); + writer.Write(1u); + writer.WriteVarBytes(Guid.NewGuid().ToByteArray()); + writer.Flush(); + + bytes = stream.ToArray(); + var state = new ContractLogState(); + var reader = new MemoryReader(bytes); + state.Deserialize(ref reader); + + lss.PutContractState(scriptHash, timestamp, index, state); + snapshot.Commit(); + + using var snapshot2 = store.GetSnapshot(); + using var lss2 = new LogStorageStore(snapshot2); + ok = lss2.TryGetContractState(scriptHash, timestamp, index, out actualState); + Assert.True(ok); + Assert.Equal(state, actualState); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 3b204cc53f..35626433d1 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -64,7 +64,6 @@ public partial class UT_RpcServer public void TestInvokeFunction() { _rpcServer.wallet = _wallet; - 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); @@ -117,6 +116,29 @@ public void TestInvokeFunction() _rpcServer.wallet = null; } + [TestMethod] + public void TestInvokeFunctionInvalid() + { + _rpcServer.wallet = _wallet; + + HttpContext context = new DefaultHttpContext(); + + var json = new JObject(); + json["id"] = 1; + json["jsonrpc"] = "2.0"; + json["method"] = "invokefunction"; + json["params"] = new JArray("0", "totalSupply", new JArray([]), validatorSigner, true); + + var resp = _rpcServer.ProcessRequestAsync(context, json).GetAwaiter().GetResult(); + + Console.WriteLine(resp); + Assert.AreEqual(resp.Count, 3); + Assert.IsNotNull(resp["error"]); + Assert.AreEqual(resp["error"]["code"], -32602); + + _rpcServer.wallet = null; + } + [TestMethod] public void TestInvokeScript() { diff --git a/tests/Neo.UnitTests/Extensions/UT_GasTokenExtensions.cs b/tests/Neo.UnitTests/Extensions/UT_GasTokenExtensions.cs new file mode 100644 index 0000000000..9b13312eb6 --- /dev/null +++ b/tests/Neo.UnitTests/Extensions/UT_GasTokenExtensions.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_GasTokenExtensions.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 Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; +using Neo.SmartContract.Native; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Extensions +{ + [TestClass] + public class UT_GasTokenExtensions + { + private NeoSystem system; + + [TestInitialize] + public void Initialize() + { + system = TestBlockchain.TheNeoSystem; + } + + [TestCleanup] + public void Clean() + { + TestBlockchain.ResetStore(); + } + + [TestMethod] + public void TestGetAccounts() + { + UInt160 expected = "0x9f8f056a53e39585c7bb52886418c7bed83d126b"; + + var accounts = NativeContract.GAS.GetAccounts(system.StoreView); + var actual = accounts.FirstOrDefault(); + + Assert.AreEqual(expected, actual.Address); + Assert.AreEqual(5200000000000000, actual.Balance); + } + } +} diff --git a/tests/Neo.UnitTests/IO/Caching/UT_CloneCache.cs b/tests/Neo.UnitTests/Persistence/UT_CloneCache.cs similarity index 100% rename from tests/Neo.UnitTests/IO/Caching/UT_CloneCache.cs rename to tests/Neo.UnitTests/Persistence/UT_CloneCache.cs diff --git a/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/Neo.UnitTests/Persistence/UT_DataCache.cs similarity index 100% rename from tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs rename to tests/Neo.UnitTests/Persistence/UT_DataCache.cs diff --git a/tests/Neo.UnitTests/UT_UInt160.cs b/tests/Neo.UnitTests/UT_UInt160.cs index 3fb9dd89a0..3c3b1ec001 100644 --- a/tests/Neo.UnitTests/UT_UInt160.cs +++ b/tests/Neo.UnitTests/UT_UInt160.cs @@ -98,6 +98,7 @@ public void TestTryParse() Assert.AreEqual("0x1230000000000000000000000000000000000000", temp.ToString()); Assert.AreEqual(false, UInt160.TryParse("000000000000000000000000000000000000000", out _)); Assert.AreEqual(false, UInt160.TryParse("0xKK00000000000000000000000000000000000000", out _)); + Assert.AreEqual(false, UInt160.TryParse(" 1 2 3 45 000000000000000000000000000000", out _)); } [TestMethod]