From 5a36443bb7928a696a973d439a9f4de1bff04935 Mon Sep 17 00:00:00 2001 From: Wi1l-B0t <201105916+Wi1l-B0t@users.noreply.github.com> Date: Mon, 19 May 2025 22:42:30 +0800 Subject: [PATCH 1/3] style: make the code style of StatuePlugin more standard --- src/Plugins/StateService/Network/StateRoot.cs | 26 +-- src/Plugins/StateService/StatePlugin.cs | 150 ++++++++++-------- .../StateService/Storage/StateSnapshot.cs | 38 ++--- .../StateService/Storage/StateStore.cs | 104 ++++++------ .../Verification/VerificationContext.cs | 33 ++-- .../Verification/VerificationService.cs | 20 +-- 6 files changed, 205 insertions(+), 166 deletions(-) diff --git a/src/Plugins/StateService/Network/StateRoot.cs b/src/Plugins/StateService/Network/StateRoot.cs index 5be191f4da..35cd9734aa 100644 --- a/src/Plugins/StateService/Network/StateRoot.cs +++ b/src/Plugins/StateService/Network/StateRoot.cs @@ -9,7 +9,6 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Cryptography.ECC; using Neo.Extensions; using Neo.IO; using Neo.Json; @@ -49,7 +48,7 @@ Witness[] IVerifiable.Witnesses { get { - return new[] { Witness }; + return [Witness]; } set { @@ -70,12 +69,12 @@ Witness[] IVerifiable.Witnesses void ISerializable.Deserialize(ref MemoryReader reader) { DeserializeUnsigned(ref reader); - Witness[] witnesses = reader.ReadSerializableArray(1); + var witnesses = reader.ReadSerializableArray(1); Witness = witnesses.Length switch { 0 => null, 1 => witnesses[0], - _ => throw new FormatException(), + _ => throw new FormatException($"Expected 1 witness, got {witnesses.Length}."), }; } @@ -92,7 +91,7 @@ void ISerializable.Serialize(BinaryWriter writer) if (Witness is null) writer.WriteVarInt(0); else - writer.Write(new[] { Witness }); + writer.Write([Witness]); } public void SerializeUnsigned(BinaryWriter writer) @@ -109,19 +108,20 @@ public bool Verify(ProtocolSettings settings, DataCache snapshot) public UInt160[] GetScriptHashesForVerifying(DataCache snapshot) { - ECPoint[] validators = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.StateValidator, Index); + var validators = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.StateValidator, Index); if (validators.Length < 1) throw new InvalidOperationException("No script hash for state root verifying"); - return new UInt160[] { Contract.GetBFTAddress(validators) }; + return [Contract.GetBFTAddress(validators)]; } public JObject ToJson() { - var json = new JObject(); - json["version"] = Version; - json["index"] = Index; - json["roothash"] = RootHash.ToString(); - json["witnesses"] = Witness is null ? new JArray() : new JArray(Witness.ToJson()); - return json; + return new() + { + ["version"] = Version, + ["index"] = Index, + ["roothash"] = RootHash.ToString(), + ["witnesses"] = Witness is null ? new JArray() : new JArray(Witness.ToJson()), + }; } } } diff --git a/src/Plugins/StateService/StatePlugin.cs b/src/Plugins/StateService/StatePlugin.cs index 559e2a6f15..039f5c5b4d 100644 --- a/src/Plugins/StateService/StatePlugin.cs +++ b/src/Plugins/StateService/StatePlugin.cs @@ -18,7 +18,6 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins.RpcServer; -using Neo.Plugins.StateService.Network; using Neo.Plugins.StateService.Storage; using Neo.Plugins.StateService.Verification; using Neo.SmartContract; @@ -45,7 +44,10 @@ public class StatePlugin : Plugin, ICommittingHandler, ICommittedHandler, IWalle internal IActorRef Store; internal IActorRef Verifier; - internal static NeoSystem _system; + private static NeoSystem _system; + + internal static NeoSystem NeoSystem => _system; + private IWalletProvider walletProvider; public StatePlugin() @@ -96,10 +98,15 @@ public override void Dispose() if (Verifier is not null) _system.EnsureStopped(Verifier); } - void ICommittingHandler.Blockchain_Committing_Handler(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList) + void ICommittingHandler.Blockchain_Committing_Handler(NeoSystem system, Block block, DataCache snapshot, + IReadOnlyList applicationExecutedList) { if (system.Settings.Network != Settings.Default.Network) return; - StateStore.Singleton.UpdateLocalStateRootSnapshot(block.Index, snapshot.GetChangeSet().Where(p => p.Value.State != TrackState.None).Where(p => p.Key.Id != NativeContract.Ledger.Id).ToList()); + StateStore.Singleton.UpdateLocalStateRootSnapshot(block.Index, + snapshot.GetChangeSet() + .Where(p => p.Value.State != TrackState.None) + .Where(p => p.Key.Id != NativeContract.Ledger.Id) + .ToList()); } void ICommittedHandler.Blockchain_Committed_Handler(NeoSystem system, Block block) @@ -108,10 +115,17 @@ void ICommittedHandler.Blockchain_Committed_Handler(NeoSystem system, Block bloc StateStore.Singleton.UpdateLocalStateRoot(block.Index); } + private void CheckNetwork() + { + var network = Settings.Default.Network; + if (_system is null || _system.Settings.Network != network) + throw new InvalidOperationException($"Network doesn't match: {_system?.Settings.Network} != {network}"); + } + [ConsoleCommand("start states", Category = "StateService", Description = "Start as a state verifier if wallet is open")] private void OnStartVerifyingState() { - if (_system is null || _system.Settings.Network != Settings.Default.Network) throw new InvalidOperationException("Network doesn't match"); + CheckNetwork(); Start(walletProvider.GetWallet()); } @@ -133,19 +147,21 @@ public void Start(Wallet wallet) [ConsoleCommand("state root", Category = "StateService", Description = "Get state root by index")] private void OnGetStateRoot(uint index) { - if (_system is null || _system.Settings.Network != Settings.Default.Network) throw new InvalidOperationException("Network doesn't match"); + CheckNetwork(); + using var snapshot = StateStore.Singleton.GetSnapshot(); - StateRoot state_root = snapshot.GetStateRoot(index); - if (state_root is null) + var stateRoot = snapshot.GetStateRoot(index); + if (stateRoot is null) ConsoleHelper.Warning("Unknown state root"); else - ConsoleHelper.Info(state_root.ToJson().ToString()); + ConsoleHelper.Info(stateRoot.ToJson().ToString()); } [ConsoleCommand("state height", Category = "StateService", Description = "Get current state root index")] private void OnGetStateHeight() { - if (_system is null || _system.Settings.Network != Settings.Default.Network) throw new InvalidOperationException("Network doesn't match"); + CheckNetwork(); + ConsoleHelper.Info("LocalRootIndex: ", $"{StateStore.Singleton.LocalRootIndex}", " ValidatedRootIndex: ", @@ -153,12 +169,12 @@ private void OnGetStateHeight() } [ConsoleCommand("get proof", Category = "StateService", Description = "Get proof of key and contract hash")] - private void OnGetProof(UInt256 root_hash, UInt160 script_hash, string key) + private void OnGetProof(UInt256 rootHash, UInt160 scriptHash, string key) { if (_system is null || _system.Settings.Network != Settings.Default.Network) throw new InvalidOperationException("Network doesn't match"); try { - ConsoleHelper.Info("Proof: ", GetProof(root_hash, script_hash, Convert.FromBase64String(key))); + ConsoleHelper.Info("Proof: ", GetProof(rootHash, scriptHash, Convert.FromBase64String(key))); } catch (RpcException e) { @@ -167,12 +183,12 @@ private void OnGetProof(UInt256 root_hash, UInt160 script_hash, string key) } [ConsoleCommand("verify proof", Category = "StateService", Description = "Verify proof, return value if successed")] - private void OnVerifyProof(UInt256 root_hash, string proof) + private void OnVerifyProof(UInt256 rootHash, string proof) { try { ConsoleHelper.Info("Verify Result: ", - VerifyProof(root_hash, Convert.FromBase64String(proof))); + VerifyProof(rootHash, Convert.FromBase64String(proof))); } catch (RpcException e) { @@ -183,17 +199,17 @@ private void OnVerifyProof(UInt256 root_hash, string proof) [RpcMethod] public JToken GetStateRoot(JArray _params) { - uint index = Result.Ok_Or(() => uint.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid state root index: {_params[0]}")); + var index = Result.Ok_Or(() => uint.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid state root index: {_params[0]}")); using var snapshot = StateStore.Singleton.GetSnapshot(); - StateRoot state_root = snapshot.GetStateRoot(index).NotNull_Or(RpcError.UnknownStateRoot); - return state_root.ToJson(); + var stateRoot = snapshot.GetStateRoot(index).NotNull_Or(RpcError.UnknownStateRoot); + return stateRoot.ToJson(); } - private string GetProof(Trie trie, int contract_id, byte[] key) + private string GetProof(Trie trie, int contractId, byte[] key) { - StorageKey skey = new() + var skey = new StorageKey() { - Id = contract_id, + Id = contractId, Key = key, }; return GetProof(trie, skey); @@ -202,8 +218,8 @@ private string GetProof(Trie trie, int contract_id, byte[] key) private string GetProof(Trie trie, StorageKey skey) { trie.TryGetProof(skey.ToArray(), out var proof).True_Or(RpcError.UnknownStorageItem); - using MemoryStream ms = new(); - using BinaryWriter writer = new(ms, Utility.StrictUTF8); + using var ms = new MemoryStream(); + using var writer = new BinaryWriter(ms, Utility.StrictUTF8); writer.WriteVarBytes(skey.ToArray()); writer.WriteVarInt(proof.Count); @@ -216,30 +232,31 @@ private string GetProof(Trie trie, StorageKey skey) return Convert.ToBase64String(ms.ToArray()); } - private string GetProof(UInt256 root_hash, UInt160 script_hash, byte[] key) + private string GetProof(UInt256 rootHash, UInt160 scriptHash, byte[] key) { - (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash).False_Or(RpcError.UnsupportedState); + (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != rootHash).False_Or(RpcError.UnsupportedState); + using var store = StateStore.Singleton.GetStoreSnapshot(); - var trie = new Trie(store, root_hash); - var contract = GetHistoricalContractState(trie, script_hash).NotNull_Or(RpcError.UnknownContract); + var trie = new Trie(store, rootHash); + var contract = GetHistoricalContractState(trie, scriptHash).NotNull_Or(RpcError.UnknownContract); return GetProof(trie, contract.Id, key); } [RpcMethod] public JToken GetProof(JArray _params) { - UInt256 root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); - UInt160 script_hash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}")); - byte[] key = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[2]}")); - return GetProof(root_hash, script_hash, key); + var rootHash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); + var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}")); + var key = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[2]}")); + return GetProof(rootHash, scriptHash, key); } - private string VerifyProof(UInt256 root_hash, byte[] proof) + private string VerifyProof(UInt256 rootHash, byte[] proof) { var proofs = new HashSet(); - using MemoryStream ms = new(proof, false); - using BinaryReader reader = new(ms, Utility.StrictUTF8); + using var ms = new MemoryStream(proof, false); + using var reader = new BinaryReader(ms, Utility.StrictUTF8); var key = reader.ReadVarBytes(Node.MaxKeyLength); var count = reader.ReadVarInt(byte.MaxValue); @@ -248,31 +265,32 @@ private string VerifyProof(UInt256 root_hash, byte[] proof) proofs.Add(reader.ReadVarBytes()); } - var value = Trie.VerifyProof(root_hash, key, proofs).NotNull_Or(RpcError.InvalidProof); + var value = Trie.VerifyProof(rootHash, key, proofs).NotNull_Or(RpcError.InvalidProof); return Convert.ToBase64String(value); } [RpcMethod] public JToken VerifyProof(JArray _params) { - UInt256 root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); - byte[] proof_bytes = Result.Ok_Or(() => Convert.FromBase64String(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid proof: {_params[1]}")); - return VerifyProof(root_hash, proof_bytes); + var rootHash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); + var proofBytes = Result.Ok_Or(() => Convert.FromBase64String(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid proof: {_params[1]}")); + return VerifyProof(rootHash, proofBytes); } [RpcMethod] public JToken GetStateHeight(JArray _params) { - var json = new JObject(); - json["localrootindex"] = StateStore.Singleton.LocalRootIndex; - json["validatedrootindex"] = StateStore.Singleton.ValidatedRootIndex; - return json; + return new JObject() + { + ["localrootindex"] = StateStore.Singleton.LocalRootIndex, + ["validatedrootindex"] = StateStore.Singleton.ValidatedRootIndex, + }; } - private ContractState GetHistoricalContractState(Trie trie, UInt160 script_hash) + private ContractState GetHistoricalContractState(Trie trie, UInt160 scriptHash) { const byte prefix = 8; - StorageKey skey = new KeyBuilder(NativeContract.ContractManagement.Id, prefix).Add(script_hash); + StorageKey skey = new KeyBuilder(NativeContract.ContractManagement.Id, prefix).Add(scriptHash); return trie.TryGetValue(skey.ToArray(), out var value) ? value.AsSerializable().GetInteroperable() : null; } @@ -288,43 +306,48 @@ private StorageKey ParseStorageKey(byte[] data) [RpcMethod] public JToken FindStates(JArray _params) { - var root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); - (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash).False_Or(RpcError.UnsupportedState); - var script_hash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}")); + var rootHash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); + (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != rootHash).False_Or(RpcError.UnsupportedState); + + var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}")); var prefix = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid prefix: {_params[2]}")); - byte[] key = Array.Empty(); + var key = Array.Empty(); if (3 < _params.Count) key = Result.Ok_Or(() => Convert.FromBase64String(_params[3].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[3]}")); + int count = Settings.Default.MaxFindResultItems; if (4 < _params.Count) count = Result.Ok_Or(() => int.Parse(_params[4].AsString()), RpcError.InvalidParams.WithData($"Invalid count: {_params[4]}")); if (Settings.Default.MaxFindResultItems < count) count = Settings.Default.MaxFindResultItems; + using var store = StateStore.Singleton.GetStoreSnapshot(); - var trie = new Trie(store, root_hash); - var contract = GetHistoricalContractState(trie, script_hash).NotNull_Or(RpcError.UnknownContract); - StorageKey pkey = new() + var trie = new Trie(store, rootHash); + var contract = GetHistoricalContractState(trie, scriptHash).NotNull_Or(RpcError.UnknownContract); + var pkey = new StorageKey() { Id = contract.Id, Key = prefix, }; - StorageKey fkey = new() + var fkey = new StorageKey() { Id = pkey.Id, Key = key, }; - JObject json = new(); - JArray jarr = new(); + + var json = new JObject(); + var jarr = new JArray(); int i = 0; foreach (var (ikey, ivalue) in trie.Find(pkey.ToArray(), 0 < key.Length ? fkey.ToArray() : null)) { if (count < i) break; if (i < count) { - JObject j = new(); - j["key"] = Convert.ToBase64String(ParseStorageKey(ikey.ToArray()).Key.Span); - j["value"] = Convert.ToBase64String(ivalue.Span); - jarr.Add(j); + jarr.Add(new JObject() + { + ["key"] = Convert.ToBase64String(ParseStorageKey(ikey.ToArray()).Key.Span), + ["value"] = Convert.ToBase64String(ivalue.Span), + }); } i++; } @@ -344,15 +367,16 @@ public JToken FindStates(JArray _params) [RpcMethod] public JToken GetState(JArray _params) { - var root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); - (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash).False_Or(RpcError.UnsupportedState); - var script_hash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}")); + var rootHash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}")); + (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != rootHash).False_Or(RpcError.UnsupportedState); + + var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}")); var key = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[2]}")); using var store = StateStore.Singleton.GetStoreSnapshot(); - var trie = new Trie(store, root_hash); + var trie = new Trie(store, rootHash); - var contract = GetHistoricalContractState(trie, script_hash).NotNull_Or(RpcError.UnknownContract); - StorageKey skey = new() + var contract = GetHistoricalContractState(trie, scriptHash).NotNull_Or(RpcError.UnknownContract); + var skey = new StorageKey() { Id = contract.Id, Key = key, diff --git a/src/Plugins/StateService/Storage/StateSnapshot.cs b/src/Plugins/StateService/Storage/StateSnapshot.cs index 62f52e84b2..60af6f29ce 100644 --- a/src/Plugins/StateService/Storage/StateSnapshot.cs +++ b/src/Plugins/StateService/Storage/StateSnapshot.cs @@ -19,29 +19,29 @@ namespace Neo.Plugins.StateService.Storage { class StateSnapshot : IDisposable { - private readonly IStoreSnapshot snapshot; + private readonly IStoreSnapshot _snapshot; public Trie Trie; public StateSnapshot(IStore store) { - snapshot = store.GetSnapshot(); - Trie = new Trie(snapshot, CurrentLocalRootHash(), Settings.Default.FullState); + _snapshot = store.GetSnapshot(); + Trie = new Trie(_snapshot, CurrentLocalRootHash(), Settings.Default.FullState); } public StateRoot GetStateRoot(uint index) { - return snapshot.TryGet(Keys.StateRoot(index), out var data) ? data.AsSerializable() : null; + return _snapshot.TryGet(Keys.StateRoot(index), out var data) ? data.AsSerializable() : null; } - public void AddLocalStateRoot(StateRoot state_root) + public void AddLocalStateRoot(StateRoot stateRoot) { - snapshot.Put(Keys.StateRoot(state_root.Index), state_root.ToArray()); - snapshot.Put(Keys.CurrentLocalRootIndex, BitConverter.GetBytes(state_root.Index)); + _snapshot.Put(Keys.StateRoot(stateRoot.Index), stateRoot.ToArray()); + _snapshot.Put(Keys.CurrentLocalRootIndex, BitConverter.GetBytes(stateRoot.Index)); } public uint? CurrentLocalRootIndex() { - if (snapshot.TryGet(Keys.CurrentLocalRootIndex, out var bytes)) + if (_snapshot.TryGet(Keys.CurrentLocalRootIndex, out var bytes)) return BitConverter.ToUInt32(bytes); return null; } @@ -53,17 +53,17 @@ public UInt256 CurrentLocalRootHash() return GetStateRoot((uint)index)?.RootHash; } - public void AddValidatedStateRoot(StateRoot state_root) + public void AddValidatedStateRoot(StateRoot stateRoot) { - if (state_root?.Witness is null) - throw new ArgumentException(nameof(state_root) + " missing witness in invalidated state root"); - snapshot.Put(Keys.StateRoot(state_root.Index), state_root.ToArray()); - snapshot.Put(Keys.CurrentValidatedRootIndex, BitConverter.GetBytes(state_root.Index)); + if (stateRoot.Witness is null) + throw new ArgumentException(nameof(stateRoot) + " missing witness in invalidated state root"); + _snapshot.Put(Keys.StateRoot(stateRoot.Index), stateRoot.ToArray()); + _snapshot.Put(Keys.CurrentValidatedRootIndex, BitConverter.GetBytes(stateRoot.Index)); } public uint? CurrentValidatedRootIndex() { - if (snapshot.TryGet(Keys.CurrentValidatedRootIndex, out var bytes)) + if (_snapshot.TryGet(Keys.CurrentValidatedRootIndex, out var bytes)) return BitConverter.ToUInt32(bytes); return null; } @@ -72,21 +72,21 @@ public UInt256 CurrentValidatedRootHash() { var index = CurrentLocalRootIndex(); if (index is null) return null; - var state_root = GetStateRoot((uint)index); - if (state_root is null || state_root.Witness is null) + var stateRoot = GetStateRoot((uint)index); + if (stateRoot is null || stateRoot.Witness is null) throw new InvalidOperationException(nameof(CurrentValidatedRootHash) + " could not get validated state root"); - return state_root.RootHash; + return stateRoot.RootHash; } public void Commit() { Trie.Commit(); - snapshot.Commit(); + _snapshot.Commit(); } public void Dispose() { - snapshot.Dispose(); + _snapshot.Dispose(); } } } diff --git a/src/Plugins/StateService/Storage/StateStore.cs b/src/Plugins/StateService/Storage/StateStore.cs index 6c0560f47b..80968468b6 100644 --- a/src/Plugins/StateService/Storage/StateStore.cs +++ b/src/Plugins/StateService/Storage/StateStore.cs @@ -27,15 +27,15 @@ namespace Neo.Plugins.StateService.Storage { class StateStore : UntypedActor { - private readonly StatePlugin system; - private readonly IStore store; + private readonly StatePlugin _system; + private readonly IStore _store; private const int MaxCacheCount = 100; - private readonly Dictionary cache = []; - private StateSnapshot currentSnapshot; - private StateSnapshot? _state_snapshot; - public UInt256 CurrentLocalRootHash => currentSnapshot.CurrentLocalRootHash(); - public uint? LocalRootIndex => currentSnapshot.CurrentLocalRootIndex(); - public uint? ValidatedRootIndex => currentSnapshot.CurrentValidatedRootIndex(); + private readonly Dictionary _cache = []; + private StateSnapshot _currentSnapshot; + private StateSnapshot? _stateSnapshot; + public UInt256 CurrentLocalRootHash => _currentSnapshot.CurrentLocalRootHash(); + public uint? LocalRootIndex => _currentSnapshot.CurrentLocalRootIndex(); + public uint? ValidatedRootIndex => _currentSnapshot.CurrentValidatedRootIndex(); private static StateStore? _singleton; public static StateStore Singleton @@ -50,26 +50,26 @@ public static StateStore Singleton public StateStore(StatePlugin system, string path) { if (_singleton != null) throw new InvalidOperationException(nameof(StateStore)); - this.system = system; - store = StatePlugin._system.LoadStore(path); + _system = system; + _store = StatePlugin.NeoSystem.LoadStore(path); _singleton = this; - StatePlugin._system.ActorSystem.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); - currentSnapshot = GetSnapshot(); + StatePlugin.NeoSystem.ActorSystem.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); + _currentSnapshot = GetSnapshot(); } public void Dispose() { - store.Dispose(); + _store.Dispose(); } public StateSnapshot GetSnapshot() { - return new StateSnapshot(store); + return new StateSnapshot(_store); } public IStoreSnapshot GetStoreSnapshot() { - return store.GetSnapshot(); + return _store.GetSnapshot(); } protected override void OnReceive(object message) @@ -92,6 +92,7 @@ private void OnStatePayload(ExtensiblePayload payload) { if (payload.Data.Length == 0) return; if ((MessageType)payload.Data.Span[0] != MessageType.StateRoot) return; + StateRoot message; try { @@ -104,89 +105,92 @@ private void OnStatePayload(ExtensiblePayload payload) OnNewStateRoot(message); } - private bool OnNewStateRoot(StateRoot state_root) + private bool OnNewStateRoot(StateRoot stateRoot) { - if (state_root?.Witness is null) return false; - if (ValidatedRootIndex != null && state_root.Index <= ValidatedRootIndex) return false; + if (stateRoot.Witness is null) return false; + if (ValidatedRootIndex != null && stateRoot.Index <= ValidatedRootIndex) return false; if (LocalRootIndex is null) throw new InvalidOperationException(nameof(StateStore) + " could not get local root index"); - if (LocalRootIndex < state_root.Index && state_root.Index < LocalRootIndex + MaxCacheCount) + if (LocalRootIndex < stateRoot.Index && stateRoot.Index < LocalRootIndex + MaxCacheCount) { - cache.Add(state_root.Index, state_root); + _cache.Add(stateRoot.Index, stateRoot); return true; } - using var state_snapshot = Singleton.GetSnapshot(); - var local_root = state_snapshot.GetStateRoot(state_root.Index); - if (local_root is null || local_root.Witness != null) return false; - if (!state_root.Verify(StatePlugin._system.Settings, StatePlugin._system.StoreView)) return false; - if (local_root.RootHash != state_root.RootHash) return false; - state_snapshot.AddValidatedStateRoot(state_root); - state_snapshot.Commit(); + + using var stateSnapshot = Singleton.GetSnapshot(); + var localRoot = stateSnapshot.GetStateRoot(stateRoot.Index); + if (localRoot is null || localRoot.Witness != null) return false; + if (!stateRoot.Verify(StatePlugin.NeoSystem.Settings, StatePlugin.NeoSystem.StoreView)) return false; + if (localRoot.RootHash != stateRoot.RootHash) return false; + + stateSnapshot.AddValidatedStateRoot(stateRoot); + stateSnapshot.Commit(); UpdateCurrentSnapshot(); - system.Verifier?.Tell(new VerificationService.ValidatedRootPersisted { Index = state_root.Index }); + _system.Verifier?.Tell(new VerificationService.ValidatedRootPersisted { Index = stateRoot.Index }); return true; } - public void UpdateLocalStateRootSnapshot(uint height, IEnumerable> change_set) + public void UpdateLocalStateRootSnapshot(uint height, IEnumerable> changeSet) { - _state_snapshot?.Dispose(); - _state_snapshot = Singleton.GetSnapshot(); - foreach (var item in change_set) + _stateSnapshot?.Dispose(); + _stateSnapshot = Singleton.GetSnapshot(); + foreach (var item in changeSet) { switch (item.Value.State) { case TrackState.Added: - _state_snapshot.Trie.Put(item.Key.ToArray(), item.Value.Item.ToArray()); + _stateSnapshot.Trie.Put(item.Key.ToArray(), item.Value.Item.ToArray()); break; case TrackState.Changed: - _state_snapshot.Trie.Put(item.Key.ToArray(), item.Value.Item.ToArray()); + _stateSnapshot.Trie.Put(item.Key.ToArray(), item.Value.Item.ToArray()); break; case TrackState.Deleted: - _state_snapshot.Trie.Delete(item.Key.ToArray()); + _stateSnapshot.Trie.Delete(item.Key.ToArray()); break; } } - var root_hash = _state_snapshot.Trie.Root.Hash; - var state_root = new StateRoot + + var rootHash = _stateSnapshot.Trie.Root.Hash; + var stateRoot = new StateRoot { Version = StateRoot.CurrentVersion, Index = height, - RootHash = root_hash, + RootHash = rootHash, Witness = null, }; - _state_snapshot.AddLocalStateRoot(state_root); + _stateSnapshot.AddLocalStateRoot(stateRoot); } public void UpdateLocalStateRoot(uint height) { - if (_state_snapshot != null) + if (_stateSnapshot != null) { - _state_snapshot.Commit(); - _state_snapshot.Dispose(); - _state_snapshot = null; + _stateSnapshot.Commit(); + _stateSnapshot.Dispose(); + _stateSnapshot = null; } UpdateCurrentSnapshot(); - system.Verifier?.Tell(new VerificationService.BlockPersisted { Index = height }); + _system.Verifier?.Tell(new VerificationService.BlockPersisted { Index = height }); CheckValidatedStateRoot(height); } private void CheckValidatedStateRoot(uint index) { - if (cache.TryGetValue(index, out var state_root)) + if (_cache.TryGetValue(index, out var stateRoot)) { - cache.Remove(index); - Self.Tell(state_root); + _cache.Remove(index); + Self.Tell(stateRoot); } } private void UpdateCurrentSnapshot() { - Interlocked.Exchange(ref currentSnapshot, GetSnapshot())?.Dispose(); + Interlocked.Exchange(ref _currentSnapshot, GetSnapshot())?.Dispose(); } protected override void PostStop() { - currentSnapshot?.Dispose(); - store?.Dispose(); + _currentSnapshot?.Dispose(); + _store?.Dispose(); base.PostStop(); } diff --git a/src/Plugins/StateService/Verification/VerificationContext.cs b/src/Plugins/StateService/Verification/VerificationContext.cs index aa4e626527..aa7d6db920 100644 --- a/src/Plugins/StateService/Verification/VerificationContext.cs +++ b/src/Plugins/StateService/Verification/VerificationContext.cs @@ -44,6 +44,7 @@ class VerificationContext public int MyIndex => myIndex; public uint RootIndex => rootIndex; public ECPoint[] Verifiers => verifiers; + public int Sender { get @@ -52,8 +53,10 @@ public int Sender return p >= 0 ? p : p + verifiers.Length; } } + public bool IsSender => myIndex == Sender; public ICancelable Timer; + public StateRoot StateRoot { get @@ -66,7 +69,9 @@ public StateRoot StateRoot return root; } } + public ExtensiblePayload StateRootMessage => rootPayload; + public ExtensiblePayload VoteMessage { get @@ -83,7 +88,7 @@ public VerificationContext(Wallet wallet, uint index) Retries = 0; myIndex = -1; rootIndex = index; - verifiers = NativeContract.RoleManagement.GetDesignatedByRole(StatePlugin._system.StoreView, Role.StateValidator, index); + verifiers = NativeContract.RoleManagement.GetDesignatedByRole(StatePlugin.NeoSystem.StoreView, Role.StateValidator, index); if (wallet is null) return; for (int i = 0; i < verifiers.Length; i++) { @@ -98,9 +103,9 @@ public VerificationContext(Wallet wallet, uint index) private ExtensiblePayload CreateVoteMessage() { if (StateRoot is null) return null; - if (!signatures.TryGetValue(myIndex, out byte[] sig)) + if (!signatures.TryGetValue(myIndex, out var sig)) { - sig = StateRoot.Sign(keyPair, StatePlugin._system.Settings.Network); + sig = StateRoot.Sign(keyPair, StatePlugin.NeoSystem.Settings.Network); signatures[myIndex] = sig; } return CreatePayload(MessageType.Vote, new Vote @@ -116,10 +121,12 @@ public bool AddSignature(int index, byte[] sig) if (M <= signatures.Count) return false; if (index < 0 || verifiers.Length <= index) return false; if (signatures.ContainsKey(index)) return false; + Utility.Log(nameof(VerificationContext), LogLevel.Info, $"vote received, height={rootIndex}, index={index}"); - ECPoint validator = verifiers[index]; - byte[] hash_data = StateRoot?.GetSignData(StatePlugin._system.Settings.Network); - if (hash_data is null || !Crypto.VerifySignature(hash_data, sig, validator)) + + var validator = verifiers[index]; + var hashData = StateRoot?.GetSignData(StatePlugin.NeoSystem.Settings.Network); + if (hashData is null || !Crypto.VerifySignature(hashData, sig, validator)) { Utility.Log(nameof(VerificationContext), LogLevel.Info, "incorrect vote, invalid signature"); return false; @@ -133,8 +140,8 @@ public bool CheckSignatures() if (signatures.Count < M) return false; if (StateRoot.Witness is null) { - Contract contract = Contract.CreateMultiSigContract(M, verifiers); - ContractParametersContext sc = new(StatePlugin._system.StoreView, StateRoot, StatePlugin._system.Settings.Network); + var contract = Contract.CreateMultiSigContract(M, verifiers); + var sc = new ContractParametersContext(StatePlugin.NeoSystem.StoreView, StateRoot, StatePlugin.NeoSystem.Settings.Network); for (int i = 0, j = 0; i < verifiers.Length && j < M; i++) { if (!signatures.TryGetValue(i, out byte[] sig)) continue; @@ -152,15 +159,16 @@ public bool CheckSignatures() private ExtensiblePayload CreatePayload(MessageType type, ISerializable payload, uint validBlockEndThreshold) { byte[] data; - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms)) + using (var ms = new MemoryStream()) + using (var writer = new BinaryWriter(ms)) { writer.Write((byte)type); payload.Serialize(writer); writer.Flush(); data = ms.ToArray(); } - ExtensiblePayload msg = new ExtensiblePayload + + var msg = new ExtensiblePayload { Category = StatePlugin.StatePayloadCategory, ValidBlockStart = StateRoot.Index, @@ -168,7 +176,8 @@ private ExtensiblePayload CreatePayload(MessageType type, ISerializable payload, Sender = Contract.CreateSignatureRedeemScript(verifiers[MyIndex]).ToScriptHash(), Data = data, }; - ContractParametersContext sc = new ContractParametersContext(StatePlugin._system.StoreView, msg, StatePlugin._system.Settings.Network); + + var sc = new ContractParametersContext(StatePlugin.NeoSystem.StoreView, msg, StatePlugin.NeoSystem.Settings.Network); wallet.Sign(sc); msg.Witness = sc.GetWitnesses()[0]; return msg; diff --git a/src/Plugins/StateService/Verification/VerificationService.cs b/src/Plugins/StateService/Verification/VerificationService.cs index a4f36e8f9b..c35cfaba70 100644 --- a/src/Plugins/StateService/Verification/VerificationService.cs +++ b/src/Plugins/StateService/Verification/VerificationService.cs @@ -30,24 +30,24 @@ public class BlockPersisted { public uint Index; } private class Timer { public uint Index; } private static readonly uint DelayMilliseconds = 3000; private readonly Wallet wallet; - private readonly ConcurrentDictionary contexts = new ConcurrentDictionary(); + private readonly ConcurrentDictionary contexts = new(); public VerificationService(Wallet wallet) { this.wallet = wallet; - StatePlugin._system.ActorSystem.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); + StatePlugin.NeoSystem.ActorSystem.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); } private void SendVote(VerificationContext context) { if (context.VoteMessage is null) return; Utility.Log(nameof(VerificationService), LogLevel.Info, $"relay vote, height={context.RootIndex}, retry={context.Retries}"); - StatePlugin._system.Blockchain.Tell(context.VoteMessage); + StatePlugin.NeoSystem.Blockchain.Tell(context.VoteMessage); } private void OnStateRootVote(Vote vote) { - if (contexts.TryGetValue(vote.RootIndex, out VerificationContext context) && context.AddSignature(vote.ValidatorIndex, vote.Signature.ToArray())) + if (contexts.TryGetValue(vote.RootIndex, out var context) && context.AddSignature(vote.ValidatorIndex, vote.Signature.ToArray())) { CheckVotes(context); } @@ -59,7 +59,7 @@ private void CheckVotes(VerificationContext context) { if (context.StateRootMessage is null) return; Utility.Log(nameof(VerificationService), LogLevel.Info, $"relay state root, height={context.StateRoot.Index}, root={context.StateRoot.RootHash}"); - StatePlugin._system.Blockchain.Tell(context.StateRootMessage); + StatePlugin.NeoSystem.Blockchain.Tell(context.StateRootMessage); } } @@ -105,10 +105,11 @@ private void OnTimer(uint index) SendVote(context); CheckVotes(context); context.Timer.CancelIfNotNull(); - context.Timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromMilliseconds((uint)StatePlugin._system.GetTimePerBlock().TotalMilliseconds << context.Retries), Self, new Timer - { - Index = index, - }, ActorRefs.NoSender); + context.Timer = Context.System.Scheduler.ScheduleTellOnceCancelable( + TimeSpan.FromMilliseconds((uint)StatePlugin.NeoSystem.GetTimePerBlock().TotalMilliseconds << context.Retries), + Self, + new Timer { Index = index, }, + ActorRefs.NoSender); context.Retries++; } } @@ -117,6 +118,7 @@ private void OnVoteMessage(ExtensiblePayload payload) { if (payload.Data.Length == 0) return; if ((MessageType)payload.Data.Span[0] != MessageType.Vote) return; + Vote message; try { From a1e5761772ff0cc635d4b77e8fffebc6fb068fe4 Mon Sep 17 00:00:00 2001 From: Will <201105916+Wi1l-B0t@users.noreply.github.com> Date: Tue, 20 May 2025 22:13:48 +0800 Subject: [PATCH 2/3] Update src/Plugins/StateService/StatePlugin.cs Co-authored-by: Shargon --- src/Plugins/StateService/StatePlugin.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Plugins/StateService/StatePlugin.cs b/src/Plugins/StateService/StatePlugin.cs index 039f5c5b4d..0118e20efb 100644 --- a/src/Plugins/StateService/StatePlugin.cs +++ b/src/Plugins/StateService/StatePlugin.cs @@ -104,8 +104,7 @@ void ICommittingHandler.Blockchain_Committing_Handler(NeoSystem system, Block bl if (system.Settings.Network != Settings.Default.Network) return; StateStore.Singleton.UpdateLocalStateRootSnapshot(block.Index, snapshot.GetChangeSet() - .Where(p => p.Value.State != TrackState.None) - .Where(p => p.Key.Id != NativeContract.Ledger.Id) + .Where(p => p.Value.State != TrackState.None && p.Key.Id != NativeContract.Ledger.Id) .ToList()); } From 9b9be47357e9065de524005755212ed54c3f6a11 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 22 May 2025 05:36:11 +0200 Subject: [PATCH 3/3] [`ut`] 100% Coverage Trie.Get (#3952) * 100% Coverage Trie.Get * fix ut --- .../Cryptography/MPTTrie/Trie.Delete.cs | 24 +-- .../Cryptography/MPTTrie/Trie.Find.cs | 6 +- .../Cryptography/MPTTrie/Trie.Get.cs | 6 +- .../Cryptography/MPTTrie/Trie.Proof.cs | 4 +- .../Cryptography/MPTTrie/Trie.Put.cs | 30 ++-- .../Cryptography/MPTTrie/Trie.cs | 24 +-- .../Cryptography/MPTTrie/UT_Trie.cs | 163 ++++++++++-------- 7 files changed, 136 insertions(+), 121 deletions(-) diff --git a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Delete.cs b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Delete.cs index 97d2b78b01..15f06ed8fa 100644 --- a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Delete.cs +++ b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Delete.cs @@ -23,7 +23,7 @@ public bool Delete(byte[] key) throw new ArgumentException("could not be empty", nameof(key)); if (path.Length > Node.MaxKeyLength) throw new ArgumentException("exceeds limit", nameof(key)); - return TryDelete(ref root, path); + return TryDelete(ref _root, path); } private bool TryDelete(ref Node node, ReadOnlySpan path) @@ -34,7 +34,7 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) { if (path.IsEmpty) { - if (!full) cache.DeleteNode(node.Hash); + if (!_full) _cache.DeleteNode(node.Hash); node = new Node(); return true; } @@ -47,7 +47,7 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) var oldHash = node.Hash; var result = TryDelete(ref node.Next, path[node.Key.Length..]); if (!result) return false; - if (!full) cache.DeleteNode(oldHash); + if (!_full) _cache.DeleteNode(oldHash); if (node.Next.IsEmpty) { node = node.Next; @@ -55,12 +55,12 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) } if (node.Next.Type == NodeType.ExtensionNode) { - if (!full) cache.DeleteNode(node.Next.Hash); + if (!_full) _cache.DeleteNode(node.Next.Hash); node.Key = new([.. node.Key.Span, .. node.Next.Key.Span]); node.Next = node.Next.Next; } node.SetDirty(); - cache.PutNode(node); + _cache.PutNode(node); return true; } return false; @@ -78,7 +78,7 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) result = TryDelete(ref node.Children[path[0]], path[1..]); } if (!result) return false; - if (!full) cache.DeleteNode(oldHash); + if (!_full) _cache.DeleteNode(oldHash); List childrenIndexes = new List(Node.BranchChildCount); for (int i = 0; i < Node.BranchChildCount; i++) { @@ -88,7 +88,7 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) if (childrenIndexes.Count > 1) { node.SetDirty(); - cache.PutNode(node); + _cache.PutNode(node); return true; } var lastChildIndex = childrenIndexes[0]; @@ -100,20 +100,20 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) } if (lastChild.Type == NodeType.HashNode) { - lastChild = cache.Resolve(lastChild.Hash); + lastChild = _cache.Resolve(lastChild.Hash); if (lastChild is null) throw new InvalidOperationException("Internal error, can't resolve hash"); } if (lastChild.Type == NodeType.ExtensionNode) { - if (!full) cache.DeleteNode(lastChild.Hash); + if (!_full) _cache.DeleteNode(lastChild.Hash); lastChild.Key = new([.. childrenIndexes.ToArray(), .. lastChild.Key.Span]); lastChild.SetDirty(); - cache.PutNode(lastChild); + _cache.PutNode(lastChild); node = lastChild; return true; } node = Node.NewExtension(childrenIndexes.ToArray(), lastChild); - cache.PutNode(node); + _cache.PutNode(node); return true; } case NodeType.Empty: @@ -122,7 +122,7 @@ private bool TryDelete(ref Node node, ReadOnlySpan path) } case NodeType.HashNode: { - var newNode = cache.Resolve(node.Hash); + var newNode = _cache.Resolve(node.Hash); if (newNode is null) throw new InvalidOperationException("Internal error, can't resolve hash when mpt delete"); node = newNode; return TryDelete(ref node, path); diff --git a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Find.cs b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Find.cs index 0132adbcb7..4becbd2496 100644 --- a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Find.cs +++ b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Find.cs @@ -34,7 +34,7 @@ private ReadOnlySpan Seek(ref Node node, ReadOnlySpan path, out Node break; case NodeType.HashNode: { - var newNode = cache.Resolve(node.Hash); + var newNode = _cache.Resolve(node.Hash); if (newNode is null) throw new InvalidOperationException("Internal error, can't resolve hash when mpt seek"); node = newNode; return Seek(ref node, path, out start); @@ -84,7 +84,7 @@ private ReadOnlySpan Seek(ref Node node, ReadOnlySpan path, out Node } if (path.Length > Node.MaxKeyLength || from.Length > Node.MaxKeyLength) throw new ArgumentException("exceeds limit"); - path = Seek(ref root, path, out Node start).ToArray(); + path = Seek(ref _root, path, out Node start).ToArray(); if (from.Length > 0) { for (int i = 0; i < from.Length && i < path.Length; i++) @@ -120,7 +120,7 @@ private ReadOnlySpan Seek(ref Node node, ReadOnlySpan path, out Node break; case NodeType.HashNode: { - var newNode = cache.Resolve(node.Hash); + var newNode = _cache.Resolve(node.Hash); if (newNode is null) throw new InvalidOperationException("Internal error, can't resolve hash when mpt find"); node = newNode; foreach (var item in Travers(node, path, from, offset)) diff --git a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Get.cs b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Get.cs index 4ff6b431db..3193637087 100644 --- a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Get.cs +++ b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Get.cs @@ -25,7 +25,7 @@ public byte[] this[byte[] key] throw new ArgumentException("could not be empty", nameof(key)); if (path.Length > Node.MaxKeyLength) throw new ArgumentException("exceeds limit", nameof(key)); - var result = TryGet(ref root, path, out var value); + var result = TryGet(ref _root, path, out var value); return result ? value.ToArray() : throw new KeyNotFoundException(); } } @@ -38,7 +38,7 @@ public bool TryGetValue(byte[] key, out byte[] value) throw new ArgumentException("could not be empty", nameof(key)); if (path.Length > Node.MaxKeyLength) throw new ArgumentException("exceeds limit", nameof(key)); - var result = TryGet(ref root, path, out var val); + var result = TryGet(ref _root, path, out var val); if (result) value = val.ToArray(); return result; @@ -61,7 +61,7 @@ private bool TryGet(ref Node node, ReadOnlySpan path, out ReadOnlySpan proof) if (path.Length > Node.MaxKeyLength) throw new ArgumentException("exceeds limit", nameof(key)); proof = new HashSet(ByteArrayEqualityComparer.Default); - return GetProof(ref root, path, proof); + return GetProof(ref _root, path, proof); } private bool GetProof(ref Node node, ReadOnlySpan path, HashSet set) @@ -46,7 +46,7 @@ private bool GetProof(ref Node node, ReadOnlySpan path, HashSet se break; case NodeType.HashNode: { - var newNode = cache.Resolve(node.Hash); + var newNode = _cache.Resolve(node.Hash); if (newNode is null) throw new InvalidOperationException("Internal error, can't resolve hash when mpt getproof"); node = newNode; return GetProof(ref node, path, set); diff --git a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Put.cs b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Put.cs index fb335df98f..409bbf2d15 100644 --- a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Put.cs +++ b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.Put.cs @@ -38,7 +38,7 @@ public void Put(byte[] key, byte[] value) if (val.Length > Node.MaxValueLength) throw new ArgumentException("exceed limit", nameof(value)); var n = Node.NewLeaf(val); - Put(ref root, path, n); + Put(ref _root, path, n); } private void Put(ref Node node, ReadOnlySpan path, Node val) @@ -49,15 +49,15 @@ private void Put(ref Node node, ReadOnlySpan path, Node val) { if (path.IsEmpty) { - if (!full) cache.DeleteNode(node.Hash); + if (!_full) _cache.DeleteNode(node.Hash); node = val; - cache.PutNode(node); + _cache.PutNode(node); return; } var branch = Node.NewBranch(); branch.Children[Node.BranchChildCount - 1] = node; Put(ref branch.Children[path[0]], path[1..], val); - cache.PutNode(branch); + _cache.PutNode(branch); node = branch; break; } @@ -67,12 +67,12 @@ private void Put(ref Node node, ReadOnlySpan path, Node val) { var oldHash = node.Hash; Put(ref node.Next, path[node.Key.Length..], val); - if (!full) cache.DeleteNode(oldHash); + if (!_full) _cache.DeleteNode(oldHash); node.SetDirty(); - cache.PutNode(node); + _cache.PutNode(node); return; } - if (!full) cache.DeleteNode(node.Hash); + if (!_full) _cache.DeleteNode(node.Hash); var prefix = CommonPrefix(node.Key.Span, path); var pathRemain = path[prefix.Length..]; var keyRemain = node.Key.Span[prefix.Length..]; @@ -85,7 +85,7 @@ private void Put(ref Node node, ReadOnlySpan path, Node val) else { var exNode = Node.NewExtension(keyRemain[1..].ToArray(), node.Next); - cache.PutNode(exNode); + _cache.PutNode(exNode); child.Children[keyRemain[0]] = exNode; } if (pathRemain.IsEmpty) @@ -98,11 +98,11 @@ private void Put(ref Node node, ReadOnlySpan path, Node val) Put(ref grandChild, pathRemain[1..], val); child.Children[pathRemain[0]] = grandChild; } - cache.PutNode(child); + _cache.PutNode(child); if (prefix.Length > 0) { var exNode = Node.NewExtension(prefix.ToArray(), child); - cache.PutNode(exNode); + _cache.PutNode(exNode); node = exNode; } else @@ -122,9 +122,9 @@ private void Put(ref Node node, ReadOnlySpan path, Node val) { Put(ref node.Children[path[0]], path[1..], val); } - if (!full) cache.DeleteNode(oldHash); + if (!_full) _cache.DeleteNode(oldHash); node.SetDirty(); - cache.PutNode(node); + _cache.PutNode(node); break; } case NodeType.Empty: @@ -137,15 +137,15 @@ private void Put(ref Node node, ReadOnlySpan path, Node val) else { newNode = Node.NewExtension(path.ToArray(), val); - cache.PutNode(newNode); + _cache.PutNode(newNode); } node = newNode; - if (val.Type == NodeType.LeafNode) cache.PutNode(val); + if (val.Type == NodeType.LeafNode) _cache.PutNode(val); break; } case NodeType.HashNode: { - Node newNode = cache.Resolve(node.Hash); + Node newNode = _cache.Resolve(node.Hash); if (newNode is null) throw new InvalidOperationException("Internal error, can't resolve hash when mpt put"); node = newNode; Put(ref node, path, val); diff --git a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.cs b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.cs index 4e5890f5ac..7a48f2dc0a 100644 --- a/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.cs +++ b/src/Neo.Cryptography.MPTTrie/Cryptography/MPTTrie/Trie.cs @@ -17,24 +17,24 @@ namespace Neo.Cryptography.MPTTrie public partial class Trie { private const byte Prefix = 0xf0; - private readonly bool full; - private readonly IStoreSnapshot store; - private Node root; - private readonly Cache cache; - public Node Root => root; + private readonly bool _full; + private readonly IStoreSnapshot _store; + private Node _root; + private readonly Cache _cache; + public Node Root => _root; public Trie(IStoreSnapshot store, UInt256 root, bool full_state = false) { - this.store = store ?? throw new ArgumentNullException(nameof(store)); - cache = new Cache(store, Prefix); - this.root = root is null ? new Node() : Node.NewHash(root); - full = full_state; + _store = store ?? throw new ArgumentNullException(nameof(store)); + _cache = new Cache(store, Prefix); + _root = root is null ? new Node() : Node.NewHash(root); + _full = full_state; } private static byte[] ToNibbles(ReadOnlySpan path) { var result = new byte[path.Length * 2]; - for (int i = 0; i < path.Length; i++) + for (var i = 0; i < path.Length; i++) { result[i * 2] = (byte)(path[i] >> 4); result[i * 2 + 1] = (byte)(path[i] & 0x0F); @@ -46,7 +46,7 @@ private static byte[] FromNibbles(ReadOnlySpan path) { if (path.Length % 2 != 0) throw new FormatException($"MPTTrie.FromNibbles invalid path."); var key = new byte[path.Length / 2]; - for (int i = 0; i < key.Length; i++) + for (var i = 0; i < key.Length; i++) { key[i] = (byte)(path[i * 2] << 4); key[i] |= path[i * 2 + 1]; @@ -56,7 +56,7 @@ private static byte[] FromNibbles(ReadOnlySpan path) public void Commit() { - cache.Commit(); + _cache.Commit(); } } } 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 a420bc5265..a5fe1290bb 100644 --- a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs +++ b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs @@ -22,21 +22,21 @@ namespace Neo.Cryptography.MPTTrie.Tests { class TestSnapshot : IStoreSnapshot { - public Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); + public Dictionary _store = new(ByteArrayEqualityComparer.Default); - private byte[] StoreKey(byte[] key) + private static byte[] StoreKey(byte[] key) { return [.. key]; } public void Put(byte[] key, byte[] value) { - store[key] = value; + _store[key] = value; } public void Delete(byte[] key) { - store.Remove(StoreKey(key)); + _store.Remove(StoreKey(key)); } public IStore Store => throw new NotImplementedException(); @@ -49,26 +49,26 @@ public void Delete(byte[] key) public byte[] TryGet(byte[] key) { - var result = store.TryGetValue(StoreKey(key), out byte[] value); + var result = _store.TryGetValue(StoreKey(key), out byte[] value); if (result) return value; return null; } public bool TryGet(byte[] key, out byte[] value) { - return store.TryGetValue(StoreKey(key), out value); + return _store.TryGetValue(StoreKey(key), out value); } public void Dispose() { throw new NotImplementedException(); } - public int Size => store.Count; + public int Size => _store.Count; } [TestClass] public class UT_Trie { - private Node root; - private IStore mptdb; + private Node _root; + private IStore _mptdb; private void PutToStore(IStore store, Node node) { @@ -85,31 +85,47 @@ public void TestInit() var v3 = Node.NewLeaf(Encoding.ASCII.GetBytes("existing"));//key=acae var v4 = Node.NewLeaf(Encoding.ASCII.GetBytes("missing")); var h3 = Node.NewHash(v3.Hash); - var e1 = Node.NewExtension(new byte[] { 0x01 }, v1); - var e3 = Node.NewExtension(new byte[] { 0x0e }, h3); - var e4 = Node.NewExtension(new byte[] { 0x01 }, v4); + var e1 = Node.NewExtension([0x01], v1); + var e3 = Node.NewExtension([0x0e], h3); + var e4 = Node.NewExtension([0x01], v4); b.Children[0] = e1; b.Children[10] = e3; b.Children[16] = v2; b.Children[15] = Node.NewHash(e4.Hash); - root = r; - mptdb = new MemoryStore(); - PutToStore(mptdb, r); - PutToStore(mptdb, b); - PutToStore(mptdb, e1); - PutToStore(mptdb, e3); - PutToStore(mptdb, v1); - PutToStore(mptdb, v2); - PutToStore(mptdb, v3); + _root = r; + _mptdb = new MemoryStore(); + PutToStore(_mptdb, r); + PutToStore(_mptdb, b); + PutToStore(_mptdb, e1); + PutToStore(_mptdb, e3); + PutToStore(_mptdb, v1); + PutToStore(_mptdb, v2); + PutToStore(_mptdb, v3); } [TestMethod] public void TestTryGet() { - var mpt = new Trie(mptdb.GetSnapshot(), root.Hash); - Assert.ThrowsExactly(() => _ = mpt[Array.Empty()]); + var mpt = new Trie(_mptdb.GetSnapshot(), _root.Hash); + + // Errors + Assert.ThrowsExactly(() => _ = mpt[[]]); + Assert.ThrowsExactly(() => _ = mpt[new byte[255]]); + Assert.ThrowsExactly(() => _ = mpt.TryGetValue([], out _)); + Assert.ThrowsExactly(() => _ = mpt.TryGetValue(new byte[255], out _)); + + //Get Assert.AreEqual("abcd", mpt["ac01".HexToBytes()].ToHexString()); Assert.AreEqual("2222", mpt["ac".HexToBytes()].ToHexString()); + + //TryGet + Assert.IsTrue(mpt.TryGetValue("ac01".HexToBytes(), out var value)); + Assert.AreEqual("abcd", value.ToHexString()); + Assert.IsTrue(mpt.TryGetValue("ac".HexToBytes(), out value)); + Assert.AreEqual("2222", value.ToHexString()); + Assert.IsFalse(mpt.TryGetValue("000102".HexToBytes(), out value)); + Assert.IsNull(value); + Assert.ThrowsExactly(() => _ = mpt["ab99".HexToBytes()]); Assert.ThrowsExactly(() => _ = mpt["ac39".HexToBytes()]); Assert.ThrowsExactly(() => _ = mpt["ac02".HexToBytes()]); @@ -121,7 +137,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new Trie(mptdb.GetSnapshot(), root.Hash); + var mpt = new Trie(_mptdb.GetSnapshot(), _root.Hash); Assert.AreEqual(Encoding.ASCII.GetBytes("existing").ToHexString(), mpt["acae".HexToBytes()].ToHexString()); } @@ -134,10 +150,10 @@ public void TestTryPut() mpt.Put("ac".HexToBytes(), "2222".HexToBytes()); mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("existing")); mpt.Put("acf1".HexToBytes(), Encoding.ASCII.GetBytes("missing")); - Assert.AreEqual(root.Hash.ToString(), mpt.Root.Hash.ToString()); - Assert.ThrowsExactly(() => mpt.Put(Array.Empty(), "01".HexToBytes())); - mpt.Put("01".HexToBytes(), Array.Empty()); - Assert.ThrowsExactly(() => mpt.Put(new byte[Node.MaxKeyLength / 2 + 1], Array.Empty())); + Assert.AreEqual(_root.Hash.ToString(), mpt.Root.Hash.ToString()); + Assert.ThrowsExactly(() => mpt.Put([], "01".HexToBytes())); + mpt.Put("01".HexToBytes(), []); + Assert.ThrowsExactly(() => mpt.Put(new byte[Node.MaxKeyLength / 2 + 1], [])); Assert.ThrowsExactly(() => mpt.Put("01".HexToBytes(), new byte[Node.MaxValueLength + 1])); mpt.Put("ac01".HexToBytes(), "ab".HexToBytes()); } @@ -145,17 +161,17 @@ public void TestTryPut() [TestMethod] public void TestPutCantResolve() { - var mpt = new Trie(mptdb.GetSnapshot(), root.Hash); - Assert.ThrowsExactly(() => mpt.Put("acf111".HexToBytes(), new byte[] { 1 })); + var mpt = new Trie(_mptdb.GetSnapshot(), _root.Hash); + Assert.ThrowsExactly(() => mpt.Put("acf111".HexToBytes(), [1])); } [TestMethod] public void TestTryDelete() { - var mpt = new Trie(mptdb.GetSnapshot(), root.Hash); + var mpt = new Trie(_mptdb.GetSnapshot(), _root.Hash); Assert.IsNotNull(mpt["ac".HexToBytes()]); Assert.IsFalse(mpt.Delete("0c99".HexToBytes())); - Assert.ThrowsExactly(() => _ = mpt.Delete(Array.Empty())); + Assert.ThrowsExactly(() => _ = mpt.Delete([])); Assert.IsFalse(mpt.Delete("ac20".HexToBytes())); Assert.ThrowsExactly(() => _ = mpt.Delete("acf1".HexToBytes())); Assert.IsTrue(mpt.Delete("ac".HexToBytes())); @@ -189,8 +205,8 @@ public void TestDeleteRemainCantResolve() var r = Node.NewExtension("0a0c".HexToBytes(), b); var v1 = Node.NewLeaf("abcd".HexToBytes());//key=ac01 var v4 = Node.NewLeaf(Encoding.ASCII.GetBytes("missing")); - var e1 = Node.NewExtension(new byte[] { 0x01 }, v1); - var e4 = Node.NewExtension(new byte[] { 0x01 }, v4); + var e1 = Node.NewExtension([0x01], v1); + var e4 = Node.NewExtension([0x01], v4); b.Children[0] = e1; b.Children[15] = Node.NewHash(e4.Hash); var store = new MemoryStore(); @@ -204,7 +220,6 @@ public void TestDeleteRemainCantResolve() Assert.ThrowsExactly(() => _ = mpt.Delete("ac01".HexToBytes())); } - [TestMethod] public void TestDeleteSameValue() { @@ -252,15 +267,15 @@ public void TestGetProof() var v3 = Node.NewLeaf(Encoding.ASCII.GetBytes("existing"));//key=acae var v4 = Node.NewLeaf(Encoding.ASCII.GetBytes("missing")); var h3 = Node.NewHash(v3.Hash); - var e1 = Node.NewExtension(new byte[] { 0x01 }, v1); - var e3 = Node.NewExtension(new byte[] { 0x0e }, h3); - var e4 = Node.NewExtension(new byte[] { 0x01 }, v4); + var e1 = Node.NewExtension([0x01], v1); + var e3 = Node.NewExtension([0x0e], h3); + var e4 = Node.NewExtension([0x01], v4); b.Children[0] = e1; b.Children[10] = e3; b.Children[16] = v2; b.Children[15] = Node.NewHash(e4.Hash); - var mpt = new Trie(mptdb.GetSnapshot(), r.Hash); + var mpt = new Trie(_mptdb.GetSnapshot(), r.Hash); Assert.AreEqual(r.Hash.ToString(), mpt.Root.Hash.ToString()); var result = mpt.TryGetProof("ac01".HexToBytes(), out var proof); Assert.IsTrue(result); @@ -279,7 +294,7 @@ public void TestGetProof() result = mpt.TryGetProof("acae".HexToBytes(), out proof); Assert.AreEqual(4, proof.Count); - Assert.ThrowsExactly(() => _ = mpt.TryGetProof(Array.Empty(), out proof)); + Assert.ThrowsExactly(() => _ = mpt.TryGetProof([], out proof)); result = mpt.TryGetProof("ac0100".HexToBytes(), out proof); Assert.IsFalse(result); @@ -290,12 +305,12 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new Trie(mptdb.GetSnapshot(), root.Hash); + var mpt = new Trie(_mptdb.GetSnapshot(), _root.Hash); var result = mpt.TryGetProof("ac01".HexToBytes(), out var proof); Assert.IsTrue(result); - var value = Trie.VerifyProof(root.Hash, "ac01".HexToBytes(), proof); + var value = Trie.VerifyProof(_root.Hash, "ac01".HexToBytes(), proof); Assert.IsNotNull(value); - Assert.AreEqual(value.ToHexString(), "abcd"); + Assert.AreEqual("abcd", value.ToHexString()); } [TestMethod] @@ -304,9 +319,9 @@ public void TestAddLongerKey() var store = new MemoryStore(); var snapshot = store.GetSnapshot(); var mpt = new Trie(snapshot, null); - mpt.Put(new byte[] { 0xab }, new byte[] { 0x01 }); - mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); - Assert.AreEqual("01", mpt[new byte[] { 0xab }].ToHexString()); + mpt.Put([0xab], [0x01]); + mpt.Put([0xab, 0xcd], [0x02]); + Assert.AreEqual("01", mpt[[0xab]].ToHexString()); } [TestMethod] @@ -315,15 +330,15 @@ public void TestSplitKey() var store = new MemoryStore(); var snapshot = store.GetSnapshot(); var mpt1 = new Trie(snapshot, null); - mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 }); - mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 }); - var r = mpt1.TryGetProof(new byte[] { 0xab, 0xcd }, out var set1); + mpt1.Put([0xab, 0xcd], [0x01]); + mpt1.Put([0xab], [0x02]); + var r = mpt1.TryGetProof([0xab, 0xcd], out var set1); Assert.IsTrue(r); Assert.AreEqual(4, set1.Count); var mpt2 = new Trie(snapshot, null); - mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 }); - mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 }); - r = mpt2.TryGetProof(new byte[] { 0xab, 0xcd }, out var set2); + mpt2.Put([0xab], [0x02]); + mpt2.Put([0xab, 0xcd], [0x01]); + r = mpt2.TryGetProof([0xab, 0xcd], out var set2); Assert.IsTrue(r); Assert.AreEqual(4, set2.Count); Assert.AreEqual(mpt1.Root.Hash, mpt2.Root.Hash); @@ -335,21 +350,21 @@ public void TestFind() var store = new MemoryStore(); var snapshot = store.GetSnapshot(); var mpt1 = new Trie(snapshot, null); - var results = mpt1.Find(ReadOnlySpan.Empty).ToArray(); + var results = mpt1.Find([]).ToArray(); Assert.AreEqual(0, results.Length); var mpt2 = new Trie(snapshot, null); - mpt2.Put(new byte[] { 0xab, 0xcd, 0xef }, new byte[] { 0x01 }); - mpt2.Put(new byte[] { 0xab, 0xcd, 0xe1 }, new byte[] { 0x02 }); - mpt2.Put(new byte[] { 0xab }, new byte[] { 0x03 }); - results = mpt2.Find(ReadOnlySpan.Empty).ToArray(); + mpt2.Put([0xab, 0xcd, 0xef], [0x01]); + mpt2.Put([0xab, 0xcd, 0xe1], [0x02]); + mpt2.Put([0xab], [0x03]); + results = [.. mpt2.Find([])]; Assert.AreEqual(3, results.Length); - results = mpt2.Find(new byte[] { 0xab }).ToArray(); + results = [.. mpt2.Find([0xab])]; Assert.AreEqual(3, results.Length); - results = mpt2.Find(new byte[] { 0xab, 0xcd }).ToArray(); + results = [.. mpt2.Find([0xab, 0xcd])]; Assert.AreEqual(2, results.Length); - results = mpt2.Find(new byte[] { 0xac }).ToArray(); + results = [.. mpt2.Find([0xac])]; Assert.AreEqual(0, results.Length); - results = mpt2.Find(new byte[] { 0xab, 0xcd, 0xef, 0x00 }).ToArray(); + results = [.. mpt2.Find([0xab, 0xcd, 0xef, 0x00])]; Assert.AreEqual(0, results.Length); } @@ -360,8 +375,8 @@ public void TestFindCantResolve() var r = Node.NewExtension("0a0c".HexToBytes(), b); var v1 = Node.NewLeaf("abcd".HexToBytes());//key=ac01 var v4 = Node.NewLeaf(Encoding.ASCII.GetBytes("missing")); - var e1 = Node.NewExtension(new byte[] { 0x01 }, v1); - var e4 = Node.NewExtension(new byte[] { 0x01 }, v4); + var e1 = Node.NewExtension([0x01], v1); + var e4 = Node.NewExtension([0x01], v4); b.Children[0] = e1; b.Children[15] = Node.NewHash(e4.Hash); var store = new MemoryStore(); @@ -381,12 +396,12 @@ public void TestFindLeadNode() // r.Key = 0x0a0c // b.Key = 0x00 // l1.Key = 0x01 - var mpt = new Trie(mptdb.GetSnapshot(), root.Hash); + var mpt = new Trie(_mptdb.GetSnapshot(), _root.Hash); var prefix = new byte[] { 0xac, 0x01 }; // = FromNibbles(path = { 0x0a, 0x0c, 0x00, 0x01 }); var results = mpt.Find(prefix).ToArray(); - Assert.AreEqual(1, results.Count()); + Assert.AreEqual(1, results.Length); - prefix = new byte[] { 0xac }; // = FromNibbles(path = { 0x0a, 0x0c }); + prefix = [0xac]; // = FromNibbles(path = { 0x0a, 0x0c }); Assert.ThrowsExactly(() => _ = mpt.Find(prefix).ToArray()); } @@ -397,7 +412,7 @@ public void TestFromNibblesException() var r = Node.NewExtension("0c".HexToBytes(), b); var v1 = Node.NewLeaf("abcd".HexToBytes());//key=ac01 var v2 = Node.NewLeaf("2222".HexToBytes());//key=ac - var e1 = Node.NewExtension(new byte[] { 0x01 }, v1); + var e1 = Node.NewExtension([0x01], v1); b.Children[0] = e1; b.Children[16] = v2; var store = new MemoryStore(); @@ -409,7 +424,7 @@ public void TestFromNibblesException() var snapshot = store.GetSnapshot(); var mpt = new Trie(snapshot, r.Hash); - Assert.ThrowsExactly(() => _ = mpt.Find(Array.Empty()).Count()); + Assert.ThrowsExactly(() => _ = mpt.Find([]).Count()); } [TestMethod] @@ -533,7 +548,7 @@ public void TestEmptyValueIssue633() var key = "01".HexToBytes(); var snapshot = new TestSnapshot(); var mpt = new Trie(snapshot, null); - mpt.Put(key, Array.Empty()); + mpt.Put(key, []); var val = mpt[key]; Assert.IsNotNull(val); Assert.AreEqual(0, val.Length); @@ -554,11 +569,11 @@ public void TestFindWithFrom() mpt.Put("aa50".HexToBytes(), "04".HexToBytes()); var r = mpt.Find("aa".HexToBytes()).ToList(); Assert.AreEqual(3, r.Count); - r = mpt.Find("aa".HexToBytes(), "aa30".HexToBytes()).ToList(); + r = [.. mpt.Find("aa".HexToBytes(), "aa30".HexToBytes())]; Assert.AreEqual(1, r.Count); - r = mpt.Find("aa".HexToBytes(), "aa60".HexToBytes()).ToList(); + r = [.. mpt.Find("aa".HexToBytes(), "aa60".HexToBytes())]; Assert.AreEqual(0, r.Count); - r = mpt.Find("aa".HexToBytes(), "aa10".HexToBytes()).ToList(); + r = [.. mpt.Find("aa".HexToBytes(), "aa10".HexToBytes())]; Assert.AreEqual(1, r.Count); } @@ -571,9 +586,9 @@ public void TestFindStatesIssue652() mpt.Put("abc3".HexToBytes(), "02".HexToBytes()); var r = mpt.Find("ab".HexToBytes(), "abd2".HexToBytes()).ToList(); Assert.AreEqual(0, r.Count); - r = mpt.Find("ab".HexToBytes(), "abb2".HexToBytes()).ToList(); + r = [.. mpt.Find("ab".HexToBytes(), "abb2".HexToBytes())]; Assert.AreEqual(2, r.Count); - r = mpt.Find("ab".HexToBytes(), "abc2".HexToBytes()).ToList(); + r = [.. mpt.Find("ab".HexToBytes(), "abc2".HexToBytes())]; Assert.AreEqual(1, r.Count); } }