diff --git a/ProjectPlugins/CodexClient/CodexTypes.cs b/ProjectPlugins/CodexClient/CodexTypes.cs index 9085aa2c..f9a0d9d2 100644 --- a/ProjectPlugins/CodexClient/CodexTypes.cs +++ b/ProjectPlugins/CodexClient/CodexTypes.cs @@ -17,6 +17,7 @@ namespace CodexClient { public string Version { get; set; } = string.Empty; public string Revision { get; set; } = string.Empty; + public string Contracts { get; set; } = string.Empty; public bool IsValid() { diff --git a/ProjectPlugins/CodexClient/Mapper.cs b/ProjectPlugins/CodexClient/Mapper.cs index 43bb15d0..16c156bd 100644 --- a/ProjectPlugins/CodexClient/Mapper.cs +++ b/ProjectPlugins/CodexClient/Mapper.cs @@ -168,7 +168,8 @@ namespace CodexClient return new DebugInfoVersion { Version = obj.Version, - Revision = obj.Revision + Revision = obj.Revision, + Contracts = obj.Contracts }; } diff --git a/ProjectPlugins/CodexClient/openapi.yaml b/ProjectPlugins/CodexClient/openapi.yaml index fd66648e..d59cbcfa 100644 --- a/ProjectPlugins/CodexClient/openapi.yaml +++ b/ProjectPlugins/CodexClient/openapi.yaml @@ -124,6 +124,9 @@ components: revision: type: string example: 0c647d8 + contracts: + type: string + example: 0b537c7 PeersTable: type: object diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs index e09a0ee7..b46f3c96 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs @@ -1,4 +1,5 @@ -using GethPlugin; +using CodexClient; +using GethPlugin; using KubernetesWorkflow; using KubernetesWorkflow.Recipe; @@ -8,14 +9,14 @@ namespace CodexContractsPlugin { public const string MarketplaceAddressFilename = "/hardhat/deployments/codexdisttestnetwork/Marketplace.json"; public const string MarketplaceArtifactFilename = "/hardhat/artifacts/contracts/Marketplace.sol/Marketplace.json"; - private readonly VersionRegistry versionRegistry; + private readonly DebugInfoVersion versionInfo; public override string AppName => "codex-contracts"; - public override string Image => versionRegistry.GetContractsDockerImage(); + public override string Image => GetContractsDockerImage(); - public CodexContractsContainerRecipe(VersionRegistry versionRegistry) + public CodexContractsContainerRecipe(DebugInfoVersion versionInfo) { - this.versionRegistry = versionRegistry; + this.versionInfo = versionInfo; } protected override void Initialize(StartupConfig startupConfig) @@ -30,5 +31,10 @@ namespace CodexContractsPlugin AddEnvVar("HARDHAT_NETWORK", "codexdisttestnetwork"); AddEnvVar("KEEP_ALIVE", "1"); } + + private string GetContractsDockerImage() + { + return $"codexstorage/codex-contracts-eth:sha-{versionInfo.Contracts}-dist-tests"; + } } } diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.cs index 6e02280d..1d122d3b 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.cs @@ -7,15 +7,11 @@ namespace CodexContractsPlugin { private readonly IPluginTools tools; private readonly CodexContractsStarter starter; - private readonly VersionRegistry versionRegistry; - private readonly CodexContractsContainerRecipe recipe; public CodexContractsPlugin(IPluginTools tools) { this.tools = tools; - versionRegistry = new VersionRegistry(tools.GetLog()); - recipe = new CodexContractsContainerRecipe(versionRegistry); - starter = new CodexContractsStarter(tools, recipe); + starter = new CodexContractsStarter(tools); } public string LogPrefix => "(CodexContracts) "; @@ -31,16 +27,16 @@ namespace CodexContractsPlugin public void AddMetadata(IAddMetadata metadata) { - metadata.Add("codexcontractsid", recipe.Image); + metadata.Add("codexcontractsid", "dynamic"); } public void Decommission() { } - public CodexContractsDeployment DeployContracts(CoreInterface ci, IGethNode gethNode) + public CodexContractsDeployment DeployContracts(CoreInterface ci, IGethNode gethNode, CodexClient.DebugInfoVersion versionInfo) { - return starter.Deploy(ci, gethNode); + return starter.Deploy(ci, gethNode, versionInfo); } public ICodexContracts WrapDeploy(IGethNode gethNode, CodexContractsDeployment deployment) @@ -48,10 +44,5 @@ namespace CodexContractsPlugin deployment = SerializeGate.Gate(deployment); return starter.Wrap(gethNode, deployment); } - - public void SetCodexDockerImageProvider(ICodexDockerImageProvider provider) - { - versionRegistry.SetProvider(provider); - } } } diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.csproj b/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.csproj index 24f87068..34f6cad1 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.csproj +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsPlugin.csproj @@ -13,6 +13,7 @@ + diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs index 5ecc22a9..c7e900be 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin.Marketplace; +using CodexClient; +using CodexContractsPlugin.Marketplace; using Core; using GethPlugin; using KubernetesWorkflow; @@ -12,15 +13,13 @@ namespace CodexContractsPlugin public class CodexContractsStarter { private readonly IPluginTools tools; - private readonly CodexContractsContainerRecipe recipe; - public CodexContractsStarter(IPluginTools tools, CodexContractsContainerRecipe recipe) + public CodexContractsStarter(IPluginTools tools) { this.tools = tools; - this.recipe = recipe; } - public CodexContractsDeployment Deploy(CoreInterface ci, IGethNode gethNode) + public CodexContractsDeployment Deploy(CoreInterface ci, IGethNode gethNode, DebugInfoVersion versionInfo) { Log("Starting Codex SmartContracts container..."); @@ -28,6 +27,9 @@ namespace CodexContractsPlugin var startupConfig = CreateStartupConfig(gethNode); startupConfig.NameOverride = "codex-contracts"; + var recipe = new CodexContractsContainerRecipe(versionInfo); + Log($"Using image: {recipe.Image}"); + var containers = workflow.Start(1, recipe, startupConfig).WaitForOnline(); if (containers.Containers.Length != 1) throw new InvalidOperationException("Expected 1 Codex contracts container to be created. Test infra failure."); var container = containers.Containers[0]; diff --git a/ProjectPlugins/CodexContractsPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexContractsPlugin/CoreInterfaceExtensions.cs index ea123bc9..d07e25f7 100644 --- a/ProjectPlugins/CodexContractsPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/CodexContractsPlugin/CoreInterfaceExtensions.cs @@ -1,13 +1,14 @@ -using Core; +using CodexClient; +using Core; using GethPlugin; namespace CodexContractsPlugin { public static class CoreInterfaceExtensions { - public static CodexContractsDeployment DeployCodexContracts(this CoreInterface ci, IGethNode gethNode) + public static CodexContractsDeployment DeployCodexContracts(this CoreInterface ci, IGethNode gethNode, DebugInfoVersion versionInfo) { - return Plugin(ci).DeployContracts(ci, gethNode); + return Plugin(ci).DeployContracts(ci, gethNode, versionInfo); } public static ICodexContracts WrapCodexContractsDeployment(this CoreInterface ci, IGethNode gethNode, CodexContractsDeployment deployment) @@ -15,17 +16,12 @@ namespace CodexContractsPlugin return Plugin(ci).WrapDeploy(gethNode, deployment); } - public static ICodexContracts StartCodexContracts(this CoreInterface ci, IGethNode gethNode) + public static ICodexContracts StartCodexContracts(this CoreInterface ci, IGethNode gethNode, DebugInfoVersion versionInfo) { - var deployment = DeployCodexContracts(ci, gethNode); + var deployment = DeployCodexContracts(ci, gethNode, versionInfo); return WrapCodexContractsDeployment(ci, gethNode, deployment); } - public static void SetCodexDockerImageProvider(this CoreInterface ci, ICodexDockerImageProvider provider) - { - Plugin(ci).SetCodexDockerImageProvider(provider); - } - private static CodexContractsPlugin Plugin(CoreInterface ci) { return ci.GetPlugin(); diff --git a/ProjectPlugins/CodexContractsPlugin/VersionRegistry.cs b/ProjectPlugins/CodexContractsPlugin/VersionRegistry.cs deleted file mode 100644 index f52a38a8..00000000 --- a/ProjectPlugins/CodexContractsPlugin/VersionRegistry.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.Diagnostics; -using Logging; - -namespace CodexContractsPlugin -{ - public interface ICodexDockerImageProvider - { - string GetCodexDockerImage(); - } - - public class VersionRegistry - { - private ICodexDockerImageProvider provider = new ExceptionProvider(); - private static readonly Dictionary cache = new Dictionary(); - private static readonly object cacheLock = new object(); - private readonly ILog log; - - public VersionRegistry(ILog log) - { - this.log = log; - } - - public void SetProvider(ICodexDockerImageProvider provider) - { - this.provider = provider; - } - - public string GetContractsDockerImage() - { - try - { - var codexImage = provider.GetCodexDockerImage(); - return GetContractsDockerImage(codexImage); - } - catch (Exception exc) - { - throw new Exception("Failed to get contracts docker image.", exc); - } - } - - private string GetContractsDockerImage(string codexImage) - { - lock (cacheLock) - { - if (cache.TryGetValue(codexImage, out string? value)) - { - return value; - } - var result = GetContractsImage(codexImage); - cache.Add(codexImage, result); - return result; - } - } - - private string GetContractsImage(string codexImage) - { - var inspectResult = InspectCodexImage(codexImage); - var image = ParseCodexContractsImageName(inspectResult); - log.Log($"From codex docker image '{codexImage}', determined codex-contracts docker image: '{image}'"); - return image; - } - - private string InspectCodexImage(string img) - { - Execute("docker", $"pull {img}"); - return Execute("docker", $"inspect {img}"); - } - - private string ParseCodexContractsImageName(string inspectResult) - { - // It is a nice json structure. But we only need this one line. - // "storage.codex.nim-codex.blockchain-image": "codexstorage/codex-contracts-eth:sha-0bf1385-dist-tests" - var lines = inspectResult.Split('\n', StringSplitOptions.RemoveEmptyEntries); - var line = lines.Single(l => l.Contains("storage.codex.nim-codex.blockchain-image")); - var tokens = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); - return tokens.Last().Replace("\"", "").Trim(); - } - - private string Execute(string cmd, string args) - { - var startInfo = new ProcessStartInfo( - fileName: cmd, - arguments: args - ); - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; - - var process = Process.Start(startInfo); - if (process == null) - { - throw new Exception("Failed to start: " + cmd + args); - } - KillAfterTimeout(process); - - process.WaitForExit(); - return process.StandardOutput.ReadToEnd(); - } - - private void KillAfterTimeout(Process process) - { - // There's a known issue that some docker commands on some platforms - // will fail to stop on their own. This has been known since 2019 and it's not fixed. - // So we will issue a kill to the process ourselves if it exceeds a timeout. - - Task.Run(() => - { - Thread.Sleep(TimeSpan.FromSeconds(30.0)); - - if (process != null && !process.HasExited) - { - process.Kill(); - } - }); - } - } - - internal class ExceptionProvider : ICodexDockerImageProvider - { - public string GetCodexDockerImage() - { - throw new InvalidOperationException("CodexContractsPlugin has not yet received a CodexDockerImageProvider " + - "and so cannot select a compatible contracts docker image."); - } - } -} diff --git a/ProjectPlugins/CodexPlugin/ApiChecker.cs b/ProjectPlugins/CodexPlugin/ApiChecker.cs index 656c6588..4b190b34 100644 --- a/ProjectPlugins/CodexPlugin/ApiChecker.cs +++ b/ProjectPlugins/CodexPlugin/ApiChecker.cs @@ -10,7 +10,7 @@ namespace CodexPlugin public class ApiChecker { // - private const string OpenApiYamlHash = "1A-F7-DF-C3-E1-C6-98-FF-32-20-21-9B-26-40-B0-51-08-35-C2-E7-DB-41-49-93-60-A9-CE-47-B5-AD-3D-A3"; + private const string OpenApiYamlHash = "06-B9-41-E8-C8-6C-DE-01-86-83-F3-9A-E4-AC-E7-30-D9-E6-64-60-E0-21-81-9E-4E-C5-93-77-2C-71-79-14"; private const string OpenApiFilePath = "/codex/openapi.yaml"; private const string DisableEnvironmentVariable = "CODEXPLUGIN_DISABLE_APICHECK"; diff --git a/ProjectPlugins/CodexPlugin/CodexDockerImage.cs b/ProjectPlugins/CodexPlugin/CodexDockerImage.cs index 5ed36c92..bbb09c0b 100644 --- a/ProjectPlugins/CodexPlugin/CodexDockerImage.cs +++ b/ProjectPlugins/CodexPlugin/CodexDockerImage.cs @@ -1,8 +1,6 @@ -using CodexContractsPlugin; - -namespace CodexPlugin +namespace CodexPlugin { - public class CodexDockerImage : ICodexDockerImageProvider + public class CodexDockerImage { private const string DefaultDockerImage = "codexstorage/nim-codex:latest-dist-tests"; diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 277b6101..0c8f4510 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -42,7 +42,6 @@ namespace CodexPlugin public void Awake(IPluginAccess access) { - access.GetPlugin().SetCodexDockerImageProvider(codexDockerImage); } public void Announce() diff --git a/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs b/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs index 263a9e8b..4b9139e6 100644 --- a/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs +++ b/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs @@ -50,8 +50,9 @@ namespace CodexReleaseTests.DataTests var blockTtl = TimeSpan.FromMinutes(1.0); var interval = TimeSpan.FromSeconds(10.0); + var bootstrapNode = StartCodex(); var geth = StartGethNode(s => s.IsMiner()); - var contracts = Ci.StartCodexContracts(geth); + var contracts = Ci.StartCodexContracts(geth, bootstrapNode.Version); var node = StartCodex(s => s .EnableMarketplace(geth, contracts, m => m.WithInitial(100.Eth(), 100.Tst())) .WithBlockTTL(blockTtl) diff --git a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs index e6711e34..a5ec0885 100644 --- a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs @@ -6,7 +6,6 @@ using CodexTests; using DistTestCore; using GethPlugin; using Nethereum.Hex.HexConvertors.Extensions; -using NUnit.Framework; using Utils; namespace CodexReleaseTests.MarketTests @@ -21,14 +20,14 @@ namespace CodexReleaseTests.MarketTests { base.LifecycleStart(lifecycle); var geth = StartGethNode(s => s.IsMiner()); - var contracts = Ci.StartCodexContracts(geth); + var contracts = Ci.StartCodexContracts(geth, BootstrapNode.Version); handles.Add(lifecycle, new MarketplaceHandle(geth, contracts)); } protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result) { - base.LifecycleStop(lifecycle, result); handles.Remove(lifecycle); + base.LifecycleStop(lifecycle, result); } protected IGethNode GetGeth() diff --git a/Tests/ExperimentalTests/AutoBootstrapDistTest.cs b/Tests/ExperimentalTests/AutoBootstrapDistTest.cs index d39c31ec..d8ac6805 100644 --- a/Tests/ExperimentalTests/AutoBootstrapDistTest.cs +++ b/Tests/ExperimentalTests/AutoBootstrapDistTest.cs @@ -1,37 +1,40 @@ using CodexClient; using CodexPlugin; using DistTestCore; -using NUnit.Framework; namespace CodexTests { public class AutoBootstrapDistTest : CodexDistTest { private readonly Dictionary bootstrapNodes = new Dictionary(); + private bool isBooting = false; - [SetUp] - public void SetUpBootstrapNode() + protected override void LifecycleStart(TestLifecycle tl) { - var tl = Get(); + base.LifecycleStart(tl); if (!bootstrapNodes.ContainsKey(tl)) { + isBooting = true; bootstrapNodes.Add(tl, StartCodex(s => s.WithName("BOOTSTRAP_" + tl.TestNamespace))); + isBooting = false; } } - [TearDown] - public void TearDownBootstrapNode() + protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result) { - bootstrapNodes.Remove(Get()); + bootstrapNodes.Remove(lifecycle); + base.LifecycleStop(lifecycle, result); } protected override void OnCodexSetup(ICodexSetup setup) { + if (isBooting) return; + var node = BootstrapNode; if (node != null) setup.WithBootstrapNode(node); } - protected ICodexNode? BootstrapNode + protected ICodexNode BootstrapNode { get { @@ -40,7 +43,7 @@ namespace CodexTests { return node; } - return null; + throw new InvalidOperationException("Bootstrap node not yet started."); } } } diff --git a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs index f8ef52d5..af213342 100644 --- a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs +++ b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs @@ -31,7 +31,7 @@ namespace ExperimentalTests.BasicTests ); var geth = StartGethNode(s => s.IsMiner().WithName("disttest-geth")); - var contracts = Ci.StartCodexContracts(geth); + var contracts = Ci.StartCodexContracts(geth, BootstrapNode.Version); var numberOfHosts = 5; var hosts = StartCodex(numberOfHosts, s => s diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs index ec128154..d0acc991 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs @@ -21,7 +21,7 @@ namespace ExperimentalTests.DownloadConnectivityTests public void MarketplaceDoesNotInterfereWithPeerDownload() { var geth = StartGethNode(s => s.IsMiner()); - var contracts = Ci.StartCodexContracts(geth); + var contracts = Ci.StartCodexContracts(geth, BootstrapNode.Version); var nodes = StartCodex(2, s => s.EnableMarketplace(geth, contracts, m => m .WithInitial(10.Eth(), 1000.TstWei()))); diff --git a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs index 901504c7..60a9be9a 100644 --- a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs +++ b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs @@ -31,7 +31,7 @@ namespace ExperimentalTests.PeerDiscoveryTests public void MarketplaceDoesNotInterfereWithPeerDiscovery() { var geth = StartGethNode(s => s.IsMiner()); - var contracts = Ci.StartCodexContracts(geth); + var contracts = Ci.StartCodexContracts(geth, BootstrapNode.Version); var nodes = StartCodex(2, s => s.EnableMarketplace(geth, contracts, m => m .WithInitial(10.Eth(), 1000.TstWei()))); diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index 753a1371..0d78962e 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -64,8 +64,12 @@ namespace CodexNetDeployer var gethDeployment = DeployGeth(ci); var gethNode = ci.WrapGethDeployment(gethDeployment, new BlockCache()); + var bootNode = ci.StartCodexNode(); + var versionInfo = bootNode.GetDebugInfo().Version; + bootNode.Stop(waitTillStopped: true); + Log("Geth started. Deploying Codex contracts..."); - var contractsDeployment = ci.DeployCodexContracts(gethNode); + var contractsDeployment = ci.DeployCodexContracts(gethNode, versionInfo); var contracts = ci.WrapCodexContractsDeployment(gethNode, contractsDeployment); Log("Codex contracts deployed.");