mirror of
https://github.com/logos-storage/logos-storage-nim-cs-dist-tests.git
synced 2026-01-07 07:53:05 +00:00
Implements autoclient with image generator
This commit is contained in:
parent
a820788c7d
commit
4c75cebcd6
@ -1,118 +0,0 @@
|
||||
using CodexOpenApi;
|
||||
using CodexPlugin;
|
||||
using Core;
|
||||
using Utils;
|
||||
using DebugInfo = CodexPlugin.DebugInfo;
|
||||
|
||||
namespace AutoClient
|
||||
{
|
||||
public class Codex
|
||||
{
|
||||
private readonly IPluginTools tools;
|
||||
private readonly Address address;
|
||||
private readonly Mapper mapper = new Mapper();
|
||||
|
||||
/// <summary>
|
||||
/// This class was largely copied from CodexAccess in CodexPlugin.
|
||||
/// Should really be generalized so CodexPlugin supports talking to custom Codex instances.
|
||||
/// </summary>
|
||||
public Codex(IPluginTools tools, Address address)
|
||||
{
|
||||
this.tools = tools;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public DebugInfo GetDebugInfo()
|
||||
{
|
||||
return mapper.Map(OnCodex(api => api.GetDebugInfoAsync()));
|
||||
}
|
||||
|
||||
public DebugPeer GetDebugPeer(string peerId)
|
||||
{
|
||||
// Cannot use openAPI: debug/peer endpoint is not specified there.
|
||||
var endpoint = GetEndpoint();
|
||||
var str = endpoint.HttpGetString($"debug/peer/{peerId}");
|
||||
|
||||
if (str.ToLowerInvariant() == "unable to find peer!")
|
||||
{
|
||||
return new DebugPeer
|
||||
{
|
||||
IsPeerFound = false
|
||||
};
|
||||
}
|
||||
|
||||
var result = endpoint.Deserialize<DebugPeer>(str);
|
||||
result.IsPeerFound = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ConnectToPeer(string peerId, string[] peerMultiAddresses)
|
||||
{
|
||||
OnCodex(api =>
|
||||
{
|
||||
Time.Wait(api.ConnectPeerAsync(peerId, peerMultiAddresses));
|
||||
return Task.FromResult(string.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
public string UploadFile(FileStream fileStream)
|
||||
{
|
||||
return OnCodex(api => api.UploadAsync(fileStream));
|
||||
}
|
||||
|
||||
public Stream DownloadFile(string contentId)
|
||||
{
|
||||
var fileResponse = OnCodex(api => api.DownloadNetworkAsync(contentId));
|
||||
if (fileResponse.StatusCode != 200) throw new Exception("Download failed with StatusCode: " + fileResponse.StatusCode);
|
||||
return fileResponse.Stream;
|
||||
}
|
||||
|
||||
public LocalDatasetList LocalFiles()
|
||||
{
|
||||
return mapper.Map(OnCodex(api => api.ListDataAsync()));
|
||||
}
|
||||
|
||||
public StorageAvailability SalesAvailability(StorageAvailability request)
|
||||
{
|
||||
var body = mapper.Map(request);
|
||||
var read = OnCodex<SalesAvailabilityREAD>(api => api.OfferStorageAsync(body));
|
||||
return mapper.Map(read);
|
||||
}
|
||||
|
||||
public string RequestStorage(StoragePurchaseRequest request)
|
||||
{
|
||||
var body = mapper.Map(request);
|
||||
return OnCodex<string>(api => api.CreateStorageRequestAsync(request.ContentId.Id, body));
|
||||
}
|
||||
|
||||
public StoragePurchase GetPurchaseStatus(string purchaseId)
|
||||
{
|
||||
return mapper.Map(OnCodex(api => api.GetPurchaseAsync(purchaseId)));
|
||||
}
|
||||
|
||||
public string GetPurchaseStatusRaw(string purchaseId)
|
||||
{
|
||||
var endpoint = GetEndpoint();
|
||||
return endpoint.HttpGetString($"storage/purchases/{purchaseId}");
|
||||
}
|
||||
|
||||
private T OnCodex<T>(Func<CodexApi, Task<T>> action)
|
||||
{
|
||||
var result = tools.CreateHttp()
|
||||
.OnClient(client =>
|
||||
{
|
||||
var api = new CodexApi(client);
|
||||
api.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
|
||||
return Time.Wait(action(api));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEndpoint GetEndpoint()
|
||||
{
|
||||
return tools
|
||||
.CreateHttp()
|
||||
.CreateEndpoint(address, "/api/codex/v1/");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,9 +19,6 @@ namespace AutoClient
|
||||
[Uniform("contract-expiry", "ce", "CONTRACTEXPIRY", false, "contract expiry in minutes. (default 15)")]
|
||||
public int ContractExpiryMinutes { get; set; } = 15;
|
||||
|
||||
[Uniform("dataset-size", "ds", "DATASETSIZE", false, "Total dataset size in bytes. (default 10MB).")]
|
||||
public int DatasetSizeBytes { get; set; } = 10 * 1024 * 1024;
|
||||
|
||||
[Uniform("num-hosts", "nh", "NUMHOSTS", false, "Number of hosts for contract. (default 5)")]
|
||||
public int NumHosts { get; set; } = 5;
|
||||
|
||||
|
||||
17
Tools/AutoClient/ImageGenerator.cs
Normal file
17
Tools/AutoClient/ImageGenerator.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace AutoClient
|
||||
{
|
||||
public class ImageGenerator
|
||||
{
|
||||
public async Task<string> GenerateImage()
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
var thing = await httpClient.GetStreamAsync("https://picsum.photos/3840/2160");
|
||||
|
||||
var filename = $"{Guid.NewGuid().ToString().ToLowerInvariant()}.jpg";
|
||||
using var file = File.OpenWrite(filename);
|
||||
await thing.CopyToAsync(file);
|
||||
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
using ArgsUniform;
|
||||
using AutoClient;
|
||||
using CodexPlugin;
|
||||
using CodexOpenApi;
|
||||
using Core;
|
||||
using Logging;
|
||||
using static Org.BouncyCastle.Math.EC.ECCurve;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var cancellationToken = cts.Token;
|
||||
Console.CancelKeyPress += (sender, args) => cts.Cancel();
|
||||
@ -28,24 +28,26 @@ public static class Program
|
||||
|
||||
log.Log($"Start. Address: {address}");
|
||||
|
||||
var tools = CreateTools(log, config);
|
||||
var fileManager = tools.GetFileManager();
|
||||
var codex = new Codex(tools, address);
|
||||
var imgGenerator = new ImageGenerator();
|
||||
|
||||
CheckCodex(codex, log);
|
||||
var client = new HttpClient();
|
||||
var codex = new CodexApi(client);
|
||||
codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
|
||||
|
||||
var runner = new Runner(log, codex, fileManager, cancellationToken, config);
|
||||
runner.Run();
|
||||
await CheckCodex(codex, log);
|
||||
|
||||
var runner = new Runner(log, client, address, codex, cancellationToken, config, imgGenerator);
|
||||
await runner.Run();
|
||||
|
||||
log.Log("Done.");
|
||||
}
|
||||
|
||||
private static void CheckCodex(Codex codex, ILog log)
|
||||
private static async Task CheckCodex(CodexApi codex, ILog log)
|
||||
{
|
||||
log.Log("Checking Codex...");
|
||||
try
|
||||
{
|
||||
var info = codex.GetDebugInfo();
|
||||
var info = await codex.GetDebugInfoAsync();
|
||||
if (string.IsNullOrEmpty(info.Id)) throw new Exception("Failed to fetch Codex node id");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using CodexContractsPlugin;
|
||||
using CodexOpenApi;
|
||||
using CodexPlugin;
|
||||
using FileUtils;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Utils;
|
||||
|
||||
namespace AutoClient
|
||||
@ -9,21 +9,25 @@ namespace AutoClient
|
||||
public class Runner
|
||||
{
|
||||
private readonly ILog log;
|
||||
private readonly Codex codex;
|
||||
private readonly IFileManager fileManager;
|
||||
private readonly HttpClient client;
|
||||
private readonly Address address;
|
||||
private readonly CodexApi codex;
|
||||
private readonly CancellationToken ct;
|
||||
private readonly Configuration config;
|
||||
private readonly ImageGenerator generator;
|
||||
|
||||
public Runner(ILog log, Codex codex, IFileManager fileManager, CancellationToken ct, Configuration config)
|
||||
public Runner(ILog log, HttpClient client, Address address, CodexApi codex, CancellationToken ct, Configuration config, ImageGenerator generator)
|
||||
{
|
||||
this.log = log;
|
||||
this.client = client;
|
||||
this.address = address;
|
||||
this.codex = codex;
|
||||
this.fileManager = fileManager;
|
||||
this.ct = ct;
|
||||
this.config = config;
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public async Task Run()
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
@ -31,10 +35,7 @@ namespace AutoClient
|
||||
|
||||
try
|
||||
{
|
||||
fileManager.ScopedFiles(() =>
|
||||
{
|
||||
DoRun();
|
||||
});
|
||||
await DoRun();
|
||||
|
||||
log.Log("Run succcessful.");
|
||||
}
|
||||
@ -42,32 +43,32 @@ namespace AutoClient
|
||||
{
|
||||
log.Error("Exception during run: " + ex);
|
||||
}
|
||||
|
||||
FixedShortDelay();
|
||||
|
||||
await FixedShortDelay();
|
||||
}
|
||||
}
|
||||
|
||||
private void DoRun()
|
||||
private async Task DoRun()
|
||||
{
|
||||
var file = CreateFile();
|
||||
var cid = UploadFile(file);
|
||||
var pid = RequestStorage(cid);
|
||||
WaitUntilStarted(pid);
|
||||
var file = await CreateFile();
|
||||
var cid = await UploadFile(file);
|
||||
var pid = await RequestStorage(cid);
|
||||
await WaitUntilStarted(pid);
|
||||
}
|
||||
|
||||
private TrackedFile CreateFile()
|
||||
private async Task<string> CreateFile()
|
||||
{
|
||||
return fileManager.GenerateFile(new ByteSize(Convert.ToInt64(config.DatasetSizeBytes)));
|
||||
return await generator.GenerateImage();
|
||||
}
|
||||
|
||||
private ContentId UploadFile(TrackedFile file)
|
||||
private async Task<ContentId> UploadFile(string filename)
|
||||
{
|
||||
// Copied from CodexNode :/
|
||||
using var fileStream = File.OpenRead(file.Filename);
|
||||
using var fileStream = File.OpenRead(filename);
|
||||
|
||||
var logMessage = $"Uploading file {file.Describe()}...";
|
||||
var logMessage = $"Uploading file {filename}...";
|
||||
log.Log(logMessage);
|
||||
var response = codex.UploadFile(fileStream);
|
||||
var response = await codex.UploadAsync(fileStream, ct);
|
||||
|
||||
if (string.IsNullOrEmpty(response)) FrameworkAssert.Fail("Received empty response.");
|
||||
if (response.StartsWith("Unable to store block")) FrameworkAssert.Fail("Node failed to store block.");
|
||||
@ -76,22 +77,44 @@ namespace AutoClient
|
||||
return new ContentId(response);
|
||||
}
|
||||
|
||||
private string RequestStorage(ContentId cid)
|
||||
private async Task<string> RequestStorage(ContentId cid)
|
||||
{
|
||||
var request = new StoragePurchaseRequest(cid)
|
||||
log.Log("Requesting storage for " + cid.Id);
|
||||
var result = await codex.CreateStorageRequestAsync(cid.Id, new StorageRequestCreation()
|
||||
{
|
||||
PricePerSlotPerSecond = config.Price.TestTokens(),
|
||||
RequiredCollateral = config.RequiredCollateral.TestTokens(),
|
||||
MinRequiredNumberOfNodes = Convert.ToUInt32(config.NumHosts),
|
||||
NodeFailureTolerance = Convert.ToUInt32(config.HostTolerance),
|
||||
Duration = TimeSpan.FromMinutes(config.ContractDurationMinutes),
|
||||
Expiry = TimeSpan.FromMinutes(config.ContractExpiryMinutes)
|
||||
};
|
||||
request.Log(log);
|
||||
return codex.RequestStorage(request);
|
||||
Collateral = config.RequiredCollateral.ToString(),
|
||||
Duration = (config.ContractDurationMinutes * 60).ToString(),
|
||||
Expiry = (config.ContractExpiryMinutes * 60).ToString(),
|
||||
Nodes = config.NumHosts,
|
||||
Reward = config.Price.ToString(),
|
||||
ProofProbability = "15",
|
||||
Tolerance = config.HostTolerance
|
||||
}, ct);
|
||||
|
||||
log.Log("Response: " + result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void WaitUntilStarted(string pid)
|
||||
private async Task<string?> GetPurchaseState(string pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
// openapi still don't match code.
|
||||
var str = await client.GetStringAsync($"{address.Host}:{address.Port}/api/codex/v1/storage/purchases/{pid}");
|
||||
if (string.IsNullOrEmpty(str)) return null;
|
||||
var sp = JsonConvert.DeserializeObject<StoragePurchase>(str)!;
|
||||
log.Log($"Purchase {pid} is {sp.State}");
|
||||
if (!string.IsNullOrEmpty(sp.Error)) log.Log($"Purchase {pid} error is {sp.Error}");
|
||||
return sp.State;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitUntilStarted(string pid)
|
||||
{
|
||||
log.Log("Waiting till contract is started, or expired...");
|
||||
try
|
||||
@ -99,30 +122,43 @@ namespace AutoClient
|
||||
var emptyResponseTolerance = 10;
|
||||
while (true)
|
||||
{
|
||||
FixedShortDelay();
|
||||
var status = codex.GetPurchaseStatusRaw(pid).ToLowerInvariant();
|
||||
log.Log($"Status response: '{status}'");
|
||||
await FixedShortDelay();
|
||||
var status = await GetPurchaseState(pid);
|
||||
if (string.IsNullOrEmpty(status))
|
||||
{
|
||||
emptyResponseTolerance--;
|
||||
if (emptyResponseTolerance == 0)
|
||||
{
|
||||
log.Log("Received 10 empty responses to '/storage/purchases/'. Applying expiry delay, then carrying on.");
|
||||
ExpiryTimeDelay();
|
||||
log.Log("Received 10 empty responses. Applying expiry delay, then carrying on.");
|
||||
await ExpiryTimeDelay();
|
||||
return;
|
||||
}
|
||||
FixedShortDelay();
|
||||
await FixedShortDelay();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (status.Contains("pending") || status.Contains("submitted"))
|
||||
{
|
||||
FixedShortDelay();
|
||||
await FixedShortDelay();
|
||||
}
|
||||
else if (status.Contains("started"))
|
||||
{
|
||||
log.Log("Started.");
|
||||
await FixedDurationDelay();
|
||||
}
|
||||
else if (status.Contains("finished"))
|
||||
{
|
||||
log.Log("Purchase finished.");
|
||||
return;
|
||||
}
|
||||
else if (status.Contains("error"))
|
||||
{
|
||||
await FixedShortDelay();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Log("Wait finished.");
|
||||
return;
|
||||
await FixedShortDelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,18 +166,23 @@ namespace AutoClient
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Log($"Wait failed with exception: {ex}. Assume contract will expire: Wait expiry time.");
|
||||
ExpiryTimeDelay();
|
||||
await ExpiryTimeDelay();
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpiryTimeDelay()
|
||||
private async Task FixedDurationDelay()
|
||||
{
|
||||
Thread.Sleep(config.ContractExpiryMinutes * 60 * 1000);
|
||||
await Task.Delay(config.ContractDurationMinutes * 60 * 1000);
|
||||
}
|
||||
|
||||
private void FixedShortDelay()
|
||||
private async Task ExpiryTimeDelay()
|
||||
{
|
||||
Thread.Sleep(15 * 1000);
|
||||
await Task.Delay(config.ContractExpiryMinutes * 60 * 1000);
|
||||
}
|
||||
|
||||
private async Task FixedShortDelay()
|
||||
{
|
||||
await Task.Delay(15 * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user