Skip to content

Commit db8f866

Browse files
authored
feat: check if CID exists in local store (#1331)
1 parent 7aca2f0 commit db8f866

File tree

7 files changed

+103
-0
lines changed

7 files changed

+103
-0
lines changed

codex/node.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,3 +897,10 @@ proc new*(
897897
contracts: contracts,
898898
trackedFutures: TrackedFutures(),
899899
)
900+
901+
proc hasLocalBlock*(
902+
self: CodexNodeRef, cid: Cid
903+
): Future[bool] {.async: (raises: [CancelledError]).} =
904+
## Returns true if the given Cid is present in the local store
905+
906+
return await (cid in self.networkStore.localStore)

codex/rest/api.nim

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,22 @@ proc initDataApi(node: CodexNodeRef, repoStore: RepoStore, router: var RestRoute
365365
let json = %formatManifest(cid.get(), manifest)
366366
return RestApiResponse.response($json, contentType = "application/json")
367367

368+
router.api(MethodGet, "/api/codex/v1/data/{cid}/exists") do(
369+
cid: Cid, resp: HttpResponseRef
370+
) -> RestApiResponse:
371+
## Only test if the give CID is available in the local store
372+
##
373+
var headers = buildCorsHeaders("GET", allowedOrigin)
374+
375+
if cid.isErr:
376+
return RestApiResponse.error(Http400, $cid.error(), headers = headers)
377+
378+
let cid = cid.get()
379+
let hasCid = await node.hasLocalBlock(cid)
380+
381+
let json = %*{$cid: hasCid}
382+
return RestApiResponse.response($json, contentType = "application/json")
383+
368384
router.api(MethodGet, "/api/codex/v1/space") do() -> RestApiResponse:
369385
let json =
370386
%RestRepoStore(

openapi.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,34 @@ paths:
727727
"500":
728728
description: Well it was bad-bad
729729

730+
"/data/{cid}/exists":
731+
get:
732+
summary: "Check if a block identified by CID exists in the local node."
733+
tags: [Data]
734+
operationId: hasBlock
735+
parameters:
736+
- in: path
737+
name: cid
738+
required: true
739+
schema:
740+
$ref: "#/components/schemas/Cid"
741+
description: "CID of the block to check."
742+
responses:
743+
"200":
744+
description: Block existence information
745+
content:
746+
application/json:
747+
schema:
748+
type: object
749+
properties:
750+
has:
751+
type: boolean
752+
description: Indicates whether the block exists in the local node
753+
"400":
754+
description: Invalid CID is specified
755+
"500":
756+
description: Well it was bad-bad
757+
730758
"/space":
731759
get:
732760
summary: "Gets a summary of the storage space allocation of the node."

tests/codex/node/testnode.nim

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,17 @@ asyncchecksuite "Test Node - Basic":
229229
check not await manifestCid in localStore
230230
for blk in blocks:
231231
check not (await blk.cid in localStore)
232+
233+
test "Should return true when a cid is already in the local store":
234+
let
235+
blocks = await makeRandomBlocks(datasetSize = 1024, blockSize = 256'nb)
236+
manifest = await storeDataGetManifest(localStore, blocks)
237+
manifestBlock = (await store.storeManifest(manifest)).tryGet()
238+
manifestCid = manifestBlock.cid
239+
240+
check (await node.hasLocalBlock(manifestCid)) == true
241+
242+
test "Should returns false when a cid is not in the local store":
243+
let randomBlock = bt.Block.new("Random block".toBytes).tryGet()
244+
245+
check (await node.hasLocalBlock(randomBlock.cid)) == false

tests/integration/5_minutes/testrestapi.nim

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,13 @@ twonodessuite "REST API":
220220

221221
let response2 = await client1.downloadRaw($cid)
222222
check (await response2.body) == contents
223+
224+
test "should returns true when the block exists", twoNodesConfig:
225+
let cid = (await client2.upload("some file contents")).get
226+
227+
var response = await client1.hasBlock(cid)
228+
check response.get() == false
229+
230+
discard (await client1.download(cid)).get
231+
response = await client1.hasBlock(cid)
232+
check response.get() == false

tests/integration/5_minutes/testrestapivalidation.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,10 @@ multinodesuite "Rest API validation":
396396
check:
397397
response.status == 422
398398
(await response.body) == "totalCollateral must be larger then zero"
399+
400+
test "has block returns error 400 when the cid is invalid", config:
401+
let response = await client.hasBlockRaw("invalid-cid")
402+
403+
check:
404+
response.status == 400
405+
(await response.body) == "Incorrect Cid"

tests/integration/codexclient.nim

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ type CodexClient* = ref object
1616
baseurl: string
1717
session: HttpSessionRef
1818

19+
type HasBlockResponse = object
20+
has: bool
21+
1922
proc new*(_: type CodexClient, baseurl: string): CodexClient =
2023
CodexClient(session: HttpSessionRef.new(), baseurl: baseurl)
2124

@@ -431,3 +434,21 @@ proc getSlots*(
431434
let url = client.baseurl & "/sales/slots"
432435
let body = await client.getContent(url)
433436
seq[Slot].fromJson(body)
437+
438+
proc hasBlock*(
439+
client: CodexClient, cid: Cid
440+
): Future[?!bool] {.async: (raises: [CancelledError, HttpError]).} =
441+
let url = client.baseurl & "/data/" & $cid & "/exists"
442+
let body = await client.getContent(url)
443+
let response = HasBlockResponse.fromJson(body)
444+
if response.isErr:
445+
return failure "Failed to parse has block response"
446+
return response.get.has.success
447+
448+
proc hasBlockRaw*(
449+
client: CodexClient, cid: string
450+
): Future[HttpClientResponseRef] {.
451+
async: (raw: true, raises: [CancelledError, HttpError])
452+
.} =
453+
let url = client.baseurl & "/data/" & cid & "/exists"
454+
return client.get(url)

0 commit comments

Comments
 (0)