diff --git a/src/Neo.CLI/CLI/MainService.Network.cs b/src/Neo.CLI/CLI/MainService.Network.cs index c579b8323e..49a056640e 100644 --- a/src/Neo.CLI/CLI/MainService.Network.cs +++ b/src/Neo.CLI/CLI/MainService.Network.cs @@ -11,6 +11,7 @@ using Akka.Actor; using Neo.ConsoleService; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P; diff --git a/src/Neo.Extensions/AssemblyExtensions.cs b/src/Neo.Extensions/AssemblyExtensions.cs new file mode 100644 index 0000000000..4f9368c98b --- /dev/null +++ b/src/Neo.Extensions/AssemblyExtensions.cs @@ -0,0 +1,23 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AssemblyExtensions.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 System.Reflection; + +namespace Neo.Extensions +{ + public static class AssemblyExtensions + { + public static string GetVersion(this Assembly assembly) + { + return assembly.GetName().Version!.ToString(3); + } + } +} diff --git a/src/Neo.Extensions/Collections/HashSetExtensions.cs b/src/Neo.Extensions/Collections/HashSetExtensions.cs new file mode 100644 index 0000000000..9cf3f13b23 --- /dev/null +++ b/src/Neo.Extensions/Collections/HashSetExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// HashSetExtensions.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 System.Collections.Generic; + +namespace Neo.Extensions +{ + public static class HashSetExtensions + { + public static void Remove(this HashSet set, ISet other) + { + if (set.Count > other.Count) + { + set.ExceptWith(other); + } + else + { + set.RemoveWhere(u => other.Contains(u)); + } + } + + public static void Remove(this HashSet set, IReadOnlyDictionary other) + { + if (set.Count > other.Count) + { + set.ExceptWith(other.Keys); + } + else + { + set.RemoveWhere(u => other.ContainsKey(u)); + } + } + } +} diff --git a/src/Neo.Extensions/DateTimeExtensions.cs b/src/Neo.Extensions/DateTimeExtensions.cs new file mode 100644 index 0000000000..130d09e78d --- /dev/null +++ b/src/Neo.Extensions/DateTimeExtensions.cs @@ -0,0 +1,38 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// DateTimeExtensions.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 System; + +namespace Neo.Extensions +{ + public static class DateTimeExtensions + { + /// + /// Converts a to timestamp. + /// + /// The to convert. + /// The converted timestamp. + public static uint ToTimestamp(this DateTime time) + { + return (uint)new DateTimeOffset(time.ToUniversalTime()).ToUnixTimeSeconds(); + } + + /// + /// Converts a to timestamp in milliseconds. + /// + /// The to convert. + /// The converted timestamp. + public static ulong ToTimestampMS(this DateTime time) + { + return (ulong)new DateTimeOffset(time.ToUniversalTime()).ToUnixTimeMilliseconds(); + } + } +} diff --git a/src/Neo.Extensions/Neo.Extensions.csproj b/src/Neo.Extensions/Neo.Extensions.csproj index cc5325843a..f424dd0809 100644 --- a/src/Neo.Extensions/Neo.Extensions.csproj +++ b/src/Neo.Extensions/Neo.Extensions.csproj @@ -3,6 +3,7 @@ netstandard2.1;net8.0 enable + true Neo.Extensions NEO;Blockchain;Extensions ../../bin/$(PackageId) diff --git a/src/Neo.Extensions/Net/IpAddressExtensions.cs b/src/Neo.Extensions/Net/IpAddressExtensions.cs new file mode 100644 index 0000000000..adafee142f --- /dev/null +++ b/src/Neo.Extensions/Net/IpAddressExtensions.cs @@ -0,0 +1,40 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IpAddressExtensions.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 System.Net; + +namespace Neo.Extensions +{ + public static class IpAddressExtensions + { + /// + /// Checks if address is IPv4 Mapped to IPv6 format, if so, Map to IPv4. + /// Otherwise, return current address. + /// + public static IPAddress UnMap(this IPAddress address) + { + if (address.IsIPv4MappedToIPv6) + address = address.MapToIPv4(); + return address; + } + + /// + /// Checks if IPEndPoint is IPv4 Mapped to IPv6 format, if so, unmap to IPv4. + /// Otherwise, return current endpoint. + /// + public static IPEndPoint UnMap(this IPEndPoint endPoint) + { + if (!endPoint.Address.IsIPv4MappedToIPv6) + return endPoint; + return new IPEndPoint(endPoint.Address.UnMap(), endPoint.Port); + } + } +} diff --git a/src/Neo.Extensions/RandomExtensions.cs b/src/Neo.Extensions/RandomExtensions.cs new file mode 100644 index 0000000000..72bcac289f --- /dev/null +++ b/src/Neo.Extensions/RandomExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// RandomExtensions.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 System; +using System.Numerics; + +namespace Neo.Extensions +{ + public static class RandomExtensions + { + public static BigInteger NextBigInteger(this Random rand, int sizeInBits) + { + if (sizeInBits < 0) + throw new ArgumentException("sizeInBits must be non-negative"); + if (sizeInBits == 0) + return 0; + Span b = stackalloc byte[sizeInBits / 8 + 1]; + rand.NextBytes(b); + if (sizeInBits % 8 == 0) + b[^1] = 0; + else + b[^1] &= (byte)((1 << sizeInBits % 8) - 1); + return new BigInteger(b); + } + } +} diff --git a/src/Neo.Extensions/SecureStringExtensions.cs b/src/Neo.Extensions/SecureStringExtensions.cs new file mode 100644 index 0000000000..e4a24f05ee --- /dev/null +++ b/src/Neo.Extensions/SecureStringExtensions.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SecureStringExtensions.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 System; +using System.Runtime.InteropServices; +using System.Security; + +namespace Neo.Extensions +{ + public static class SecureStringExtensions + { + public static string? GetClearText(this SecureString secureString) + { + if (secureString is null) + throw new ArgumentNullException(nameof(secureString)); + + var unmanagedStringPtr = IntPtr.Zero; + + try + { + unmanagedStringPtr = Marshal.SecureStringToGlobalAllocUnicode(secureString); + return Marshal.PtrToStringUni(unmanagedStringPtr); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(unmanagedStringPtr); + } + } + + public static SecureString ToSecureString(this string value, bool asReadOnly = true) + { + unsafe + { + fixed (char* passwordChars = value) + { + var securePasswordString = new SecureString(passwordChars, value.Length); + + if (asReadOnly) + securePasswordString.MakeReadOnly(); + return securePasswordString; + } + } + } + } +} diff --git a/src/Neo.Extensions/StringExtensions.cs b/src/Neo.Extensions/StringExtensions.cs new file mode 100644 index 0000000000..8d851bf905 --- /dev/null +++ b/src/Neo.Extensions/StringExtensions.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// StringExtensions.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 System; +using System.Globalization; + +namespace Neo.Extensions +{ + public static class StringExtensions + { + /// + /// Converts a hex to byte array. + /// + /// The hex to convert. + /// The converted byte array. + public static byte[] HexToBytes(this string value) + { + if (value == null || value.Length == 0) + return []; + if (value.Length % 2 == 1) + throw new FormatException(); + var result = new byte[value.Length / 2]; + for (var i = 0; i < result.Length; i++) + result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier); + return result; + } + } +} diff --git a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs index 5dd10f858f..d28696f138 100644 --- a/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs +++ b/src/Neo.GUI/GUI/CreateMultiSigContractDialog.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.SmartContract; using Neo.Wallets; using System; diff --git a/src/Neo.GUI/GUI/ImportCustomContractDialog.cs b/src/Neo.GUI/GUI/ImportCustomContractDialog.cs index b8df534699..8faa23d593 100644 --- a/src/Neo.GUI/GUI/ImportCustomContractDialog.cs +++ b/src/Neo.GUI/GUI/ImportCustomContractDialog.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.SmartContract; using Neo.Wallets; using System; diff --git a/src/Neo.GUI/GUI/ParametersEditor.cs b/src/Neo.GUI/GUI/ParametersEditor.cs index 19eac82c31..f5156d6bfb 100644 --- a/src/Neo.GUI/GUI/ParametersEditor.cs +++ b/src/Neo.GUI/GUI/ParametersEditor.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.SmartContract; using System; using System.Collections.Generic; diff --git a/src/Neo.IO/Caching/HashSetCache.cs b/src/Neo.IO/Caching/HashSetCache.cs index 577893e924..6e39f5fd8b 100644 --- a/src/Neo.IO/Caching/HashSetCache.cs +++ b/src/Neo.IO/Caching/HashSetCache.cs @@ -15,7 +15,7 @@ namespace Neo.IO.Caching { - class HashSetCache : IReadOnlyCollection where T : IEquatable + internal class HashSetCache : IReadOnlyCollection where T : IEquatable { /// /// Sets where the Hashes are stored diff --git a/src/Neo.IO/Caching/KeyedCollectionSlim.cs b/src/Neo.IO/Caching/KeyedCollectionSlim.cs index f632dc0927..b825739ddd 100644 --- a/src/Neo.IO/Caching/KeyedCollectionSlim.cs +++ b/src/Neo.IO/Caching/KeyedCollectionSlim.cs @@ -15,7 +15,7 @@ namespace Neo.IO.Caching; -abstract class KeyedCollectionSlim +internal abstract class KeyedCollectionSlim where TKey : notnull where TItem : class, IStructuralEquatable, IStructuralComparable, IComparable { diff --git a/src/Neo.IO/Neo.IO.csproj b/src/Neo.IO/Neo.IO.csproj index a51ec1110a..deb98b053b 100644 --- a/src/Neo.IO/Neo.IO.csproj +++ b/src/Neo.IO/Neo.IO.csproj @@ -12,7 +12,8 @@ - + + diff --git a/src/Neo/Cryptography/ECC/ECCurve.cs b/src/Neo/Cryptography/ECC/ECCurve.cs index def1ee0cb1..805507a08e 100644 --- a/src/Neo/Cryptography/ECC/ECCurve.cs +++ b/src/Neo/Cryptography/ECC/ECCurve.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using System.Globalization; using System.Numerics; diff --git a/src/Neo/Extensions/HashSetExtensions.cs b/src/Neo/Extensions/HashSetExtensions.cs new file mode 100644 index 0000000000..fb4648b16e --- /dev/null +++ b/src/Neo/Extensions/HashSetExtensions.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// HashSetExtensions.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.IO.Caching; +using System; +using System.Collections.Generic; + +namespace Neo.Extensions +{ + /// + /// A helper class that provides common functions. + /// + public static class HashSetExtensions + { + internal static void Remove(this HashSet set, HashSetCache other) + where T : IEquatable + { + if (set.Count > other.Count) + { + set.ExceptWith(other); + } + else + { + set.RemoveWhere(u => other.Contains(u)); + } + } + } +} diff --git a/src/Neo/Helper.cs b/src/Neo/Helper.cs deleted file mode 100644 index 4458d04f42..0000000000 --- a/src/Neo/Helper.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// Helper.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.IO.Caching; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Numerics; -using System.Reflection; - -namespace Neo -{ - /// - /// A helper class that provides common functions. - /// - public static class Helper - { - private static readonly DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - internal static void Remove(this HashSet set, ISet other) - { - if (set.Count > other.Count) - { - set.ExceptWith(other); - } - else - { - set.RemoveWhere(u => other.Contains(u)); - } - } - - internal static void Remove(this HashSet set, HashSetCache other) - where T : IEquatable - { - if (set.Count > other.Count) - { - set.ExceptWith(other); - } - else - { - set.RemoveWhere(u => other.Contains(u)); - } - } - - internal static void Remove(this HashSet set, IReadOnlyDictionary other) - { - if (set.Count > other.Count) - { - set.ExceptWith(other.Keys); - } - else - { - set.RemoveWhere(u => other.ContainsKey(u)); - } - } - - internal static string GetVersion(this Assembly assembly) - { - CustomAttributeData attribute = assembly.CustomAttributes.FirstOrDefault(p => p.AttributeType == typeof(AssemblyInformationalVersionAttribute)); - if (attribute == null) return assembly.GetName().Version.ToString(3); - return (string)attribute.ConstructorArguments[0].Value; - } - - /// - /// Converts a hex to byte array. - /// - /// The hex to convert. - /// The converted byte array. - public static byte[] HexToBytes(this string value) - { - if (value == null || value.Length == 0) - return Array.Empty(); - if (value.Length % 2 == 1) - throw new FormatException(); - byte[] result = new byte[value.Length / 2]; - for (int i = 0; i < result.Length; i++) - result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier); - return result; - } - - internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) - { - if (sizeInBits < 0) - throw new ArgumentException("sizeInBits must be non-negative"); - if (sizeInBits == 0) - return 0; - Span b = stackalloc byte[sizeInBits / 8 + 1]; - rand.NextBytes(b); - if (sizeInBits % 8 == 0) - b[^1] = 0; - else - b[^1] &= (byte)((1 << sizeInBits % 8) - 1); - return new BigInteger(b); - } - - /// - /// Converts a to timestamp. - /// - /// The to convert. - /// The converted timestamp. - public static uint ToTimestamp(this DateTime time) - { - return (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; - } - - /// - /// Converts a to timestamp in milliseconds. - /// - /// The to convert. - /// The converted timestamp. - public static ulong ToTimestampMS(this DateTime time) - { - return (ulong)(time.ToUniversalTime() - unixEpoch).TotalMilliseconds; - } - - /// - /// Checks if address is IPv4 Mapped to IPv6 format, if so, Map to IPv4. - /// Otherwise, return current address. - /// - internal static IPAddress Unmap(this IPAddress address) - { - if (address.IsIPv4MappedToIPv6) - address = address.MapToIPv4(); - return address; - } - - /// - /// Checks if IPEndPoint is IPv4 Mapped to IPv6 format, if so, unmap to IPv4. - /// Otherwise, return current endpoint. - /// - internal static IPEndPoint Unmap(this IPEndPoint endPoint) - { - if (!endPoint.Address.IsIPv4MappedToIPv6) - return endPoint; - return new IPEndPoint(endPoint.Address.Unmap(), endPoint.Port); - } - } -} diff --git a/src/Neo/NeoSystem.cs b/src/Neo/NeoSystem.cs index 3121432db5..99be387712 100644 --- a/src/Neo/NeoSystem.cs +++ b/src/Neo/NeoSystem.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Akka.Actor; +using Neo.Extensions; using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P; diff --git a/src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs b/src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs index d3cfd15f66..0b7e0acc37 100644 --- a/src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs +++ b/src/Neo/Network/P2P/Payloads/NetworkAddressWithTime.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Capabilities; using System; @@ -68,7 +69,7 @@ void ISerializable.Deserialize(ref MemoryReader reader) // Address ReadOnlyMemory data = reader.ReadMemory(16); - Address = new IPAddress(data.Span).Unmap(); + Address = new IPAddress(data.Span).UnMap(); // Capabilities Capabilities = new NodeCapability[reader.ReadVarInt(VersionPayload.MaxCapabilities)]; diff --git a/src/Neo/Network/P2P/Payloads/PingPayload.cs b/src/Neo/Network/P2P/Payloads/PingPayload.cs index f6296a7713..6ac3d2d94e 100644 --- a/src/Neo/Network/P2P/Payloads/PingPayload.cs +++ b/src/Neo/Network/P2P/Payloads/PingPayload.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System; using System.IO; diff --git a/src/Neo/Network/P2P/Payloads/VersionPayload.cs b/src/Neo/Network/P2P/Payloads/VersionPayload.cs index 8cec6278e7..f8fe74a856 100644 --- a/src/Neo/Network/P2P/Payloads/VersionPayload.cs +++ b/src/Neo/Network/P2P/Payloads/VersionPayload.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Capabilities; using System; diff --git a/src/Neo/Network/P2P/Peer.cs b/src/Neo/Network/P2P/Peer.cs index 2cad442b95..bf6b23901f 100644 --- a/src/Neo/Network/P2P/Peer.cs +++ b/src/Neo/Network/P2P/Peer.cs @@ -11,6 +11,7 @@ using Akka.Actor; using Akka.IO; +using Neo.Extensions; using Neo.IO; using System; using System.Buffers.Binary; @@ -138,7 +139,7 @@ protected virtual int ConnectingMax static Peer() { - localAddresses.UnionWith(NetworkInterface.GetAllNetworkInterfaces().SelectMany(p => p.GetIPProperties().UnicastAddresses).Select(p => p.Address.Unmap())); + localAddresses.UnionWith(NetworkInterface.GetAllNetworkInterfaces().SelectMany(p => p.GetIPProperties().UnicastAddresses).Select(p => p.Address.UnMap())); } /// @@ -163,7 +164,7 @@ protected internal void AddPeers(IEnumerable peers) /// Indicates whether the remote node is trusted. A trusted node will always be connected. protected void ConnectToPeer(IPEndPoint endPoint, bool isTrusted = false) { - endPoint = endPoint.Unmap(); + endPoint = endPoint.UnMap(); // If the address is the same, the ListenerTcpPort should be different, otherwise, return if (endPoint.Port == ListenerTcpPort && localAddresses.Contains(endPoint.Address)) return; @@ -210,7 +211,7 @@ protected override void OnReceive(object message) ConnectToPeer(connect.EndPoint, connect.IsTrusted); break; case Tcp.Connected connected: - OnTcpConnected(((IPEndPoint)connected.RemoteAddress).Unmap(), ((IPEndPoint)connected.LocalAddress).Unmap()); + OnTcpConnected(((IPEndPoint)connected.RemoteAddress).UnMap(), ((IPEndPoint)connected.LocalAddress).UnMap()); break; case Tcp.Bound _: tcp_listener = Sender; @@ -302,7 +303,7 @@ private void OnTcpCommandFailed(Tcp.Command cmd) switch (cmd) { case Tcp.Connect connect: - ImmutableInterlocked.Update(ref ConnectingPeers, p => p.Remove(((IPEndPoint)connect.RemoteAddress).Unmap())); + ImmutableInterlocked.Update(ref ConnectingPeers, p => p.Remove(((IPEndPoint)connect.RemoteAddress).UnMap())); break; } } diff --git a/src/Neo/Network/P2P/TaskManager.cs b/src/Neo/Network/P2P/TaskManager.cs index e9c12e58a4..c5708df923 100644 --- a/src/Neo/Network/P2P/TaskManager.cs +++ b/src/Neo/Network/P2P/TaskManager.cs @@ -12,6 +12,7 @@ using Akka.Actor; using Akka.Configuration; using Akka.IO; +using Neo.Extensions; using Neo.IO.Actors; using Neo.IO.Caching; using Neo.Ledger; diff --git a/src/Neo/Wallets/Helper.cs b/src/Neo/Wallets/Helper.cs index b8427d078d..5beae49ab9 100644 --- a/src/Neo/Wallets/Helper.cs +++ b/src/Neo/Wallets/Helper.cs @@ -119,7 +119,8 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, { var contract = NativeContract.ContractManagement.GetContract(snapshot, hash); if (contract is null) - throw new ArgumentException($"The smart contract or address {hash} is not found"); + throw new ArgumentException($"The smart contract or address {hash} ({hash.ToAddress(settings.AddressVersion)}) is not found. " + + $"If this is your wallet address and you want to sign a transaction with it, make sure you have opened this wallet."); var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, ContractBasicMethod.VerifyPCount); if (md is null) throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method"); diff --git a/src/Neo/Wallets/NEP6/NEP6Account.cs b/src/Neo/Wallets/NEP6/NEP6Account.cs index 7998b9ea29..b8e3470ab0 100644 --- a/src/Neo/Wallets/NEP6/NEP6Account.cs +++ b/src/Neo/Wallets/NEP6/NEP6Account.cs @@ -134,7 +134,7 @@ internal void ChangePasswordCommit() } } - internal void ChangePasswordRoolback() + internal void ChangePasswordRollback() { nep2KeyNew = null; } diff --git a/src/Neo/Wallets/NEP6/NEP6Wallet.cs b/src/Neo/Wallets/NEP6/NEP6Wallet.cs index 17a59fc830..3edb41c5aa 100644 --- a/src/Neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/Neo/Wallets/NEP6/NEP6Wallet.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.Json; using Neo.SmartContract; using System; @@ -16,6 +17,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; @@ -28,7 +30,7 @@ namespace Neo.Wallets.NEP6 /// https://github.com/neo-project/proposals/blob/master/nep-6.mediawiki public class NEP6Wallet : Wallet { - private string password; + private SecureString password; private string name; private Version version; private readonly Dictionary accounts; @@ -55,10 +57,10 @@ public class NEP6Wallet : Wallet /// The name of the wallet. If the wallet is loaded from an existing file, this parameter is ignored. public NEP6Wallet(string path, string password, ProtocolSettings settings, string name = null) : base(path, settings) { - this.password = password; + this.password = password.ToSecureString(); if (File.Exists(path)) { - JObject wallet = (JObject)JToken.Parse(File.ReadAllBytes(path)); + var wallet = (JObject)JToken.Parse(File.ReadAllBytes(path)); LoadFromJson(wallet, out Scrypt, out accounts, out extra); } else @@ -80,7 +82,7 @@ public NEP6Wallet(string path, string password, ProtocolSettings settings, strin /// The JSON object representing the wallet. public NEP6Wallet(string path, string password, ProtocolSettings settings, JObject json) : base(path, settings) { - this.password = password; + this.password = password.ToSecureString(); LoadFromJson(json, out Scrypt, out accounts, out extra); } @@ -91,7 +93,7 @@ private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dicti scrypt = ScryptParameters.FromJson((JObject)wallet["scrypt"]); accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson((JObject)p, this)).ToDictionary(p => p.ScriptHash); extra = wallet["extra"]; - if (!VerifyPasswordInternal(password)) + if (!VerifyPasswordInternal(password.GetClearText())) throw new InvalidOperationException("Wrong password."); } @@ -144,7 +146,7 @@ public override WalletAccount CreateAccount(byte[] privateKey) ParameterNames = new[] { "signature" }, Deployed = false }; - NEP6Account account = new(this, contract.ScriptHash, key, password) + NEP6Account account = new(this, contract.ScriptHash, key, password.GetClearText()) { Contract = contract }; @@ -168,7 +170,7 @@ public override WalletAccount CreateAccount(Contract contract, KeyPair key = nul if (key == null) account = new NEP6Account(this, nep6contract.ScriptHash); else - account = new NEP6Account(this, nep6contract.ScriptHash, key, password); + account = new NEP6Account(this, nep6contract.ScriptHash, key, password.GetClearText()); account.Contract = nep6contract; AddAccount(account); return account; @@ -188,7 +190,7 @@ public override WalletAccount CreateAccount(UInt160 scriptHash) /// The decrypted private key. internal KeyPair DecryptKey(string nep2key) { - return new KeyPair(GetPrivateKeyFromNEP2(nep2key, password, ProtocolSettings.AddressVersion, Scrypt.N, Scrypt.R, Scrypt.P)); + return new KeyPair(GetPrivateKeyFromNEP2(nep2key, password.GetClearText(), ProtocolSettings.AddressVersion, Scrypt.N, Scrypt.R, Scrypt.P)); } public override void Delete() @@ -240,7 +242,7 @@ public override WalletAccount Import(X509Certificate2 cert) ParameterNames = new[] { "signature" }, Deployed = false }; - NEP6Account account = new(this, contract.ScriptHash, key, password) + NEP6Account account = new(this, contract.ScriptHash, key, password.GetClearText()) { Contract = contract }; @@ -258,7 +260,7 @@ public override WalletAccount Import(string wif) ParameterNames = new[] { "signature" }, Deployed = false }; - NEP6Account account = new(this, contract.ScriptHash, key, password) + NEP6Account account = new(this, contract.ScriptHash, key, password.GetClearText()) { Contract = contract }; @@ -308,7 +310,7 @@ public override void Save() public override bool VerifyPassword(string password) { - return this.password == password; + return this.password.GetClearText() == password; } private bool VerifyPasswordInternal(string password) @@ -358,12 +360,12 @@ public override bool ChangePassword(string oldPassword, string newPassword) { foreach (NEP6Account account in accounts.Values) account.ChangePasswordCommit(); - password = newPassword; + password = newPassword.ToSecureString(); } else { foreach (NEP6Account account in accounts.Values) - account.ChangePasswordRoolback(); + account.ChangePasswordRollback(); } return succeed; } diff --git a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs index f68f004c3c..a1e9b28bc4 100644 --- a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs +++ b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Plugins.DBFTPlugin.Messages; diff --git a/src/Plugins/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs b/src/Plugins/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs index 3c6ab8e243..b00fdd9393 100644 --- a/src/Plugins/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs +++ b/src/Plugins/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs @@ -11,6 +11,7 @@ using Akka.Actor; using Neo.Cryptography; +using Neo.Extensions; using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; diff --git a/src/Plugins/RpcClient/RpcClient.cs b/src/Plugins/RpcClient/RpcClient.cs index 27b0023ec0..7866c7e40f 100644 --- a/src/Plugins/RpcClient/RpcClient.cs +++ b/src/Plugins/RpcClient/RpcClient.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; diff --git a/src/Plugins/RpcClient/Utility.cs b/src/Plugins/RpcClient/Utility.cs index 659942f8f8..37331eec1b 100644 --- a/src/Plugins/RpcClient/Utility.cs +++ b/src/Plugins/RpcClient/Utility.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Network.P2P.Payloads.Conditions; diff --git a/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs b/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs new file mode 100644 index 0000000000..fe35d9c3e5 --- /dev/null +++ b/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs @@ -0,0 +1,61 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BlockHashOrIndex.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 System.Diagnostics.CodeAnalysis; + +namespace Neo.Plugins.RpcServer.Model; + +public class BlockHashOrIndex +{ + private readonly object _value; + + public BlockHashOrIndex(uint index) + { + _value = index; + } + + public BlockHashOrIndex(UInt256 hash) + { + _value = hash; + } + + public bool IsIndex => _value is uint; + + public static bool TryParse(string value, [NotNullWhen(true)] out BlockHashOrIndex blockHashOrIndex) + { + if (uint.TryParse(value, out var index)) + { + blockHashOrIndex = new BlockHashOrIndex(index); + return true; + } + if (UInt256.TryParse(value, out var hash)) + { + blockHashOrIndex = new BlockHashOrIndex(hash); + return true; + } + blockHashOrIndex = null; + return false; + } + + public uint AsIndex() + { + if (_value is uint intValue) + return intValue; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid block index")); + } + + public UInt256 AsHash() + { + if (_value is UInt256 hash) + return hash; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid block hash")); + } +} diff --git a/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs b/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs new file mode 100644 index 0000000000..f1b7c2c92e --- /dev/null +++ b/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs @@ -0,0 +1,81 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ContractNameOrHashOrId.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 System.Diagnostics.CodeAnalysis; + +namespace Neo.Plugins.RpcServer.Model; + +public class ContractNameOrHashOrId +{ + private readonly object _value; + + public ContractNameOrHashOrId(int id) + { + _value = id; + } + + public ContractNameOrHashOrId(UInt160 hash) + { + _value = hash; + } + + public ContractNameOrHashOrId(string name) + { + _value = name; + } + + public bool IsId => _value is int; + public bool IsHash => _value is UInt160; + public bool IsName => _value is string; + + public static bool TryParse(string value, [NotNullWhen(true)] out ContractNameOrHashOrId contractNameOrHashOrId) + { + if (int.TryParse(value, out var id)) + { + contractNameOrHashOrId = new ContractNameOrHashOrId(id); + return true; + } + if (UInt160.TryParse(value, out var hash)) + { + contractNameOrHashOrId = new ContractNameOrHashOrId(hash); + return true; + } + + if (value.Length > 0) + { + contractNameOrHashOrId = new ContractNameOrHashOrId(value); + return true; + } + contractNameOrHashOrId = null; + return false; + } + + public int AsId() + { + if (_value is int intValue) + return intValue; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract id")); + } + + public UInt160 AsHash() + { + if (_value is UInt160 hash) + return hash; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract hash")); + } + + public string AsName() + { + if (_value is string name) + return name; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract name")); + } +} diff --git a/src/Plugins/RpcServer/ParameterConverter.cs b/src/Plugins/RpcServer/ParameterConverter.cs new file mode 100644 index 0000000000..7d9b754f7c --- /dev/null +++ b/src/Plugins/RpcServer/ParameterConverter.cs @@ -0,0 +1,149 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ParameterConverter.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.Json; +using Neo.Plugins.RpcServer.Model; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using JToken = Neo.Json.JToken; + +namespace Neo.Plugins.RpcServer; + +public static class ParameterConverter +{ + private static readonly Dictionary> s_conversionStrategies; + + static ParameterConverter() + { + s_conversionStrategies = new Dictionary> + { + { typeof(string), token => Result.Ok_Or(token.AsString, CreateInvalidParamError(token)) }, + { typeof(byte), ConvertNumeric }, + { typeof(sbyte), ConvertNumeric }, + { typeof(short), ConvertNumeric }, + { typeof(ushort), ConvertNumeric }, + { typeof(int), ConvertNumeric }, + { typeof(uint), ConvertNumeric }, + { typeof(long), ConvertNumeric }, + { typeof(ulong), ConvertNumeric }, + { typeof(double), token => Result.Ok_Or(token.AsNumber, CreateInvalidParamError(token)) }, + { typeof(bool), token => Result.Ok_Or(token.AsBoolean, CreateInvalidParamError(token)) }, + { typeof(UInt256), ConvertUInt256 }, + { typeof(ContractNameOrHashOrId), ConvertContractNameOrHashOrId }, + { typeof(BlockHashOrIndex), ConvertBlockHashOrIndex } + }; + } + + internal static object ConvertParameter(JToken token, Type targetType) + { + if (s_conversionStrategies.TryGetValue(targetType, out var conversionStrategy)) + return conversionStrategy(token); + throw new RpcException(RpcError.InvalidParams.WithData($"Unsupported parameter type: {targetType}")); + } + + private static object ConvertNumeric(JToken token) where T : struct + { + if (TryConvertDoubleToNumericType(token, out var result)) + { + return result; + } + + throw new RpcException(CreateInvalidParamError(token)); + } + + private static bool TryConvertDoubleToNumericType(JToken token, out T result) where T : struct + { + result = default; + try + { + var value = token.AsNumber(); + var minValue = Convert.ToDouble(typeof(T).GetField("MinValue").GetValue(null)); + var maxValue = Convert.ToDouble(typeof(T).GetField("MaxValue").GetValue(null)); + + if (value < minValue || value > maxValue) + { + return false; + } + + if (!typeof(T).IsFloatingPoint() && !IsValidInteger(value)) + { + return false; + } + + result = (T)Convert.ChangeType(value, typeof(T)); + return true; + } + catch + { + return false; + } + } + + private static bool IsValidInteger(double value) + { + // Integer values are safe if they are within the range of MIN_SAFE_INTEGER and MAX_SAFE_INTEGER + if (value < JNumber.MIN_SAFE_INTEGER || value > JNumber.MAX_SAFE_INTEGER) + return false; + return Math.Abs(value % 1) <= double.Epsilon; + } + + internal static object ConvertUInt160(JToken token, byte addressVersion) + { + var value = token.AsString(); + if (UInt160.TryParse(value, out var scriptHash)) + { + return scriptHash; + } + return Result.Ok_Or(() => value.ToScriptHash(addressVersion), + RpcError.InvalidParams.WithData($"Invalid UInt160 Format: {token}")); + } + + private static object ConvertUInt256(JToken token) + { + if (UInt256.TryParse(token.AsString(), out var hash)) + { + return hash; + } + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid UInt256 Format: {token}")); + } + + private static object ConvertContractNameOrHashOrId(JToken token) + { + if (ContractNameOrHashOrId.TryParse(token.AsString(), out var contractNameOrHashOrId)) + { + return contractNameOrHashOrId; + } + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid contract hash or id Format: {token}")); + } + + private static object ConvertBlockHashOrIndex(JToken token) + { + if (BlockHashOrIndex.TryParse(token.AsString(), out var blockHashOrIndex)) + { + return blockHashOrIndex; + } + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid block hash or index Format: {token}")); + } + + private static RpcError CreateInvalidParamError(JToken token) + { + return RpcError.InvalidParams.WithData($"Invalid {typeof(T)} value: {token}"); + } +} + +public static class TypeExtensions +{ + public static bool IsFloatingPoint(this Type type) + { + return type == typeof(float) || type == typeof(double) || type == typeof(decimal); + } +} 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/RpcMethodWithParamsAttribute.cs b/src/Plugins/RpcServer/RpcMethodWithParamsAttribute.cs new file mode 100644 index 0000000000..c53dd1fd85 --- /dev/null +++ b/src/Plugins/RpcServer/RpcMethodWithParamsAttribute.cs @@ -0,0 +1,20 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// RpcMethodWithParamsAttribute.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 System; + +namespace Neo.Plugins.RpcServer; + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class RpcMethodWithParamsAttribute : Attribute +{ + public string Name { get; set; } +} diff --git a/src/Plugins/RpcServer/RpcServer.Blockchain.cs b/src/Plugins/RpcServer/RpcServer.Blockchain.cs index fe01974412..446f1b4f16 100644 --- a/src/Plugins/RpcServer/RpcServer.Blockchain.cs +++ b/src/Plugins/RpcServer/RpcServer.Blockchain.cs @@ -13,6 +13,7 @@ using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; +using Neo.Plugins.RpcServer.Model; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -28,10 +29,9 @@ partial class RpcServer /// /// Gets the hash of the best (most recent) block. /// - /// An empty array; no parameters are required. /// The hash of the best block as a . - [RpcMethod] - protected internal virtual JToken GetBestBlockHash(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetBestBlockHash() { return NativeContract.Ledger.CurrentHash(system.StoreView).ToString(); } @@ -39,29 +39,15 @@ protected internal virtual JToken GetBestBlockHash(JArray _params) /// /// Gets a block by its hash or index. /// - /// - /// An array containing the block hash or index as the first element, - /// and an optional boolean indicating whether to return verbose information. - /// + /// The block hash or index. + /// Optional, the default value is false. /// The block data as a . If the second item of _params is true, then /// block data is json format, otherwise, the return type is Base64-encoded byte array. - [RpcMethod] - protected internal virtual JToken GetBlock(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetBlock(BlockHashOrIndex blockHashOrIndex, bool verbose = false) { - JToken key = Result.Ok_Or(() => _params[0], RpcError.InvalidParams.WithData($"Invalid Block Hash or Index: {_params[0]}")); - bool verbose = _params.Count >= 2 && _params[1].AsBoolean(); using var snapshot = system.GetSnapshotCache(); - Block block; - if (key is JNumber) - { - uint index = uint.Parse(key.AsString()); - block = NativeContract.Ledger.GetBlock(snapshot, index); - } - else - { - UInt256 hash = Result.Ok_Or(() => UInt256.Parse(key.AsString()), RpcError.InvalidParams.WithData($"Invalid block hash {_params[0]}")); - block = NativeContract.Ledger.GetBlock(snapshot, hash); - } + var block = blockHashOrIndex.IsIndex ? NativeContract.Ledger.GetBlock(snapshot, blockHashOrIndex.AsIndex()) : NativeContract.Ledger.GetBlock(snapshot, blockHashOrIndex.AsHash()); block.NotNull_Or(RpcError.UnknownBlock); if (verbose) { @@ -78,10 +64,9 @@ protected internal virtual JToken GetBlock(JArray _params) /// /// Gets the number of block headers in the blockchain. /// - /// An empty array; no parameters are required. /// The count of block headers as a . - [RpcMethod] - internal virtual JToken GetBlockHeaderCount(JArray _params) + [RpcMethodWithParams] + internal virtual JToken GetBlockHeaderCount() { return (system.HeaderCache.Last?.Index ?? NativeContract.Ledger.CurrentIndex(system.StoreView)) + 1; } @@ -89,10 +74,9 @@ internal virtual JToken GetBlockHeaderCount(JArray _params) /// /// Gets the number of blocks in the blockchain. /// - /// An empty array; no parameters are required. /// The count of blocks as a . - [RpcMethod] - protected internal virtual JToken GetBlockCount(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetBlockCount() { return NativeContract.Ledger.CurrentIndex(system.StoreView) + 1; } @@ -100,12 +84,11 @@ protected internal virtual JToken GetBlockCount(JArray _params) /// /// Gets the hash of the block at the specified height. /// - /// An array containing the block height as the first element. + /// Block index (block height) /// The hash of the block at the specified height as a . - [RpcMethod] - protected internal virtual JToken GetBlockHash(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetBlockHash(uint height) { - uint height = Result.Ok_Or(() => uint.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid Height: {_params[0]}")); var snapshot = system.StoreView; if (height <= NativeContract.Ledger.CurrentIndex(snapshot)) { @@ -117,27 +100,26 @@ protected internal virtual JToken GetBlockHash(JArray _params) /// /// Gets a block header by its hash or index. /// - /// - /// An array containing the block header hash or index as the first element, - /// and an optional boolean indicating whether to return verbose information. - /// + /// The block script hash or index (i.e. block height=number of blocks - 1). + /// Optional, the default value is false. + /// + /// When verbose is false, serialized information of the block is returned in a hexadecimal string. + /// If you need the detailed information, use the SDK for deserialization. + /// When verbose is true or 1, detailed information of the block is returned in Json format. + /// /// The block header data as a . In json format if the second item of _params is true, otherwise Base64-encoded byte array. - [RpcMethod] - protected internal virtual JToken GetBlockHeader(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetBlockHeader(BlockHashOrIndex blockHashOrIndex, bool verbose = false) { - JToken key = _params[0]; - bool verbose = _params.Count >= 2 && _params[1].AsBoolean(); var snapshot = system.StoreView; Header header; - if (key is JNumber) + if (blockHashOrIndex.IsIndex) { - uint height = uint.Parse(key.AsString()); - header = NativeContract.Ledger.GetHeader(snapshot, height).NotNull_Or(RpcError.UnknownBlock); + header = NativeContract.Ledger.GetHeader(snapshot, blockHashOrIndex.AsIndex()).NotNull_Or(RpcError.UnknownBlock); } else { - UInt256 hash = Result.Ok_Or(() => UInt256.Parse(key.AsString()), RpcError.InvalidParams.WithData($"Invalid block hash {_params[0]}")); - header = NativeContract.Ledger.GetHeader(snapshot, hash).NotNull_Or(RpcError.UnknownBlock); + header = NativeContract.Ledger.GetHeader(snapshot, blockHashOrIndex.AsHash()).NotNull_Or(RpcError.UnknownBlock); } if (verbose) { @@ -155,19 +137,19 @@ protected internal virtual JToken GetBlockHeader(JArray _params) /// /// Gets the state of a contract by its ID or script hash or (only for native contracts) by case-insensitive name. /// - /// An array containing the contract ID or script hash or case-insensitive native contract name as the first element. + /// Contract name or script hash or the native contract id. /// The contract state in json format as a . - [RpcMethod] - protected internal virtual JToken GetContractState(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetContractState(ContractNameOrHashOrId contractNameOrHashOrId) { - if (int.TryParse(_params[0].AsString(), out int contractId)) + if (contractNameOrHashOrId.IsId) { - var contractState = NativeContract.ContractManagement.GetContractById(system.StoreView, contractId); + var contractState = NativeContract.ContractManagement.GetContractById(system.StoreView, contractNameOrHashOrId.AsId()); return contractState.NotNull_Or(RpcError.UnknownContract).ToJson(); } - var scriptHash = Result.Ok_Or(() => ToScriptHash(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid contract hash {_params[0]}")); - var contract = NativeContract.ContractManagement.GetContract(system.StoreView, scriptHash); + var hash = contractNameOrHashOrId.IsName ? ToScriptHash(contractNameOrHashOrId.AsName()) : contractNameOrHashOrId.AsHash(); + var contract = NativeContract.ContractManagement.GetContract(system.StoreView, hash); return contract.NotNull_Or(RpcError.UnknownContract).ToJson(); } @@ -185,12 +167,11 @@ private static UInt160 ToScriptHash(string keyword) /// /// Gets the current memory pool transactions. /// - /// An array containing an optional boolean indicating whether to include unverified transactions. + /// Optional, the default value is false. /// The memory pool transactions in json format as a . - [RpcMethod] - protected internal virtual JToken GetRawMemPool(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetRawMemPool(bool shouldGetUnverified = false) { - bool shouldGetUnverified = _params.Count >= 1 && _params[0].AsBoolean(); if (!shouldGetUnverified) return new JArray(system.MemPool.GetVerifiedTransactions().Select(p => (JToken)p.Hash.ToString())); @@ -207,27 +188,23 @@ protected internal virtual JToken GetRawMemPool(JArray _params) /// /// Gets a transaction by its hash. /// - /// - /// An array containing the transaction hash as the first element, - /// and an optional boolean indicating whether to return verbose information. - /// - /// The transaction data as a . In json format if the second item of _params is true, otherwise base64string. - [RpcMethod] - protected internal virtual JToken GetRawTransaction(JArray _params) + /// The transaction hash. + /// Optional, the default value is false. + /// The transaction data as a . In json format if verbose is true, otherwise base64string. + [RpcMethodWithParams] + protected internal virtual JToken GetRawTransaction(UInt256 hash, bool verbose = false) { - UInt256 hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid Transaction Hash: {_params[0]}")); - bool verbose = _params.Count >= 2 && _params[1].AsBoolean(); - if (system.MemPool.TryGetValue(hash, out Transaction tx) && !verbose) + if (system.MemPool.TryGetValue(hash, out var tx) && !verbose) return Convert.ToBase64String(tx.ToArray()); var snapshot = system.StoreView; - TransactionState state = NativeContract.Ledger.GetTransactionState(snapshot, hash); + var state = NativeContract.Ledger.GetTransactionState(snapshot, hash); tx ??= state?.Transaction; tx.NotNull_Or(RpcError.UnknownTransaction); if (!verbose) return Convert.ToBase64String(tx.ToArray()); - JObject json = Utility.TransactionToJson(tx, system.Settings); + var json = Utility.TransactionToJson(tx, system.Settings); if (state is not null) { - TrimmedBlock block = NativeContract.Ledger.GetTrimmedBlock(snapshot, NativeContract.Ledger.GetBlockHash(snapshot, state.BlockIndex)); + var block = NativeContract.Ledger.GetTrimmedBlock(snapshot, NativeContract.Ledger.GetBlockHash(snapshot, state.BlockIndex)); json["blockhash"] = block.Hash.ToString(); json["confirmations"] = NativeContract.Ledger.CurrentIndex(snapshot) - block.Index + 1; json["blocktime"] = block.Header.Timestamp; @@ -238,23 +215,26 @@ protected internal virtual JToken GetRawTransaction(JArray _params) /// /// Gets the storage item by contract ID or script hash and key. /// - /// - /// An array containing the contract ID or script hash as the first element, - /// and the storage key as the second element. - /// + /// The contract ID or script hash. + /// The Base64-encoded storage key. /// The storage item as a . - [RpcMethod] - protected internal virtual JToken GetStorage(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetStorage(ContractNameOrHashOrId contractNameOrHashOrId, string base64Key) { using var snapshot = system.GetSnapshotCache(); - if (!int.TryParse(_params[0].AsString(), out int id)) + int id; + if (contractNameOrHashOrId.IsHash) { - UInt160 hash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid contract hash {_params[0]}")); - ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash).NotNull_Or(RpcError.UnknownContract); + var hash = contractNameOrHashOrId.AsHash(); + var contract = NativeContract.ContractManagement.GetContract(snapshot, hash).NotNull_Or(RpcError.UnknownContract); id = contract.Id; } - byte[] key = Convert.FromBase64String(_params[1].AsString()); - StorageItem item = snapshot.TryGet(new StorageKey + else + { + id = contractNameOrHashOrId.AsId(); + } + var key = Convert.FromBase64String(base64Key); + var item = snapshot.TryGet(new StorageKey { Id = id, Key = key @@ -265,32 +245,28 @@ protected internal virtual JToken GetStorage(JArray _params) /// /// Finds storage items by contract ID or script hash and prefix. /// - /// - /// An array containing the contract ID or script hash as the first element, - /// the Base64-encoded storage key prefix as the second element, - /// and an optional start index as the third element. - /// + /// The contract ID (int) or script hash (UInt160). + /// The Base64-encoded storage key prefix. + /// The start index. /// The found storage items as a . - [RpcMethod] - protected internal virtual JToken FindStorage(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken FindStorage(ContractNameOrHashOrId contractNameOrHashOrId, string base64KeyPrefix, int start = 0) { using var snapshot = system.GetSnapshotCache(); - if (!int.TryParse(_params[0].AsString(), out int id)) + int id; + if (contractNameOrHashOrId.IsHash) { - UInt160 hash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid contract hash {_params[0]}")); - ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash).NotNull_Or(RpcError.UnknownContract); + ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, contractNameOrHashOrId.AsHash()).NotNull_Or(RpcError.UnknownContract); id = contract.Id; } - - byte[] prefix = Result.Ok_Or(() => Convert.FromBase64String(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid Base64 string{_params[1]}")); - byte[] prefix_key = StorageKey.CreateSearchPrefix(id, prefix); - - int start = 0; - if (_params.Count > 2 && !int.TryParse(_params[2].AsString(), out start)) + else { - start = 0; + id = contractNameOrHashOrId.AsId(); } + byte[] prefix = Result.Ok_Or(() => Convert.FromBase64String(base64KeyPrefix), RpcError.InvalidParams.WithData($"Invalid Base64 string{base64KeyPrefix}")); + byte[] prefix_key = StorageKey.CreateSearchPrefix(id, prefix); + JObject json = new(); JArray jarr = new(); int pageSize = settings.FindStoragePageSize; @@ -324,12 +300,11 @@ protected internal virtual JToken FindStorage(JArray _params) /// /// Gets the height of a transaction by its hash. /// - /// An array containing the transaction hash as the first element. + /// The transaction hash. /// The height of the transaction as a . - [RpcMethod] - protected internal virtual JToken GetTransactionHeight(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetTransactionHeight(UInt256 hash) { - UInt256 hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid Transaction Hash: {_params[0]}")); uint? height = NativeContract.Ledger.GetTransactionState(system.StoreView, hash)?.BlockIndex; if (height.HasValue) return height.Value; throw new RpcException(RpcError.UnknownTransaction); @@ -338,10 +313,9 @@ protected internal virtual JToken GetTransactionHeight(JArray _params) /// /// Gets the next block validators. /// - /// An empty array; no parameters are required. /// The next block validators as a . - [RpcMethod] - protected internal virtual JToken GetNextBlockValidators(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetNextBlockValidators() { using var snapshot = system.GetSnapshotCache(); var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, system.Settings.ValidatorsCount); @@ -357,10 +331,9 @@ protected internal virtual JToken GetNextBlockValidators(JArray _params) /// /// Gets the list of candidates for the next block validators. /// - /// An empty array; no parameters are required. /// The candidates public key list as a JToken. - [RpcMethod] - protected internal virtual JToken GetCandidates(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetCandidates() { using var snapshot = system.GetSnapshotCache(); byte[] script; @@ -414,10 +387,9 @@ protected internal virtual JToken GetCandidates(JArray _params) /// /// Gets the list of committee members. /// - /// An empty array; no parameters are required. /// The committee members publickeys as a . - [RpcMethod] - protected internal virtual JToken GetCommittee(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetCommittee() { return new JArray(NativeContract.NEO.GetCommittee(system.StoreView).Select(p => (JToken)p.ToString())); } @@ -425,10 +397,9 @@ protected internal virtual JToken GetCommittee(JArray _params) /// /// Gets the list of native contracts. /// - /// An empty array; no parameters are required. /// The native contract states as a . - [RpcMethod] - protected internal virtual JToken GetNativeContracts(JArray _params) + [RpcMethodWithParams] + protected internal virtual JToken GetNativeContracts() { return new JArray(NativeContract.Contracts.Select(p => NativeContract.ContractManagement.GetContract(system.StoreView, p.Hash).ToJson())); } 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/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/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 50f3c7a1e0..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, ContractBasicMethod.VerifyPCount).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/src/Plugins/RpcServer/RpcServer.cs b/src/Plugins/RpcServer/RpcServer.cs index c53f17b860..c90bbad539 100644 --- a/src/Plugins/RpcServer/RpcServer.cs +++ b/src/Plugins/RpcServer/RpcServer.cs @@ -23,6 +23,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Linq.Expressions; using System.Net.Security; using System.Reflection; using System.Security.Cryptography.X509Certificates; @@ -36,6 +37,7 @@ public partial class RpcServer : IDisposable private const int MaxParamsDepth = 32; private readonly Dictionary> methods = new(); + private readonly Dictionary _methodsWithParams = new(); private IWebHost host; private RpcServerSettings settings; @@ -261,24 +263,83 @@ public async Task ProcessAsync(HttpContext context) private async Task ProcessRequestAsync(HttpContext context, JObject request) { if (!request.ContainsProperty("id")) return null; - JToken @params = request["params"] ?? new JArray(); + var @params = request["params"] ?? new JArray(); if (!request.ContainsProperty("method") || @params is not JArray) { return CreateErrorResponse(request["id"], RpcError.InvalidRequest); } - JObject response = CreateResponse(request["id"]); + + var jsonParameters = (JArray)@params; + var response = CreateResponse(request["id"]); try { - string method = request["method"].AsString(); + var method = request["method"].AsString(); (CheckAuth(context) && !settings.DisabledMethods.Contains(method)).True_Or(RpcError.AccessDenied); - methods.TryGetValue(method, out var func).True_Or(RpcErrorFactory.MethodNotFound(method)); - response["result"] = func((JArray)@params) switch + + if (methods.TryGetValue(method, out var func)) + { + response["result"] = func(jsonParameters) switch + { + JToken result => result, + Task task => await task, + _ => throw new NotSupportedException() + }; + return response; + } + + if (_methodsWithParams.TryGetValue(method, out var func2)) { - JToken result => result, - Task task => await task, - _ => throw new NotSupportedException() - }; - return response; + var paramInfos = func2.Method.GetParameters(); + var args = new object[paramInfos.Length]; + + for (var i = 0; i < paramInfos.Length; i++) + { + var param = paramInfos[i]; + if (jsonParameters.Count > i && jsonParameters[i] != null) + { + try + { + if (param.ParameterType == typeof(UInt160)) + { + args[i] = ParameterConverter.ConvertUInt160(jsonParameters[i], system.Settings.AddressVersion); + } + else + { + args[i] = ParameterConverter.ConvertParameter(jsonParameters[i], param.ParameterType); + } + } + catch (Exception e) when (e is not RpcException) + { + throw new ArgumentException($"Invalid value for parameter '{param.Name}'", e); + } + } + else + { + if (param.IsOptional) + { + args[i] = param.DefaultValue; + } + else if (param.ParameterType.IsValueType && Nullable.GetUnderlyingType(param.ParameterType) == null) + { + throw new ArgumentException($"Required parameter '{param.Name}' is missing"); + } + else + { + args[i] = null; + } + } + } + + response["result"] = func2.DynamicInvoke(args) switch + { + JToken result => result, + Task task => await task, + _ => throw new NotSupportedException() + }; + return response; + } + + throw new RpcException(RpcError.MethodNotFound.WithData(method)); } catch (FormatException ex) { @@ -288,24 +349,40 @@ private async Task ProcessRequestAsync(HttpContext context, JObject req { return CreateErrorResponse(request["id"], RpcError.InvalidParams.WithData(ex.Message)); } - catch (Exception ex) + catch (Exception ex) when (ex is not RpcException) { #if DEBUG return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message, ex.StackTrace)); #else - return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message)); + return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message)); #endif } } public void RegisterMethods(object handler) { - foreach (MethodInfo method in handler.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + foreach (var method in handler.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { - RpcMethodAttribute attribute = method.GetCustomAttribute(); - if (attribute is null) continue; - string name = string.IsNullOrEmpty(attribute.Name) ? method.Name.ToLowerInvariant() : attribute.Name; - methods[name] = method.CreateDelegate>(handler); + var attribute = method.GetCustomAttribute(); + var attributeWithParams = method.GetCustomAttribute(); + if (attribute is null && attributeWithParams is null) continue; + if (attribute is not null && attributeWithParams is not null) throw new InvalidOperationException("Method cannot have both RpcMethodAttribute and RpcMethodWithParamsAttribute"); + + if (attribute is not null) + { + var name = string.IsNullOrEmpty(attribute.Name) ? method.Name.ToLowerInvariant() : attribute.Name; + methods[name] = method.CreateDelegate>(handler); + } + + if (attributeWithParams is not null) + { + var name = string.IsNullOrEmpty(attributeWithParams.Name) ? method.Name.ToLowerInvariant() : attributeWithParams.Name; + + var parameters = method.GetParameters().Select(p => p.ParameterType).ToArray(); + var delegateType = Expression.GetDelegateType(parameters.Concat([method.ReturnType]).ToArray()); + + _methodsWithParams[name] = Delegate.CreateDelegate(delegateType, handler, method); + } } } } diff --git a/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs b/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs index d4698cba1e..71bfceb49d 100644 --- a/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs +++ b/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs index 05592d366b..2e53456545 100644 --- a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs +++ b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs @@ -17,7 +17,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using static Neo.Helper; namespace Neo.Cryptography.MPTTrie.Tests { diff --git a/tests/Neo.Extensions.Tests/Collections/UT_HashSetExtensions.cs b/tests/Neo.Extensions.Tests/Collections/UT_HashSetExtensions.cs new file mode 100644 index 0000000000..cb475a7e5c --- /dev/null +++ b/tests/Neo.Extensions.Tests/Collections/UT_HashSetExtensions.cs @@ -0,0 +1,75 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_HashSetExtensions.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.IO.Caching; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.Extensions.Tests.Collections +{ + [TestClass] + public class UT_HashSetExtensions + { + [TestMethod] + public void TestRemoveHashsetDictionary() + { + var a = new HashSet + { + 1, + 2, + 3 + }; + + var b = new Dictionary + { + [2] = null + }; + + a.Remove(b); + + CollectionAssert.AreEqual(new int[] { 1, 3 }, a.ToArray()); + + b[4] = null; + b[5] = null; + b[1] = null; + a.Remove(b); + + CollectionAssert.AreEqual(new int[] { 3 }, a.ToArray()); + } + + [TestMethod] + public void TestRemoveHashsetSet() + { + var a = new HashSet + { + 1, + 2, + 3 + }; + + var b = new SortedSet() + { + 2 + }; + + a.Remove(b); + + CollectionAssert.AreEqual(new int[] { 1, 3 }, a.ToArray()); + + b.Add(4); + b.Add(5); + b.Add(1); + a.Remove(b); + + CollectionAssert.AreEqual(new int[] { 3 }, a.ToArray()); + } + } +} diff --git a/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj b/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj index 3c13dd0953..03809a4fe3 100644 --- a/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj +++ b/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/tests/Neo.Extensions.Tests/Net/UT_IpAddressExtensions.cs b/tests/Neo.Extensions.Tests/Net/UT_IpAddressExtensions.cs new file mode 100644 index 0000000000..bbf26c5cb7 --- /dev/null +++ b/tests/Neo.Extensions.Tests/Net/UT_IpAddressExtensions.cs @@ -0,0 +1,43 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_IpAddressExtensions.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 Neo.Extensions; +using System.Net; + +namespace Neo.Extensions.Tests.Net +{ + [TestClass] + public class UT_IpAddressExtensions + { + [TestMethod] + public void TestUnmapForIPAddress() + { + var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); + addr.UnMap().Should().Be(addr); + + var addr2 = addr.MapToIPv6(); + addr2.UnMap().Should().Be(addr); + } + + [TestMethod] + public void TestUnmapForIPEndPoin() + { + var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); + var endPoint = new IPEndPoint(addr, 8888); + endPoint.UnMap().Should().Be(endPoint); + + var addr2 = addr.MapToIPv6(); + var endPoint2 = new IPEndPoint(addr2, 8888); + endPoint2.UnMap().Should().Be(endPoint); + } + } +} diff --git a/tests/Neo.Extensions.Tests/UT_AssemblyExtensions.cs b/tests/Neo.Extensions.Tests/UT_AssemblyExtensions.cs new file mode 100644 index 0000000000..43f66f95da --- /dev/null +++ b/tests/Neo.Extensions.Tests/UT_AssemblyExtensions.cs @@ -0,0 +1,33 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_AssemblyExtensions.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 System; +using System.Linq; + +namespace Neo.Extensions.Tests +{ + [TestClass] + public class UT_AssemblyExtensions + { + [TestMethod] + public void TestGetVersion() + { + // assembly without version + + var asm = AppDomain.CurrentDomain.GetAssemblies() + .Where(u => u.FullName == "Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") + .FirstOrDefault(); + string version = asm?.GetVersion() ?? ""; + version.Should().Be("0.0.0"); + } + } +} diff --git a/tests/Neo.Extensions.Tests/UT_DateTimeExtensions.cs b/tests/Neo.Extensions.Tests/UT_DateTimeExtensions.cs new file mode 100644 index 0000000000..43414a8f81 --- /dev/null +++ b/tests/Neo.Extensions.Tests/UT_DateTimeExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_DateTimeExtensions.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 System; + +namespace Neo.Extensions.Tests +{ + [TestClass] + public class UT_DateTimeExtensions + { + private static readonly DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + [TestMethod] + public void TestToTimestamp() + { + var time = DateTime.Now; + var expected = (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; + var actual = time.ToTimestamp(); + + actual.Should().Be(expected); + } + + [TestMethod] + public void TestToTimestampMS() + { + var time = DateTime.Now; + var expected = (ulong)(time.ToUniversalTime() - unixEpoch).TotalMilliseconds; + var actual = time.ToTimestampMS(); + + actual.Should().Be(expected); + } + } +} diff --git a/tests/Neo.Extensions.Tests/UT_RandomExtensions.cs b/tests/Neo.Extensions.Tests/UT_RandomExtensions.cs new file mode 100644 index 0000000000..fb7c6a8cf7 --- /dev/null +++ b/tests/Neo.Extensions.Tests/UT_RandomExtensions.cs @@ -0,0 +1,32 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_RandomExtensions.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 System; + +namespace Neo.Extensions.Tests +{ + [TestClass] + public class UT_RandomExtensions + { + [TestMethod] + public void TestNextBigIntegerForRandom() + { + Random ran = new(); + Action action1 = () => ran.NextBigInteger(-1); + action1.Should().Throw(); + + ran.NextBigInteger(0).Should().Be(0); + ran.NextBigInteger(8).Should().NotBeNull(); + ran.NextBigInteger(9).Should().NotBeNull(); + } + } +} diff --git a/tests/Neo.Extensions.Tests/UT_SecureStringExtensions.cs b/tests/Neo.Extensions.Tests/UT_SecureStringExtensions.cs new file mode 100644 index 0000000000..cd583187b1 --- /dev/null +++ b/tests/Neo.Extensions.Tests/UT_SecureStringExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_SecureStringExtensions.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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.Extensions.Tests +{ + [TestClass] + public class UT_SecureStringExtensions + { + [TestMethod] + public void Test_String_To_SecureString() + { + var expected = "Hello World"; + var expectedSecureString = expected.ToSecureString(); + + var actual = expectedSecureString.GetClearText(); + + Assert.IsTrue(expectedSecureString.IsReadOnly()); + Assert.AreEqual(expected, actual); + } + } +} diff --git a/tests/Neo.Extensions.Tests/UT_StringExtensions.cs b/tests/Neo.Extensions.Tests/UT_StringExtensions.cs new file mode 100644 index 0000000000..9954f24765 --- /dev/null +++ b/tests/Neo.Extensions.Tests/UT_StringExtensions.cs @@ -0,0 +1,39 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_StringExtensdions.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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.Extensions.Tests +{ + [TestClass] + public class UT_StringExtensions + { + [TestMethod] + public void TestHexToBytes() + { + string nullStr = null; + _ = nullStr.HexToBytes().ToHexString().Should().Be(Array.Empty().ToHexString()); + string emptyStr = ""; + emptyStr.HexToBytes().ToHexString().Should().Be(Array.Empty().ToHexString()); + string str1 = "hab"; + Action action = () => str1.HexToBytes(); + action.Should().Throw(); + string str2 = "0102"; + byte[] bytes = str2.HexToBytes(); + bytes.ToHexString().Should().Be(new byte[] { 0x01, 0x02 }.ToHexString()); + } + } +} diff --git a/tests/Neo.Network.RPC.Tests/UT_ContractClient.cs b/tests/Neo.Network.RPC.Tests/UT_ContractClient.cs index c3cf226a3e..6ac90dafc5 100644 --- a/tests/Neo.Network.RPC.Tests/UT_ContractClient.cs +++ b/tests/Neo.Network.RPC.Tests/UT_ContractClient.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Neo.Extensions; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; diff --git a/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs b/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs index 863d424405..ef156a5b05 100644 --- a/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs +++ b/tests/Neo.Network.RPC.Tests/UT_Nep17API.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Neo.Extensions; using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -19,7 +20,6 @@ using System.Linq; using System.Numerics; using System.Threading.Tasks; -using static Neo.Helper; namespace Neo.Network.RPC.Tests { diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs new file mode 100644 index 0000000000..a87f1c7961 --- /dev/null +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs @@ -0,0 +1,414 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_Parameters.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.Json; +using Neo.Plugins.RpcServer.Model; +using Neo.UnitTests; +using Neo.Wallets; +using System; + +namespace Neo.Plugins.RpcServer.Tests; + +[TestClass] +public class UT_Parameters +{ + [TestMethod] + public void TestTryParse_ContractNameOrHashOrId() + { + Assert.IsTrue(ContractNameOrHashOrId.TryParse("1", out var contractNameOrHashOrId)); + Assert.IsTrue(contractNameOrHashOrId.IsId); + Assert.IsTrue(ContractNameOrHashOrId.TryParse("0x1234567890abcdef1234567890abcdef12345678", out contractNameOrHashOrId)); + Assert.IsTrue(contractNameOrHashOrId.IsHash); + Assert.IsTrue(ContractNameOrHashOrId.TryParse("test", out contractNameOrHashOrId)); + Assert.IsTrue(contractNameOrHashOrId.IsName); + Assert.IsFalse(ContractNameOrHashOrId.TryParse("", out _)); + + JToken token = 1; + Assert.AreEqual(1, ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token, typeof(ContractNameOrHashOrId))).AsId()); + + JToken token2 = "1"; + Assert.AreEqual(1, ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token2, typeof(ContractNameOrHashOrId))).AsId()); + + JToken token3 = "0x1234567890abcdef1234567890abcdef12345678"; + Assert.AreEqual(UInt160.Parse("0x1234567890abcdef1234567890abcdef12345678"), ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token3, typeof(ContractNameOrHashOrId))).AsHash()); + + JToken token4 = "0xabc"; + Assert.ThrowsException(() => ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token4, typeof(ContractNameOrHashOrId))).AsHash()); + } + + [TestMethod] + public void TestTryParse_BlockHashOrIndex() + { + Assert.IsTrue(BlockHashOrIndex.TryParse("1", out var blockHashOrIndex)); + Assert.IsTrue(blockHashOrIndex.IsIndex); + Assert.AreEqual(1u, blockHashOrIndex.AsIndex()); + Assert.IsTrue(BlockHashOrIndex.TryParse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d", out blockHashOrIndex)); + Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), blockHashOrIndex.AsHash()); + Assert.IsFalse(BlockHashOrIndex.TryParse("", out _)); + + JToken token = 1; + Assert.AreEqual(1u, ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token2 = -1; + Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token2, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token3 = "1"; + Assert.AreEqual(1u, ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token3, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token4 = "-1"; + Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token4, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token5 = "0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"; + Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token5, typeof(BlockHashOrIndex))).AsHash()); + + JToken token6 = "761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"; + Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token6, typeof(BlockHashOrIndex))).AsHash()); + + JToken token7 = "0xabc"; + Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token7, typeof(BlockHashOrIndex))).AsHash()); + } + + [TestMethod] + public void TestUInt160() + { + JToken token = "0x1234567890abcdef1234567890abcdef12345678"; + Assert.AreEqual(UInt160.Parse("0x1234567890abcdef1234567890abcdef12345678"), ParameterConverter.ConvertUInt160(token, TestProtocolSettings.Default.AddressVersion)); + + JToken token2 = "0xabc"; + Assert.ThrowsException(() => ParameterConverter.ConvertUInt160(token2, TestProtocolSettings.Default.AddressVersion)); + + JToken token3 = "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf"; + Assert.AreEqual("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(TestProtocolSettings.Default.AddressVersion), ParameterConverter.ConvertUInt160(token3, TestProtocolSettings.Default.AddressVersion)); + } + + [TestMethod] + public void TestUInt256() + { + JToken token = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + Assert.AreEqual(UInt256.Parse("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"), ParameterConverter.ConvertParameter(token, typeof(UInt256))); + + JToken token2 = "0xabc"; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(UInt256))); + } + + [TestMethod] + public void TestInteger() + { + JToken token = 1; + Assert.AreEqual(1, ParameterConverter.ConvertParameter(token, typeof(int))); + Assert.AreEqual((long)1, ParameterConverter.ConvertParameter(token, typeof(long))); + Assert.AreEqual((uint)1, ParameterConverter.ConvertParameter(token, typeof(uint))); + Assert.AreEqual((ulong)1, ParameterConverter.ConvertParameter(token, typeof(ulong))); + Assert.AreEqual((short)1, ParameterConverter.ConvertParameter(token, typeof(short))); + Assert.AreEqual((ushort)1, ParameterConverter.ConvertParameter(token, typeof(ushort))); + Assert.AreEqual((byte)1, ParameterConverter.ConvertParameter(token, typeof(byte))); + Assert.AreEqual((sbyte)1, ParameterConverter.ConvertParameter(token, typeof(sbyte))); + + JToken token2 = 1.1; + + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(ulong))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(byte))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(sbyte))); + + JToken token3 = "1"; + + Assert.AreEqual((int)1, ParameterConverter.ConvertParameter(token3, typeof(int))); + Assert.AreEqual((long)1, ParameterConverter.ConvertParameter(token3, typeof(long))); + Assert.AreEqual((uint)1, ParameterConverter.ConvertParameter(token3, typeof(uint))); + Assert.AreEqual((ulong)1, ParameterConverter.ConvertParameter(token3, typeof(ulong))); + Assert.AreEqual((short)1, ParameterConverter.ConvertParameter(token3, typeof(short))); + Assert.AreEqual((ushort)1, ParameterConverter.ConvertParameter(token3, typeof(ushort))); + Assert.AreEqual((byte)1, ParameterConverter.ConvertParameter(token3, typeof(byte))); + Assert.AreEqual((sbyte)1, ParameterConverter.ConvertParameter(token3, typeof(sbyte))); + + JToken token4 = "1.1"; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(ulong))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(byte))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(sbyte))); + + JToken token5 = "abc"; + + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(ulong))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(byte))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(sbyte))); + + JToken token6 = -1; + + Assert.AreEqual(-1, ParameterConverter.ConvertParameter(token6, typeof(int))); + Assert.AreEqual((long)-1, ParameterConverter.ConvertParameter(token6, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(ulong))); + Assert.AreEqual((short)-1, ParameterConverter.ConvertParameter(token6, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(byte))); + Assert.AreEqual((sbyte)-1, ParameterConverter.ConvertParameter(token6, typeof(sbyte))); + } + + [TestMethod] + public void TestBoolean() + { + JToken token = true; + Assert.AreEqual(true, ParameterConverter.ConvertParameter(token, typeof(bool))); + JToken token2 = false; + Assert.AreEqual(false, ParameterConverter.ConvertParameter(token2, typeof(bool))); + JToken token6 = 1; + Assert.AreEqual(true, ParameterConverter.ConvertParameter(token6, typeof(bool))); + JToken token7 = 0; + Assert.AreEqual(false, ParameterConverter.ConvertParameter(token7, typeof(bool))); + } + + [TestMethod] + public void TestNumericTypeConversions() + { + // Test integer conversions + TestIntegerConversions(); + + // Test byte conversions + TestByteConversions(); + + // Test sbyte conversions + TestSByteConversions(); + + // Test short conversions + TestShortConversions(); + + // Test ushort conversions + TestUShortConversions(); + + // Test uint conversions + TestUIntConversions(); + + // Test long conversions + TestLongConversions(); + + // Test ulong conversions + TestULongConversions(); + } + + private void TestIntegerConversions() + { + // Test max value + JToken maxToken = int.MaxValue; + Assert.AreEqual(int.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(int))); + + // Test min value + JToken minToken = int.MinValue; + Assert.AreEqual(int.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(int))); + + // Test overflow + JToken overflowToken = (long)int.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(int))); + + // Test underflow + JToken underflowToken = (long)int.MinValue - 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(int))); + } + + private void TestByteConversions() + { + // Test max value + JToken maxToken = byte.MaxValue; + Assert.AreEqual(byte.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(byte))); + + // Test min value + JToken minToken = byte.MinValue; + Assert.AreEqual(byte.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(byte))); + + // Test overflow + JToken overflowToken = (int)byte.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(byte))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(byte))); + } + + private void TestSByteConversions() + { + // Test max value + JToken maxToken = sbyte.MaxValue; + Assert.AreEqual(sbyte.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(sbyte))); + + // Test min value + JToken minToken = sbyte.MinValue; + Assert.AreEqual(sbyte.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(sbyte))); + + // Test overflow + JToken overflowToken = (int)sbyte.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(sbyte))); + + // Test underflow + JToken underflowToken = (int)sbyte.MinValue - 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(sbyte))); + } + + private void TestShortConversions() + { + // Test max value + JToken maxToken = short.MaxValue; + Assert.AreEqual(short.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(short))); + + // Test min value + JToken minToken = short.MinValue; + Assert.AreEqual(short.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(short))); + + // Test overflow + JToken overflowToken = (int)short.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(short))); + + // Test underflow + JToken underflowToken = (int)short.MinValue - 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(short))); + } + + private void TestUShortConversions() + { + // Test max value + JToken maxToken = ushort.MaxValue; + Assert.AreEqual(ushort.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(ushort))); + + // Test min value + JToken minToken = ushort.MinValue; + Assert.AreEqual(ushort.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(ushort))); + + // Test overflow + JToken overflowToken = (int)ushort.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(ushort))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(ushort))); + } + + private void TestUIntConversions() + { + // Test max value + JToken maxToken = uint.MaxValue; + Assert.AreEqual(uint.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(uint))); + + // Test min value + JToken minToken = uint.MinValue; + Assert.AreEqual(uint.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(uint))); + + // Test overflow + JToken overflowToken = (ulong)uint.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(uint))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(uint))); + } + + private void TestLongConversions() + { + // Test max value + JToken maxToken = JNumber.MAX_SAFE_INTEGER; + Assert.AreEqual(JNumber.MAX_SAFE_INTEGER, ParameterConverter.ConvertParameter(maxToken, typeof(long))); + + // Test min value + JToken minToken = JNumber.MIN_SAFE_INTEGER; + Assert.AreEqual(JNumber.MIN_SAFE_INTEGER, ParameterConverter.ConvertParameter(minToken, typeof(long))); + + // Test overflow + JToken overflowToken = $"{JNumber.MAX_SAFE_INTEGER}0"; // This will be parsed as a string, causing overflow + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(long))); + + // Test underflow + JToken underflowToken = $"-{JNumber.MIN_SAFE_INTEGER}0"; // This will be parsed as a string, causing underflow + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(long))); + } + + private void TestULongConversions() + { + // Test max value + JToken maxToken = JNumber.MAX_SAFE_INTEGER; + Assert.AreEqual((ulong)JNumber.MAX_SAFE_INTEGER, ParameterConverter.ConvertParameter(maxToken, typeof(ulong))); + + // Test min value + JToken minToken = ulong.MinValue; + Assert.AreEqual(ulong.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(ulong))); + + // Test overflow + JToken overflowToken = $"{JNumber.MAX_SAFE_INTEGER}0"; // This will be parsed as a string, causing overflow + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(ulong))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(ulong))); + } + + [TestMethod] + public void TestAdditionalEdgeCases() + { + // Test conversion of fractional values slightly less than integers + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(0.9999999999999, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(-0.0000000000001, typeof(int))); + + // Test conversion of very large double values to integer types + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.MaxValue, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.MinValue, typeof(long))); + + // Test conversion of NaN and Infinity + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.NaN, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.PositiveInfinity, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.NegativeInfinity, typeof(ulong))); + + // Test conversion of string representations of numbers + Assert.AreEqual(int.MaxValue, ParameterConverter.ConvertParameter(int.MaxValue.ToString(), typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(long.MinValue.ToString(), typeof(long))); + + // Test conversion of hexadecimal string representations + Assert.ThrowsException(() => ParameterConverter.ConvertParameter("0xFF", typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter("0x100", typeof(byte))); + + // Test conversion of whitespace-padded strings + Assert.AreEqual(42, ParameterConverter.ConvertParameter(" 42 ", typeof(int))); + Assert.AreEqual(42, ParameterConverter.ConvertParameter(" 42.0 ", typeof(int))); + + // Test conversion of empty or null values + Assert.AreEqual(0, ParameterConverter.ConvertParameter("", typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(JToken.Null, typeof(int))); + + // Test conversion to non-numeric types + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(42, typeof(DateTime))); + + // Test conversion of values just outside the safe integer range for long and ulong + Assert.ThrowsException(() => ParameterConverter.ConvertParameter((double)long.MaxValue, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter((double)ulong.MaxValue, typeof(ulong))); + + // Test conversion of scientific notation + Assert.AreEqual(1000000, ParameterConverter.ConvertParameter("1e6", typeof(int))); + Assert.AreEqual(150, ParameterConverter.ConvertParameter("1.5e2", typeof(int))); + + // Test conversion of boolean values to numeric types + Assert.AreEqual(1, ParameterConverter.ConvertParameter(true, typeof(int))); + Assert.AreEqual(0, ParameterConverter.ConvertParameter(false, typeof(int))); + + // Test conversion of Unicode numeric characters + Assert.ThrowsException(() => ParameterConverter.ConvertParameter("1234", typeof(int))); + } +} diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs index 6a71bd774f..58e2fe3a1e 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs @@ -18,6 +18,7 @@ using Neo.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Plugins.RpcServer.Model; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests; @@ -44,7 +45,7 @@ public void TestGetBestBlockHash() b.Index = 100; snapshot.Commit(); - var result = _rpcServer.GetBestBlockHash([]); + var result = _rpcServer.GetBestBlockHash(); // Assert Assert.AreEqual(expectedHash.ToString(), result.AsString()); } @@ -57,8 +58,7 @@ public void TestGetBlockByHash() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(block.Hash.ToString(), false); - var result = _rpcServer.GetBlock(parameters); + var result = _rpcServer.GetBlock(new BlockHashOrIndex(block.Hash), false); var blockArr = Convert.FromBase64String(result.AsString()); var block2 = blockArr.AsSerializable(); block2.Transactions.ForEach(tx => @@ -66,8 +66,7 @@ public void TestGetBlockByHash() Assert.AreEqual(VerifyResult.Succeed, tx.VerifyStateIndependent(UnitTests.TestProtocolSettings.Default)); }); - parameters = new JArray(block.Hash.ToString(), true); - result = _rpcServer.GetBlock(parameters); + result = _rpcServer.GetBlock(new BlockHashOrIndex(block.Hash), true); var block3 = block.ToJson(UnitTests.TestProtocolSettings.Default); block3["confirmations"] = NativeContract.Ledger.CurrentIndex(snapshot) - block.Index + 1; result.ToString().Should().Be(block3.ToString()); @@ -81,8 +80,7 @@ public void TestGetBlockByIndex() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(block.Index, false); - var result = _rpcServer.GetBlock(parameters); + var result = _rpcServer.GetBlock(new BlockHashOrIndex(block.Index), false); var blockArr = Convert.FromBase64String(result.AsString()); var block2 = blockArr.AsSerializable(); block2.Transactions.ForEach(tx => @@ -90,8 +88,7 @@ public void TestGetBlockByIndex() Assert.AreEqual(VerifyResult.Succeed, tx.VerifyStateIndependent(UnitTests.TestProtocolSettings.Default)); }); - parameters = new JArray(block.Index, true); - result = _rpcServer.GetBlock(parameters); + result = _rpcServer.GetBlock(new BlockHashOrIndex(block.Index), true); var block3 = block.ToJson(UnitTests.TestProtocolSettings.Default); block3["confirmations"] = NativeContract.Ledger.CurrentIndex(snapshot) - block.Index + 1; result.ToString().Should().Be(block3.ToString()); @@ -101,7 +98,7 @@ public void TestGetBlockByIndex() public void TestGetBlockCount() { var expectedCount = 1; - var result = _rpcServer.GetBlockCount(new JArray()); + var result = _rpcServer.GetBlockCount(); Assert.AreEqual(expectedCount, result.AsNumber()); } @@ -109,7 +106,7 @@ public void TestGetBlockCount() public void TestGetBlockHeaderCount() { var expectedCount = 1; - var result = _rpcServer.GetBlockHeaderCount(new JArray()); + var result = _rpcServer.GetBlockHeaderCount(); Assert.AreEqual(expectedCount, result.AsNumber()); } @@ -122,7 +119,7 @@ public void TestGetBlockHash() // snapshot.Commit(); var reason = _neoSystem.Blockchain.Ask(block).Result; var expectedHash = block.Hash.ToString(); - var result = _rpcServer.GetBlockHash(new JArray(block.Index)); + var result = _rpcServer.GetBlockHash(block.Index); Assert.AreEqual(expectedHash, result.AsString()); } @@ -133,14 +130,12 @@ public void TestGetBlockHeader() var block = TestUtils.CreateBlockWithValidTransactions(snapshot, _wallet, _walletAccount, 3); TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(block.Hash.ToString(), true); - var result = _rpcServer.GetBlockHeader(parameters); + var result = _rpcServer.GetBlockHeader(new BlockHashOrIndex(block.Hash), true); var header = block.Header.ToJson(_neoSystem.Settings); header["confirmations"] = NativeContract.Ledger.CurrentIndex(snapshot) - block.Index + 1; Assert.AreEqual(header.ToString(), result.ToString()); - parameters = new JArray(block.Index, false); - result = _rpcServer.GetBlockHeader(parameters); + result = _rpcServer.GetBlockHeader(new BlockHashOrIndex(block.Hash), false); var headerArr = Convert.FromBase64String(result.AsString()); var header2 = headerArr.AsSerializable
(); header2.ToJson(_neoSystem.Settings).ToString().Should().Be(block.Header.ToJson(_neoSystem.Settings).ToString()); @@ -154,20 +149,22 @@ public void TestGetContractState() snapshot.AddContract(contractState.Hash, contractState); snapshot.Commit(); - var result = _rpcServer.GetContractState(new JArray(contractState.Hash.ToString())); + var result = _rpcServer.GetContractState(new ContractNameOrHashOrId(contractState.Hash)); Assert.AreEqual(contractState.ToJson().ToString(), result.ToString()); - result = _rpcServer.GetContractState(new JArray(contractState.Id)); + result = _rpcServer.GetContractState(new ContractNameOrHashOrId(contractState.Id)); Assert.AreEqual(contractState.ToJson().ToString(), result.ToString()); - var byId = _rpcServer.GetContractState(new JArray(-1)); - var byName = _rpcServer.GetContractState(new JArray("ContractManagement")); + var byId = _rpcServer.GetContractState(new ContractNameOrHashOrId(-1)); + var byName = _rpcServer.GetContractState(new ContractNameOrHashOrId("ContractManagement")); byId.ToString().Should().Be(byName.ToString()); snapshot.DeleteContract(contractState.Hash); snapshot.Commit(); - Assert.ThrowsException(() => _rpcServer.GetContractState(new JArray(contractState.Hash.ToString())), RpcError.UnknownContract.Message); - Assert.ThrowsException(() => _rpcServer.GetContractState(new JArray(contractState.Id)), RpcError.UnknownContract.Message); + Action act = () => _rpcServer.GetContractState(new ContractNameOrHashOrId(contractState.Hash)); + act.Should().Throw().WithMessage(RpcError.UnknownContract.Message); + act = () => _rpcServer.GetContractState(new ContractNameOrHashOrId(contractState.Id)); + act.Should().Throw().WithMessage(RpcError.UnknownContract.Message); } [TestMethod] @@ -178,10 +175,10 @@ public void TestGetRawMemPool() snapshot.Commit(); _neoSystem.MemPool.TryAdd(tx, snapshot); - var result = _rpcServer.GetRawMemPool(new JArray()); + var result = _rpcServer.GetRawMemPool(); Assert.IsTrue(((JArray)result).Any(p => p.AsString() == tx.Hash.ToString())); - result = _rpcServer.GetRawMemPool(new JArray("true")); + result = _rpcServer.GetRawMemPool(true); Assert.IsTrue(((JArray)result["verified"]).Any(p => p.AsString() == tx.Hash.ToString())); } @@ -192,14 +189,12 @@ public void TestGetRawTransaction() var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); _neoSystem.MemPool.TryAdd(tx, snapshot); snapshot.Commit(); - var parameters = new JArray(tx.Hash.ToString(), true); - var result = _rpcServer.GetRawTransaction(parameters); + var result = _rpcServer.GetRawTransaction(tx.Hash, true); var json = Utility.TransactionToJson(tx, _neoSystem.Settings); Assert.AreEqual(json.ToString(), result.ToString()); - parameters = new JArray(tx.Hash.ToString(), false); - result = _rpcServer.GetRawTransaction(parameters); + result = _rpcServer.GetRawTransaction(tx.Hash, false); var tx2 = Convert.FromBase64String(result.AsString()).AsSerializable(); tx2.ToJson(_neoSystem.Settings).ToString().Should().Be(tx.ToJson(_neoSystem.Settings).ToString()); } @@ -215,7 +210,7 @@ public void TestGetStorage() TestUtils.StorageItemAdd(snapshot, contractState.Id, key, value); snapshot.Commit(); - var result = _rpcServer.GetStorage(new JArray(contractState.Hash.ToString(), Convert.ToBase64String(key))); + var result = _rpcServer.GetStorage(new ContractNameOrHashOrId(contractState.Hash), Convert.ToBase64String(key)); Assert.AreEqual(Convert.ToBase64String(value), result.AsString()); } @@ -229,7 +224,7 @@ public void TestFindStorage() var value = new byte[] { 0x02 }; TestUtils.StorageItemAdd(snapshot, contractState.Id, key, value); snapshot.Commit(); - var result = _rpcServer.FindStorage(new JArray(contractState.Hash.ToString(), Convert.ToBase64String(key), 0)); + var result = _rpcServer.FindStorage(new ContractNameOrHashOrId(contractState.Hash), Convert.ToBase64String(key), 0); var json = new JObject(); var jarr = new JArray(); @@ -242,14 +237,12 @@ public void TestFindStorage() json["results"] = jarr; Assert.AreEqual(json.ToString(), result.ToString()); - var result2 = _rpcServer.FindStorage(new JArray(contractState.Hash.ToString(), Convert.ToBase64String(key), "x")); - var result3 = _rpcServer.FindStorage(new JArray(contractState.Hash.ToString(), Convert.ToBase64String(key))); + var result2 = _rpcServer.FindStorage(new ContractNameOrHashOrId(contractState.Hash), Convert.ToBase64String(key)); result2.ToString().Should().Be(result.ToString()); - result3.ToString().Should().Be(result.ToString()); Enumerable.Range(0, 51).ToList().ForEach(i => TestUtils.StorageItemAdd(snapshot, contractState.Id, new byte[] { 0x01, (byte)i }, new byte[] { 0x02 })); snapshot.Commit(); - var result4 = _rpcServer.FindStorage(new JArray(contractState.Hash.ToString(), Convert.ToBase64String(new byte[] { 0x01 }), 0)); + var result4 = _rpcServer.FindStorage(new ContractNameOrHashOrId(contractState.Hash), Convert.ToBase64String(new byte[] { 0x01 }), 0); result4["next"].Should().Be(RpcServerSettings.Default.FindStoragePageSize); (result4["truncated"]).AsBoolean().Should().Be(true); } @@ -262,7 +255,7 @@ public void TestGetTransactionHeight() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); var tx = block.Transactions[0]; - var result = _rpcServer.GetTransactionHeight(new JArray(tx.Hash.ToString())); + var result = _rpcServer.GetTransactionHeight(tx.Hash); Assert.AreEqual(block.Index, result.AsNumber()); } @@ -270,7 +263,7 @@ public void TestGetTransactionHeight() public void TestGetNextBlockValidators() { var snapshot = _neoSystem.GetSnapshotCache(); - var result = _rpcServer.GetNextBlockValidators(new JArray()); + var result = _rpcServer.GetNextBlockValidators(); var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, _neoSystem.Settings.ValidatorsCount); var expected = validators.Select(p => @@ -287,6 +280,8 @@ public void TestGetNextBlockValidators() public void TestGetCandidates() { var snapshot = _neoSystem.GetSnapshotCache(); + + var result = _rpcServer.GetCandidates(); var json = new JArray(); var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, _neoSystem.Settings.ValidatorsCount); @@ -294,8 +289,7 @@ public void TestGetCandidates() snapshot.Add(key, new StorageItem(new CandidateState() { Registered = true, Votes = 10000 })); snapshot.Commit(); var candidates = NativeContract.NEO.GetCandidates(_neoSystem.GetSnapshotCache()); - var result = _rpcServer.GetCandidates(new JArray()); - + result = _rpcServer.GetCandidates(); foreach (var candidate in candidates) { var item = new JObject(); @@ -311,7 +305,7 @@ public void TestGetCandidates() public void TestGetCommittee() { var snapshot = _neoSystem.GetSnapshotCache(); - var result = _rpcServer.GetCommittee(new JArray()); + var result = _rpcServer.GetCommittee(); var committee = NativeContract.NEO.GetCommittee(snapshot); var expected = new JArray(committee.Select(p => (JToken)p.ToString())); Assert.AreEqual(expected.ToString(), result.ToString()); @@ -320,7 +314,7 @@ public void TestGetCommittee() [TestMethod] public void TestGetNativeContracts() { - var result = _rpcServer.GetNativeContracts(new JArray()); + var result = _rpcServer.GetNativeContracts(); var contracts = new JArray(NativeContract.Contracts.Select(p => NativeContract.ContractManagement.GetContract(_neoSystem.GetSnapshotCache(), p.Hash).ToJson())); Assert.AreEqual(contracts.ToString(), result.ToString()); } @@ -333,10 +327,9 @@ public void TestGetBlockByUnknownIndex() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(int.MaxValue, false); try { - _rpcServer.GetBlock(parameters); + _rpcServer.GetBlock(new BlockHashOrIndex(int.MaxValue), false); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -353,10 +346,9 @@ public void TestGetBlockByUnknownHash() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(TestUtils.RandomUInt256().ToString(), false); try { - _rpcServer.GetBlock(parameters); + _rpcServer.GetBlock(new BlockHashOrIndex(TestUtils.RandomUInt256()), false); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -373,10 +365,9 @@ public void TestGetBlockByUnKnownIndex() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(int.MaxValue, false); try { - _rpcServer.GetBlock(parameters); + _rpcServer.GetBlock(new BlockHashOrIndex(int.MaxValue), false); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -393,10 +384,9 @@ public void TestGetBlockByUnKnownHash() TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - var parameters = new JArray(TestUtils.RandomUInt256().ToString(), false); try { - _rpcServer.GetBlock(parameters); + _rpcServer.GetBlock(new BlockHashOrIndex(TestUtils.RandomUInt256()), false); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -412,7 +402,8 @@ public void TestGetBlockHashInvalidIndex() var block = TestUtils.CreateBlockWithValidTransactions(snapshot, _wallet, _walletAccount, 3); TestUtils.BlocksAdd(snapshot, block.Hash, block); snapshot.Commit(); - Assert.ThrowsException(() => _rpcServer.GetBlockHash(new JArray(block.Index + 1))); + Action act = () => _rpcServer.GetBlockHash(block.Index + 1); + act.Should().Throw(); } [TestMethod] @@ -422,7 +413,7 @@ public void TestGetContractStateUnknownContract() var randomHash = TestUtils.RandomUInt160(); try { - _rpcServer.GetContractState(new JArray(randomHash.ToString())); + _rpcServer.GetContractState(new ContractNameOrHashOrId(randomHash)); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -439,7 +430,7 @@ public void TestGetStorageUnknownContract() var key = new byte[] { 0x01 }; try { - _rpcServer.GetStorage(new JArray(randomHash.ToString(), Convert.ToBase64String(key))); + _rpcServer.GetStorage(new ContractNameOrHashOrId(randomHash), Convert.ToBase64String(key)); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -459,7 +450,7 @@ public void TestGetStorageUnknownStorageItem() var key = new byte[] { 0x01 }; try { - _rpcServer.GetStorage(new JArray(contractState.Hash.ToString(), Convert.ToBase64String(key))); + _rpcServer.GetStorage(new ContractNameOrHashOrId(contractState.Hash), Convert.ToBase64String(key)); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -474,7 +465,7 @@ public void TestGetTransactionHeightUnknownTransaction() var randomHash = TestUtils.RandomUInt256(); try { - _rpcServer.GetTransactionHeight(new JArray(randomHash.ToString())); + _rpcServer.GetTransactionHeight(randomHash); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -489,7 +480,7 @@ public void TestGetRawTransactionUnknownTransaction() var randomHash = TestUtils.RandomUInt256(); try { - _rpcServer.GetRawTransaction(new JArray(randomHash.ToString(), true)); + _rpcServer.GetRawTransaction(randomHash, true); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -498,160 +489,13 @@ public void TestGetRawTransactionUnknownTransaction() } } - [TestMethod] - public void TestGetBlockInvalidParams() - { - try - { - _rpcServer.GetBlock(new JArray("invalid_hash", false)); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - catch (FormatException) - { - } - catch - { - Assert.Fail("Unexpected exception"); - } - } - - [TestMethod] - public void TestGetBlockHashInvalidParams() - { - try - { - _rpcServer.GetBlockHash(new JArray("invalid_index")); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - } - - [TestMethod] - public void TestGetBlockHeaderInvalidParams() - { - try - { - _rpcServer.GetBlockHeader(new JArray("invalid_hash", true)); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - catch (FormatException) - { - } - catch - { - Assert.Fail("Unexpected exception"); - } - } - - [TestMethod] - public void TestGetContractStateInvalidParams() - { - try - { - _rpcServer.GetContractState(new JArray("invalid_hash")); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - catch (FormatException) - { - } - catch - { - Assert.Fail("Unexpected exception"); - } - } - - [TestMethod] - public void TestGetStorageInvalidParams() - { - try - { - _rpcServer.GetStorage(new JArray("invalid_hash", "invalid_key")); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - catch (FormatException) - { - } - catch - { - Assert.Fail("Unexpected exception"); - } - } - - [TestMethod] - public void TestFindStorageInvalidParams() - { - try - { - _rpcServer.FindStorage(new JArray("invalid_hash", "invalid_prefix", "invalid_start")); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - catch (FormatException) - { - } - catch - { - Assert.Fail("Unexpected exception"); - } - } - - [TestMethod] - public void TestGetTransactionHeightInvalidParams() - { - try - { - _rpcServer.GetTransactionHeight(new JArray("invalid_hash")); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - } - - [TestMethod] - public void TestGetRawTransactionInvalidParams() - { - try - { - _rpcServer.GetRawTransaction(new JArray("invalid_hash", true)); - Assert.Fail("Expected RpcException was not thrown."); - } - catch (RpcException ex) - { - Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - } - } - [TestMethod] public void TestInternalServerError() { _memoryStore.Reset(); try { - _rpcServer.GetCandidates(new JArray()); + _rpcServer.GetCandidates(); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -665,7 +509,7 @@ public void TestUnknownHeight() { try { - _rpcServer.GetBlockHash(new JArray(int.MaxValue)); + _rpcServer.GetBlockHash(int.MaxValue); Assert.Fail("Expected RpcException was not thrown."); } catch (RpcException ex) @@ -674,4 +518,4 @@ public void TestUnknownHeight() } } } -} +} \ No newline at end of file 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..9a8b87da9a --- /dev/null +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -0,0 +1,234 @@ +// 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.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.Network.P2P.Payloads.Conditions; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.UnitTests; +using Neo.UnitTests.Extensions; +using Neo.Wallets; +using System; +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 NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; + static readonly string NeoTransferScript = "CxEMFPlu76Cuc\u002BbgteStE4ozsOWTNUdrDBQtYNweHko3YcnMFOes3ceblcI/lRTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I="; + 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"] = ValidatorScriptHash.ToString(), + ["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) } }]), + }]; + static readonly JArray multisigSigner = [new JObject() + { + ["account"] = MultisigScriptHash.ToString(), + ["scopes"] = nameof(WitnessScope.CalledByEntry), + }]; + + [TestMethod] + 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); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + 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"], 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)); + Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + Assert.IsTrue(resp.ContainsProperty("tx")); + + 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"], 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)); + 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(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"], NeoToken.NEO.Hash.ToString()); + Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash} ({MultisigAddress}) is not found. " + + $"If this is your wallet address and you want to sign a transaction with it, make sure you have opened this wallet."); + JArray notifications = (JArray)resp["notifications"]; + Assert.AreEqual(notifications.Count, 2); + Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); + 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(), GasToken.GAS.Hash.ToString()); + Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); + + _rpcServer.wallet = null; + } + + [TestMethod] + public void TestInvokeScript() + { + 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"], NeoToken.NEO.Hash.ToString()); + 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)); + 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"], nameof(Neo.VM.Types.Boolean)); + Assert.AreEqual(resp["stack"][0]["value"], false); + } + + [TestMethod] + public void TestTraverseIterator() + { + // GetAllCandidates that should return 0 candidates + 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]); + Assert.AreEqual(respArray.Count, 0); + _rpcServer.TerminateSession([sessionId]); + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); + + // register candidate in snapshot + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "registerCandidate", + new JArray([new JObject() + { + ["type"] = nameof(ContractParameterType.PublicKey), + ["value"] = TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString(), + }]), validatorSigner, true)); + Assert.AreEqual(resp["state"], nameof(VM.VMState.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(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"], nameof(Neo.VM.Types.Struct)); + JArray value = (JArray)respArray[0]["value"]; + Assert.AreEqual(value.Count, 2); + 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"], nameof(Neo.VM.Types.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(NeoToken.NEO.Hash.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); + 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((int)_rpcServerSettings.SessionExpirationTime.TotalMilliseconds + 1); + // build another session that did not expire + 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()); + 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); + + // Mocking disposal + 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(); + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); + } + + [TestMethod] + public void TestGetUnclaimedGas() + { + 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"], ValidatorAddress); + } +} 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..a0f2384ce5 --- /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"], nameof(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); + } +} diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 2db71ae570..0897a12804 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -14,6 +14,8 @@ 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; using Neo.UnitTests; @@ -212,6 +214,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"], nameof(WitnessScope.CalledByEntry)); + _rpcServer.wallet = null; } [TestMethod] @@ -222,6 +234,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"], nameof(WitnessScope.CalledByEntry)); + _rpcServer.wallet = null; } [TestMethod] @@ -233,6 +255,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"], nameof(WitnessScope.CalledByEntry)); + _rpcServer.wallet = null; } [TestMethod] @@ -333,6 +365,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] @@ -346,6 +392,62 @@ 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); + // 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."); } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index 2561171c81..b63380f803 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,6 +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 @@ -27,19 +31,30 @@ 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; + 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); + _rpcServerSettings = RpcServerSettings.Default with + { + SessionEnabled = true, + SessionExpirationTime = TimeSpan.FromSeconds(0.3), + 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(); diff --git a/tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs index 3d4e4c56f2..0d1e20c37a 100644 --- a/tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs +++ b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.Extensions; using System; using System.Globalization; using System.Numerics; diff --git a/tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs index 4b0c7d484f..e5767acd37 100644 --- a/tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs +++ b/tests/Neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using System; using System.IO; diff --git a/tests/Neo.UnitTests/Cryptography/UT_Base58.cs b/tests/Neo.UnitTests/Cryptography/UT_Base58.cs index e7cb467dfa..091513862c 100644 --- a/tests/Neo.UnitTests/Cryptography/UT_Base58.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Base58.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.Extensions; using System; using System.Collections.Generic; diff --git a/tests/Neo.UnitTests/Cryptography/UT_Crypto.cs b/tests/Neo.UnitTests/Cryptography/UT_Crypto.cs index e311d6cafe..2f7de65b77 100644 --- a/tests/Neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Crypto.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.Extensions; using Neo.Wallets; using System; using System.Security.Cryptography; diff --git a/tests/Neo.UnitTests/IO/Caching/UT_KeyedCollectionSlim.cs b/tests/Neo.UnitTests/IO/Caching/UT_KeyedCollectionSlim.cs new file mode 100644 index 0000000000..3c382cc7a8 --- /dev/null +++ b/tests/Neo.UnitTests/IO/Caching/UT_KeyedCollectionSlim.cs @@ -0,0 +1,135 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_KeyedCollectionSlim.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.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_KeyedCollectionSlim + { + [TestMethod] + public void Add_ShouldAddItem() + { + // Arrange + var collection = new TestKeyedCollectionSlim(); + var item = new TestItem { Id = 1, Name = "Item1" }; + + // Act + collection.Add(item); + + // Assert + collection.Count.Should().Be(1); + collection.Contains(1).Should().BeTrue(); + collection.First.Should().Be(item); + } + + [TestMethod] + public void Add_ShouldThrowException_WhenKeyAlreadyExists() + { + // Arrange + var collection = new TestKeyedCollectionSlim(); + var item1 = new TestItem { Id = 1, Name = "Item1" }; + var item2 = new TestItem { Id = 1, Name = "Item2" }; // Same ID as item1 + + // Act + collection.Add(item1); + + // Assert + var act = (() => collection.Add(item2)); + act.Should().Throw(); + } + + [TestMethod] + public void Remove_ShouldRemoveItem() + { + // Arrange + var collection = new TestKeyedCollectionSlim(); + var item = new TestItem { Id = 1, Name = "Item1" }; + collection.Add(item); + + // Act + collection.Remove(1); + + // Assert + collection.Count.Should().Be(0); + collection.Contains(1).Should().BeFalse(); + } + + [TestMethod] + public void RemoveFirst_ShouldRemoveFirstItem() + { + // Arrange + var collection = new TestKeyedCollectionSlim(); + var item1 = new TestItem { Id = 1, Name = "Item1" }; + var item2 = new TestItem { Id = 2, Name = "Item2" }; + collection.Add(item1); + collection.Add(item2); + + // Act + collection.RemoveFirst(); + + // Assert + collection.Count.Should().Be(1); + collection.Contains(1).Should().BeFalse(); + collection.Contains(2).Should().BeTrue(); + } + + public class TestItem : IStructuralEquatable, IStructuralComparable, IComparable + { + public int Id { get; set; } + + public string Name { get; set; } + + public int CompareTo(object? obj) + { + if (obj is not TestItem other) throw new ArgumentException("Object is not a TestItem"); + return Id.CompareTo(other.Id); + } + + public bool Equals(object? other, IEqualityComparer comparer) + { + return other is TestItem item && Id == item.Id && Name == item.Name; + } + + public int GetHashCode(IEqualityComparer comparer) + { + return HashCode.Combine(Id, Name); + } + + public int CompareTo(TestItem other) + { + return Id.CompareTo(other.Id); + } + + public int CompareTo(object other, IComparer comparer) + { + throw new NotImplementedException(); + } + } + + internal class TestKeyedCollectionSlim : KeyedCollectionSlim + { + protected override int GetKeyForItem(TestItem? item) + { + return item?.Id ?? throw new ArgumentNullException(nameof(item), "Item cannot be null"); + } + } + } +} diff --git a/tests/Neo.UnitTests/IO/UT_MemoryReader.cs b/tests/Neo.UnitTests/IO/UT_MemoryReader.cs index a045a0b688..3f514e09a8 100644 --- a/tests/Neo.UnitTests/IO/UT_MemoryReader.cs +++ b/tests/Neo.UnitTests/IO/UT_MemoryReader.cs @@ -9,8 +9,12 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; +using Newtonsoft.Json.Linq; +using System; using System.IO; using System.Text; @@ -49,5 +53,175 @@ public void TestReadNullableArray() var n = reader.ReadNullableArray(); Assert.AreEqual(5, reader.Position); } + + [TestMethod] + public void TestReadSByte() + { + var values = new sbyte[] { 0, 1, -1, 5, -5, sbyte.MaxValue, sbyte.MinValue }; + foreach (var v in values) + { + byte[] byteArray = new byte[1]; + byteArray[0] = (byte)v; + MemoryReader reader = new(byteArray); + var n = reader.ReadSByte(); + n.Should().Be(v); + } + + var values2 = new long[] { (long)int.MaxValue + 1, (long)int.MinValue - 1 }; + foreach (var v in values2) + { + byte[] byteArray = new byte[1]; + byteArray[0] = (byte)v; + MemoryReader reader = new(byteArray); + var n = reader.ReadSByte(); + n.Should().Be((sbyte)v); + } + } + + [TestMethod] + public void TestReadInt32() + { + var values = new int[] { 0, 1, -1, 5, -5, int.MaxValue, int.MinValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + MemoryReader reader = new(bytes); + var n = reader.ReadInt32(); + n.Should().Be(v); + } + + var values2 = new long[] { (long)int.MaxValue + 1, (long)int.MinValue - 1 }; + foreach (var v in values2) + { + byte[] bytes = BitConverter.GetBytes(v); + MemoryReader reader = new(bytes); + var n = reader.ReadInt32(); + n.Should().Be((int)v); + } + } + + [TestMethod] + public void TestReadUInt64() + { + var values = new ulong[] { 0, 1, 5, ulong.MaxValue, ulong.MinValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + MemoryReader reader = new(bytes); + var n = reader.ReadUInt64(); + n.Should().Be(v); + } + + var values2 = new long[] { long.MinValue, -1, long.MaxValue }; + foreach (var v in values2) + { + byte[] bytes = BitConverter.GetBytes(v); + MemoryReader reader = new(bytes); + var n = reader.ReadUInt64(); + n.Should().Be((ulong)v); + } + } + + [TestMethod] + public void TestReadInt16BigEndian() + { + var values = new short[] { short.MinValue, -1, 0, 1, 12345, short.MaxValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + MemoryReader reader = new(bytes); + var n = reader.ReadInt16BigEndian(); + n.Should().Be(v); + } + } + + [TestMethod] + public void TestReadUInt16BigEndian() + { + var values = new ushort[] { ushort.MinValue, 0, 1, 12345, ushort.MaxValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + MemoryReader reader = new(bytes); + var n = reader.ReadUInt16BigEndian(); + n.Should().Be(v); + } + } + + [TestMethod] + public void TestReadInt32BigEndian() + { + var values = new int[] { int.MinValue, -1, 0, 1, 12345, int.MaxValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + MemoryReader reader = new(bytes); + var n = reader.ReadInt32BigEndian(); + n.Should().Be(v); + } + } + + [TestMethod] + public void TestReadUInt32BigEndian() + { + var values = new uint[] { uint.MinValue, 0, 1, 12345, uint.MaxValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + MemoryReader reader = new(bytes); + var n = reader.ReadUInt32BigEndian(); + n.Should().Be(v); + } + } + + [TestMethod] + public void TestReadInt64BigEndian() + { + var values = new long[] { long.MinValue, int.MinValue, -1, 0, 1, 12345, int.MaxValue, long.MaxValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + MemoryReader reader = new(bytes); + var n = reader.ReadInt64BigEndian(); + n.Should().Be(v); + } + } + + [TestMethod] + public void TestReadUInt64BigEndian() + { + var values = new ulong[] { ulong.MinValue, 0, 1, 12345, ulong.MaxValue }; + foreach (var v in values) + { + byte[] bytes = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + MemoryReader reader = new(bytes); + var n = reader.ReadUInt64BigEndian(); + n.Should().Be(v); + } + } } } diff --git a/tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs index 45cdee05c7..ca4e08341e 100644 --- a/tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/Neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -14,6 +14,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Neo.Cryptography; +using Neo.Extensions; using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 4e2e866281..fdbcf671b2 100644 --- a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 42308d91f2..d223c747f6 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/TestUtils.Block.cs b/tests/Neo.UnitTests/TestUtils.Block.cs index bed85131e6..dd1ad77625 100644 --- a/tests/Neo.UnitTests/TestUtils.Block.cs +++ b/tests/Neo.UnitTests/TestUtils.Block.cs @@ -11,6 +11,7 @@ using Akka.Util.Internal; using Neo.Cryptography; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/TestUtils.Transaction.cs b/tests/Neo.UnitTests/TestUtils.Transaction.cs index f6d6ebd4b5..12e9b4ec0e 100644 --- a/tests/Neo.UnitTests/TestUtils.Transaction.cs +++ b/tests/Neo.UnitTests/TestUtils.Transaction.cs @@ -11,7 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; -using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/UT_Helper.cs b/tests/Neo.UnitTests/UT_Helper.cs index 413c9dcf31..6cf1605a24 100644 --- a/tests/Neo.UnitTests/UT_Helper.cs +++ b/tests/Neo.UnitTests/UT_Helper.cs @@ -51,75 +51,6 @@ public void ToScriptHash() res.Should().Be(UInt160.Parse("2d3b96ae1bcc5a585e075e3b81920210dec16302")); } - [TestMethod] - public void TestHexToBytes() - { - string nullStr = null; - _ = nullStr.HexToBytes().ToHexString().Should().Be(Array.Empty().ToHexString()); - string emptyStr = ""; - emptyStr.HexToBytes().ToHexString().Should().Be(Array.Empty().ToHexString()); - string str1 = "hab"; - Action action = () => str1.HexToBytes(); - action.Should().Throw(); - string str2 = "0102"; - byte[] bytes = str2.HexToBytes(); - bytes.ToHexString().Should().Be(new byte[] { 0x01, 0x02 }.ToHexString()); - } - - [TestMethod] - public void TestRemoveHashsetDictionary() - { - var a = new HashSet - { - 1, - 2, - 3 - }; - - var b = new Dictionary - { - [2] = null - }; - - a.Remove(b); - - CollectionAssert.AreEqual(new int[] { 1, 3 }, a.ToArray()); - - b[4] = null; - b[5] = null; - b[1] = null; - a.Remove(b); - - CollectionAssert.AreEqual(new int[] { 3 }, a.ToArray()); - } - - [TestMethod] - public void TestRemoveHashsetSet() - { - var a = new HashSet - { - 1, - 2, - 3 - }; - - var b = new SortedSet() - { - 2 - }; - - a.Remove(b); - - CollectionAssert.AreEqual(new int[] { 1, 3 }, a.ToArray()); - - b.Add(4); - b.Add(5); - b.Add(1); - a.Remove(b); - - CollectionAssert.AreEqual(new int[] { 3 }, a.ToArray()); - } - [TestMethod] public void TestRemoveHashsetHashSetCache() { @@ -146,51 +77,5 @@ public void TestRemoveHashsetHashSetCache() CollectionAssert.AreEqual(new int[] { 3 }, a.ToArray()); } - - [TestMethod] - public void TestGetVersion() - { - // assembly without version - - var asm = AppDomain.CurrentDomain.GetAssemblies() - .Where(u => u.FullName == "Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") - .FirstOrDefault(); - string version = asm?.GetVersion() ?? ""; - version.Should().Be("0.0.0"); - } - - [TestMethod] - public void TestNextBigIntegerForRandom() - { - Random ran = new(); - Action action1 = () => ran.NextBigInteger(-1); - action1.Should().Throw(); - - ran.NextBigInteger(0).Should().Be(0); - ran.NextBigInteger(8).Should().NotBeNull(); - ran.NextBigInteger(9).Should().NotBeNull(); - } - - [TestMethod] - public void TestUnmapForIPAddress() - { - var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); - addr.Unmap().Should().Be(addr); - - var addr2 = addr.MapToIPv6(); - addr2.Unmap().Should().Be(addr); - } - - [TestMethod] - public void TestUnmapForIPEndPoin() - { - var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); - var endPoint = new IPEndPoint(addr, 8888); - endPoint.Unmap().Should().Be(endPoint); - - var addr2 = addr.MapToIPv6(); - var endPoint2 = new IPEndPoint(addr2, 8888); - endPoint2.Unmap().Should().Be(endPoint); - } } } diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index 65ec90ab7e..599cd418a1 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -60,7 +60,7 @@ public void TestChangePassword() _account.ChangePasswordPrepare("b", "Satoshi").Should().BeTrue(); _account.ChangePasswordCommit(); _account.ChangePasswordPrepare("Satoshi", "b").Should().BeTrue(); - _account.ChangePasswordRoolback(); + _account.ChangePasswordRollback(); _account.VerifyPassword("Satoshi").Should().BeTrue(); } diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs index 37f816c240..bac6c0d4eb 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.Json; using Neo.SmartContract; using Neo.Wallets.NEP6; diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index a914b235b7..3f3a61ca03 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract;