From 6147c14c9b3ae033940cec3bd566f65e7d725fd1 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 17 Jul 2023 09:26:54 +0200 Subject: [PATCH 1/7] setting up --- DistTestCore/DistTest.cs | 8 +++++--- docker/docker-compose.yaml | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 docker/docker-compose.yaml diff --git a/DistTestCore/DistTest.cs b/DistTestCore/DistTest.cs index e2a5c77e..6a0f2f85 100644 --- a/DistTestCore/DistTest.cs +++ b/DistTestCore/DistTest.cs @@ -36,6 +36,11 @@ namespace DistTestCore [OneTimeSetUp] public void GlobalSetup() { + fixtureLog.Log($"Codex Distributed Tests are starting..."); + fixtureLog.Log($"Codex image: '{CodexContainerRecipe.DockerImage}'"); + fixtureLog.Log($"Prometheus image: '{PrometheusContainerRecipe.DockerImage}'"); + fixtureLog.Log($"Geth image: '{GethContainerRecipe.DockerImage}'"); + // Previous test run may have been interrupted. // Begin by cleaning everything up. try @@ -54,9 +59,6 @@ namespace DistTestCore } fixtureLog.Log("Global setup cleanup successful"); - fixtureLog.Log($"Codex image: '{CodexContainerRecipe.DockerImage}'"); - fixtureLog.Log($"Prometheus image: '{PrometheusContainerRecipe.DockerImage}'"); - fixtureLog.Log($"Geth image: '{GethContainerRecipe.DockerImage}'"); } [SetUp] diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 00000000..6445b935 --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,11 @@ +services: + codex-node1: + # image: codexstorage/nim-codex:sha-82b0399 + build: + context: .. + dockerfile: docker/Dockerfile + environment: + - CODEX_LOG_LEVEL=${CODEX_LOG_LEVEL:-TRACE} + - CODEX_METRICS=${CODEX_METRICS:-false} + ports: + - 8080:8080/tcp # REST API From fa7ba9149eacdba7cef7582a381d93b094fc94f0 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 17 Jul 2023 11:12:14 +0200 Subject: [PATCH 2/7] Sets docker images in constructor of recipes --- DistTestCore/Codex/CodexContainerRecipe.cs | 23 ++++++++----------- DistTestCore/DistTest.cs | 7 +++--- .../CodexContractsContainerRecipe.cs | 16 ++++++++----- .../Marketplace/GethContainerRecipe.cs | 19 ++++++++------- .../Metrics/PrometheusContainerRecipe.cs | 7 ++++-- KubernetesWorkflow/ContainerRecipeFactory.cs | 2 +- docker/docker-compose.yaml | 6 +++-- 7 files changed, 45 insertions(+), 35 deletions(-) diff --git a/DistTestCore/Codex/CodexContainerRecipe.cs b/DistTestCore/Codex/CodexContainerRecipe.cs index 67e59efa..6f2871b5 100644 --- a/DistTestCore/Codex/CodexContainerRecipe.cs +++ b/DistTestCore/Codex/CodexContainerRecipe.cs @@ -5,12 +5,6 @@ namespace DistTestCore.Codex { public class CodexContainerRecipe : ContainerRecipeFactory { -#if Arm64 - public const string DockerImage = "codexstorage/nim-codex:sha-6dd7e55"; -#else - public const string DockerImage = "thatbenbierens/nim-codex:loopingyeah"; - //public const string DockerImage = "codexstorage/nim-codex:sha-6dd7e55"; -#endif public const string MetricsPortTag = "metrics_port"; public const string DiscoveryPortTag = "discovery-port"; @@ -18,15 +12,11 @@ namespace DistTestCore.Codex public static readonly TimeSpan MaxUploadTimePerMegabyte = TimeSpan.FromSeconds(2.0); public static readonly TimeSpan MaxDownloadTimePerMegabyte = TimeSpan.FromSeconds(2.0); - public static string DockerImageOverride = string.Empty; + public override string Image { get; } - protected override string Image + public CodexContainerRecipe() { - get - { - if (!string.IsNullOrEmpty(DockerImageOverride)) return DockerImageOverride; - return DockerImage; - } + Image = GetDockerImage(); } protected override void Initialize(StartupConfig startupConfig) @@ -92,5 +82,12 @@ namespace DistTestCore.Codex if (marketplaceConfig.AccountIndexOverride != null) return marketplaceConfig.AccountIndexOverride.Value; return Index; } + + private string GetDockerImage() + { + var image = Environment.GetEnvironmentVariable("CODEXDOCKERIMAGE"); + if (!string.IsNullOrEmpty(image)) return image; + return "codexstorage/nim-codex:sha-0265cad"; + } } } diff --git a/DistTestCore/DistTest.cs b/DistTestCore/DistTest.cs index 6a0f2f85..87208ad3 100644 --- a/DistTestCore/DistTest.cs +++ b/DistTestCore/DistTest.cs @@ -37,9 +37,10 @@ namespace DistTestCore public void GlobalSetup() { fixtureLog.Log($"Codex Distributed Tests are starting..."); - fixtureLog.Log($"Codex image: '{CodexContainerRecipe.DockerImage}'"); - fixtureLog.Log($"Prometheus image: '{PrometheusContainerRecipe.DockerImage}'"); - fixtureLog.Log($"Geth image: '{GethContainerRecipe.DockerImage}'"); + fixtureLog.Log($"Codex image: '{new CodexContainerRecipe().Image}'"); + fixtureLog.Log($"CodexContracts image: '{new CodexContractsContainerRecipe().Image}'"); + fixtureLog.Log($"Prometheus image: '{new PrometheusContainerRecipe().Image}'"); + fixtureLog.Log($"Geth image: '{new GethContainerRecipe().Image}'"); // Previous test run may have been interrupted. // Begin by cleaning everything up. diff --git a/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs b/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs index 1ff0c06d..d34aa6f8 100644 --- a/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs +++ b/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs @@ -4,15 +4,19 @@ namespace DistTestCore.Marketplace { public class CodexContractsContainerRecipe : ContainerRecipeFactory { - #if Arm64 - public const string DockerImage = "emizzle/codex-contracts-deployment:latest"; - #else - public const string DockerImage = "thatbenbierens/codex-contracts-deployment:nomint2"; - #endif public const string MarketplaceAddressFilename = "/usr/app/deployments/codexdisttestnetwork/Marketplace.json"; public const string MarketplaceArtifactFilename = "/usr/app/artifacts/contracts/Marketplace.sol/Marketplace.json"; - protected override string Image => DockerImage; + public override string Image { get; } + + public CodexContractsContainerRecipe() + { +#if Arm64 + Image = "emizzle/codex-contracts-deployment:latest"; +#else + Image = "thatbenbierens/codex-contracts-deployment:nomint2"; +#endif + } protected override void Initialize(StartupConfig startupConfig) { diff --git a/DistTestCore/Marketplace/GethContainerRecipe.cs b/DistTestCore/Marketplace/GethContainerRecipe.cs index a377d69f..c94f73d9 100644 --- a/DistTestCore/Marketplace/GethContainerRecipe.cs +++ b/DistTestCore/Marketplace/GethContainerRecipe.cs @@ -4,19 +4,22 @@ namespace DistTestCore.Marketplace { public class GethContainerRecipe : ContainerRecipeFactory { - #if Arm64 - public const string DockerImage = "emizzle/geth-confenv:latest"; - #else - public const string DockerImage = "thatbenbierens/geth-confenv:onethousand"; - #endif + private const string defaultArgs = "--ipcdisable --syncmode full"; public const string HttpPortTag = "http_port"; public const string DiscoveryPortTag = "disc_port"; - private const string defaultArgs = "--ipcdisable --syncmode full"; - public const string AccountsFilename = "accounts.csv"; - protected override string Image => DockerImage; + public override string Image { get; } + + public GethContainerRecipe() + { +#if Arm64 + Image = "emizzle/geth-confenv:latest"; +#else + Image = "thatbenbierens/geth-confenv:onethousand"; +#endif + } protected override void Initialize(StartupConfig startupConfig) { diff --git a/DistTestCore/Metrics/PrometheusContainerRecipe.cs b/DistTestCore/Metrics/PrometheusContainerRecipe.cs index 46587cf9..50e433ab 100644 --- a/DistTestCore/Metrics/PrometheusContainerRecipe.cs +++ b/DistTestCore/Metrics/PrometheusContainerRecipe.cs @@ -4,9 +4,12 @@ namespace DistTestCore.Metrics { public class PrometheusContainerRecipe : ContainerRecipeFactory { - public const string DockerImage = "thatbenbierens/prometheus-envconf:latest"; + public override string Image { get; } - protected override string Image => DockerImage; + public PrometheusContainerRecipe() + { + Image = "thatbenbierens/prometheus-envconf:latest"; + } protected override void Initialize(StartupConfig startupConfig) { diff --git a/KubernetesWorkflow/ContainerRecipeFactory.cs b/KubernetesWorkflow/ContainerRecipeFactory.cs index 07dc58e9..340ed2da 100644 --- a/KubernetesWorkflow/ContainerRecipeFactory.cs +++ b/KubernetesWorkflow/ContainerRecipeFactory.cs @@ -27,7 +27,7 @@ return recipe; } - protected abstract string Image { get; } + public abstract string Image { get; } protected int ContainerNumber { get; private set; } = 0; protected int Index { get; private set; } = 0; protected abstract void Initialize(StartupConfig config); diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 6445b935..afe4f8a6 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -5,7 +5,9 @@ services: context: .. dockerfile: docker/Dockerfile environment: - - CODEX_LOG_LEVEL=${CODEX_LOG_LEVEL:-TRACE} - - CODEX_METRICS=${CODEX_METRICS:-false} + - BRANCH="feature/docker-image-testruns" + - KUBECONFIG=kubeconfigplease + - LOGPATH="/" + - RUNNERLOCATION="ExternalToCluster" ports: - 8080:8080/tcp # REST API From 78434048b7ca4bab555e3b7e35cb0718bb2c40a5 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 17 Jul 2023 11:29:27 +0200 Subject: [PATCH 3/7] Example docker-compose --- docker/docker-compose.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index afe4f8a6..9f37c25e 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -1,13 +1,11 @@ services: - codex-node1: - # image: codexstorage/nim-codex:sha-82b0399 + dist-test-run: build: context: .. dockerfile: docker/Dockerfile environment: + - CODEXDOCKERIMAGE=thatbenbierens/nim-codex:dhting - BRANCH="feature/docker-image-testruns" - - KUBECONFIG=kubeconfigplease - - LOGPATH="/" - - RUNNERLOCATION="ExternalToCluster" - ports: - - 8080:8080/tcp # REST API + - KUBECONFIG=/opt/kubeconfig + - LOGPATH=/opt/logs + - RUNNERLOCATION=ExternalToCluster From 933932b091322db49789dc6ed9a22aef477d70c2 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 21 Jul 2023 09:39:27 +0200 Subject: [PATCH 4/7] Fixes fetching of docker image for status log --- CodexNetDeployer/Program.cs | 9 +++++---- DistTestCore/DistTest.cs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CodexNetDeployer/Program.cs b/CodexNetDeployer/Program.cs index fc4d5f62..c2fc1733 100644 --- a/CodexNetDeployer/Program.cs +++ b/CodexNetDeployer/Program.cs @@ -4,6 +4,7 @@ using DistTestCore; using DistTestCore.Codex; using DistTestCore.Marketplace; using DistTestCore.Metrics; +using Logging; using Newtonsoft.Json; using Configuration = CodexNetDeployer.Configuration; @@ -33,10 +34,10 @@ public class Program } Console.WriteLine("Using images:" + nl + - $"\tCodex image: '{CodexContainerRecipe.DockerImage}'" + nl + - $"\tCodex Contracts image: '{CodexContractsContainerRecipe.DockerImage}'" + nl + - $"\tPrometheus image: '{PrometheusContainerRecipe.DockerImage}'" + nl + - $"\tGeth image: '{GethContainerRecipe.DockerImage}'" + nl); + $"\tCodex image: '{new CodexContainerRecipe().Image}'" + nl + + $"\tCodexContracts image: '{new CodexContractsContainerRecipe().Image}'" + nl + + $"\tPrometheus image: '{new PrometheusContainerRecipe().Image}'" + nl + + $"\tGeth image: '{new GethContainerRecipe().Image}'" + nl); if (!args.Any(a => a == "-y")) { diff --git a/DistTestCore/DistTest.cs b/DistTestCore/DistTest.cs index 45fb4082..252a2f42 100644 --- a/DistTestCore/DistTest.cs +++ b/DistTestCore/DistTest.cs @@ -28,7 +28,7 @@ namespace DistTestCore var logConfig = configuration.GetLogConfig(); var startTime = DateTime.UtcNow; fixtureLog = new FixtureLog(logConfig, startTime); - statusLog = new StatusLog(logConfig, startTime, CodexContainerRecipe.DefaultDockerImage); + statusLog = new StatusLog(logConfig, startTime, new CodexContainerRecipe().Image); PeerConnectionTestHelpers = new PeerConnectionTestHelpers(this); PeerDownloadTestHelpers = new PeerDownloadTestHelpers(this); From 10ffd2925749f5e7c662a18528c4e9d64c72ca12 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 21 Jul 2023 09:44:46 +0200 Subject: [PATCH 5/7] Cleanup --- CodexNetDeployer/Program.cs | 1 - docker/docker-compose.yaml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CodexNetDeployer/Program.cs b/CodexNetDeployer/Program.cs index c2fc1733..d96b25f1 100644 --- a/CodexNetDeployer/Program.cs +++ b/CodexNetDeployer/Program.cs @@ -4,7 +4,6 @@ using DistTestCore; using DistTestCore.Codex; using DistTestCore.Marketplace; using DistTestCore.Metrics; -using Logging; using Newtonsoft.Json; using Configuration = CodexNetDeployer.Configuration; diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 9f37c25e..98af2465 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -4,7 +4,7 @@ services: context: .. dockerfile: docker/Dockerfile environment: - - CODEXDOCKERIMAGE=thatbenbierens/nim-codex:dhting + - CODEXDOCKERIMAGE=codexstorage/nim-codex:sha-14c5270 - BRANCH="feature/docker-image-testruns" - KUBECONFIG=/opt/kubeconfig - LOGPATH=/opt/logs From 9a7946c863b9e85c4ec255ae61ba620066280abf Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 31 Jul 2023 09:16:52 +0200 Subject: [PATCH 6/7] Basic tests pass --- DistTestCore/Codex/CodexContainerRecipe.cs | 2 +- .../CodexContractsContainerRecipe.cs | 10 ++--- .../Marketplace/GethContainerRecipe.cs | 6 +-- DistTestCore/Marketplace/MarketplaceAccess.cs | 12 +++++ .../Metrics/PrometheusContainerRecipe.cs | 2 +- Tests/BasicTests/ExampleTests.cs | 45 +++++++++++++++++-- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/DistTestCore/Codex/CodexContainerRecipe.cs b/DistTestCore/Codex/CodexContainerRecipe.cs index edf15da6..4039d541 100644 --- a/DistTestCore/Codex/CodexContainerRecipe.cs +++ b/DistTestCore/Codex/CodexContainerRecipe.cs @@ -5,7 +5,7 @@ namespace DistTestCore.Codex { public class CodexContainerRecipe : ContainerRecipeFactory { - private const string DefaultDockerImage = "codexstorage/nim-codex:sha-14c5270"; + private const string DefaultDockerImage = "codexstorage/nim-codex:sha-3e80de3"; public const string MetricsPortTag = "metrics_port"; public const string DiscoveryPortTag = "discovery-port"; diff --git a/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs b/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs index d34aa6f8..51e62d59 100644 --- a/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs +++ b/DistTestCore/Marketplace/CodexContractsContainerRecipe.cs @@ -4,18 +4,14 @@ namespace DistTestCore.Marketplace { public class CodexContractsContainerRecipe : ContainerRecipeFactory { - public const string MarketplaceAddressFilename = "/usr/app/deployments/codexdisttestnetwork/Marketplace.json"; - public const string MarketplaceArtifactFilename = "/usr/app/artifacts/contracts/Marketplace.sol/Marketplace.json"; + public const string MarketplaceAddressFilename = "/hardhat/deployments/codexdisttestnetwork/Marketplace.json"; + public const string MarketplaceArtifactFilename = "/hardhat/artifacts/contracts/Marketplace.sol/Marketplace.json"; public override string Image { get; } public CodexContractsContainerRecipe() { -#if Arm64 - Image = "emizzle/codex-contracts-deployment:latest"; -#else - Image = "thatbenbierens/codex-contracts-deployment:nomint2"; -#endif + Image = "codexstorage/dist-tests-codex-contracts-eth:sha-b4e4897"; } protected override void Initialize(StartupConfig startupConfig) diff --git a/DistTestCore/Marketplace/GethContainerRecipe.cs b/DistTestCore/Marketplace/GethContainerRecipe.cs index c94f73d9..6dc62e92 100644 --- a/DistTestCore/Marketplace/GethContainerRecipe.cs +++ b/DistTestCore/Marketplace/GethContainerRecipe.cs @@ -14,11 +14,7 @@ namespace DistTestCore.Marketplace public GethContainerRecipe() { -#if Arm64 - Image = "emizzle/geth-confenv:latest"; -#else - Image = "thatbenbierens/geth-confenv:onethousand"; -#endif + Image = "codexstorage/dist-tests-geth:sha-b788a2d"; } protected override void Initialize(StartupConfig startupConfig) diff --git a/DistTestCore/Marketplace/MarketplaceAccess.cs b/DistTestCore/Marketplace/MarketplaceAccess.cs index 8e3fa89d..b1791211 100644 --- a/DistTestCore/Marketplace/MarketplaceAccess.cs +++ b/DistTestCore/Marketplace/MarketplaceAccess.cs @@ -12,6 +12,7 @@ namespace DistTestCore.Marketplace string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration); string RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration); void AssertThatBalance(IResolveConstraint constraint, string message = ""); + CodexStoragePurchase GetPurchaseStatus(string purchaseId); TestToken GetBalance(); } @@ -102,6 +103,11 @@ namespace DistTestCore.Marketplace AssertHelpers.RetryAssert(constraint, GetBalance, message); } + public CodexStoragePurchase GetPurchaseStatus(string purchaseId) + { + return codexAccess.GetPurchaseStatus(purchaseId); + } + public TestToken GetBalance() { var interaction = marketplaceNetwork.StartInteraction(lifecycle); @@ -138,6 +144,12 @@ namespace DistTestCore.Marketplace Unavailable(); } + public CodexStoragePurchase GetPurchaseStatus(string purchaseId) + { + Unavailable(); + return new CodexStoragePurchase(); + } + public TestToken GetBalance() { Unavailable(); diff --git a/DistTestCore/Metrics/PrometheusContainerRecipe.cs b/DistTestCore/Metrics/PrometheusContainerRecipe.cs index 50e433ab..b7022225 100644 --- a/DistTestCore/Metrics/PrometheusContainerRecipe.cs +++ b/DistTestCore/Metrics/PrometheusContainerRecipe.cs @@ -8,7 +8,7 @@ namespace DistTestCore.Metrics public PrometheusContainerRecipe() { - Image = "thatbenbierens/prometheus-envconf:latest"; + Image = "codexstorage/dist-tests-prometheus:sha-f97d7fd"; } protected override void Initialize(StartupConfig startupConfig) diff --git a/Tests/BasicTests/ExampleTests.cs b/Tests/BasicTests/ExampleTests.cs index 3007ae48..371e9e1d 100644 --- a/Tests/BasicTests/ExampleTests.cs +++ b/Tests/BasicTests/ExampleTests.cs @@ -1,4 +1,7 @@ using DistTestCore; +using DistTestCore.Codex; +using DistTestCore.Marketplace; +using Newtonsoft.Json; using NUnit.Framework; using Utils; @@ -61,23 +64,59 @@ namespace Tests.BasicTests var buyer = SetupCodexNode(s => s .WithBootstrapNode(seller) .EnableMarketplace(buyerInitialBalance)); + + buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance)); var contentId = buyer.UploadFile(testFile); - buyer.Marketplace.RequestStorage(contentId, + var purchaseId = buyer.Marketplace.RequestStorage(contentId, pricePerSlotPerSecond: 2.TestTokens(), requiredCollateral: 10.TestTokens(), minRequiredNumberOfNodes: 1, proofProbability: 5, duration: TimeSpan.FromMinutes(1)); - Time.Sleep(TimeSpan.FromSeconds(10)); + + WaitForContractToStart(buyer.Marketplace, 10.MB(), purchaseId); seller.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed."); - Time.Sleep(TimeSpan.FromMinutes(1)); + Time.Sleep(TimeSpan.FromMinutes(2)); seller.Marketplace.AssertThatBalance(Is.GreaterThan(sellerInitialBalance), "Seller was not paid for storage."); buyer.Marketplace.AssertThatBalance(Is.LessThan(buyerInitialBalance), "Buyer was not charged for storage."); } + + private void WaitForContractToStart(IMarketplaceAccess access, ByteSize fileSize, string purchaseId) + { + var lastState = ""; + var waitStart = DateTime.UtcNow; + var filesizeInMb = fileSize.SizeInBytes / (1024 * 1024); + var maxWaitTime = TimeSpan.FromSeconds(filesizeInMb * 10.0); + + Log($"{nameof(WaitForContractToStart)} for {Time.FormatDuration(maxWaitTime)}"); + while (lastState != "started") + { + var purchaseStatus = access.GetPurchaseStatus(purchaseId); + var statusJson = JsonConvert.SerializeObject(purchaseStatus); + if (purchaseStatus != null && purchaseStatus.state != lastState) + { + lastState = purchaseStatus.state; + Log("Purchase status: " + statusJson); + } + + Thread.Sleep(2000); + + if (lastState == "errored") + { + Assert.Fail("Contract start failed: " + statusJson); + } + + if (DateTime.UtcNow - waitStart > maxWaitTime) + { + Assert.Fail($"Contract was not picked up within {maxWaitTime.TotalSeconds} seconds timeout: {statusJson}"); + } + } + Log("Contract started."); + } } } From 8d229f172079e8d88190e3260f32fe0de75243cd Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 31 Jul 2023 09:56:37 +0200 Subject: [PATCH 7/7] cleans up waiting for storage contract state helper methods. --- ContinuousTests/Tests/MarketplaceTest.cs | 35 ------ DistTestCore/Marketplace/MarketplaceAccess.cs | 114 +++++++++++++++--- Tests/BasicTests/ExampleTests.cs | 43 +------ 3 files changed, 101 insertions(+), 91 deletions(-) diff --git a/ContinuousTests/Tests/MarketplaceTest.cs b/ContinuousTests/Tests/MarketplaceTest.cs index 86500a24..09170f7a 100644 --- a/ContinuousTests/Tests/MarketplaceTest.cs +++ b/ContinuousTests/Tests/MarketplaceTest.cs @@ -60,40 +60,5 @@ // file.AssertIsEqual(result); // }); // } - -// private void WaitForContractToStart(CodexAccess codexAccess, string purchaseId) -// { -// var lastState = ""; -// var waitStart = DateTime.UtcNow; -// var filesizeInMb = fileSize.SizeInBytes / (1024 * 1024); -// var maxWaitTime = TimeSpan.FromSeconds(filesizeInMb * 10.0); - -// Log.Log($"{nameof(WaitForContractToStart)} for {Time.FormatDuration(maxWaitTime)}"); -// while (lastState != "started") -// { -// CancelToken.ThrowIfCancellationRequested(); - -// var purchaseStatus = codexAccess.Node.GetPurchaseStatus(purchaseId); -// var statusJson = JsonConvert.SerializeObject(purchaseStatus); -// if (purchaseStatus != null && purchaseStatus.state != lastState) -// { -// lastState = purchaseStatus.state; -// Log.Log("Purchase status: " + statusJson); -// } - -// Thread.Sleep(2000); - -// if (lastState == "errored") -// { -// Assert.Fail("Contract start failed: " + statusJson); -// } - -// if (DateTime.UtcNow - waitStart > maxWaitTime) -// { -// Assert.Fail($"Contract was not picked up within {maxWaitTime.TotalSeconds} seconds timeout: {statusJson}"); -// } -// } -// Log.Log("Contract started."); -// } // } //} diff --git a/DistTestCore/Marketplace/MarketplaceAccess.cs b/DistTestCore/Marketplace/MarketplaceAccess.cs index b1791211..06715826 100644 --- a/DistTestCore/Marketplace/MarketplaceAccess.cs +++ b/DistTestCore/Marketplace/MarketplaceAccess.cs @@ -1,5 +1,7 @@ using DistTestCore.Codex; using DistTestCore.Helpers; +using Logging; +using Newtonsoft.Json; using NUnit.Framework; using NUnit.Framework.Constraints; using System.Numerics; @@ -10,9 +12,8 @@ namespace DistTestCore.Marketplace public interface IMarketplaceAccess { string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration); - string RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration); + StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration); void AssertThatBalance(IResolveConstraint constraint, string message = ""); - CodexStoragePurchase GetPurchaseStatus(string purchaseId); TestToken GetBalance(); } @@ -31,7 +32,7 @@ namespace DistTestCore.Marketplace this.codexAccess = codexAccess; } - public string RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration) + public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration) { var request = new CodexSalesRequestStorageRequest { @@ -58,9 +59,9 @@ namespace DistTestCore.Marketplace throw new InvalidOperationException(response); } - Log($"Storage requested successfully. PurchaseId: {response}"); + Log($"Storage requested successfully. PurchaseId: '{response}'."); - return response; + return new StoragePurchaseContract(lifecycle.Log, codexAccess, response, duration); } public string MakeStorageAvailable(ByteSize totalSpace, TestToken minPriceForTotalSpace, TestToken maxCollateral, TimeSpan maxDuration) @@ -103,11 +104,6 @@ namespace DistTestCore.Marketplace AssertHelpers.RetryAssert(constraint, GetBalance, message); } - public CodexStoragePurchase GetPurchaseStatus(string purchaseId) - { - return codexAccess.GetPurchaseStatus(purchaseId); - } - public TestToken GetBalance() { var interaction = marketplaceNetwork.StartInteraction(lifecycle); @@ -127,10 +123,10 @@ namespace DistTestCore.Marketplace public class MarketplaceUnavailable : IMarketplaceAccess { - public string RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration) + public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration) { Unavailable(); - return string.Empty; + return null!; } public string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan duration) @@ -144,12 +140,6 @@ namespace DistTestCore.Marketplace Unavailable(); } - public CodexStoragePurchase GetPurchaseStatus(string purchaseId) - { - Unavailable(); - return new CodexStoragePurchase(); - } - public TestToken GetBalance() { Unavailable(); @@ -162,4 +152,92 @@ namespace DistTestCore.Marketplace throw new InvalidOperationException(); } } + + public class StoragePurchaseContract + { + private readonly BaseLog log; + private readonly CodexAccess codexAccess; + private DateTime? contractStartUtc; + + public StoragePurchaseContract(BaseLog log, CodexAccess codexAccess, string purchaseId, TimeSpan contractDuration) + { + this.log = log; + this.codexAccess = codexAccess; + PurchaseId = purchaseId; + ContractDuration = contractDuration; + } + + public string PurchaseId { get; } + public TimeSpan ContractDuration { get; } + + public void WaitForStorageContractStarted() + { + WaitForStorageContractStarted(TimeSpan.FromSeconds(30)); + } + + public void WaitForStorageContractFinished() + { + if (!contractStartUtc.HasValue) + { + WaitForStorageContractStarted(); + } + var gracePeriod = TimeSpan.FromSeconds(10); + var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value; + var timeout = (ContractDuration - currentContractTime) + gracePeriod; + WaitForStorageContractState(timeout, "finished"); + } + + /// + /// Wait for contract to start. Max timeout depends on contract filesize. Allows more time for larger files. + /// + public void WaitForStorageContractStarted(ByteSize contractFileSize) + { + var filesizeInMb = contractFileSize.SizeInBytes / (1024 * 1024); + var maxWaitTime = TimeSpan.FromSeconds(filesizeInMb * 10.0); + + WaitForStorageContractStarted(maxWaitTime); + } + + public void WaitForStorageContractStarted(TimeSpan timeout) + { + WaitForStorageContractState(timeout, "started"); + contractStartUtc = DateTime.UtcNow; + } + + private void WaitForStorageContractState(TimeSpan timeout, string desiredState) + { + var lastState = ""; + var waitStart = DateTime.UtcNow; + + log.Log($"Waiting for {Time.FormatDuration(timeout)} for contract '{PurchaseId}' to reach state '{desiredState}'."); + while (lastState != desiredState) + { + var purchaseStatus = codexAccess.GetPurchaseStatus(PurchaseId); + var statusJson = JsonConvert.SerializeObject(purchaseStatus); + if (purchaseStatus != null && purchaseStatus.state != lastState) + { + lastState = purchaseStatus.state; + log.Debug("Purchase status: " + statusJson); + } + + Thread.Sleep(1000); + + if (lastState == "errored") + { + Assert.Fail("Contract errored: " + statusJson); + } + + if (DateTime.UtcNow - waitStart > timeout) + { + Assert.Fail($"Contract did not reach '{desiredState}' within timeout. {statusJson}"); + } + } + log.Log($"Contract '{desiredState}'."); + } + + public CodexStoragePurchase GetPurchaseStatus(string purchaseId) + { + return codexAccess.GetPurchaseStatus(purchaseId); + } + } } diff --git a/Tests/BasicTests/ExampleTests.cs b/Tests/BasicTests/ExampleTests.cs index 371e9e1d..420b6fad 100644 --- a/Tests/BasicTests/ExampleTests.cs +++ b/Tests/BasicTests/ExampleTests.cs @@ -47,6 +47,7 @@ namespace Tests.BasicTests { var sellerInitialBalance = 234.TestTokens(); var buyerInitialBalance = 1000.TestTokens(); + var fileSize = 10.MB(); var seller = SetupCodexNode(s => s .WithStorageQuota(11.GB()) @@ -59,7 +60,7 @@ namespace Tests.BasicTests maxCollateral: 20.TestTokens(), maxDuration: TimeSpan.FromMinutes(3)); - var testFile = GenerateTestFile(10.MB()); + var testFile = GenerateTestFile(fileSize); var buyer = SetupCodexNode(s => s .WithBootstrapNode(seller) @@ -68,55 +69,21 @@ namespace Tests.BasicTests buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance)); var contentId = buyer.UploadFile(testFile); - var purchaseId = buyer.Marketplace.RequestStorage(contentId, + var purchaseContract = buyer.Marketplace.RequestStorage(contentId, pricePerSlotPerSecond: 2.TestTokens(), requiredCollateral: 10.TestTokens(), minRequiredNumberOfNodes: 1, proofProbability: 5, duration: TimeSpan.FromMinutes(1)); - - WaitForContractToStart(buyer.Marketplace, 10.MB(), purchaseId); + purchaseContract.WaitForStorageContractStarted(fileSize); seller.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed."); - Time.Sleep(TimeSpan.FromMinutes(2)); + purchaseContract.WaitForStorageContractFinished(); seller.Marketplace.AssertThatBalance(Is.GreaterThan(sellerInitialBalance), "Seller was not paid for storage."); buyer.Marketplace.AssertThatBalance(Is.LessThan(buyerInitialBalance), "Buyer was not charged for storage."); } - - private void WaitForContractToStart(IMarketplaceAccess access, ByteSize fileSize, string purchaseId) - { - var lastState = ""; - var waitStart = DateTime.UtcNow; - var filesizeInMb = fileSize.SizeInBytes / (1024 * 1024); - var maxWaitTime = TimeSpan.FromSeconds(filesizeInMb * 10.0); - - Log($"{nameof(WaitForContractToStart)} for {Time.FormatDuration(maxWaitTime)}"); - while (lastState != "started") - { - var purchaseStatus = access.GetPurchaseStatus(purchaseId); - var statusJson = JsonConvert.SerializeObject(purchaseStatus); - if (purchaseStatus != null && purchaseStatus.state != lastState) - { - lastState = purchaseStatus.state; - Log("Purchase status: " + statusJson); - } - - Thread.Sleep(2000); - - if (lastState == "errored") - { - Assert.Fail("Contract start failed: " + statusJson); - } - - if (DateTime.UtcNow - waitStart > maxWaitTime) - { - Assert.Fail($"Contract was not picked up within {maxWaitTime.TotalSeconds} seconds timeout: {statusJson}"); - } - } - Log("Contract started."); - } } }