diff --git a/src/Neo.CLI/CLI/MainService.Plugins.cs b/src/Neo.CLI/CLI/MainService.Plugins.cs index 77fb73f8f8..a3974d5e48 100644 --- a/src/Neo.CLI/CLI/MainService.Plugins.cs +++ b/src/Neo.CLI/CLI/MainService.Plugins.cs @@ -80,6 +80,7 @@ private void OnReinstallCommand(string pluginName) /// Downloaded content private static async Task DownloadPluginAsync(string pluginName, Version pluginVersion, string? customDownloadUrl = null, bool prerelease = false) { + ConsoleHelper.Info($"Downloading {pluginName} {pluginVersion}..."); using var httpClient = new HttpClient(); var asmName = Assembly.GetExecutingAssembly().GetName(); @@ -104,7 +105,6 @@ private static async Task DownloadPluginAsync(string pluginName, Version ?? throw new Exception($"Could not find {pluginName}"); var downloadUrl = jsonPlugin["browser_download_url"]!.GetValue(); - return await httpClient.GetStreamAsync(downloadUrl); } diff --git a/src/Neo.CLI/CLI/MainService.cs b/src/Neo.CLI/CLI/MainService.cs index 19ea5d9334..de667bc63d 100644 --- a/src/Neo.CLI/CLI/MainService.cs +++ b/src/Neo.CLI/CLI/MainService.cs @@ -33,7 +33,6 @@ using System.Net; using System.Numerics; using System.Reflection; -using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -383,7 +382,7 @@ public async void Start(CommandLineOptions options) } catch (DllNotFoundException ex) when (ex.Message.Contains("libleveldb")) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (OperatingSystem.IsWindows()) { if (File.Exists("libleveldb.dll")) { @@ -392,25 +391,32 @@ public async void Start(CommandLineOptions options) } else { - DisplayError("DLL not found, please get libleveldb.dll."); + DisplayError("DLL not found, please get libleveldb.dll.", + "Download from https://github.com/neo-ngd/leveldb/releases"); } } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (OperatingSystem.IsLinux()) + { + DisplayError("Shared library libleveldb.so not found, please get libleveldb.so.", + "Use command \"sudo apt-get install libleveldb-dev\" in terminal or download from https://github.com/neo-ngd/leveldb/releases"); + } + else if (OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst()) { DisplayError("Shared library libleveldb.dylib not found, please get libleveldb.dylib.", - "From https://github.com/neo-project/neo/releases"); + "Use command \"brew install leveldb\" in terminal or download from https://github.com/neo-ngd/leveldb/releases"); } else { DisplayError("Neo CLI is broken, please reinstall it.", - "From https://github.com/neo-project/neo/releases"); + "Download from https://github.com/neo-project/neo/releases"); } - + return; } catch (DllNotFoundException) { DisplayError("Neo CLI is broken, please reinstall it.", - "From https://github.com/neo-project/neo/releases"); + "Download from https://github.com/neo-project/neo/releases"); + return; } NeoSystem.AddService(this); diff --git a/src/Neo/IO/Caching/Cache.cs b/src/Neo.IO/Caching/Cache.cs similarity index 80% rename from src/Neo/IO/Caching/Cache.cs rename to src/Neo.IO/Caching/Cache.cs index 7895a8ca2d..1a66e6dca5 100644 --- a/src/Neo/IO/Caching/Cache.cs +++ b/src/Neo.IO/Caching/Cache.cs @@ -17,25 +17,21 @@ namespace Neo.IO.Caching { - internal abstract class Cache : ICollection, IDisposable + internal abstract class Cache + (int max_capacity, IEqualityComparer? comparer = null) : ICollection, IDisposable + where TKey : notnull { protected class CacheItem + (TKey key, TValue value) { - public readonly TKey Key; - public readonly TValue Value; - public readonly DateTime Time; - - public CacheItem(TKey key, TValue value) - { - Key = key; - Value = value; - Time = TimeProvider.Current.UtcNow; - } + public readonly TKey Key = key; + public readonly TValue Value = value; + public readonly DateTime Time = DateTime.UtcNow; } protected readonly ReaderWriterLockSlim RwSyncRootLock = new(LockRecursionPolicy.SupportsRecursion); - protected readonly Dictionary InnerDictionary; - private readonly int max_capacity; + protected readonly Dictionary InnerDictionary = new Dictionary(comparer); + private readonly int _max_capacity = max_capacity; public TValue this[TKey key] { @@ -44,7 +40,7 @@ public CacheItem(TKey key, TValue value) RwSyncRootLock.EnterReadLock(); try { - if (!InnerDictionary.TryGetValue(key, out CacheItem item)) throw new KeyNotFoundException(); + if (!InnerDictionary.TryGetValue(key, out CacheItem? item)) throw new KeyNotFoundException(); OnAccess(item); return item.Value; } @@ -73,15 +69,9 @@ public int Count public bool IsReadOnly => false; - public Cache(int max_capacity, IEqualityComparer comparer = null) - { - this.max_capacity = max_capacity; - InnerDictionary = new Dictionary(comparer); - } - public void Add(TValue item) { - TKey key = GetKeyForItem(item); + var key = GetKeyForItem(item); RwSyncRootLock.EnterWriteLock(); try { @@ -95,16 +85,16 @@ public void Add(TValue item) private void AddInternal(TKey key, TValue item) { - if (InnerDictionary.TryGetValue(key, out CacheItem cacheItem)) + if (InnerDictionary.TryGetValue(key, out CacheItem? cacheItem)) { OnAccess(cacheItem); } else { - if (InnerDictionary.Count >= max_capacity) + if (InnerDictionary.Count >= _max_capacity) { //TODO: Perform a performance test on the PLINQ query to determine which algorithm is better here (parallel or not) - foreach (CacheItem item_del in InnerDictionary.Values.AsParallel().OrderBy(p => p.Time).Take(InnerDictionary.Count - max_capacity + 1)) + foreach (var item_del in InnerDictionary.Values.AsParallel().OrderBy(p => p.Time).Take(InnerDictionary.Count - _max_capacity + 1)) { RemoveInternal(item_del); } @@ -118,9 +108,9 @@ public void AddRange(IEnumerable items) RwSyncRootLock.EnterWriteLock(); try { - foreach (TValue item in items) + foreach (var item in items) { - TKey key = GetKeyForItem(item); + var key = GetKeyForItem(item); AddInternal(key, item); } } @@ -135,7 +125,7 @@ public void Clear() RwSyncRootLock.EnterWriteLock(); try { - foreach (CacheItem item_del in InnerDictionary.Values.ToArray()) + foreach (var item_del in InnerDictionary.Values.ToArray()) { RemoveInternal(item_del); } @@ -151,7 +141,7 @@ public bool Contains(TKey key) RwSyncRootLock.EnterReadLock(); try { - if (!InnerDictionary.TryGetValue(key, out CacheItem cacheItem)) return false; + if (!InnerDictionary.TryGetValue(key, out CacheItem? cacheItem)) return false; OnAccess(cacheItem); return true; } @@ -171,7 +161,7 @@ public void CopyTo(TValue[] array, int arrayIndex) if (array == null) throw new ArgumentNullException(); if (arrayIndex < 0) throw new ArgumentOutOfRangeException(); if (arrayIndex + InnerDictionary.Count > array.Length) throw new ArgumentException(); - foreach (TValue item in this) + foreach (var item in this) { array[arrayIndex++] = item; } @@ -188,7 +178,7 @@ public IEnumerator GetEnumerator() RwSyncRootLock.EnterReadLock(); try { - foreach (TValue item in InnerDictionary.Values.Select(p => p.Value)) + foreach (var item in InnerDictionary.Values.Select(p => p.Value)) { yield return item; } @@ -211,7 +201,7 @@ public bool Remove(TKey key) RwSyncRootLock.EnterWriteLock(); try { - if (!InnerDictionary.TryGetValue(key, out CacheItem cacheItem)) return false; + if (!InnerDictionary.TryGetValue(key, out CacheItem? cacheItem)) return false; RemoveInternal(cacheItem); return true; } @@ -242,7 +232,7 @@ public bool TryGet(TKey key, out TValue item) RwSyncRootLock.EnterReadLock(); try { - if (InnerDictionary.TryGetValue(key, out CacheItem cacheItem)) + if (InnerDictionary.TryGetValue(key, out CacheItem? cacheItem)) { OnAccess(cacheItem); item = cacheItem.Value; @@ -253,7 +243,7 @@ public bool TryGet(TKey key, out TValue item) { RwSyncRootLock.ExitReadLock(); } - item = default; + item = default!; return false; } } diff --git a/src/Neo/IO/Caching/FIFOCache.cs b/src/Neo.IO/Caching/FIFOCache.cs similarity index 72% rename from src/Neo/IO/Caching/FIFOCache.cs rename to src/Neo.IO/Caching/FIFOCache.cs index af3e5e469d..b7e9f39e98 100644 --- a/src/Neo/IO/Caching/FIFOCache.cs +++ b/src/Neo.IO/Caching/FIFOCache.cs @@ -13,13 +13,10 @@ namespace Neo.IO.Caching { - internal abstract class FIFOCache : Cache + internal abstract class FIFOCache + (int max_capacity, IEqualityComparer? comparer = null) : Cache(max_capacity, comparer) + where TKey : notnull { - public FIFOCache(int max_capacity, IEqualityComparer comparer = null) - : base(max_capacity, comparer) - { - } - protected override void OnAccess(CacheItem item) { } diff --git a/src/Neo/IO/Caching/HashSetCache.cs b/src/Neo.IO/Caching/HashSetCache.cs similarity index 76% rename from src/Neo/IO/Caching/HashSetCache.cs rename to src/Neo.IO/Caching/HashSetCache.cs index bdef1c5e3a..577893e924 100644 --- a/src/Neo/IO/Caching/HashSetCache.cs +++ b/src/Neo.IO/Caching/HashSetCache.cs @@ -20,17 +20,17 @@ class HashSetCache : IReadOnlyCollection where T : IEquatable /// /// Sets where the Hashes are stored /// - private readonly LinkedList> sets = new(); + private readonly LinkedList> _sets = new(); /// - /// Maximum capacity of each bucket inside each HashSet of . + /// Maximum capacity of each bucket inside each HashSet of . /// - private readonly int bucketCapacity; + private readonly int _bucketCapacity; /// /// Maximum number of buckets for the LinkedList, meaning its maximum cardinality. /// - private readonly int maxBucketCount; + private readonly int _maxBucketCount; /// /// Entry count @@ -43,32 +43,32 @@ public HashSetCache(int bucketCapacity, int maxBucketCount = 10) if (maxBucketCount <= 0) throw new ArgumentOutOfRangeException($"{nameof(maxBucketCount)} should be greater than 0"); Count = 0; - this.bucketCapacity = bucketCapacity; - this.maxBucketCount = maxBucketCount; - sets.AddFirst(new HashSet()); + _bucketCapacity = bucketCapacity; + _maxBucketCount = maxBucketCount; + _sets.AddFirst([]); } public bool Add(T item) { if (Contains(item)) return false; Count++; - if (sets.First.Value.Count < bucketCapacity) return sets.First.Value.Add(item); + if (_sets.First?.Value.Count < _bucketCapacity) return _sets.First.Value.Add(item); var newSet = new HashSet { item }; - sets.AddFirst(newSet); - if (sets.Count > maxBucketCount) + _sets.AddFirst(newSet); + if (_sets.Count > _maxBucketCount) { - Count -= sets.Last.Value.Count; - sets.RemoveLast(); + Count -= _sets.Last?.Value.Count ?? 0; + _sets.RemoveLast(); } return true; } public bool Contains(T item) { - foreach (var set in sets) + foreach (var set in _sets) { if (set.Contains(item)) return true; } @@ -77,17 +77,17 @@ public bool Contains(T item) public void ExceptWith(IEnumerable items) { - List> removeList = null; + List> removeList = default!; foreach (var item in items) { - foreach (var set in sets) + foreach (var set in _sets) { if (set.Remove(item)) { Count--; if (set.Count == 0) { - removeList ??= new List>(); + removeList ??= []; removeList.Add(set); } break; @@ -97,13 +97,13 @@ public void ExceptWith(IEnumerable items) if (removeList == null) return; foreach (var set in removeList) { - sets.Remove(set); + _sets.Remove(set); } } public IEnumerator GetEnumerator() { - foreach (var set in sets) + foreach (var set in _sets) { foreach (var item in set) { diff --git a/src/Neo/IO/Caching/IndexedQueue.cs b/src/Neo.IO/Caching/IndexedQueue.cs similarity index 95% rename from src/Neo/IO/Caching/IndexedQueue.cs rename to src/Neo.IO/Caching/IndexedQueue.cs index 54440a871b..29b39a4947 100644 --- a/src/Neo/IO/Caching/IndexedQueue.cs +++ b/src/Neo.IO/Caching/IndexedQueue.cs @@ -89,14 +89,14 @@ public void Enqueue(T item) { if (_array.Length == _count) { - int newSize = _array.Length * GrowthFactor; + var newSize = _array.Length * GrowthFactor; if (_head == 0) { Array.Resize(ref _array, newSize); } else { - T[] buffer = new T[newSize]; + var buffer = new T[newSize]; Array.Copy(_array, _head, buffer, 0, _array.Length - _head); Array.Copy(_array, 0, buffer, _array.Length - _head, _head); _array = buffer; @@ -127,7 +127,7 @@ public bool TryPeek(out T item) { if (_count == 0) { - item = default; + item = default!; return false; } else @@ -145,7 +145,7 @@ public T Dequeue() { if (_count == 0) throw new InvalidOperationException("The queue is empty"); - T result = _array[_head]; + var result = _array[_head]; ++_head; _head %= _array.Length; --_count; @@ -161,7 +161,7 @@ public bool TryDequeue(out T item) { if (_count == 0) { - item = default; + item = default!; return false; } else @@ -194,7 +194,7 @@ public void TrimExcess() } else if (_array.Length * TrimThreshold >= _count) { - T[] arr = new T[_count]; + var arr = new T[_count]; CopyTo(arr, 0); _array = arr; _head = 0; @@ -228,14 +228,14 @@ public void CopyTo(T[] array, int arrayIndex) /// An array containing the queue's items public T[] ToArray() { - T[] result = new T[_count]; + var result = new T[_count]; CopyTo(result, 0); return result; } public IEnumerator GetEnumerator() { - for (int i = 0; i < _count; i++) + for (var i = 0; i < _count; i++) yield return _array[(_head + i) % _array.Length]; } diff --git a/src/Neo/IO/Caching/KeyedCollectionSlim.cs b/src/Neo.IO/Caching/KeyedCollectionSlim.cs similarity index 61% rename from src/Neo/IO/Caching/KeyedCollectionSlim.cs rename to src/Neo.IO/Caching/KeyedCollectionSlim.cs index a24dd87e4c..f632dc0927 100644 --- a/src/Neo/IO/Caching/KeyedCollectionSlim.cs +++ b/src/Neo.IO/Caching/KeyedCollectionSlim.cs @@ -10,43 +10,46 @@ // modifications are permitted. using System; +using System.Collections; using System.Collections.Generic; namespace Neo.IO.Caching; -abstract class KeyedCollectionSlim where TKey : notnull +abstract class KeyedCollectionSlim + where TKey : notnull + where TItem : class, IStructuralEquatable, IStructuralComparable, IComparable { private readonly LinkedList _items = new(); - private readonly Dictionary> dict = new(); + private readonly Dictionary> _dict = []; - public int Count => dict.Count; - public TItem First => _items.First.Value; + public int Count => _dict.Count; + public TItem? First => _items.First?.Value; - protected abstract TKey GetKeyForItem(TItem item); + protected abstract TKey GetKeyForItem(TItem? item); public void Add(TItem item) { var key = GetKeyForItem(item); var node = _items.AddLast(item); - if (!dict.TryAdd(key, node)) + if (!_dict.TryAdd(key, node)) { _items.RemoveLast(); throw new ArgumentException("An element with the same key already exists in the collection."); } } - public bool Contains(TKey key) => dict.ContainsKey(key); + public bool Contains(TKey key) => _dict.ContainsKey(key); public void Remove(TKey key) { - if (dict.Remove(key, out var node)) + if (_dict.Remove(key, out var node)) _items.Remove(node); } public void RemoveFirst() { - var key = GetKeyForItem(_items.First.Value); - dict.Remove(key); + var key = GetKeyForItem(_items.First?.Value); + _dict.Remove(key); _items.RemoveFirst(); } } diff --git a/src/Neo/Cryptography/Crypto.cs b/src/Neo/Cryptography/Crypto.cs index 85ed0411a8..a2e89dc787 100644 --- a/src/Neo/Cryptography/Crypto.cs +++ b/src/Neo/Cryptography/Crypto.cs @@ -170,7 +170,7 @@ public static ECDsa CreateECDsa(ECC.ECPoint pubkey) { if (CacheECDsa.TryGet(pubkey, out var cache)) { - return cache.value; + return cache.Value; } var curve = pubkey.Curve == ECC.ECCurve.Secp256r1 ? ECCurve.NamedCurves.nistP256 : diff --git a/src/Neo/IO/Caching/ECDsaCache.cs b/src/Neo/IO/Caching/ECDsaCache.cs index b25f29da8e..b41a058b61 100644 --- a/src/Neo/IO/Caching/ECDsaCache.cs +++ b/src/Neo/IO/Caching/ECDsaCache.cs @@ -14,7 +14,8 @@ namespace Neo.IO.Caching { - record ECDsaCacheItem(Cryptography.ECC.ECPoint key, ECDsa value); + record ECDsaCacheItem(Cryptography.ECC.ECPoint Key, ECDsa Value); + internal class ECDsaCache : FIFOCache { public ECDsaCache(int max_capacity = 20000) : base(max_capacity, EqualityComparer.Default) @@ -23,7 +24,7 @@ public ECDsaCache(int max_capacity = 20000) : base(max_capacity, EqualityCompare protected override Cryptography.ECC.ECPoint GetKeyForItem(ECDsaCacheItem item) { - return item.key; + return item.Key; } } } diff --git a/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 4606723cd0..4e7861a562 100644 --- a/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -30,9 +30,9 @@ namespace Neo.Network.P2P partial class RemoteNode { private class Timer { } - private class PendingKnownHashesCollection : KeyedCollectionSlim + private class PendingKnownHashesCollection : KeyedCollectionSlim> { - protected override UInt256 GetKeyForItem((UInt256, DateTime) item) + protected override UInt256 GetKeyForItem(Tuple item) { return item.Item1; } @@ -354,7 +354,7 @@ private void OnInvMessageReceived(InvPayload payload) } if (hashes.Length == 0) return; foreach (UInt256 hash in hashes) - pendingKnownHashes.Add((hash, TimeProvider.Current.UtcNow)); + pendingKnownHashes.Add(Tuple.Create(hash, TimeProvider.Current.UtcNow)); system.TaskManager.Tell(new TaskManager.NewTasks { Payload = InvPayload.Create(payload.Type, hashes) }); } diff --git a/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index f1f22d99d8..d950d25c62 100644 --- a/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -114,7 +114,8 @@ public bool Equals(ContractPermissionDescriptor other) if (this == other) return true; if (IsWildcard == other.IsWildcard) return true; if (IsHash) return Hash.Equals(other.Hash); - else return Group.Equals(other.Group); + if (IsGroup) return Group.Equals(other.Group); + return false; } public override int GetHashCode() diff --git a/src/Neo/SmartContract/Native/ContractEventAttribute.cs b/src/Neo/SmartContract/Native/ContractEventAttribute.cs index 656ecef725..b2ff30891a 100644 --- a/src/Neo/SmartContract/Native/ContractEventAttribute.cs +++ b/src/Neo/SmartContract/Native/ContractEventAttribute.cs @@ -17,11 +17,12 @@ namespace Neo.SmartContract.Native { [DebuggerDisplay("{Descriptor.Name}")] [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true)] - internal class ContractEventAttribute : Attribute + internal class ContractEventAttribute : Attribute, IHardforkActivable { public int Order { get; init; } public ContractEventDescriptor Descriptor { get; set; } public Hardfork? ActiveIn { get; init; } = null; + public Hardfork? DeprecatedIn { get; init; } = null; public ContractEventAttribute(Hardfork activeIn, int order, string name, string arg1Name, ContractParameterType arg1Value) : this(order, name, arg1Name, arg1Value) @@ -29,23 +30,35 @@ internal class ContractEventAttribute : Attribute ActiveIn = activeIn; } + public ContractEventAttribute(Hardfork activeIn, int order, string name, + string arg1Name, ContractParameterType arg1Value, Hardfork deprecatedIn) : this(activeIn, order, name, arg1Name, arg1Value) + { + DeprecatedIn = deprecatedIn; + } + public ContractEventAttribute(int order, string name, string arg1Name, ContractParameterType arg1Value) { Order = order; Descriptor = new ContractEventDescriptor() { Name = name, - Parameters = new ContractParameterDefinition[] - { + Parameters = + [ new ContractParameterDefinition() { Name = arg1Name, Type = arg1Value } - } + ] }; } + public ContractEventAttribute(int order, string name, string arg1Name, ContractParameterType arg1Value, Hardfork deprecatedIn) + : this(order, name, arg1Name, arg1Value) + { + DeprecatedIn = deprecatedIn; + } + public ContractEventAttribute(Hardfork activeIn, int order, string name, string arg1Name, ContractParameterType arg1Value, string arg2Name, ContractParameterType arg2Value) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value) @@ -53,6 +66,13 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP ActiveIn = activeIn; } + public ContractEventAttribute(Hardfork activeIn, int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value, Hardfork deprecatedIn) : this(activeIn, order, name, arg1Name, arg1Value, arg2Name, arg2Value) + { + DeprecatedIn = deprecatedIn; + } + public ContractEventAttribute(int order, string name, string arg1Name, ContractParameterType arg1Value, string arg2Name, ContractParameterType arg2Value) @@ -61,8 +81,8 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP Descriptor = new ContractEventDescriptor() { Name = name, - Parameters = new ContractParameterDefinition[] - { + Parameters = + [ new ContractParameterDefinition() { Name = arg1Name, @@ -73,10 +93,18 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP Name = arg2Name, Type = arg2Value } - } + ] }; } + public ContractEventAttribute(int order, string name, + string arg1Name, ContractParameterType arg1Value, + string arg2Name, ContractParameterType arg2Value, Hardfork deprecatedIn) : this(order, name, arg1Name, arg1Value, arg2Name, arg2Value) + { + DeprecatedIn = deprecatedIn; + } + + public ContractEventAttribute(Hardfork activeIn, int order, string name, string arg1Name, ContractParameterType arg1Value, string arg2Name, ContractParameterType arg2Value, @@ -95,8 +123,8 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP Descriptor = new ContractEventDescriptor() { Name = name, - Parameters = new ContractParameterDefinition[] - { + Parameters = + [ new ContractParameterDefinition() { Name = arg1Name, @@ -112,7 +140,7 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP Name = arg3Name, Type = arg3Value } - } + ] }; } @@ -136,8 +164,8 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP Descriptor = new ContractEventDescriptor() { Name = name, - Parameters = new ContractParameterDefinition[] - { + Parameters = + [ new ContractParameterDefinition() { Name = arg1Name, @@ -158,7 +186,7 @@ public ContractEventAttribute(int order, string name, string arg1Name, ContractP Name = arg4Name, Type = arg4Value } - } + ] }; } } diff --git a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs index 7caa27c8b0..38bc065533 100644 --- a/src/Neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/Neo/SmartContract/Native/ContractMethodAttribute.cs @@ -16,7 +16,7 @@ namespace Neo.SmartContract.Native { [DebuggerDisplay("{Name}")] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] - internal class ContractMethodAttribute : Attribute + internal class ContractMethodAttribute : Attribute, IHardforkActivable { public string Name { get; init; } public CallFlags RequiredCallFlags { get; init; } @@ -32,6 +32,11 @@ public ContractMethodAttribute(Hardfork activeIn) ActiveIn = activeIn; } + public ContractMethodAttribute(Hardfork activeIn, Hardfork deprecatedIn) : this(activeIn) + { + DeprecatedIn = deprecatedIn; + } + public ContractMethodAttribute(bool isDeprecated, Hardfork deprecatedIn) { if (!isDeprecated) throw new ArgumentException("isDeprecated must be true", nameof(isDeprecated)); diff --git a/src/Neo/SmartContract/Native/ContractMethodMetadata.cs b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs index 30874efb47..fd5a02be6a 100644 --- a/src/Neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/Neo/SmartContract/Native/ContractMethodMetadata.cs @@ -24,7 +24,7 @@ namespace Neo.SmartContract.Native { [DebuggerDisplay("{Name}")] - internal class ContractMethodMetadata + internal class ContractMethodMetadata : IHardforkActivable { public string Name { get; } public MethodInfo Handler { get; } diff --git a/src/Neo/SmartContract/Native/IHardforkActivable.cs b/src/Neo/SmartContract/Native/IHardforkActivable.cs new file mode 100644 index 0000000000..7794d03f25 --- /dev/null +++ b/src/Neo/SmartContract/Native/IHardforkActivable.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IHardforkActivable.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. + +namespace Neo.SmartContract.Native +{ + internal interface IHardforkActivable + { + public Hardfork? ActiveIn { get; } + public Hardfork? DeprecatedIn { get; } + } +} diff --git a/src/Neo/SmartContract/Native/NativeContract.cs b/src/Neo/SmartContract/Native/NativeContract.cs index 1cc244408d..b030f06c9b 100644 --- a/src/Neo/SmartContract/Native/NativeContract.cs +++ b/src/Neo/SmartContract/Native/NativeContract.cs @@ -161,6 +161,7 @@ protected NativeContract() _usedHardforks = _methodDescriptors.Select(u => u.ActiveIn) .Concat(_methodDescriptors.Select(u => u.DeprecatedIn)) + .Concat(_eventsDescriptors.Select(u => u.DeprecatedIn)) .Concat(_eventsDescriptors.Select(u => u.ActiveIn)) .Concat([ActiveIn]) .Where(u => u is not null) @@ -184,15 +185,7 @@ private NativeContractsCache.CacheEntry GetAllowedMethods(IsHardforkEnabledDeleg byte[] script; using (ScriptBuilder sb = new()) { - foreach (ContractMethodMetadata method in _methodDescriptors.Where(u - => - // no hardfork is involved - u.ActiveIn is null && u.DeprecatedIn is null || - // deprecated method hardfork is involved - u.DeprecatedIn is not null && hfChecker(u.DeprecatedIn.Value, blockHeight) == false || - // active method hardfork is involved - u.ActiveIn is not null && hfChecker(u.ActiveIn.Value, blockHeight)) - ) + foreach (ContractMethodMetadata method in _methodDescriptors.Where(u => IsActive(u, hfChecker, blockHeight))) { method.Descriptor.Offset = sb.Length; sb.EmitPush(0); //version @@ -215,6 +208,16 @@ private NativeContractsCache.CacheEntry GetAllowedMethods(IsHardforkEnabledDeleg [MethodImpl(MethodImplOptions.AggressiveInlining)] public ContractState GetContractState(ProtocolSettings settings, uint blockHeight) => GetContractState(settings.IsHardforkEnabled, blockHeight); + internal static bool IsActive(IHardforkActivable u, IsHardforkEnabledDelegate hfChecker, uint blockHeight) + { + return // no hardfork is involved + u.ActiveIn is null && u.DeprecatedIn is null || + // deprecated method hardfork is involved + u.DeprecatedIn is not null && hfChecker(u.DeprecatedIn.Value, blockHeight) == false || + // active method hardfork is involved + u.ActiveIn is not null && hfChecker(u.ActiveIn.Value, blockHeight); + } + /// /// The of the native contract. /// @@ -245,7 +248,7 @@ public ContractState GetContractState(IsHardforkEnabledDelegate hfChecker, uint Abi = new ContractAbi { Events = _eventsDescriptors - .Where(u => u.ActiveIn is null || hfChecker(u.ActiveIn.Value, blockHeight)) + .Where(u => IsActive(u, hfChecker, blockHeight)) .Select(p => p.Descriptor).ToArray(), Methods = allowedMethods.Methods.Values .Select(p => p.Descriptor).ToArray() diff --git a/src/Plugins/Directory.Build.props b/src/Plugins/Directory.Build.props index 72e96f0300..ecd9a2735f 100644 --- a/src/Plugins/Directory.Build.props +++ b/src/Plugins/Directory.Build.props @@ -3,11 +3,6 @@ - - - - - diff --git a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs index 0dee8b0f67..1ef2397d69 100644 --- a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; using Neo.Wallets; using System.Security.Cryptography; @@ -44,5 +45,18 @@ public void TestFromAndToJson() Assert.AreEqual(null, result.Hash); Assert.AreEqual(result.Group, result.Group); } + + [TestMethod] + public void TestEquals() + { + var descriptor1 = ContractPermissionDescriptor.CreateWildcard(); + var descriptor2 = ContractPermissionDescriptor.Create(LedgerContract.NEO.Hash); + + Assert.AreNotEqual(descriptor1, descriptor2); + + var descriptor3 = ContractPermissionDescriptor.Create(LedgerContract.NEO.Hash); + + Assert.AreEqual(descriptor2, descriptor3); + } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 1989b4323b..61e6e67f48 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -58,6 +58,28 @@ public void Clean() TestBlockchain.ResetStore(); } + class active : IHardforkActivable + { + public Hardfork? ActiveIn { get; init; } + public Hardfork? DeprecatedIn { get; init; } + } + + [TestMethod] + public void TestActiveDeprecatedIn() + { + string json = UT_ProtocolSettings.CreateHKSettings("\"HF_Cockatrice\": 20"); + var file = Path.GetTempFileName(); + File.WriteAllText(file, json); + ProtocolSettings settings = ProtocolSettings.Load(file, false); + File.Delete(file); + + Assert.IsFalse(NativeContract.IsActive(new active() { ActiveIn = Hardfork.HF_Cockatrice, DeprecatedIn = null }, settings.IsHardforkEnabled, 1)); + Assert.IsTrue(NativeContract.IsActive(new active() { ActiveIn = Hardfork.HF_Cockatrice, DeprecatedIn = null }, settings.IsHardforkEnabled, 20)); + + Assert.IsTrue(NativeContract.IsActive(new active() { ActiveIn = null, DeprecatedIn = Hardfork.HF_Cockatrice }, settings.IsHardforkEnabled, 1)); + Assert.IsFalse(NativeContract.IsActive(new active() { ActiveIn = null, DeprecatedIn = Hardfork.HF_Cockatrice }, settings.IsHardforkEnabled, 20)); + } + [TestMethod] public void TestGetContract() {