feat: check if CID exists in local store (#1331)

This commit is contained in:
Arnaud 2025-11-02 08:32:47 +04:00 committed by GitHub
parent 7aca2f0e61
commit db8f866db4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 103 additions and 0 deletions

View File

@ -897,3 +897,10 @@ proc new*(
contracts: contracts,
trackedFutures: TrackedFutures(),
)
proc hasLocalBlock*(
self: CodexNodeRef, cid: Cid
): Future[bool] {.async: (raises: [CancelledError]).} =
## Returns true if the given Cid is present in the local store
return await (cid in self.networkStore.localStore)

View File

@ -365,6 +365,22 @@ proc initDataApi(node: CodexNodeRef, repoStore: RepoStore, router: var RestRoute
let json = %formatManifest(cid.get(), manifest)
return RestApiResponse.response($json, contentType = "application/json")
router.api(MethodGet, "/api/codex/v1/data/{cid}/exists") do(
cid: Cid, resp: HttpResponseRef
) -> RestApiResponse:
## Only test if the give CID is available in the local store
##
var headers = buildCorsHeaders("GET", allowedOrigin)
if cid.isErr:
return RestApiResponse.error(Http400, $cid.error(), headers = headers)
let cid = cid.get()
let hasCid = await node.hasLocalBlock(cid)
let json = %*{$cid: hasCid}
return RestApiResponse.response($json, contentType = "application/json")
router.api(MethodGet, "/api/codex/v1/space") do() -> RestApiResponse:
let json =
%RestRepoStore(

View File

@ -727,6 +727,34 @@ paths:
"500":
description: Well it was bad-bad
"/data/{cid}/exists":
get:
summary: "Check if a block identified by CID exists in the local node."
tags: [Data]
operationId: hasBlock
parameters:
- in: path
name: cid
required: true
schema:
$ref: "#/components/schemas/Cid"
description: "CID of the block to check."
responses:
"200":
description: Block existence information
content:
application/json:
schema:
type: object
properties:
has:
type: boolean
description: Indicates whether the block exists in the local node
"400":
description: Invalid CID is specified
"500":
description: Well it was bad-bad
"/space":
get:
summary: "Gets a summary of the storage space allocation of the node."

View File

@ -229,3 +229,17 @@ asyncchecksuite "Test Node - Basic":
check not await manifestCid in localStore
for blk in blocks:
check not (await blk.cid in localStore)
test "Should return true when a cid is already in the local store":
let
blocks = await makeRandomBlocks(datasetSize = 1024, blockSize = 256'nb)
manifest = await storeDataGetManifest(localStore, blocks)
manifestBlock = (await store.storeManifest(manifest)).tryGet()
manifestCid = manifestBlock.cid
check (await node.hasLocalBlock(manifestCid)) == true
test "Should returns false when a cid is not in the local store":
let randomBlock = bt.Block.new("Random block".toBytes).tryGet()
check (await node.hasLocalBlock(randomBlock.cid)) == false

View File

@ -220,3 +220,13 @@ twonodessuite "REST API":
let response2 = await client1.downloadRaw($cid)
check (await response2.body) == contents
test "should returns true when the block exists", twoNodesConfig:
let cid = (await client2.upload("some file contents")).get
var response = await client1.hasBlock(cid)
check response.get() == false
discard (await client1.download(cid)).get
response = await client1.hasBlock(cid)
check response.get() == false

View File

@ -396,3 +396,10 @@ multinodesuite "Rest API validation":
check:
response.status == 422
(await response.body) == "totalCollateral must be larger then zero"
test "has block returns error 400 when the cid is invalid", config:
let response = await client.hasBlockRaw("invalid-cid")
check:
response.status == 400
(await response.body) == "Incorrect Cid"

View File

@ -16,6 +16,9 @@ type CodexClient* = ref object
baseurl: string
session: HttpSessionRef
type HasBlockResponse = object
has: bool
proc new*(_: type CodexClient, baseurl: string): CodexClient =
CodexClient(session: HttpSessionRef.new(), baseurl: baseurl)
@ -431,3 +434,21 @@ proc getSlots*(
let url = client.baseurl & "/sales/slots"
let body = await client.getContent(url)
seq[Slot].fromJson(body)
proc hasBlock*(
client: CodexClient, cid: Cid
): Future[?!bool] {.async: (raises: [CancelledError, HttpError]).} =
let url = client.baseurl & "/data/" & $cid & "/exists"
let body = await client.getContent(url)
let response = HasBlockResponse.fromJson(body)
if response.isErr:
return failure "Failed to parse has block response"
return response.get.has.success
proc hasBlockRaw*(
client: CodexClient, cid: string
): Future[HttpClientResponseRef] {.
async: (raw: true, raises: [CancelledError, HttpError])
.} =
let url = client.baseurl & "/data/" & cid & "/exists"
return client.get(url)