cs-codex-dist-tests/Tools/AutoClient/Purchaser.cs

167 lines
5.4 KiB
C#
Raw Normal View History

using CodexOpenApi;
2024-04-01 18:40:03 +00:00
using CodexPlugin;
using Logging;
using Newtonsoft.Json;
2024-04-01 18:40:03 +00:00
using Utils;
namespace AutoClient
{
2024-06-28 06:47:09 +00:00
public class Purchaser
2024-04-01 18:40:03 +00:00
{
private readonly ILog log;
private readonly HttpClient client;
private readonly Address address;
private readonly CodexApi codex;
2024-04-01 18:40:03 +00:00
private readonly Configuration config;
private readonly ImageGenerator generator;
2024-07-23 07:58:29 +00:00
private readonly CancellationToken ct;
2024-04-01 18:40:03 +00:00
2024-07-23 07:58:29 +00:00
public Purchaser(ILog log, HttpClient client, Address address, CodexApi codex, Configuration config, ImageGenerator generator, CancellationToken ct)
2024-04-01 18:40:03 +00:00
{
this.log = log;
this.client = client;
this.address = address;
2024-04-01 18:40:03 +00:00
this.codex = codex;
this.config = config;
this.generator = generator;
2024-07-23 07:58:29 +00:00
this.ct = ct;
2024-04-01 18:40:03 +00:00
}
2024-06-28 06:47:09 +00:00
public void Start()
{
Task.Run(Worker);
}
private async Task Worker()
2024-04-01 18:40:03 +00:00
{
while (!ct.IsCancellationRequested)
{
2024-06-28 06:47:09 +00:00
var pid = await StartNewPurchase();
await WaitTillFinished(pid);
2024-04-01 18:40:03 +00:00
}
}
2024-06-28 06:47:09 +00:00
private async Task<string> StartNewPurchase()
2024-04-01 18:40:03 +00:00
{
var file = await CreateFile();
var cid = await UploadFile(file);
2024-06-28 06:47:09 +00:00
return await RequestStorage(cid);
2024-04-01 18:40:03 +00:00
}
private async Task<string> CreateFile()
2024-04-01 18:40:03 +00:00
{
return await generator.GenerateImage();
2024-04-01 18:40:03 +00:00
}
private async Task<ContentId> UploadFile(string filename)
2024-04-01 18:40:03 +00:00
{
// Copied from CodexNode :/
using var fileStream = File.OpenRead(filename);
2024-04-01 18:40:03 +00:00
2024-06-28 06:47:09 +00:00
log.Log($"Uploading file {filename}...");
var response = await codex.UploadAsync(fileStream, ct);
2024-04-01 18:40:03 +00:00
if (string.IsNullOrEmpty(response)) FrameworkAssert.Fail("Received empty response.");
if (response.StartsWith("Unable to store block")) FrameworkAssert.Fail("Node failed to store block.");
log.Log($"Uploaded file. Received contentId: '{response}'.");
return new ContentId(response);
}
private async Task<string> RequestStorage(ContentId cid)
2024-04-01 18:40:03 +00:00
{
log.Log("Requesting storage for " + cid.Id);
var result = await codex.CreateStorageRequestAsync(cid.Id, new StorageRequestCreation()
2024-04-01 18:40:03 +00:00
{
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);
2024-06-28 06:47:09 +00:00
log.Log("Purchase ID: " + result);
return result;
}
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;
}
2024-06-28 06:47:09 +00:00
catch
{
return null;
}
2024-04-01 18:40:03 +00:00
}
2024-06-28 06:47:09 +00:00
private async Task WaitTillFinished(string pid)
2024-04-01 18:40:03 +00:00
{
2024-06-28 06:47:09 +00:00
log.Log("Waiting...");
2024-04-01 18:40:03 +00:00
try
{
var emptyResponseTolerance = 10;
2024-04-01 18:40:03 +00:00
while (true)
{
2024-06-28 06:47:09 +00:00
var status = (await GetPurchaseState(pid))?.ToLowerInvariant();
if (string.IsNullOrEmpty(status))
2024-04-01 18:40:03 +00:00
{
emptyResponseTolerance--;
if (emptyResponseTolerance == 0)
{
2024-06-28 06:47:09 +00:00
log.Log("Received 10 empty responses. Stop tracking this purchase.");
await ExpiryTimeDelay();
return;
}
}
else
{
2024-06-28 06:47:09 +00:00
if (status.Contains("cancel") ||
status.Contains("error") ||
status.Contains("finished"))
2024-04-01 18:40:03 +00:00
{
return;
}
2024-06-28 06:47:09 +00:00
if (status.Contains("started"))
2024-04-01 18:40:03 +00:00
{
2024-06-28 06:47:09 +00:00
await FixedDurationDelay();
}
2024-04-01 18:40:03 +00:00
}
2024-06-28 06:47:09 +00:00
await FixedShortDelay();
2024-04-01 18:40:03 +00:00
}
}
catch (Exception ex)
{
log.Log($"Wait failed with exception: {ex}. Assume contract will expire: Wait expiry time.");
await ExpiryTimeDelay();
2024-04-01 18:40:03 +00:00
}
}
private async Task FixedDurationDelay()
{
2024-06-28 06:47:09 +00:00
await Task.Delay(config.ContractDurationMinutes * 60 * 1000, ct);
}
private async Task ExpiryTimeDelay()
2024-04-01 18:40:03 +00:00
{
2024-06-28 06:47:09 +00:00
await Task.Delay(config.ContractExpiryMinutes * 60 * 1000, ct);
2024-04-01 18:40:03 +00:00
}
private async Task FixedShortDelay()
2024-04-01 18:40:03 +00:00
{
2024-06-28 06:47:09 +00:00
await Task.Delay(15 * 1000, ct);
2024-04-01 18:40:03 +00:00
}
}
}