From 7bc096783023d0b58bfd480d2ba7d03699b488ac Mon Sep 17 00:00:00 2001 From: merl111 Date: Fri, 25 Nov 2022 18:49:14 +0100 Subject: [PATCH 1/2] add WorkNetFixture - WorkNetFixture allows you to define specific contracts which can then be used in a unit test with the state they currenlty have on the specified network (mainnet, testnet, etc.). - WorkNetConfig attribute: [WorkNetConfig("", "", "", )] * PATH is used to define the storage path for the prefetched state, default is the OS temp directory + "data". - PrefetchContract attribute: [PrefetchContract("")] --- src/Directory.Build.props | 8 +- src/test-harness/Extensions.cs | 1 + src/test-harness/PrefetchContractAttribute.cs | 16 +++ src/test-harness/WorkNetConfigAttribute.cs | 37 ++++++ src/test-harness/WorkNetFixture.cs | 115 ++++++++++++++++++ src/test-harness/WorkNetFixtureOfT.cs | 32 +++++ src/test-harness/test-harness.csproj | 6 +- 7 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 src/test-harness/PrefetchContractAttribute.cs create mode 100644 src/test-harness/WorkNetConfigAttribute.cs create mode 100644 src/test-harness/WorkNetFixture.cs create mode 100644 src/test-harness/WorkNetFixtureOfT.cs diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7aa9a62..847b4eb 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -24,9 +24,11 @@ - 3.4.13 + + local - ..\..\..\lib-bctk + + ../../../neo-blockchaintoolkit-library/ 3.4.0 ..\..\..\..\official\3neo-monorepo @@ -42,4 +44,4 @@ - \ No newline at end of file + diff --git a/src/test-harness/Extensions.cs b/src/test-harness/Extensions.cs index 60fbcf6..b84e99c 100644 --- a/src/test-harness/Extensions.cs +++ b/src/test-harness/Extensions.cs @@ -7,6 +7,7 @@ using System.Linq.Expressions; using Neo; using Neo.BlockchainToolkit; +using Neo.BlockchainToolkit.Utilities; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; diff --git a/src/test-harness/PrefetchContractAttribute.cs b/src/test-harness/PrefetchContractAttribute.cs new file mode 100644 index 0000000..324c436 --- /dev/null +++ b/src/test-harness/PrefetchContractAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace NeoTestHarness +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class PrefetchContractAttribute : Attribute + { + public string Name { get; init; } = string.Empty; + + public PrefetchContractAttribute(string name) + { + Name = name; + } + } +} + diff --git a/src/test-harness/WorkNetConfigAttribute.cs b/src/test-harness/WorkNetConfigAttribute.cs new file mode 100644 index 0000000..15754b8 --- /dev/null +++ b/src/test-harness/WorkNetConfigAttribute.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; +using Neo; + +namespace NeoTestHarness +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class WorkNetConfigAttribute : Attribute + { + public string RpcUri { get; init; } = string.Empty; + public string Height { get; init; } = string.Empty; + public string DbPath { get; init; } = string.Empty; + public ProtocolSettings Settings { get; init; } + + public WorkNetConfigAttribute(string uri, string height = "", string dbPath = "", string settingsPath = "") + { + RpcUri = uri; + Height = height; + + if (dbPath == "tmp") + { + dbPath = Path.GetTempPath() + "data"; + } + + DbPath = dbPath; + + ProtocolSettings settings = null; + if (File.Exists(settingsPath)) + { + settings = ProtocolSettings.Load(settingsPath); + } + + Settings = settings; + } + } +} + diff --git a/src/test-harness/WorkNetFixture.cs b/src/test-harness/WorkNetFixture.cs new file mode 100644 index 0000000..4050ac2 --- /dev/null +++ b/src/test-harness/WorkNetFixture.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Neo; +using Neo.BlockchainToolkit; +using Neo.BlockchainToolkit.Models; +using Neo.BlockchainToolkit.Persistence; +using Neo.Network.RPC; +using Neo.Persistence; +using Xunit; + +namespace NeoTestHarness +{ + public abstract class WorkNetFixture : IAsyncLifetime + { + private readonly static Lazy defaultFileSystem = new Lazy(() => new FileSystem()); + private StateServiceStore workNetStore; + private RocksDbSharp.RocksDb db; + private ProtocolSettings settings; + private uint height; + private RpcClient client; + private WorkNetConfigAttribute workNetConfig; + private string[] prefetchContracts; + private Dictionary contractNameCache = new(); + + public IReadOnlyStore WorkNetStore => workNetStore; + public ProtocolSettings ProtocolSettings => workNetStore.Settings; + + public WorkNetFixture( + string[] prefetchContracts, + WorkNetConfigAttribute cfg) + { + this.workNetConfig = cfg; + this.prefetchContracts = prefetchContracts; + + settings = workNetConfig.Settings ?? ProtocolSettings.Default with + { + Network = 7630401, // mainnet + AddressVersion = 0x35, + MillisecondsPerBlock = 15000, + }; + + client = new RpcClient(new Uri((workNetConfig.RpcUri)), null, null, settings); + + if (workNetConfig.Height == "latest") + { + height = client.GetBlockCountAsync().GetAwaiter().GetResult() - 1; + } + else + { + if (!UInt32.TryParse(workNetConfig.Height, out height)) + { + throw new Exception($""); + } + } + } + + public async Task InitializeAsync() + { + var branchInfo = await StateServiceStore.GetBranchInfoAsync(client, height); + + db = RocksDbUtility.OpenDb(workNetConfig.DbPath); + workNetStore = new StateServiceStore(client, branchInfo, db); + + var options = new ParallelOptions { MaxDegreeOfParallelism = 3 }; + var nativeContracts = branchInfo.Contracts.Where( x => x.Id < 0); + await Parallel.ForEachAsync(nativeContracts, options, async (contract, token) => + { + var contractHash = contract.Hash ?? throw new Exception("Null contract address in branch info"); + contractNameCache.Add(contractHash, contract.Name); + await workNetStore.PrefetchAsync(contractHash, CancellationToken.None, + ( foundStates ) => + { + Console.WriteLine($"found states for (native) {GetContractName(contractHash)}"); + }); + }); + + await Parallel.ForEachAsync(prefetchContracts, options, async (contractName, token) => + { + var contract = branchInfo.Contracts.Where( x => x.Name == contractName ).FirstOrDefault(); + contractNameCache.Add(contract.Hash, contract.Name); + await workNetStore.PrefetchAsync(contract.Hash, CancellationToken.None, + ( foundStates ) => + { + Console.WriteLine($"found states for {GetContractName(contract.Hash)}"); + }); + }); + } + + private string GetContractName(UInt160 hash) + { + if (contractNameCache.TryGetValue(hash, out string name)) + { + return name; + } + + return "unknown_" + hash; + } + + public Task DisposeAsync() + { + workNetStore.Dispose(); + client.Dispose(); + + return Task.CompletedTask; + } + + public ExpressChain FindChain(string fileName = Constants.DEFAULT_EXPRESS_FILENAME, IFileSystem? fileSystem = null, string? searchFolder = null) + => (fileSystem ?? defaultFileSystem.Value).FindChain(fileName, searchFolder); + } +} + diff --git a/src/test-harness/WorkNetFixtureOfT.cs b/src/test-harness/WorkNetFixtureOfT.cs new file mode 100644 index 0000000..5eb4d98 --- /dev/null +++ b/src/test-harness/WorkNetFixtureOfT.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; + +namespace NeoTestHarness +{ + public class WorkNetFixture : WorkNetFixture + { + static string[] GetPrefetchContracts() + { + var attribs = Attribute.GetCustomAttributes(typeof(T), typeof(PrefetchContractAttribute)) as PrefetchContractAttribute[]; + + if (attribs is null || attribs.Length == 0) + { + throw new Exception("No prefetch contracts defined, need at least one"); + } + + return attribs?.Select( x => x.Name).ToArray(); + } + + static WorkNetConfigAttribute GetWorkNetConfig() + { + var attrib = Attribute.GetCustomAttribute(typeof(T), typeof(WorkNetConfigAttribute)) as WorkNetConfigAttribute; + + return attrib ?? throw new Exception($"Missing {nameof(WorkNetConfigAttribute)} on {typeof(T).Name}"); + } + + public WorkNetFixture() : base(GetPrefetchContracts(), GetWorkNetConfig()) + { + } + } +} + diff --git a/src/test-harness/test-harness.csproj b/src/test-harness/test-harness.csproj index fe2187d..ea74830 100644 --- a/src/test-harness/test-harness.csproj +++ b/src/test-harness/test-harness.csproj @@ -18,4 +18,8 @@ - \ No newline at end of file + + + + + From 62b21711bb88068d7d9e00f5606c19f17f565e4b Mon Sep 17 00:00:00 2001 From: merl111 Date: Wed, 7 Dec 2022 10:22:14 +0100 Subject: [PATCH 2/2] add GetSnapshot for WorkNetFixture --- src/test-harness/Extensions.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test-harness/Extensions.cs b/src/test-harness/Extensions.cs index b84e99c..3548ecc 100644 --- a/src/test-harness/Extensions.cs +++ b/src/test-harness/Extensions.cs @@ -7,6 +7,7 @@ using System.Linq.Expressions; using Neo; using Neo.BlockchainToolkit; +using Neo.BlockchainToolkit.Persistence; using Neo.BlockchainToolkit.Utilities; using Neo.Persistence; using Neo.SmartContract; @@ -228,5 +229,11 @@ public static SnapshotCache GetSnapshot(this CheckpointFixture fixture) { return new SnapshotCache(fixture.CheckpointStore); } + + public static SnapshotCache GetSnapshot(this WorkNetFixture fixture) + { + var store = new MemoryTrackingStore(fixture.WorkNetStore); + return new SnapshotCache(store); + } } }