Skip to content

Commit 60b489c

Browse files
authored
Merge pull request #104 from codex-storage/feature/bot-checks-cids
Feature/bot checks cids
2 parents e352e5c + fc942b1 commit 60b489c

File tree

11 files changed

+308
-14
lines changed

11 files changed

+308
-14
lines changed

ProjectPlugins/CodexPlugin/ApiChecker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace CodexPlugin
1010
public class ApiChecker
1111
{
1212
// <INSERT-OPENAPI-YAML-HASH>
13-
private const string OpenApiYamlHash = "AC-19-7F-3A-88-07-CB-43-53-60-4F-21-3D-A6-B1-53-47-65-07-3B-91-C6-88-B9-76-B2-7E-33-6A-1C-69-F4";
13+
private const string OpenApiYamlHash = "D5-C3-18-71-E8-FF-8F-89-9C-6B-98-3C-F2-C2-D2-37-0A-9F-27-23-35-67-EA-F6-1F-F9-D5-C6-63-34-5A-92";
1414
private const string OpenApiFilePath = "/codex/openapi.yaml";
1515
private const string DisableEnvironmentVariable = "CODEXPLUGIN_DISABLE_APICHECK";
1616

ProjectPlugins/CodexPlugin/CodexAccess.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public string UploadFile(FileStream fileStream, Action<Failure> onFailure)
7373
public Stream DownloadFile(string contentId, Action<Failure> onFailure)
7474
{
7575
var fileResponse = OnCodex(
76-
api => api.DownloadNetworkAsync(contentId),
76+
api => api.DownloadNetworkStreamAsync(contentId),
7777
CreateRetryConfig(nameof(DownloadFile), onFailure));
7878

7979
if (fileResponse.StatusCode != 200) throw new Exception("Download failed with StatusCode: " + fileResponse.StatusCode);
@@ -88,25 +88,25 @@ public LocalDatasetList LocalFiles()
8888
public StorageAvailability SalesAvailability(StorageAvailability request)
8989
{
9090
var body = mapper.Map(request);
91-
var read = OnCodex<SalesAvailabilityREAD>(api => api.OfferStorageAsync(body));
91+
var read = OnCodex(api => api.OfferStorageAsync(body));
9292
return mapper.Map(read);
9393
}
9494

9595
public StorageAvailability[] GetAvailabilities()
9696
{
97-
var collection = OnCodex<ICollection<SalesAvailabilityREAD>>(api => api.GetAvailabilitiesAsync());
97+
var collection = OnCodex(api => api.GetAvailabilitiesAsync());
9898
return mapper.Map(collection);
9999
}
100100

101101
public string RequestStorage(StoragePurchaseRequest request)
102102
{
103103
var body = mapper.Map(request);
104-
return OnCodex<string>(api => api.CreateStorageRequestAsync(request.ContentId.Id, body));
104+
return OnCodex(api => api.CreateStorageRequestAsync(request.ContentId.Id, body));
105105
}
106106

107107
public CodexSpace Space()
108108
{
109-
var space = OnCodex<Space>(api => api.SpaceAsync());
109+
var space = OnCodex(api => api.SpaceAsync());
110110
return mapper.Map(space);
111111
}
112112

ProjectPlugins/CodexPlugin/openapi.yaml

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,35 @@ paths:
456456

457457
"/data/{cid}/network":
458458
get:
459-
summary: "Download a file from the network in a streaming manner. If the file is not available locally, it will be retrieved from other nodes in the network if able."
459+
summary: "Download a file from the network to the local node if it's not available locally. Note: Download is performed async. Call can return before download is completed."
460460
tags: [ Data ]
461461
operationId: downloadNetwork
462+
parameters:
463+
- in: path
464+
name: cid
465+
required: true
466+
schema:
467+
$ref: "#/components/schemas/Cid"
468+
description: "File to be downloaded."
469+
responses:
470+
"200":
471+
description: Manifest information for download that has been started.
472+
content:
473+
application/json:
474+
schema:
475+
$ref: "#/components/schemas/DataItem"
476+
"400":
477+
description: Invalid CID is specified
478+
"404":
479+
description: Failed to download dataset manifest
480+
"500":
481+
description: Well it was bad-bad
482+
483+
"/data/{cid}/network/stream":
484+
get:
485+
summary: "Download a file from the network in a streaming manner. If the file is not available locally, it will be retrieved from other nodes in the network if able."
486+
tags: [ Data ]
487+
operationId: downloadNetworkStream
462488
parameters:
463489
- in: path
464490
name: cid
@@ -481,6 +507,32 @@ paths:
481507
"500":
482508
description: Well it was bad-bad
483509

510+
"/data/{cid}/network/manifest":
511+
get:
512+
summary: "Download only the dataset manifest from the network to the local node if it's not available locally."
513+
tags: [ Data ]
514+
operationId: downloadNetworkManifest
515+
parameters:
516+
- in: path
517+
name: cid
518+
required: true
519+
schema:
520+
$ref: "#/components/schemas/Cid"
521+
description: "File for which the manifest is to be downloaded."
522+
responses:
523+
"200":
524+
description: Manifest information.
525+
content:
526+
application/json:
527+
schema:
528+
$ref: "#/components/schemas/DataItem"
529+
"400":
530+
description: Invalid CID is specified
531+
"404":
532+
description: Failed to download dataset manifest
533+
"500":
534+
description: Well it was bad-bad
535+
484536
"/space":
485537
get:
486538
summary: "Gets a summary of the storage space allocation of the node."
@@ -726,7 +778,7 @@ paths:
726778
"503":
727779
description: Persistence is not enabled
728780

729-
"/node/spr":
781+
"/spr":
730782
get:
731783
summary: "Get Node's SPR"
732784
operationId: getSPR
@@ -744,7 +796,7 @@ paths:
744796
"503":
745797
description: Node SPR not ready, try again later
746798

747-
"/node/peerid":
799+
"/peerid":
748800
get:
749801
summary: "Get Node's PeerID"
750802
operationId: getPeerId
@@ -792,4 +844,4 @@ paths:
792844
content:
793845
application/json:
794846
schema:
795-
$ref: "#/components/schemas/DebugInfo"
847+
$ref: "#/components/schemas/DebugInfo"

Tools/AutoClient/Purchaser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ private async Task DownloadForeignCid()
6868
var filename = Guid.NewGuid().ToString().ToLowerInvariant();
6969
{
7070
using var fileStream = File.OpenWrite(filename);
71-
var fileResponse = await codex.DownloadNetworkAsync(cid);
71+
var fileResponse = await codex.DownloadNetworkStreamAsync(cid);
7272
fileResponse.Stream.CopyTo(fileStream);
7373
}
7474
var time = sw.Elapsed;

Tools/BiblioTech/BaseCommand.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Discord.WebSocket;
22
using BiblioTech.Options;
33
using Discord;
4-
using k8s.KubeConfigModels;
54

65
namespace BiblioTech
76
{

Tools/BiblioTech/BiblioTech.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<ProjectReference Include="..\..\Framework\ArgsUniform\ArgsUniform.csproj" />
1313
<ProjectReference Include="..\..\Framework\DiscordRewards\DiscordRewards.csproj" />
1414
<ProjectReference Include="..\..\Framework\GethConnector\GethConnector.csproj" />
15+
<ProjectReference Include="..\..\ProjectPlugins\CodexPlugin\CodexPlugin.csproj" />
1516
</ItemGroup>
1617

1718
</Project>

Tools/BiblioTech/CodexCidChecker.cs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
using CodexOpenApi;
2+
using IdentityModel.Client;
3+
using Utils;
4+
5+
namespace BiblioTech
6+
{
7+
public class CodexCidChecker
8+
{
9+
private static readonly string nl = Environment.NewLine;
10+
private readonly Configuration config;
11+
private CodexApi? currentCodexNode;
12+
13+
public CodexCidChecker(Configuration config)
14+
{
15+
this.config = config;
16+
}
17+
18+
public async Task<CheckResponse> PerformCheck(string cid)
19+
{
20+
if (string.IsNullOrEmpty(config.CodexEndpoint))
21+
{
22+
return new CheckResponse(false, "Codex CID checker is not (yet) available.", "");
23+
}
24+
25+
try
26+
{
27+
var codex = GetCodex();
28+
var nodeCheck = await CheckCodex(codex);
29+
if (!nodeCheck) return new CheckResponse(false, "Codex node is not available. Cannot perform check.", $"Codex node at '{config.CodexEndpoint}' did not respond correctly to debug/info.");
30+
31+
return await PerformCheck(codex, cid);
32+
}
33+
catch (Exception ex)
34+
{
35+
return new CheckResponse(false, "Internal server error", ex.ToString());
36+
}
37+
}
38+
39+
private async Task<CheckResponse> PerformCheck(CodexApi codex, string cid)
40+
{
41+
try
42+
{
43+
var manifest = await codex.DownloadNetworkManifestAsync(cid);
44+
return SuccessMessage(manifest);
45+
}
46+
catch (ApiException apiEx)
47+
{
48+
if (apiEx.StatusCode == 400) return CidFormatInvalid(apiEx.Response);
49+
if (apiEx.StatusCode == 404) return FailedToFetch(apiEx.Response);
50+
return UnexpectedReturnCode(apiEx.Response);
51+
}
52+
catch (Exception ex)
53+
{
54+
return UnexpectedException(ex);
55+
}
56+
}
57+
58+
#region Response formatting
59+
60+
private CheckResponse SuccessMessage(DataItem content)
61+
{
62+
return FormatResponse(
63+
success: true,
64+
title: $"Success: '{content.Cid}'",
65+
error: "",
66+
$"size: {content.Manifest.OriginalBytes} bytes",
67+
$"blockSize: {content.Manifest.BlockSize} bytes",
68+
$"protected: {content.Manifest.Protected}"
69+
);
70+
}
71+
72+
private CheckResponse UnexpectedException(Exception ex)
73+
{
74+
return FormatResponse(
75+
success: false,
76+
title: "Unexpected error",
77+
error: ex.ToString(),
78+
content: "Details will be sent to the bot-admin channel."
79+
);
80+
}
81+
82+
private CheckResponse UnexpectedReturnCode(string response)
83+
{
84+
var msg = "Unexpected return code. Response: " + response;
85+
return FormatResponse(
86+
success: false,
87+
title: "Unexpected return code",
88+
error: msg,
89+
content: msg
90+
);
91+
}
92+
93+
private CheckResponse FailedToFetch(string response)
94+
{
95+
var msg = "Failed to download content. Response: " + response;
96+
return FormatResponse(
97+
success: false,
98+
title: "Could not download content",
99+
error: msg,
100+
msg,
101+
$"Connection trouble? See 'https://docs.codex.storage/learn/troubleshoot'"
102+
);
103+
}
104+
105+
private CheckResponse CidFormatInvalid(string response)
106+
{
107+
return FormatResponse(
108+
success: false,
109+
title: "Invalid format",
110+
error: "",
111+
content: "Provided CID is not formatted correctly."
112+
);
113+
}
114+
115+
private CheckResponse FormatResponse(bool success, string title, string error, params string[] content)
116+
{
117+
var msg = string.Join(nl,
118+
new string[]
119+
{
120+
title,
121+
"```"
122+
}
123+
.Concat(content)
124+
.Concat(new string[]
125+
{
126+
"```"
127+
})
128+
) + nl + nl;
129+
130+
return new CheckResponse(success, msg, error);
131+
}
132+
133+
#endregion
134+
135+
#region Codex Node API
136+
137+
private CodexApi GetCodex()
138+
{
139+
if (currentCodexNode == null) currentCodexNode = CreateCodex();
140+
return currentCodexNode;
141+
}
142+
143+
private async Task<bool> CheckCodex(CodexApi codex)
144+
{
145+
try
146+
{
147+
var info = await currentCodexNode!.GetDebugInfoAsync();
148+
if (info == null || string.IsNullOrEmpty(info.Id)) return false;
149+
return true;
150+
}
151+
catch (Exception e)
152+
{
153+
return false;
154+
}
155+
}
156+
157+
private CodexApi CreateCodex()
158+
{
159+
var endpoint = config.CodexEndpoint;
160+
var splitIndex = endpoint.LastIndexOf(':');
161+
var host = endpoint.Substring(0, splitIndex);
162+
var port = Convert.ToInt32(endpoint.Substring(splitIndex + 1));
163+
164+
var address = new Address(
165+
host: host,
166+
port: port
167+
);
168+
169+
var client = new HttpClient();
170+
if (!string.IsNullOrEmpty(config.CodexEndpointAuth) && config.CodexEndpointAuth.Contains(":"))
171+
{
172+
var tokens = config.CodexEndpointAuth.Split(':');
173+
if (tokens.Length != 2) throw new Exception("Expected '<username>:<password>' in CodexEndpointAuth parameter.");
174+
client.SetBasicAuthentication(tokens[0], tokens[1]);
175+
}
176+
177+
var codex = new CodexApi(client);
178+
codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
179+
return codex;
180+
}
181+
182+
#endregion
183+
}
184+
185+
public class CheckResponse
186+
{
187+
public CheckResponse(bool success, string message, string error)
188+
{
189+
Success = success;
190+
Message = message;
191+
Error = error;
192+
}
193+
194+
public bool Success { get; }
195+
public string Message { get; }
196+
public string Error { get; }
197+
}
198+
}

0 commit comments

Comments
 (0)