feat(codex-node): add dataset deletion API to Codex node

This commit is contained in:
gmega 2025-02-06 09:39:16 -03:00
parent 54d499be41
commit c71c8dffed
No known key found for this signature in database
GPG Key ID: 6290D34EAD824B18
3 changed files with 93 additions and 13 deletions

View File

@ -267,6 +267,59 @@ proc retrieve*(
await self.streamEntireDataset(manifest, cid)
proc deleteSingleBlock(self: CodexNodeRef, cid: Cid): Future[?!void] {.async.} =
if err =? (await self.networkStore.delBlock(cid)).errorOption:
error "Error deleting block", cid, err = err.msg
return failure(err)
trace "Deleted block", cid
return success()
proc deleteEntireDataset(self: CodexNodeRef, cid: Cid): Future[?!void] {.async.} =
# Deletion is a strictly local operation
var store = self.networkStore.localStore
if not (await cid in store):
return failure("Dataset's manifest is not available locally - nothing to delete")
without manifestBlock =? await store.getBlock(cid), err:
return failure(err)
without manifest =? Manifest.decode(manifestBlock), err:
return failure(err)
for i in 0 ..< manifest.blocksCount:
if err =? (await store.delBlock(manifest.treeCid, i)).errorOption:
# The contract for delBlock is fuzzy, but we assume that if the block is
# simply missing we won't get an error. This is a best effort operation and
# can simply be retried.
error "Failed to delete block within dataset", index = i, err = err.msg
return failure(err)
if err =? (await store.delBlock(cid)).errorOption:
error "Error deleting manifest block", err = err.msg
success()
proc delete*(
self: CodexNodeRef, cid: Cid
): Future[?!void] {.async: (raises: [CatchableError]).} =
## Deletes a whole dataset, if Cid is a Manifest Cid, or a single block, if Cid a block Cid,
## from the underlying block store. This is a strictly local operation.
##
## Missing blocks in dataset deletes are ignored.
##
without isManifest =? cid.isManifest, err:
trace "Bad content type for CID:", err = err.msg
return failure(err)
if not isManifest:
return await self.deleteSingleBlock(cid)
echo "dispatch to entire dataset"
await self.deleteEntireDataset(cid)
proc store*(
self: CodexNodeRef,
stream: LPStream,

View File

@ -85,30 +85,31 @@ proc makeWantList*(
)
proc storeDataGetManifest*(
store: BlockStore, chunker: Chunker
store: BlockStore, blocks: seq[Block]
): Future[Manifest] {.async.} =
var cids = newSeq[Cid]()
while (let chunk = await chunker.getBytes(); chunk.len > 0):
let blk = Block.new(chunk).tryGet()
cids.add(blk.cid)
for blk in blocks:
(await store.putBlock(blk)).tryGet()
let
tree = CodexTree.init(cids).tryGet()
(manifest, tree) = makeManifestAndTree(blocks).tryGet()
treeCid = tree.rootCid.tryGet()
manifest = Manifest.new(
treeCid = treeCid,
blockSize = NBytes(chunker.chunkSize),
datasetSize = NBytes(chunker.offset),
)
for i in 0 ..< tree.leavesCount:
let proof = tree.getProof(i).tryGet()
(await store.putCidAndProof(treeCid, i, cids[i], proof)).tryGet()
(await store.putCidAndProof(treeCid, i, blocks[i].cid, proof)).tryGet()
return manifest
proc storeDataGetManifest*(
store: BlockStore, chunker: Chunker
): Future[Manifest] {.async.} =
var blocks = newSeq[Block]()
while (let chunk = await chunker.getBytes(); chunk.len > 0):
blocks.add(Block.new(chunk).tryGet())
return await storeDataGetManifest(store, blocks)
proc makeRandomBlocks*(
datasetSize: int, blockSize: NBytes
): Future[seq[Block]] {.async.} =

View File

@ -37,6 +37,7 @@ import ../examples
import ../helpers
import ../helpers/mockmarket
import ../helpers/mockclock
import ../slots/helpers
import ./helpers
@ -166,3 +167,28 @@ asyncchecksuite "Test Node - Basic":
(await verifiableBlock.cid in localStore) == true
request.content.cid == $verifiableBlock.cid
request.content.merkleRoot == builder.verifyRoot.get.toBytes
test "Should delete a single block":
let randomBlock = bt.Block.new("Random block".toBytes).tryGet()
(await localStore.putBlock(randomBlock)).tryGet()
check (await randomBlock.cid in localStore) == true
(await node.delete(randomBlock.cid)).tryGet()
check (await randomBlock.cid in localStore) == false
test "Should delete an entire dataset":
let
blocks = await makeRandomBlocks(datasetSize = 2048, blockSize = 256'nb)
manifest = await storeDataGetManifest(localStore, blocks)
manifestBlock = (await store.storeManifest(manifest)).tryGet()
manifestCid = manifestBlock.cid
check await manifestCid in localStore
for blk in blocks:
check await blk.cid in localStore
(await node.delete(manifestCid)).tryGet()
check not await manifestCid in localStore
for blk in blocks:
check not (await blk.cid in localStore)