diff --git a/CodexPlugin/CodexPlugin.csproj b/CodexPlugin/CodexPlugin.csproj
index 4619e26..a00f24f 100644
--- a/CodexPlugin/CodexPlugin.csproj
+++ b/CodexPlugin/CodexPlugin.csproj
@@ -14,6 +14,7 @@
+
diff --git a/CodexPlugin/GethStarter.cs b/CodexPlugin/GethStarter.cs
deleted file mode 100644
index f6381b3..0000000
--- a/CodexPlugin/GethStarter.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-//using DistTestCore.Marketplace;
-
-//namespace CodexPlugin
-//{
-// public class GethStarter : BaseStarter
-// {
-// private readonly MarketplaceNetworkCache marketplaceNetworkCache;
-// private readonly GethCompanionNodeStarter companionNodeStarter;
-
-// public GethStarter(TestLifecycle lifecycle)
-// : base(lifecycle)
-// {
-// marketplaceNetworkCache = new MarketplaceNetworkCache(
-// new GethBootstrapNodeStarter(lifecycle),
-// new CodexContractsStarter(lifecycle));
-// companionNodeStarter = new GethCompanionNodeStarter(lifecycle);
-// }
-
-// public GethStartResult BringOnlineMarketplaceFor(CodexSetup codexSetup)
-// {
-// if (codexSetup.MarketplaceConfig == null) return CreateMarketplaceUnavailableResult();
-
-// var marketplaceNetwork = marketplaceNetworkCache.Get();
-// var companionNode = StartCompanionNode(codexSetup, marketplaceNetwork);
-
-// LogStart("Setting up initial balance...");
-// TransferInitialBalance(marketplaceNetwork, codexSetup.MarketplaceConfig, companionNode);
-// LogEnd($"Initial balance of {codexSetup.MarketplaceConfig.InitialTestTokens} set for {codexSetup.NumberOfNodes} nodes.");
-
-// return CreateGethStartResult(marketplaceNetwork, companionNode);
-// }
-
-// private void TransferInitialBalance(MarketplaceNetwork marketplaceNetwork, MarketplaceInitialConfig marketplaceConfig, GethCompanionNodeInfo companionNode)
-// {
-// if (marketplaceConfig.InitialTestTokens.Amount == 0) return;
-
-// var interaction = marketplaceNetwork.StartInteraction(lifecycle);
-// var tokenAddress = marketplaceNetwork.Marketplace.TokenAddress;
-
-// var accounts = companionNode.Accounts.Select(a => a.Account).ToArray();
-// interaction.MintTestTokens(accounts, marketplaceConfig.InitialTestTokens.Amount, tokenAddress);
-// }
-
-// private GethStartResult CreateGethStartResult(MarketplaceNetwork marketplaceNetwork, GethCompanionNodeInfo companionNode)
-// {
-// return new GethStartResult(CreateMarketplaceAccessFactory(marketplaceNetwork), marketplaceNetwork, companionNode);
-// }
-
-// private GethStartResult CreateMarketplaceUnavailableResult()
-// {
-// return new GethStartResult(new MarketplaceUnavailableAccessFactory(), null!, null!);
-// }
-
-// private IMarketplaceAccessFactory CreateMarketplaceAccessFactory(MarketplaceNetwork marketplaceNetwork)
-// {
-// return new GethMarketplaceAccessFactory(lifecycle, marketplaceNetwork);
-// }
-
-// private GethCompanionNodeInfo StartCompanionNode(CodexSetup codexSetup, MarketplaceNetwork marketplaceNetwork)
-// {
-// return companionNodeStarter.StartCompanionNodeFor(codexSetup, marketplaceNetwork);
-// }
-// }
-
-// public class MarketplaceNetworkCache
-// {
-// private readonly GethBootstrapNodeStarter bootstrapNodeStarter;
-// private readonly CodexContractsStarter codexContractsStarter;
-// private MarketplaceNetwork? network;
-
-// public MarketplaceNetworkCache(GethBootstrapNodeStarter bootstrapNodeStarter, CodexContractsStarter codexContractsStarter)
-// {
-// this.bootstrapNodeStarter = bootstrapNodeStarter;
-// this.codexContractsStarter = codexContractsStarter;
-// }
-
-// public MarketplaceNetwork Get()
-// {
-// if (network == null)
-// {
-// var bootstrapInfo = bootstrapNodeStarter.StartGethBootstrapNode();
-// var marketplaceInfo = codexContractsStarter.Start(bootstrapInfo);
-// network = new MarketplaceNetwork(bootstrapInfo, marketplaceInfo );
-// }
-// return network;
-// }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethBootstrapNodeInfo.cs b/CodexPlugin/Marketplace/GethBootstrapNodeInfo.cs
deleted file mode 100644
index 3e84dec..0000000
--- a/CodexPlugin/Marketplace/GethBootstrapNodeInfo.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-//using KubernetesWorkflow;
-//using NethereumWorkflow;
-
-//namespace DistTestCore.Marketplace
-//{
-// public class GethBootstrapNodeInfo
-// {
-// public GethBootstrapNodeInfo(RunningContainers runningContainers, AllGethAccounts allAccounts, string pubKey, Port discoveryPort)
-// {
-// RunningContainers = runningContainers;
-// AllAccounts = allAccounts;
-// Account = allAccounts.Accounts[0];
-// PubKey = pubKey;
-// DiscoveryPort = discoveryPort;
-// }
-
-// public RunningContainers RunningContainers { get; }
-// public AllGethAccounts AllAccounts { get; }
-// public GethAccount Account { get; }
-// public string PubKey { get; }
-// public Port DiscoveryPort { get; }
-
-// public NethereumInteraction StartInteraction(TestLifecycle lifecycle)
-// {
-// var address = lifecycle.Configuration.GetAddress(RunningContainers.Containers[0]);
-// var account = Account;
-
-// var creator = new NethereumInteractionCreator(lifecycle.Log, address.Host, address.Port, account.PrivateKey);
-// return creator.CreateWorkflow();
-// }
-// }
-
-// public class AllGethAccounts
-// {
-// public GethAccount[] Accounts { get; }
-
-// public AllGethAccounts(GethAccount[] accounts)
-// {
-// Accounts = accounts;
-// }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethBootstrapNodeStarter.cs b/CodexPlugin/Marketplace/GethBootstrapNodeStarter.cs
deleted file mode 100644
index b94d041..0000000
--- a/CodexPlugin/Marketplace/GethBootstrapNodeStarter.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-//using KubernetesWorkflow;
-
-//namespace DistTestCore.Marketplace
-//{
-// public class GethBootstrapNodeStarter : BaseStarter
-// {
-// public GethBootstrapNodeStarter(TestLifecycle lifecycle)
-// : base(lifecycle)
-// {
-// }
-
-// public GethBootstrapNodeInfo StartGethBootstrapNode()
-// {
-// LogStart("Starting Geth bootstrap node...");
-// var startupConfig = CreateBootstrapStartupConfig();
-
-// var workflow = lifecycle.WorkflowCreator.CreateWorkflow();
-// var containers = workflow.Start(1, Location.Unspecified, new GethContainerRecipe(), startupConfig);
-// if (containers.Containers.Length != 1) throw new InvalidOperationException("Expected 1 Geth bootstrap node to be created. Test infra failure.");
-// var bootstrapContainer = containers.Containers[0];
-
-// var extractor = new ContainerInfoExtractor(lifecycle.Log, workflow, bootstrapContainer);
-// var accounts = extractor.ExtractAccounts();
-// var pubKey = extractor.ExtractPubKey();
-// var discoveryPort = bootstrapContainer.Recipe.GetPortByTag(GethContainerRecipe.DiscoveryPortTag);
-// var result = new GethBootstrapNodeInfo(containers, accounts, pubKey, discoveryPort);
-
-// LogEnd($"Geth bootstrap node started with account '{result.Account.Account}'");
-
-// return result;
-// }
-
-// private StartupConfig CreateBootstrapStartupConfig()
-// {
-// var config = new StartupConfig();
-// config.Add(new GethStartupConfig(true, null!, 0, 0));
-// return config;
-// }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethCompanionNodeInfo.cs b/CodexPlugin/Marketplace/GethCompanionNodeInfo.cs
deleted file mode 100644
index 30f2e78..0000000
--- a/CodexPlugin/Marketplace/GethCompanionNodeInfo.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-//using KubernetesWorkflow;
-//using NethereumWorkflow;
-
-//namespace DistTestCore.Marketplace
-//{
-// public class GethCompanionNodeInfo
-// {
-// public GethCompanionNodeInfo(RunningContainer runningContainer, GethAccount[] accounts)
-// {
-// RunningContainer = runningContainer;
-// Accounts = accounts;
-// }
-
-// public RunningContainer RunningContainer { get; }
-// public GethAccount[] Accounts { get; }
-
-// public NethereumInteraction StartInteraction(TestLifecycle lifecycle, GethAccount account)
-// {
-// var address = lifecycle.Configuration.GetAddress(RunningContainer);
-// var privateKey = account.PrivateKey;
-
-// var creator = new NethereumInteractionCreator(lifecycle.Log, address.Host, address.Port, privateKey);
-// return creator.CreateWorkflow();
-// }
-// }
-
-// public class GethAccount
-// {
-// public GethAccount(string account, string privateKey)
-// {
-// Account = account;
-// PrivateKey = privateKey;
-// }
-
-// public string Account { get; }
-// public string PrivateKey { get; }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethCompanionNodeStarter.cs b/CodexPlugin/Marketplace/GethCompanionNodeStarter.cs
deleted file mode 100644
index 9c8a303..0000000
--- a/CodexPlugin/Marketplace/GethCompanionNodeStarter.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-//using KubernetesWorkflow;
-//using Utils;
-
-//namespace DistTestCore.Marketplace
-//{
-// public class GethCompanionNodeStarter : BaseStarter
-// {
-// private int companionAccountIndex = 0;
-
-// public GethCompanionNodeStarter(TestLifecycle lifecycle)
-// : base(lifecycle)
-// {
-// }
-
-// public GethCompanionNodeInfo StartCompanionNodeFor(CodexSetup codexSetup, MarketplaceNetwork marketplace)
-// {
-// LogStart($"Initializing companion for {codexSetup.NumberOfNodes} Codex nodes.");
-
-// var config = CreateCompanionNodeStartupConfig(marketplace.Bootstrap, codexSetup.NumberOfNodes);
-
-// var workflow = lifecycle.WorkflowCreator.CreateWorkflow();
-// var containers = workflow.Start(1, Location.Unspecified, new GethContainerRecipe(), CreateStartupConfig(config));
-// if (containers.Containers.Length != 1) throw new InvalidOperationException("Expected one Geth companion node to be created. Test infra failure.");
-// var container = containers.Containers[0];
-
-// var node = CreateCompanionInfo(container, marketplace, config);
-// EnsureCompanionNodeIsSynced(node, marketplace);
-
-// LogEnd($"Initialized one companion node for {codexSetup.NumberOfNodes} Codex nodes. Their accounts: [{string.Join(",", node.Accounts.Select(a => a.Account))}]");
-// return node;
-// }
-
-// private GethCompanionNodeInfo CreateCompanionInfo(RunningContainer container, MarketplaceNetwork marketplace, GethStartupConfig config)
-// {
-// var accounts = ExtractAccounts(marketplace, config);
-// return new GethCompanionNodeInfo(container, accounts);
-// }
-
-// private static GethAccount[] ExtractAccounts(MarketplaceNetwork marketplace, GethStartupConfig config)
-// {
-// return marketplace.Bootstrap.AllAccounts.Accounts
-// .Skip(1 + config.CompanionAccountStartIndex)
-// .Take(config.NumberOfCompanionAccounts)
-// .ToArray();
-// }
-
-// private void EnsureCompanionNodeIsSynced(GethCompanionNodeInfo node, MarketplaceNetwork marketplace)
-// {
-// try
-// {
-// Time.WaitUntil(() =>
-// {
-// var interaction = node.StartInteraction(lifecycle, node.Accounts.First());
-// return interaction.IsSynced(marketplace.Marketplace.Address, marketplace.Marketplace.Abi);
-// }, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(3));
-// }
-// catch (Exception e)
-// {
-// throw new Exception("Geth companion node did not sync within timeout. Test infra failure.", e);
-// }
-// }
-
-// private GethStartupConfig CreateCompanionNodeStartupConfig(GethBootstrapNodeInfo bootstrapNode, int numberOfAccounts)
-// {
-// var config = new GethStartupConfig(false, bootstrapNode, companionAccountIndex, numberOfAccounts);
-// companionAccountIndex += numberOfAccounts;
-// return config;
-// }
-
-// private StartupConfig CreateStartupConfig(GethStartupConfig gethConfig)
-// {
-// var config = new StartupConfig();
-// config.Add(gethConfig);
-// return config;
-// }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethContainerRecipe.cs b/CodexPlugin/Marketplace/GethContainerRecipe.cs
deleted file mode 100644
index e5b2f9b..0000000
--- a/CodexPlugin/Marketplace/GethContainerRecipe.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-//using KubernetesWorkflow;
-
-//namespace DistTestCore.Marketplace
-//{
-// public class GethContainerRecipe : DefaultContainerRecipe
-// {
-// private const string defaultArgs = "--ipcdisable --syncmode full";
-
-// public const string HttpPortTag = "http_port";
-// public const string DiscoveryPortTag = "disc_port";
-// public const string AccountsFilename = "accounts.csv";
-
-// public override string AppName => "geth";
-// public override string Image => "codexstorage/dist-tests-geth:latest";
-
-// protected override void InitializeRecipe(StartupConfig startupConfig)
-// {
-// var config = startupConfig.Get();
-
-// var args = CreateArgs(config);
-
-// AddEnvVar("GETH_ARGS", args);
-// }
-
-// private string CreateArgs(GethStartupConfig config)
-// {
-// var discovery = AddInternalPort(tag: DiscoveryPortTag);
-
-// if (config.IsBootstrapNode)
-// {
-// return CreateBootstapArgs(discovery);
-// }
-
-// return CreateCompanionArgs(discovery, config);
-// }
-
-// private string CreateBootstapArgs(Port discovery)
-// {
-// AddEnvVar("ENABLE_MINER", "1");
-// UnlockAccounts(0, 1);
-// var exposedPort = AddExposedPort(tag: HttpPortTag);
-// return $"--http.port {exposedPort.Number} --port {discovery.Number} --discovery.port {discovery.Number} {defaultArgs}";
-// }
-
-// private string CreateCompanionArgs(Port discovery, GethStartupConfig config)
-// {
-// UnlockAccounts(
-// config.CompanionAccountStartIndex + 1,
-// config.NumberOfCompanionAccounts);
-
-// var port = AddInternalPort();
-// var authRpc = AddInternalPort();
-// var httpPort = AddExposedPort(tag: HttpPortTag);
-
-// var bootPubKey = config.BootstrapNode.PubKey;
-// var bootIp = config.BootstrapNode.RunningContainers.Containers[0].Pod.PodInfo.Ip;
-// var bootPort = config.BootstrapNode.DiscoveryPort.Number;
-// var bootstrapArg = $"--bootnodes enode://{bootPubKey}@{bootIp}:{bootPort} --nat=extip:{bootIp}";
-
-// return $"--port {port.Number} --discovery.port {discovery.Number} --authrpc.port {authRpc.Number} --http.addr 0.0.0.0 --http.port {httpPort.Number} --ws --ws.addr 0.0.0.0 --ws.port {httpPort.Number} {bootstrapArg} {defaultArgs}";
-// }
-
-// private void UnlockAccounts(int startIndex, int numberOfAccounts)
-// {
-// if (startIndex < 0) throw new ArgumentException();
-// if (numberOfAccounts < 1) throw new ArgumentException();
-// if (startIndex + numberOfAccounts > 1000) throw new ArgumentException("Out of accounts!");
-
-// AddEnvVar("UNLOCK_START_INDEX", startIndex.ToString());
-// AddEnvVar("UNLOCK_NUMBER", numberOfAccounts.ToString());
-// }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethStartResult.cs b/CodexPlugin/Marketplace/GethStartResult.cs
deleted file mode 100644
index f9e1048..0000000
--- a/CodexPlugin/Marketplace/GethStartResult.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-//using Newtonsoft.Json;
-
-//namespace DistTestCore.Marketplace
-//{
-// public class GethStartResult
-// {
-// public GethStartResult(IMarketplaceAccessFactory marketplaceAccessFactory, MarketplaceNetwork marketplaceNetwork, GethCompanionNodeInfo companionNode)
-// {
-// MarketplaceAccessFactory = marketplaceAccessFactory;
-// MarketplaceNetwork = marketplaceNetwork;
-// CompanionNode = companionNode;
-// }
-
-// [JsonIgnore]
-// public IMarketplaceAccessFactory MarketplaceAccessFactory { get; }
-// public MarketplaceNetwork MarketplaceNetwork { get; }
-// public GethCompanionNodeInfo CompanionNode { get; }
-// }
-//}
diff --git a/CodexPlugin/Marketplace/GethStartupConfig.cs b/CodexPlugin/Marketplace/GethStartupConfig.cs
deleted file mode 100644
index 67ad0d5..0000000
--- a/CodexPlugin/Marketplace/GethStartupConfig.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-//namespace DistTestCore.Marketplace
-//{
-// public class GethStartupConfig
-// {
-// public GethStartupConfig(bool isBootstrapNode, GethBootstrapNodeInfo bootstrapNode, int companionAccountStartIndex, int numberOfCompanionAccounts)
-// {
-// IsBootstrapNode = isBootstrapNode;
-// BootstrapNode = bootstrapNode;
-// CompanionAccountStartIndex = companionAccountStartIndex;
-// NumberOfCompanionAccounts = numberOfCompanionAccounts;
-// }
-
-// public bool IsBootstrapNode { get; }
-// public GethBootstrapNodeInfo BootstrapNode { get; }
-// public int CompanionAccountStartIndex { get; }
-// public int NumberOfCompanionAccounts { get; }
-// }
-//}
diff --git a/Core/CoreInterface.cs b/Core/CoreInterface.cs
index 43a66b5..83ea593 100644
--- a/Core/CoreInterface.cs
+++ b/Core/CoreInterface.cs
@@ -31,6 +31,11 @@ namespace Core
return logHandler.DownloadLog();
}
+ public string ExecuteContainerCommand(IHasContainer containerSource, string command, params string[] args)
+ {
+ return ExecuteContainerCommand(containerSource.Container, command, args);
+ }
+
public string ExecuteContainerCommand(RunningContainer container, string command, params string[] args)
{
var workflow = entryPoint.Tools.CreateWorkflow();
diff --git a/GethPlugin/CoreInterfaceExtensions.cs b/GethPlugin/CoreInterfaceExtensions.cs
new file mode 100644
index 0000000..6625d58
--- /dev/null
+++ b/GethPlugin/CoreInterfaceExtensions.cs
@@ -0,0 +1,44 @@
+using Core;
+using KubernetesWorkflow;
+
+namespace GethPlugin
+{
+ public static class CoreInterfaceExtensions
+ {
+ //public static RunningContainers[] StartCodexNodes(this CoreInterface ci, int number, Action setup)
+ //{
+ // return Plugin(ci).StartCodexNodes(number, setup);
+ //}
+
+ //public static ICodexNodeGroup WrapCodexContainers(this CoreInterface ci, RunningContainers[] containers)
+ //{
+ // return Plugin(ci).WrapCodexContainers(containers);
+ //}
+
+ //public static IOnlineCodexNode SetupCodexNode(this CoreInterface ci)
+ //{
+ // return ci.SetupCodexNodes(1)[0];
+ //}
+
+ //public static IOnlineCodexNode SetupCodexNode(this CoreInterface ci, Action setup)
+ //{
+ // return ci.SetupCodexNodes(1, setup)[0];
+ //}
+
+ //public static ICodexNodeGroup SetupCodexNodes(this CoreInterface ci, int number, Action setup)
+ //{
+ // var rc = ci.StartCodexNodes(number, setup);
+ // return ci.WrapCodexContainers(rc);
+ //}
+
+ //public static ICodexNodeGroup SetupCodexNodes(this CoreInterface ci, int number)
+ //{
+ // return ci.SetupCodexNodes(number, s => { });
+ //}
+
+ //private static CodexPlugin Plugin(CoreInterface ci)
+ //{
+ // return ci.GetPlugin();
+ //}
+ }
+}
diff --git a/GethPlugin/GethBootstrapNodeInfo.cs b/GethPlugin/GethBootstrapNodeInfo.cs
new file mode 100644
index 0000000..8dffbc9
--- /dev/null
+++ b/GethPlugin/GethBootstrapNodeInfo.cs
@@ -0,0 +1,42 @@
+using KubernetesWorkflow;
+using NethereumWorkflow;
+
+namespace GethPlugin
+{
+ public class GethBootstrapNodeInfo
+ {
+ public GethBootstrapNodeInfo(RunningContainers runningContainers, AllGethAccounts allAccounts, string pubKey, Port discoveryPort)
+ {
+ RunningContainers = runningContainers;
+ AllAccounts = allAccounts;
+ Account = allAccounts.Accounts[0];
+ PubKey = pubKey;
+ DiscoveryPort = discoveryPort;
+ }
+
+ public RunningContainers RunningContainers { get; }
+ public AllGethAccounts AllAccounts { get; }
+ public GethAccount Account { get; }
+ public string PubKey { get; }
+ public Port DiscoveryPort { get; }
+
+ public NethereumInteraction StartInteraction(TestLifecycle lifecycle)
+ {
+ var address = lifecycle.Configuration.GetAddress(RunningContainers.Containers[0]);
+ var account = Account;
+
+ var creator = new NethereumInteractionCreator(lifecycle.Log, address.Host, address.Port, account.PrivateKey);
+ return creator.CreateWorkflow();
+ }
+ }
+
+ public class AllGethAccounts
+ {
+ public GethAccount[] Accounts { get; }
+
+ public AllGethAccounts(GethAccount[] accounts)
+ {
+ Accounts = accounts;
+ }
+ }
+}
diff --git a/GethPlugin/GethBootstrapNodeStarter.cs b/GethPlugin/GethBootstrapNodeStarter.cs
new file mode 100644
index 0000000..b18292a
--- /dev/null
+++ b/GethPlugin/GethBootstrapNodeStarter.cs
@@ -0,0 +1,35 @@
+using KubernetesWorkflow;
+
+namespace GethPlugin
+{
+ public class GethBootstrapNodeStarter
+ {
+ public GethBootstrapNodeInfo StartGethBootstrapNode()
+ {
+ LogStart("Starting Geth bootstrap node...");
+ var startupConfig = CreateBootstrapStartupConfig();
+
+ var workflow = lifecycle.WorkflowCreator.CreateWorkflow();
+ var containers = workflow.Start(1, Location.Unspecified, new GethContainerRecipe(), startupConfig);
+ if (containers.Containers.Length != 1) throw new InvalidOperationException("Expected 1 Geth bootstrap node to be created. Test infra failure.");
+ var bootstrapContainer = containers.Containers[0];
+
+ var extractor = new ContainerInfoExtractor(lifecycle.Log, workflow, bootstrapContainer);
+ var accounts = extractor.ExtractAccounts();
+ var pubKey = extractor.ExtractPubKey();
+ var discoveryPort = bootstrapContainer.Recipe.GetPortByTag(GethContainerRecipe.DiscoveryPortTag);
+ var result = new GethBootstrapNodeInfo(containers, accounts, pubKey, discoveryPort);
+
+ LogEnd($"Geth bootstrap node started with account '{result.Account.Account}'");
+
+ return result;
+ }
+
+ private StartupConfig CreateBootstrapStartupConfig()
+ {
+ var config = new StartupConfig();
+ config.Add(new GethStartupConfig(true, null!, 0, 0));
+ return config;
+ }
+ }
+}
diff --git a/GethPlugin/GethCompanionNodeInfo.cs b/GethPlugin/GethCompanionNodeInfo.cs
new file mode 100644
index 0000000..313271c
--- /dev/null
+++ b/GethPlugin/GethCompanionNodeInfo.cs
@@ -0,0 +1,38 @@
+using KubernetesWorkflow;
+using NethereumWorkflow;
+
+namespace GethPlugin
+{
+ public class GethCompanionNodeInfo
+ {
+ public GethCompanionNodeInfo(RunningContainer runningContainer, GethAccount[] accounts)
+ {
+ RunningContainer = runningContainer;
+ Accounts = accounts;
+ }
+
+ public RunningContainer RunningContainer { get; }
+ public GethAccount[] Accounts { get; }
+
+ public NethereumInteraction StartInteraction(TestLifecycle lifecycle, GethAccount account)
+ {
+ var address = lifecycle.Configuration.GetAddress(RunningContainer);
+ var privateKey = account.PrivateKey;
+
+ var creator = new NethereumInteractionCreator(lifecycle.Log, address.Host, address.Port, privateKey);
+ return creator.CreateWorkflow();
+ }
+ }
+
+ public class GethAccount
+ {
+ public GethAccount(string account, string privateKey)
+ {
+ Account = account;
+ PrivateKey = privateKey;
+ }
+
+ public string Account { get; }
+ public string PrivateKey { get; }
+ }
+}
diff --git a/GethPlugin/GethCompanionNodeStarter.cs b/GethPlugin/GethCompanionNodeStarter.cs
new file mode 100644
index 0000000..998e8f2
--- /dev/null
+++ b/GethPlugin/GethCompanionNodeStarter.cs
@@ -0,0 +1,72 @@
+using KubernetesWorkflow;
+using Utils;
+
+namespace GethPlugin
+{
+ public class GethCompanionNodeStarter
+ {
+ private int companionAccountIndex = 0;
+
+ public GethCompanionNodeInfo StartCompanionNodeFor(CodexSetup codexSetup, MarketplaceNetwork marketplace)
+ {
+ LogStart($"Initializing companion for {codexSetup.NumberOfNodes} Codex nodes.");
+
+ var config = CreateCompanionNodeStartupConfig(marketplace.Bootstrap, codexSetup.NumberOfNodes);
+
+ var workflow = lifecycle.WorkflowCreator.CreateWorkflow();
+ var containers = workflow.Start(1, Location.Unspecified, new GethContainerRecipe(), CreateStartupConfig(config));
+ if (containers.Containers.Length != 1) throw new InvalidOperationException("Expected one Geth companion node to be created. Test infra failure.");
+ var container = containers.Containers[0];
+
+ var node = CreateCompanionInfo(container, marketplace, config);
+ EnsureCompanionNodeIsSynced(node, marketplace);
+
+ LogEnd($"Initialized one companion node for {codexSetup.NumberOfNodes} Codex nodes. Their accounts: [{string.Join(",", node.Accounts.Select(a => a.Account))}]");
+ return node;
+ }
+
+ private GethCompanionNodeInfo CreateCompanionInfo(RunningContainer container, MarketplaceNetwork marketplace, GethStartupConfig config)
+ {
+ var accounts = ExtractAccounts(marketplace, config);
+ return new GethCompanionNodeInfo(container, accounts);
+ }
+
+ private static GethAccount[] ExtractAccounts(MarketplaceNetwork marketplace, GethStartupConfig config)
+ {
+ return marketplace.Bootstrap.AllAccounts.Accounts
+ .Skip(1 + config.CompanionAccountStartIndex)
+ .Take(config.NumberOfCompanionAccounts)
+ .ToArray();
+ }
+
+ private void EnsureCompanionNodeIsSynced(GethCompanionNodeInfo node, MarketplaceNetwork marketplace)
+ {
+ try
+ {
+ Time.WaitUntil(() =>
+ {
+ var interaction = node.StartInteraction(lifecycle, node.Accounts.First());
+ return interaction.IsSynced(marketplace.Marketplace.Address, marketplace.Marketplace.Abi);
+ }, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(3));
+ }
+ catch (Exception e)
+ {
+ throw new Exception("Geth companion node did not sync within timeout. Test infra failure.", e);
+ }
+ }
+
+ private GethStartupConfig CreateCompanionNodeStartupConfig(GethBootstrapNodeInfo bootstrapNode, int numberOfAccounts)
+ {
+ var config = new GethStartupConfig(false, bootstrapNode, companionAccountIndex, numberOfAccounts);
+ companionAccountIndex += numberOfAccounts;
+ return config;
+ }
+
+ private StartupConfig CreateStartupConfig(GethStartupConfig gethConfig)
+ {
+ var config = new StartupConfig();
+ config.Add(gethConfig);
+ return config;
+ }
+ }
+}
diff --git a/GethPlugin/GethContainerRecipe.cs b/GethPlugin/GethContainerRecipe.cs
new file mode 100644
index 0000000..1c1b2e1
--- /dev/null
+++ b/GethPlugin/GethContainerRecipe.cs
@@ -0,0 +1,73 @@
+using KubernetesWorkflow;
+
+namespace GethPlugin
+{
+ public class GethContainerRecipe : ContainerRecipeFactory
+ {
+ private const string defaultArgs = "--ipcdisable --syncmode full";
+
+ public const string HttpPortTag = "http_port";
+ public const string DiscoveryPortTag = "disc_port";
+ public const string AccountsFilename = "accounts.csv";
+
+ public override string AppName => "geth";
+ public override string Image => "codexstorage/dist-tests-geth:latest";
+
+ protected override void Initialize(StartupConfig startupConfig)
+ {
+ var config = startupConfig.Get();
+
+ var args = CreateArgs(config);
+
+ AddEnvVar("GETH_ARGS", args);
+ }
+
+ private string CreateArgs(GethStartupConfig config)
+ {
+ var discovery = AddInternalPort(tag: DiscoveryPortTag);
+
+ if (config.IsBootstrapNode)
+ {
+ return CreateBootstapArgs(discovery);
+ }
+
+ return CreateCompanionArgs(discovery, config);
+ }
+
+ private string CreateBootstapArgs(Port discovery)
+ {
+ AddEnvVar("ENABLE_MINER", "1");
+ UnlockAccounts(0, 1);
+ var exposedPort = AddExposedPort(tag: HttpPortTag);
+ return $"--http.port {exposedPort.Number} --port {discovery.Number} --discovery.port {discovery.Number} {defaultArgs}";
+ }
+
+ private string CreateCompanionArgs(Port discovery, GethStartupConfig config)
+ {
+ UnlockAccounts(
+ config.CompanionAccountStartIndex + 1,
+ config.NumberOfCompanionAccounts);
+
+ var port = AddInternalPort();
+ var authRpc = AddInternalPort();
+ var httpPort = AddExposedPort(tag: HttpPortTag);
+
+ var bootPubKey = config.BootstrapNode.PubKey;
+ var bootIp = config.BootstrapNode.RunningContainers.Containers[0].Pod.PodInfo.Ip;
+ var bootPort = config.BootstrapNode.DiscoveryPort.Number;
+ var bootstrapArg = $"--bootnodes enode://{bootPubKey}@{bootIp}:{bootPort} --nat=extip:{bootIp}";
+
+ return $"--port {port.Number} --discovery.port {discovery.Number} --authrpc.port {authRpc.Number} --http.addr 0.0.0.0 --http.port {httpPort.Number} --ws --ws.addr 0.0.0.0 --ws.port {httpPort.Number} {bootstrapArg} {defaultArgs}";
+ }
+
+ private void UnlockAccounts(int startIndex, int numberOfAccounts)
+ {
+ if (startIndex < 0) throw new ArgumentException();
+ if (numberOfAccounts < 1) throw new ArgumentException();
+ if (startIndex + numberOfAccounts > 1000) throw new ArgumentException("Out of accounts!");
+
+ AddEnvVar("UNLOCK_START_INDEX", startIndex.ToString());
+ AddEnvVar("UNLOCK_NUMBER", numberOfAccounts.ToString());
+ }
+ }
+}
diff --git a/GethPlugin/GethPlugin.cs b/GethPlugin/GethPlugin.cs
new file mode 100644
index 0000000..fcd15c2
--- /dev/null
+++ b/GethPlugin/GethPlugin.cs
@@ -0,0 +1,45 @@
+using Core;
+using KubernetesWorkflow;
+
+namespace GethPlugin
+{
+ public class GethPlugin : IProjectPlugin, IHasLogPrefix, IHasMetadata
+ {
+ private readonly IPluginTools tools;
+
+ public GethPlugin(IPluginTools tools)
+ {
+ //codexStarter = new CodexStarter(tools);
+ this.tools = tools;
+ }
+
+ public string LogPrefix => "(Geth) ";
+
+ public void Announce()
+ {
+ //tools.GetLog().Log($"Loaded with Codex ID: '{codexStarter.GetCodexId()}'");
+ }
+
+ public void AddMetadata(IAddMetadata metadata)
+ {
+ //metadata.Add("codexid", codexStarter.GetCodexId());
+ }
+
+ public void Decommission()
+ {
+ }
+
+ //public RunningContainers[] StartCodexNodes(int numberOfNodes, Action setup)
+ //{
+ // var codexSetup = new CodexSetup(numberOfNodes);
+ // codexSetup.LogLevel = defaultLogLevel;
+ // setup(codexSetup);
+ // return codexStarter.BringOnline(codexSetup);
+ //}
+
+ //public ICodexNodeGroup WrapCodexContainers(RunningContainers[] containers)
+ //{
+ // return codexStarter.WrapCodexContainers(containers);
+ //}
+ }
+}
diff --git a/GethPlugin/GethPlugin.csproj b/GethPlugin/GethPlugin.csproj
new file mode 100644
index 0000000..3b52869
--- /dev/null
+++ b/GethPlugin/GethPlugin.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/GethPlugin/GethStartResult.cs b/GethPlugin/GethStartResult.cs
new file mode 100644
index 0000000..c098870
--- /dev/null
+++ b/GethPlugin/GethStartResult.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+
+namespace GethPlugin
+{
+ public class GethStartResult
+ {
+ public GethStartResult(IMarketplaceAccessFactory marketplaceAccessFactory, MarketplaceNetwork marketplaceNetwork, GethCompanionNodeInfo companionNode)
+ {
+ MarketplaceAccessFactory = marketplaceAccessFactory;
+ MarketplaceNetwork = marketplaceNetwork;
+ CompanionNode = companionNode;
+ }
+
+ [JsonIgnore]
+ public IMarketplaceAccessFactory MarketplaceAccessFactory { get; }
+ public MarketplaceNetwork MarketplaceNetwork { get; }
+ public GethCompanionNodeInfo CompanionNode { get; }
+ }
+}
diff --git a/GethPlugin/GethStarter.cs b/GethPlugin/GethStarter.cs
new file mode 100644
index 0000000..f9e2162
--- /dev/null
+++ b/GethPlugin/GethStarter.cs
@@ -0,0 +1,86 @@
+namespace CodexPlugin
+{
+ public class GethStarter
+ {
+ private readonly MarketplaceNetworkCache marketplaceNetworkCache;
+ private readonly GethCompanionNodeStarter companionNodeStarter;
+
+ public GethStarter(TestLifecycle lifecycle)
+ : base(lifecycle)
+ {
+ marketplaceNetworkCache = new MarketplaceNetworkCache(
+ new GethBootstrapNodeStarter(lifecycle),
+ new CodexContractsStarter(lifecycle));
+ companionNodeStarter = new GethCompanionNodeStarter(lifecycle);
+ }
+
+ public GethStartResult BringOnlineMarketplaceFor(CodexSetup codexSetup)
+ {
+ if (codexSetup.MarketplaceConfig == null) return CreateMarketplaceUnavailableResult();
+
+ var marketplaceNetwork = marketplaceNetworkCache.Get();
+ var companionNode = StartCompanionNode(codexSetup, marketplaceNetwork);
+
+ LogStart("Setting up initial balance...");
+ TransferInitialBalance(marketplaceNetwork, codexSetup.MarketplaceConfig, companionNode);
+ LogEnd($"Initial balance of {codexSetup.MarketplaceConfig.InitialTestTokens} set for {codexSetup.NumberOfNodes} nodes.");
+
+ return CreateGethStartResult(marketplaceNetwork, companionNode);
+ }
+
+ private void TransferInitialBalance(MarketplaceNetwork marketplaceNetwork, MarketplaceInitialConfig marketplaceConfig, GethCompanionNodeInfo companionNode)
+ {
+ if (marketplaceConfig.InitialTestTokens.Amount == 0) return;
+
+ var interaction = marketplaceNetwork.StartInteraction(lifecycle);
+ var tokenAddress = marketplaceNetwork.Marketplace.TokenAddress;
+
+ var accounts = companionNode.Accounts.Select(a => a.Account).ToArray();
+ interaction.MintTestTokens(accounts, marketplaceConfig.InitialTestTokens.Amount, tokenAddress);
+ }
+
+ private GethStartResult CreateGethStartResult(MarketplaceNetwork marketplaceNetwork, GethCompanionNodeInfo companionNode)
+ {
+ return new GethStartResult(CreateMarketplaceAccessFactory(marketplaceNetwork), marketplaceNetwork, companionNode);
+ }
+
+ private GethStartResult CreateMarketplaceUnavailableResult()
+ {
+ return new GethStartResult(new MarketplaceUnavailableAccessFactory(), null!, null!);
+ }
+
+ private IMarketplaceAccessFactory CreateMarketplaceAccessFactory(MarketplaceNetwork marketplaceNetwork)
+ {
+ return new GethMarketplaceAccessFactory(lifecycle, marketplaceNetwork);
+ }
+
+ private GethCompanionNodeInfo StartCompanionNode(CodexSetup codexSetup, MarketplaceNetwork marketplaceNetwork)
+ {
+ return companionNodeStarter.StartCompanionNodeFor(codexSetup, marketplaceNetwork);
+ }
+ }
+
+ public class MarketplaceNetworkCache
+ {
+ private readonly GethBootstrapNodeStarter bootstrapNodeStarter;
+ private readonly CodexContractsStarter codexContractsStarter;
+ private MarketplaceNetwork? network;
+
+ public MarketplaceNetworkCache(GethBootstrapNodeStarter bootstrapNodeStarter, CodexContractsStarter codexContractsStarter)
+ {
+ this.bootstrapNodeStarter = bootstrapNodeStarter;
+ this.codexContractsStarter = codexContractsStarter;
+ }
+
+ public MarketplaceNetwork Get()
+ {
+ if (network == null)
+ {
+ var bootstrapInfo = bootstrapNodeStarter.StartGethBootstrapNode();
+ var marketplaceInfo = codexContractsStarter.Start(bootstrapInfo);
+ network = new MarketplaceNetwork(bootstrapInfo, marketplaceInfo);
+ }
+ return network;
+ }
+ }
+}
diff --git a/GethPlugin/GethStartupConfig.cs b/GethPlugin/GethStartupConfig.cs
new file mode 100644
index 0000000..d662a42
--- /dev/null
+++ b/GethPlugin/GethStartupConfig.cs
@@ -0,0 +1,18 @@
+namespace GethPlugin
+{
+ public class GethStartupConfig
+ {
+ public GethStartupConfig(bool isBootstrapNode, GethBootstrapNodeInfo bootstrapNode, int companionAccountStartIndex, int numberOfCompanionAccounts)
+ {
+ IsBootstrapNode = isBootstrapNode;
+ BootstrapNode = bootstrapNode;
+ CompanionAccountStartIndex = companionAccountStartIndex;
+ NumberOfCompanionAccounts = numberOfCompanionAccounts;
+ }
+
+ public bool IsBootstrapNode { get; }
+ public GethBootstrapNodeInfo BootstrapNode { get; }
+ public int CompanionAccountStartIndex { get; }
+ public int NumberOfCompanionAccounts { get; }
+ }
+}
diff --git a/cs-codex-dist-testing.sln b/cs-codex-dist-testing.sln
index 4065079..d0e90d0 100644
--- a/cs-codex-dist-testing.sln
+++ b/cs-codex-dist-testing.sln
@@ -29,7 +29,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexPlugin", "CodexPlugin\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{F2BF34B3-C660-43EF-BD42-BC5C60237FC4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetricsPlugin", "MetricsPlugin\MetricsPlugin.csproj", "{FCC74AF1-463D-4E5A-9FE7-B4A13F7C8820}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MetricsPlugin", "MetricsPlugin\MetricsPlugin.csproj", "{FCC74AF1-463D-4E5A-9FE7-B4A13F7C8820}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GethPlugin", "GethPlugin\GethPlugin.csproj", "{5A1EF1DD-9E81-4501-B44C-493C72D2B166}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -93,6 +95,10 @@ Global
{FCC74AF1-463D-4E5A-9FE7-B4A13F7C8820}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCC74AF1-463D-4E5A-9FE7-B4A13F7C8820}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCC74AF1-463D-4E5A-9FE7-B4A13F7C8820}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A1EF1DD-9E81-4501-B44C-493C72D2B166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5A1EF1DD-9E81-4501-B44C-493C72D2B166}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A1EF1DD-9E81-4501-B44C-493C72D2B166}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5A1EF1DD-9E81-4501-B44C-493C72D2B166}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE