BlockStore API got new return types (rationale in https://github.com/status-im/nim-codex/issues/123#issuecomment-1163797753): - getBlock: Future[?! (?Block)] - putBlock/delBlock/listBlocks: Future[?!void] - hasBlock: Future[?!bool] Plus refactored readOnce(StoreStream) and check received data in its tests. And replaced local use of AsyncHeapQueue with seq.sort.
This commit is contained in:
parent
afdb5be2d4
commit
0bfe26440e
|
@ -83,7 +83,7 @@ proc advertiseQueueLoop*(b: DiscoveryEngine) {.async.} =
|
||||||
trace "Exception listing blocks", exc = exc.msg
|
trace "Exception listing blocks", exc = exc.msg
|
||||||
|
|
||||||
while b.discEngineRunning:
|
while b.discEngineRunning:
|
||||||
await b.localStore.listBlocks(onBlock)
|
discard await b.localStore.listBlocks(onBlock)
|
||||||
|
|
||||||
trace "About to sleep advertise loop", sleep = b.advertiseLoopSleep
|
trace "About to sleep advertise loop", sleep = b.advertiseLoopSleep
|
||||||
await sleepAsync(b.advertiseLoopSleep)
|
await sleepAsync(b.advertiseLoopSleep)
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import std/sets
|
import std/sets
|
||||||
|
import std/options
|
||||||
|
import std/algorithm
|
||||||
|
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
import pkg/chronicles
|
import pkg/chronicles
|
||||||
|
@ -200,7 +202,7 @@ proc blockPresenceHandler*(
|
||||||
.filter do(cid: Cid) -> bool:
|
.filter do(cid: Cid) -> bool:
|
||||||
not b.peers.anyIt( cid in it.peerHave ))
|
not b.peers.anyIt( cid in it.peerHave ))
|
||||||
|
|
||||||
proc scheduleTasks(b: BlockExcEngine, blocks: seq[bt.Block]) =
|
proc scheduleTasks(b: BlockExcEngine, blocks: seq[bt.Block]) {.async.} =
|
||||||
trace "Schedule a task for new blocks"
|
trace "Schedule a task for new blocks"
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -209,9 +211,10 @@ proc scheduleTasks(b: BlockExcEngine, blocks: seq[bt.Block]) =
|
||||||
# schedule any new peers to provide blocks to
|
# schedule any new peers to provide blocks to
|
||||||
for p in b.peers:
|
for p in b.peers:
|
||||||
for c in cids: # for each cid
|
for c in cids: # for each cid
|
||||||
# schedule a peer if it wants at least one
|
# schedule a peer if it wants at least one cid
|
||||||
# cid and we have it in our local store
|
# and we have it in our local store
|
||||||
if c in p.peerWants and c in b.localStore:
|
if c in p.peerWants:
|
||||||
|
if await (c in b.localStore):
|
||||||
if b.scheduleTask(p):
|
if b.scheduleTask(p):
|
||||||
trace "Task scheduled for peer", peer = p.id
|
trace "Task scheduled for peer", peer = p.id
|
||||||
else:
|
else:
|
||||||
|
@ -219,7 +222,7 @@ proc scheduleTasks(b: BlockExcEngine, blocks: seq[bt.Block]) =
|
||||||
|
|
||||||
break # do next peer
|
break # do next peer
|
||||||
|
|
||||||
proc resolveBlocks*(b: BlockExcEngine, blocks: seq[bt.Block]) =
|
proc resolveBlocks*(b: BlockExcEngine, blocks: seq[bt.Block]) {.async.} =
|
||||||
## Resolve pending blocks from the pending blocks manager
|
## Resolve pending blocks from the pending blocks manager
|
||||||
## and schedule any new task to be ran
|
## and schedule any new task to be ran
|
||||||
##
|
##
|
||||||
|
@ -227,7 +230,7 @@ proc resolveBlocks*(b: BlockExcEngine, blocks: seq[bt.Block]) =
|
||||||
trace "Resolving blocks", blocks = blocks.len
|
trace "Resolving blocks", blocks = blocks.len
|
||||||
|
|
||||||
b.pendingBlocks.resolve(blocks)
|
b.pendingBlocks.resolve(blocks)
|
||||||
b.scheduleTasks(blocks)
|
await b.scheduleTasks(blocks)
|
||||||
b.discovery.queueProvideBlocksReq(blocks.mapIt( it.cid ))
|
b.discovery.queueProvideBlocksReq(blocks.mapIt( it.cid ))
|
||||||
|
|
||||||
proc payForBlocks(engine: BlockExcEngine,
|
proc payForBlocks(engine: BlockExcEngine,
|
||||||
|
@ -250,11 +253,10 @@ proc blocksHandler*(
|
||||||
|
|
||||||
trace "Got blocks from peer", peer, len = blocks.len
|
trace "Got blocks from peer", peer, len = blocks.len
|
||||||
for blk in blocks:
|
for blk in blocks:
|
||||||
if not (await b.localStore.putBlock(blk)):
|
if isErr (await b.localStore.putBlock(blk)):
|
||||||
trace "Unable to store block", cid = blk.cid
|
trace "Unable to store block", cid = blk.cid
|
||||||
continue
|
|
||||||
|
|
||||||
b.resolveBlocks(blocks)
|
await b.resolveBlocks(blocks)
|
||||||
let peerCtx = b.peers.get(peer)
|
let peerCtx = b.peers.get(peer)
|
||||||
if peerCtx != nil:
|
if peerCtx != nil:
|
||||||
b.payForBlocks(peerCtx, blocks)
|
b.payForBlocks(peerCtx, blocks)
|
||||||
|
@ -289,7 +291,8 @@ proc wantListHandler*(
|
||||||
|
|
||||||
# peer might want to ask for the same cid with
|
# peer might want to ask for the same cid with
|
||||||
# different want params
|
# different want params
|
||||||
if e.sendDontHave and e.cid notin b.localStore:
|
if e.sendDontHave:
|
||||||
|
if not(await e.cid in b.localStore):
|
||||||
dontHaves.add(e.cid)
|
dontHaves.add(e.cid)
|
||||||
|
|
||||||
# send don't have's to remote
|
# send don't have's to remote
|
||||||
|
@ -358,22 +361,25 @@ proc dropPeer*(b: BlockExcEngine, peer: PeerID) =
|
||||||
proc taskHandler*(b: BlockExcEngine, task: BlockExcPeerCtx) {.gcsafe, async.} =
|
proc taskHandler*(b: BlockExcEngine, task: BlockExcPeerCtx) {.gcsafe, async.} =
|
||||||
trace "Handling task for peer", peer = task.id
|
trace "Handling task for peer", peer = task.id
|
||||||
|
|
||||||
var wantsBlocks = newAsyncHeapQueue[Entry](queueType = QueueType.Max)
|
# PART 1: Send to the peer blocks he wants to get,
|
||||||
# get blocks and wants to send to the remote
|
# if they present in our local store
|
||||||
for e in task.peerWants:
|
|
||||||
if e.wantType == WantType.wantBlock:
|
|
||||||
await wantsBlocks.push(e)
|
|
||||||
|
|
||||||
# TODO: There should be all sorts of accounting of
|
# TODO: There should be all sorts of accounting of
|
||||||
# bytes sent/received here
|
# bytes sent/received here
|
||||||
|
|
||||||
|
var wantsBlocks = task.peerWants.filterIt(it.wantType == WantType.wantBlock)
|
||||||
|
|
||||||
if wantsBlocks.len > 0:
|
if wantsBlocks.len > 0:
|
||||||
|
wantsBlocks.sort(SortOrder.Descending)
|
||||||
|
|
||||||
let blockFuts = await allFinished(wantsBlocks.mapIt(
|
let blockFuts = await allFinished(wantsBlocks.mapIt(
|
||||||
b.localStore.getBlock(it.cid)
|
b.localStore.getBlock(it.cid)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# Extract succesfully received blocks
|
||||||
let blocks = blockFuts
|
let blocks = blockFuts
|
||||||
.filterIt((not it.failed) and it.read.isOk)
|
.filterIt(it.completed and it.read.isOk and it.read.get.isSome)
|
||||||
.mapIt(!it.read)
|
.mapIt(it.read.get.get)
|
||||||
|
|
||||||
if blocks.len > 0:
|
if blocks.len > 0:
|
||||||
trace "Sending blocks to peer", peer = task.id, blocks = blocks.len
|
trace "Sending blocks to peer", peer = task.id, blocks = blocks.len
|
||||||
|
@ -387,13 +393,17 @@ proc taskHandler*(b: BlockExcEngine, task: BlockExcPeerCtx) {.gcsafe, async.} =
|
||||||
not blocks.anyIt( it.cid == e.cid )
|
not blocks.anyIt( it.cid == e.cid )
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# PART 2: Send to the peer prices of the blocks he wants to discover,
|
||||||
|
# if they present in our local store
|
||||||
|
|
||||||
var wants: seq[BlockPresence]
|
var wants: seq[BlockPresence]
|
||||||
# do not remove wants from the queue unless
|
# do not remove wants from the queue unless
|
||||||
# we send the block or get a cancel
|
# we send the block or get a cancel
|
||||||
for e in task.peerWants:
|
for e in task.peerWants:
|
||||||
if e.wantType == WantType.wantHave:
|
if e.wantType == WantType.wantHave:
|
||||||
var presence = Presence(cid: e.cid)
|
var presence = Presence(cid: e.cid)
|
||||||
presence.have = b.localStore.hasblock(presence.cid)
|
presence.have = await (presence.cid in b.localStore)
|
||||||
if presence.have and price =? b.pricing.?price:
|
if presence.have and price =? b.pricing.?price:
|
||||||
presence.price = price
|
presence.price = price
|
||||||
wants.add(BlockPresence.init(presence))
|
wants.add(BlockPresence.init(presence))
|
||||||
|
|
|
@ -21,6 +21,14 @@ import pkg/questionable/results
|
||||||
|
|
||||||
import ./errors
|
import ./errors
|
||||||
|
|
||||||
|
const
|
||||||
|
BlockSize* = 31 * 64 * 4 # block size
|
||||||
|
|
||||||
|
type
|
||||||
|
Block* = ref object of RootObj
|
||||||
|
cid*: Cid
|
||||||
|
data*: seq[byte]
|
||||||
|
|
||||||
template EmptyCid*: untyped =
|
template EmptyCid*: untyped =
|
||||||
var
|
var
|
||||||
emptyCid {.global, threadvar.}:
|
emptyCid {.global, threadvar.}:
|
||||||
|
@ -90,14 +98,6 @@ template EmptyBlock*: untyped =
|
||||||
|
|
||||||
emptyBlock
|
emptyBlock
|
||||||
|
|
||||||
const
|
|
||||||
BlockSize* = 31 * 64 * 4 # block size
|
|
||||||
|
|
||||||
type
|
|
||||||
Block* = ref object of RootObj
|
|
||||||
cid*: Cid
|
|
||||||
data*: seq[byte]
|
|
||||||
|
|
||||||
proc isEmpty*(cid: Cid): bool =
|
proc isEmpty*(cid: Cid): bool =
|
||||||
cid == EmptyCid[cid.cidver]
|
cid == EmptyCid[cid.cidver]
|
||||||
.catch
|
.catch
|
||||||
|
|
|
@ -114,10 +114,14 @@ proc encode*(
|
||||||
for j in 0..<blocks:
|
for j in 0..<blocks:
|
||||||
let idx = blockIdx[j]
|
let idx = blockIdx[j]
|
||||||
if idx < manifest.len:
|
if idx < manifest.len:
|
||||||
without var blk =? await dataBlocks[j], error:
|
without blkOrNone =? await dataBlocks[j], error:
|
||||||
trace "Unable to retrieve block", msg = error.msg
|
trace "Unable to retrieve block", error = error.msg
|
||||||
return error.failure
|
return error.failure
|
||||||
|
|
||||||
|
without blk =? blkOrNone:
|
||||||
|
trace "Block not found", cid = encoded[idx]
|
||||||
|
return failure("Block not found")
|
||||||
|
|
||||||
trace "Encoding block", cid = blk.cid, pos = idx
|
trace "Encoding block", cid = blk.cid, pos = idx
|
||||||
shallowCopy(data[j], blk.data)
|
shallowCopy(data[j], blk.data)
|
||||||
else:
|
else:
|
||||||
|
@ -125,11 +129,11 @@ proc encode*(
|
||||||
data[j] = newSeq[byte](manifest.blockSize)
|
data[j] = newSeq[byte](manifest.blockSize)
|
||||||
|
|
||||||
trace "Erasure coding data", data = data.len, parity = parityData.len
|
trace "Erasure coding data", data = data.len, parity = parityData.len
|
||||||
if (
|
|
||||||
let err = encoder.encode(data, parityData);
|
let res = encoder.encode(data, parityData);
|
||||||
err.isErr):
|
if res.isErr:
|
||||||
trace "Unable to encode manifest!", err = $err.error
|
trace "Unable to encode manifest!", error = $res.error
|
||||||
return failure($err.error)
|
return failure($res.error)
|
||||||
|
|
||||||
for j in 0..<parity:
|
for j in 0..<parity:
|
||||||
let idx = encoded.rounded + blockIdx[j]
|
let idx = encoded.rounded + blockIdx[j]
|
||||||
|
@ -139,7 +143,7 @@ proc encode*(
|
||||||
|
|
||||||
trace "Adding parity block", cid = blk.cid, pos = idx
|
trace "Adding parity block", cid = blk.cid, pos = idx
|
||||||
encoded[idx] = blk.cid
|
encoded[idx] = blk.cid
|
||||||
if not (await self.store.putBlock(blk)):
|
if isErr (await self.store.putBlock(blk)):
|
||||||
trace "Unable to store block!", cid = blk.cid
|
trace "Unable to store block!", cid = blk.cid
|
||||||
return failure("Unable to store block!")
|
return failure("Unable to store block!")
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
|
@ -205,10 +209,12 @@ proc decode*(
|
||||||
|
|
||||||
idxPendingBlocks.del(idxPendingBlocks.find(done))
|
idxPendingBlocks.del(idxPendingBlocks.find(done))
|
||||||
|
|
||||||
without blk =? (await done), error:
|
without blkOrNone =? (await done), error:
|
||||||
trace "Failed retrieving block", exc = error.msg
|
trace "Failed retrieving block", exc = error.msg
|
||||||
|
return error.failure
|
||||||
|
|
||||||
if blk.isNil:
|
without blk =? blkOrNone:
|
||||||
|
trace "Block not found"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if idx >= encoded.K:
|
if idx >= encoded.K:
|
||||||
|
@ -242,7 +248,7 @@ proc decode*(
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
trace "Recovered block", cid = blk.cid
|
trace "Recovered block", cid = blk.cid
|
||||||
if not (await self.store.putBlock(blk)):
|
if isErr (await self.store.putBlock(blk)):
|
||||||
trace "Unable to store block!", cid = blk.cid
|
trace "Unable to store block!", cid = blk.cid
|
||||||
return failure("Unable to store block!")
|
return failure("Unable to store block!")
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
|
|
|
@ -66,9 +66,12 @@ proc retrieve*(
|
||||||
cid: Cid): Future[?!LPStream] {.async.} =
|
cid: Cid): Future[?!LPStream] {.async.} =
|
||||||
|
|
||||||
trace "Received retrieval request", cid
|
trace "Received retrieval request", cid
|
||||||
without blk =? await node.blockStore.getBlock(cid):
|
without blkOrNone =? await node.blockStore.getBlock(cid), error:
|
||||||
return failure(
|
return failure(error)
|
||||||
newException(CodexError, "Couldn't retrieve block for Cid!"))
|
|
||||||
|
without blk =? blkOrNone:
|
||||||
|
trace "Block not found", cid
|
||||||
|
return failure("Block not found")
|
||||||
|
|
||||||
if manifest =? Manifest.decode(blk.data, blk.cid):
|
if manifest =? Manifest.decode(blk.data, blk.cid):
|
||||||
|
|
||||||
|
@ -134,7 +137,7 @@ proc store*(
|
||||||
return failure("Unable to init block from chunk!")
|
return failure("Unable to init block from chunk!")
|
||||||
|
|
||||||
blockManifest.add(blk.cid)
|
blockManifest.add(blk.cid)
|
||||||
if not (await node.blockStore.putBlock(blk)):
|
if isErr (await node.blockStore.putBlock(blk)):
|
||||||
# trace "Unable to store block", cid = blk.cid
|
# trace "Unable to store block", cid = blk.cid
|
||||||
return failure("Unable to store block " & $blk.cid)
|
return failure("Unable to store block " & $blk.cid)
|
||||||
|
|
||||||
|
@ -155,7 +158,7 @@ proc store*(
|
||||||
trace "Unable to init block from manifest data!"
|
trace "Unable to init block from manifest data!"
|
||||||
return failure("Unable to init block from manifest data!")
|
return failure("Unable to init block from manifest data!")
|
||||||
|
|
||||||
if not (await node.blockStore.putBlock(manifest)):
|
if isErr (await node.blockStore.putBlock(manifest)):
|
||||||
trace "Unable to store manifest", cid = manifest.cid
|
trace "Unable to store manifest", cid = manifest.cid
|
||||||
return failure("Unable to store manifest " & $manifest.cid)
|
return failure("Unable to store manifest " & $manifest.cid)
|
||||||
|
|
||||||
|
@ -187,13 +190,17 @@ proc store(node: CodexNodeRef, cids: seq[Cid]): Future[?!void] {.async.} =
|
||||||
proc store(node: CodexNodeRef, cid: Cid): Future[?!void] {.async.} =
|
proc store(node: CodexNodeRef, cid: Cid): Future[?!void] {.async.} =
|
||||||
## Retrieves dataset from the network, and stores it locally
|
## Retrieves dataset from the network, and stores it locally
|
||||||
|
|
||||||
if node.blockstore.hasBlock(cid):
|
without present =? await node.blockstore.hasBlock(cid):
|
||||||
|
return failure newException(CodexError, "Unable to find block " & $cid)
|
||||||
|
if present:
|
||||||
return success()
|
return success()
|
||||||
|
|
||||||
without blk =? await node.blockstore.getBlock(cid):
|
without blkOrNone =? await node.blockstore.getBlock(cid):
|
||||||
|
return failure newException(CodexError, "Unable to retrieve block " & $cid)
|
||||||
|
without blk =? blkOrNone:
|
||||||
return failure newException(CodexError, "Unable to retrieve block " & $cid)
|
return failure newException(CodexError, "Unable to retrieve block " & $cid)
|
||||||
|
|
||||||
if not (await node.blockstore.putBlock(blk)):
|
if isErr (await node.blockstore.putBlock(blk)):
|
||||||
return failure newException(CodexError, "Unable to store block " & $cid)
|
return failure newException(CodexError, "Unable to store block " & $cid)
|
||||||
|
|
||||||
if manifest =? Manifest.decode(blk.data, blk.cid):
|
if manifest =? Manifest.decode(blk.data, blk.cid):
|
||||||
|
@ -224,10 +231,14 @@ proc requestStorage*(self: CodexNodeRef,
|
||||||
trace "Purchasing not available"
|
trace "Purchasing not available"
|
||||||
return failure "Purchasing not available"
|
return failure "Purchasing not available"
|
||||||
|
|
||||||
without blk =? (await self.blockStore.getBlock(cid)), error:
|
without blkOrNone =? (await self.blockStore.getBlock(cid)), error:
|
||||||
trace "Unable to retrieve manifest block", cid
|
trace "Unable to retrieve manifest block", cid
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
|
without blk =? blkOrNone:
|
||||||
|
trace "Manifest block not found", cid
|
||||||
|
return failure("Manifest block not found")
|
||||||
|
|
||||||
without mc =? blk.cid.contentType():
|
without mc =? blk.cid.contentType():
|
||||||
trace "Couldn't identify Cid!", cid
|
trace "Couldn't identify Cid!", cid
|
||||||
return failure("Couldn't identify Cid! " & $cid)
|
return failure("Couldn't identify Cid! " & $cid)
|
||||||
|
@ -254,7 +265,7 @@ proc requestStorage*(self: CodexNodeRef,
|
||||||
trace "Unable to create block from encoded manifest"
|
trace "Unable to create block from encoded manifest"
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
if not (await self.blockStore.putBlock(encodedBlk)):
|
if isErr (await self.blockStore.putBlock(encodedBlk)):
|
||||||
trace "Unable to store encoded manifest block", cid = encodedBlk.cid
|
trace "Unable to store encoded manifest block", cid = encodedBlk.cid
|
||||||
return failure("Unable to store encoded manifest block")
|
return failure("Unable to store encoded manifest block")
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ push: {.upraises: [].}
|
||||||
|
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
import pkg/libp2p
|
import pkg/libp2p
|
||||||
|
import pkg/questionable
|
||||||
import pkg/questionable/results
|
import pkg/questionable/results
|
||||||
|
|
||||||
import ../blocktype
|
import ../blocktype
|
||||||
|
@ -23,41 +24,39 @@ type
|
||||||
OnBlock* = proc(cid: Cid): Future[void] {.upraises: [], gcsafe.}
|
OnBlock* = proc(cid: Cid): Future[void] {.upraises: [], gcsafe.}
|
||||||
BlockStore* = ref object of RootObj
|
BlockStore* = ref object of RootObj
|
||||||
|
|
||||||
method getBlock*(
|
method getBlock*(self: BlockStore, cid: Cid): Future[?! (? Block)] {.base.} =
|
||||||
b: BlockStore,
|
## Get a block from the blockstore
|
||||||
cid: Cid): Future[?!Block] {.base.} =
|
|
||||||
## Get a block from the stores
|
|
||||||
##
|
##
|
||||||
|
|
||||||
raiseAssert("Not implemented!")
|
raiseAssert("Not implemented!")
|
||||||
|
|
||||||
method putBlock*(
|
method putBlock*(self: BlockStore, blk: Block): Future[?!void] {.base.} =
|
||||||
s: BlockStore,
|
|
||||||
blk: Block): Future[bool] {.base.} =
|
|
||||||
## Put a block to the blockstore
|
## Put a block to the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
raiseAssert("Not implemented!")
|
raiseAssert("Not implemented!")
|
||||||
|
|
||||||
method delBlock*(
|
method delBlock*(self: BlockStore, cid: Cid): Future[?!void] {.base.} =
|
||||||
s: BlockStore,
|
|
||||||
cid: Cid): Future[?!void] {.base.} =
|
|
||||||
## Delete a block from the blockstore
|
## Delete a block from the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
raiseAssert("Not implemented!")
|
raiseAssert("Not implemented!")
|
||||||
|
|
||||||
method hasBlock*(s: BlockStore, cid: Cid): bool {.base.} =
|
method hasBlock*(self: BlockStore, cid: Cid): Future[?!bool] {.base.} =
|
||||||
## Check if the block exists in the blockstore
|
## Check if the block exists in the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
return false
|
raiseAssert("Not implemented!")
|
||||||
|
|
||||||
method listBlocks*(s: BlockStore, onBlock: OnBlock): Future[void] {.base.} =
|
method listBlocks*(self: BlockStore, onBlock: OnBlock): Future[?!void] {.base.} =
|
||||||
## Get the list of blocks in the BlockStore. This is an intensive operation
|
## Get the list of blocks in the BlockStore. This is an intensive operation
|
||||||
##
|
##
|
||||||
|
|
||||||
raiseAssert("Not implemented!")
|
raiseAssert("Not implemented!")
|
||||||
|
|
||||||
proc contains*(s: BlockStore, blk: Cid): bool =
|
proc contains*(self: BlockStore, blk: Cid): Future[bool] {.async.} =
|
||||||
s.hasBlock(blk)
|
## Check if the block exists in the blockstore.
|
||||||
|
## Return false if error encountered
|
||||||
|
##
|
||||||
|
|
||||||
|
return (await self.hasBlock(blk)) |? false
|
||||||
|
|
|
@ -44,34 +44,45 @@ const
|
||||||
DefaultCacheSizeMiB* = 100
|
DefaultCacheSizeMiB* = 100
|
||||||
DefaultCacheSize* = DefaultCacheSizeMiB * MiB # bytes
|
DefaultCacheSize* = DefaultCacheSizeMiB * MiB # bytes
|
||||||
|
|
||||||
method getBlock*(
|
method getBlock*(self: CacheStore, cid: Cid): Future[?! (? Block)] {.async.} =
|
||||||
self: CacheStore,
|
|
||||||
cid: Cid): Future[?!Block] {.async.} =
|
|
||||||
## Get a block from the stores
|
## Get a block from the stores
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Getting block from cache", cid
|
trace "Getting block from cache", cid
|
||||||
if cid.isEmpty:
|
if cid.isEmpty:
|
||||||
trace "Empty block, ignoring"
|
trace "Empty block, ignoring"
|
||||||
return cid.emptyBlock.success
|
return cid.emptyBlock.some.success
|
||||||
|
|
||||||
return self.cache[cid].catch()
|
if cid notin self.cache:
|
||||||
|
return Block.none.success
|
||||||
|
|
||||||
method hasBlock*(self: CacheStore, cid: Cid): bool =
|
try:
|
||||||
## check if the block exists
|
let blk = self.cache[cid]
|
||||||
|
return blk.some.success
|
||||||
|
except CatchableError as exc:
|
||||||
|
trace "Exception requesting block", cid, exc = exc.msg
|
||||||
|
return failure(exc)
|
||||||
|
|
||||||
|
method hasBlock*(self: CacheStore, cid: Cid): Future[?!bool] {.async.} =
|
||||||
|
## Check if the block exists in the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Checking for block presence in cache", cid
|
trace "Checking CacheStore for block presence", cid
|
||||||
if cid.isEmpty:
|
if cid.isEmpty:
|
||||||
trace "Empty block, ignoring"
|
trace "Empty block, ignoring"
|
||||||
return true
|
return true.success
|
||||||
|
|
||||||
cid in self.cache
|
return (cid in self.cache).success
|
||||||
|
|
||||||
|
method listBlocks*(s: CacheStore, onBlock: OnBlock): Future[?!void] {.async.} =
|
||||||
|
## Get the list of blocks in the BlockStore. This is an intensive operation
|
||||||
|
##
|
||||||
|
|
||||||
method listBlocks*(s: CacheStore, onBlock: OnBlock) {.async.} =
|
|
||||||
for cid in toSeq(s.cache.keys):
|
for cid in toSeq(s.cache.keys):
|
||||||
await onBlock(cid)
|
await onBlock(cid)
|
||||||
|
|
||||||
|
return success()
|
||||||
|
|
||||||
func putBlockSync(self: CacheStore, blk: Block): bool =
|
func putBlockSync(self: CacheStore, blk: Block): bool =
|
||||||
|
|
||||||
let blkSize = blk.data.len # in bytes
|
let blkSize = blk.data.len # in bytes
|
||||||
|
@ -94,22 +105,19 @@ func putBlockSync(self: CacheStore, blk: Block): bool =
|
||||||
self.currentSize += blkSize
|
self.currentSize += blkSize
|
||||||
return true
|
return true
|
||||||
|
|
||||||
method putBlock*(
|
method putBlock*(self: CacheStore, blk: Block): Future[?!void] {.async.} =
|
||||||
self: CacheStore,
|
|
||||||
blk: Block): Future[bool] {.async.} =
|
|
||||||
## Put a block to the blockstore
|
## Put a block to the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Storing block in cache", cid = blk.cid
|
trace "Storing block in cache", cid = blk.cid
|
||||||
if blk.isEmpty:
|
if blk.isEmpty:
|
||||||
trace "Empty block, ignoring"
|
trace "Empty block, ignoring"
|
||||||
return true
|
return success()
|
||||||
|
|
||||||
return self.putBlockSync(blk)
|
discard self.putBlockSync(blk)
|
||||||
|
return success()
|
||||||
|
|
||||||
method delBlock*(
|
method delBlock*(self: CacheStore, cid: Cid): Future[?!void] {.async.} =
|
||||||
self: CacheStore,
|
|
||||||
cid: Cid): Future[?!void] {.async.} =
|
|
||||||
## Delete a block from the blockstore
|
## Delete a block from the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|
|
@ -37,67 +37,73 @@ type
|
||||||
template blockPath*(self: FSStore, cid: Cid): string =
|
template blockPath*(self: FSStore, cid: Cid): string =
|
||||||
self.repoDir / ($cid)[^self.postfixLen..^1] / $cid
|
self.repoDir / ($cid)[^self.postfixLen..^1] / $cid
|
||||||
|
|
||||||
method getBlock*(
|
method getBlock*(self: FSStore, cid: Cid): Future[?! (? Block)] {.async.} =
|
||||||
self: FSStore,
|
|
||||||
cid: Cid): Future[?!Block] {.async.} =
|
|
||||||
## Get a block from the stores
|
## Get a block from the stores
|
||||||
##
|
##
|
||||||
|
|
||||||
|
trace "Getting block from filestore", cid
|
||||||
if cid.isEmpty:
|
if cid.isEmpty:
|
||||||
trace "Empty block, ignoring"
|
trace "Empty block, ignoring"
|
||||||
return cid.emptyBlock.success
|
return cid.emptyBlock.some.success
|
||||||
|
|
||||||
if cid in self.cache:
|
let cachedBlock = await self.cache.getBlock(cid)
|
||||||
return await self.cache.getBlock(cid)
|
if cachedBlock.isErr:
|
||||||
|
return cachedBlock
|
||||||
if cid notin self:
|
if cachedBlock.get.isSome:
|
||||||
return Block.failure("Couldn't find block in fs store")
|
trace "Retrieved block from cache", cid
|
||||||
|
return cachedBlock
|
||||||
|
|
||||||
|
# Read file contents
|
||||||
var data: seq[byte]
|
var data: seq[byte]
|
||||||
let path = self.blockPath(cid)
|
let
|
||||||
if (
|
path = self.blockPath(cid)
|
||||||
let res = io2.readFile(path, data);
|
res = io2.readFile(path, data)
|
||||||
res.isErr):
|
|
||||||
|
if res.isErr:
|
||||||
|
if not isFile(path): # May be, check instead that "res.error == ERROR_FILE_NOT_FOUND" ?
|
||||||
|
return Block.none.success
|
||||||
|
else:
|
||||||
let error = io2.ioErrorMsg(res.error)
|
let error = io2.ioErrorMsg(res.error)
|
||||||
trace "Cannot read file from fs store", path , error
|
trace "Cannot read file from filestore", path, error
|
||||||
return Block.failure("Cannot read file from fs store")
|
return failure("Cannot read file from filestore")
|
||||||
|
|
||||||
return Block.new(cid, data)
|
without var blk =? Block.new(cid, data), error:
|
||||||
|
return error.failure
|
||||||
|
|
||||||
method putBlock*(
|
# TODO: add block to the cache
|
||||||
self: FSStore,
|
return blk.some.success
|
||||||
blk: Block): Future[bool] {.async.} =
|
|
||||||
## Put a block to the blockstore
|
method putBlock*(self: FSStore, blk: Block): Future[?!void] {.async.} =
|
||||||
|
## Write block contents to file with name based on blk.cid,
|
||||||
|
## save second copy to the cache
|
||||||
##
|
##
|
||||||
|
|
||||||
if blk.isEmpty:
|
if blk.isEmpty:
|
||||||
trace "Empty block, ignoring"
|
trace "Empty block, ignoring"
|
||||||
return true
|
return success()
|
||||||
|
|
||||||
if blk.cid in self:
|
|
||||||
return true
|
|
||||||
|
|
||||||
# if directory exists it wont fail
|
|
||||||
if io2.createPath(self.blockPath(blk.cid).parentDir).isErr:
|
|
||||||
trace "Unable to create block prefix dir", dir = self.blockPath(blk.cid).parentDir
|
|
||||||
return false
|
|
||||||
|
|
||||||
let path = self.blockPath(blk.cid)
|
let path = self.blockPath(blk.cid)
|
||||||
if (
|
if isFile(path):
|
||||||
let res = io2.writeFile(path, blk.data);
|
return success()
|
||||||
res.isErr):
|
|
||||||
|
# If directory exists createPath wont fail
|
||||||
|
let dir = path.parentDir
|
||||||
|
if io2.createPath(dir).isErr:
|
||||||
|
trace "Unable to create block prefix dir", dir
|
||||||
|
return failure("Unable to create block prefix dir")
|
||||||
|
|
||||||
|
let res = io2.writeFile(path, blk.data)
|
||||||
|
if res.isErr:
|
||||||
let error = io2.ioErrorMsg(res.error)
|
let error = io2.ioErrorMsg(res.error)
|
||||||
trace "Unable to store block", path, cid = blk.cid, error
|
trace "Unable to store block", path, cid = blk.cid, error
|
||||||
return false
|
return failure("Unable to store block")
|
||||||
|
|
||||||
if not (await self.cache.putBlock(blk)):
|
if isErr (await self.cache.putBlock(blk)):
|
||||||
trace "Unable to store block in cache", cid = blk.cid
|
trace "Unable to store block in cache", cid = blk.cid
|
||||||
|
|
||||||
return true
|
return success()
|
||||||
|
|
||||||
method delBlock*(
|
method delBlock*(self: FSStore, cid: Cid): Future[?!void] {.async.} =
|
||||||
self: FSStore,
|
|
||||||
cid: Cid): Future[?!void] {.async.} =
|
|
||||||
## Delete a block from the blockstore
|
## Delete a block from the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@ -111,33 +117,35 @@ method delBlock*(
|
||||||
res = io2.removeFile(path)
|
res = io2.removeFile(path)
|
||||||
|
|
||||||
if res.isErr:
|
if res.isErr:
|
||||||
let errmsg = io2.ioErrorMsg(res.error)
|
let error = io2.ioErrorMsg(res.error)
|
||||||
trace "Unable to delete block", path, cid, errmsg
|
trace "Unable to delete block", path, cid, error
|
||||||
return errmsg.failure
|
return error.failure
|
||||||
|
|
||||||
return await self.cache.delBlock(cid)
|
return await self.cache.delBlock(cid)
|
||||||
|
|
||||||
method hasBlock*(self: FSStore, cid: Cid): bool =
|
method hasBlock*(self: FSStore, cid: Cid): Future[?!bool] {.async.} =
|
||||||
## Check if the block exists in the blockstore
|
## Check if the block exists in the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Checking for block existence", cid
|
trace "Checking filestore for block existence", cid
|
||||||
if cid.isEmpty:
|
if cid.isEmpty:
|
||||||
trace "Empty block, ignoring"
|
trace "Empty block, ignoring"
|
||||||
return true
|
return true.success
|
||||||
|
|
||||||
self.blockPath(cid).isFile()
|
return self.blockPath(cid).isFile().success
|
||||||
|
|
||||||
method listBlocks*(self: FSStore, onBlock: OnBlock) {.async.} =
|
method listBlocks*(self: FSStore, onBlock: OnBlock): Future[?!void] {.async.} =
|
||||||
debug "Listing all blocks in store"
|
## Get the list of blocks in the BlockStore. This is an intensive operation
|
||||||
|
##
|
||||||
|
|
||||||
|
trace "Listing all blocks in filestore"
|
||||||
for (pkind, folderPath) in self.repoDir.walkDir():
|
for (pkind, folderPath) in self.repoDir.walkDir():
|
||||||
if pkind != pcDir: continue
|
if pkind != pcDir: continue
|
||||||
let baseName = basename(folderPath)
|
if len(folderPath.basename) != self.postfixLen: continue
|
||||||
if baseName.len != self.postfixLen: continue
|
|
||||||
|
|
||||||
for (fkind, filePath) in folderPath.walkDir(false):
|
for (fkind, filename) in folderPath.walkDir(relative = true):
|
||||||
if fkind != pcFile: continue
|
if fkind != pcFile: continue
|
||||||
let cid = Cid.init(basename(filePath))
|
let cid = Cid.init(filename)
|
||||||
if cid.isOk:
|
if cid.isOk:
|
||||||
# getting a weird `Error: unhandled exception: index 1 not in 0 .. 0 [IndexError]`
|
# getting a weird `Error: unhandled exception: index 1 not in 0 .. 0 [IndexError]`
|
||||||
# compilation error if using different syntax/construct bellow
|
# compilation error if using different syntax/construct bellow
|
||||||
|
@ -149,6 +157,8 @@ method listBlocks*(self: FSStore, onBlock: OnBlock) {.async.} =
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
trace "Couldn't get block", cid = $(cid.get())
|
trace "Couldn't get block", cid = $(cid.get())
|
||||||
|
|
||||||
|
return success()
|
||||||
|
|
||||||
proc new*(
|
proc new*(
|
||||||
T: type FSStore,
|
T: type FSStore,
|
||||||
repoDir: string,
|
repoDir: string,
|
||||||
|
|
|
@ -31,57 +31,57 @@ type
|
||||||
engine*: BlockExcEngine # blockexc decision engine
|
engine*: BlockExcEngine # blockexc decision engine
|
||||||
localStore*: BlockStore # local block store
|
localStore*: BlockStore # local block store
|
||||||
|
|
||||||
method getBlock*(
|
method getBlock*(self: NetworkStore, cid: Cid): Future[?! (? bt.Block)] {.async.} =
|
||||||
self: NetworkStore,
|
|
||||||
cid: Cid): Future[?!bt.Block] {.async.} =
|
|
||||||
## Get a block from a remote peer
|
## Get a block from a remote peer
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Getting block", cid
|
trace "Getting block from network store", cid
|
||||||
without var blk =? (await self.localStore.getBlock(cid)):
|
|
||||||
trace "Couldn't get from local store", cid
|
let blk = await self.localStore.getBlock(cid)
|
||||||
|
if blk.isErr:
|
||||||
|
return blk
|
||||||
|
if blk.get.isSome:
|
||||||
|
trace "Retrieved block from local store", cid
|
||||||
|
return blk
|
||||||
|
|
||||||
|
trace "Block not found in local store", cid
|
||||||
try:
|
try:
|
||||||
blk = await self.engine.requestBlock(cid)
|
# TODO: What if block isn't available in the engine too?
|
||||||
|
let blk = await self.engine.requestBlock(cid)
|
||||||
|
# TODO: add block to the local store
|
||||||
|
return blk.some.success
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
trace "Exception requesting block", cid, exc = exc.msg
|
trace "Exception requesting block", cid, exc = exc.msg
|
||||||
return failure(exc.msg)
|
return failure(exc)
|
||||||
|
|
||||||
trace "Retrieved block from local store", cid
|
method putBlock*(self: NetworkStore, blk: bt.Block): Future[?!void] {.async.} =
|
||||||
return blk.success
|
## Store block locally and notify the network
|
||||||
|
|
||||||
method putBlock*(
|
|
||||||
self: NetworkStore,
|
|
||||||
blk: bt.Block): Future[bool] {.async.} =
|
|
||||||
## Store block locally and notify the
|
|
||||||
## network
|
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Puting block", cid = blk.cid
|
trace "Puting block into network store", cid = blk.cid
|
||||||
|
|
||||||
if not (await self.localStore.putBlock(blk)):
|
let res = await self.localStore.putBlock(blk)
|
||||||
return false
|
if res.isErr:
|
||||||
|
return res
|
||||||
|
|
||||||
self.engine.resolveBlocks(@[blk])
|
await self.engine.resolveBlocks(@[blk])
|
||||||
return true
|
return success()
|
||||||
|
|
||||||
method delBlock*(
|
method delBlock*(self: NetworkStore, cid: Cid): Future[?!void] =
|
||||||
self: NetworkStore,
|
|
||||||
cid: Cid): Future[?!void] =
|
|
||||||
## Delete a block from the blockstore
|
## Delete a block from the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Deleting block from networkstore", cid
|
trace "Deleting block from network store", cid
|
||||||
return self.localStore.delBlock(cid)
|
return self.localStore.delBlock(cid)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
method hasBlock*(
|
method hasBlock*(self: NetworkStore, cid: Cid): Future[?!bool] {.async.} =
|
||||||
self: NetworkStore,
|
|
||||||
cid: Cid): bool =
|
|
||||||
## Check if the block exists in the blockstore
|
## Check if the block exists in the blockstore
|
||||||
##
|
##
|
||||||
|
|
||||||
self.localStore.hasBlock(cid)
|
trace "Checking network store for block existence", cid
|
||||||
|
return await self.localStore.hasBlock(cid)
|
||||||
|
|
||||||
proc new*(
|
proc new*(
|
||||||
T: type NetworkStore,
|
T: type NetworkStore,
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
## This file may not be copied, modified, or distributed except according to
|
## This file may not be copied, modified, or distributed except according to
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
|
import std/options
|
||||||
|
|
||||||
import pkg/upraises
|
import pkg/upraises
|
||||||
|
|
||||||
push: {.upraises: [].}
|
push: {.upraises: [].}
|
||||||
|
@ -52,36 +54,40 @@ proc `size=`*(self: StoreStream, size: int)
|
||||||
{.error: "Setting the size is forbidden".} =
|
{.error: "Setting the size is forbidden".} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
method atEof*(self: StoreStream): bool =
|
||||||
|
self.offset >= self.size
|
||||||
|
|
||||||
method readOnce*(
|
method readOnce*(
|
||||||
self: StoreStream,
|
self: StoreStream,
|
||||||
pbytes: pointer,
|
pbytes: pointer,
|
||||||
nbytes: int): Future[int] {.async.} =
|
nbytes: int): Future[int] {.async.} =
|
||||||
|
## Read `nbytes` from current position in the StoreStream into output buffer pointed by `pbytes`.
|
||||||
|
## Return how many bytes were actually read before EOF was encountered.
|
||||||
|
## Raise exception if we are already at EOF.
|
||||||
|
|
||||||
|
trace "Reading from manifest", cid = self.manifest.cid.get(), blocks = self.manifest.len
|
||||||
if self.atEof:
|
if self.atEof:
|
||||||
raise newLPStreamEOFError()
|
raise newLPStreamEOFError()
|
||||||
|
|
||||||
var
|
# The loop iterates over blocks in the StoreStream,
|
||||||
read = 0
|
# reading them and copying their data into outbuf
|
||||||
|
var read = 0 # Bytes read so far, and thus write offset in the outbuf
|
||||||
trace "Reading from manifest", cid = self.manifest.cid.get(), blocks = self.manifest.len
|
|
||||||
while read < nbytes and not self.atEof:
|
while read < nbytes and not self.atEof:
|
||||||
|
# Compute from the current stream position `self.offset` the block num/offset to read
|
||||||
|
# Compute how many bytes to read from this block
|
||||||
let
|
let
|
||||||
pos = self.offset div self.manifest.blockSize
|
blockNum = self.offset div self.manifest.blockSize
|
||||||
blk = (await self.store.getBlock(self.manifest[pos])).tryGet()
|
blockOffset = self.offset mod self.manifest.blockSize
|
||||||
|
readBytes = min(nbytes - read, self.manifest.blockSize - blockOffset)
|
||||||
|
|
||||||
blockOffset =
|
# Read contents of block `blockNum`
|
||||||
if self.offset >= self.manifest.blockSize:
|
without blkOrNone =? await self.store.getBlock(self.manifest[blockNum]), error:
|
||||||
self.offset mod self.manifest.blockSize
|
raise newLPStreamReadError(error)
|
||||||
else:
|
without blk =? blkOrNone:
|
||||||
self.offset
|
raise newLPStreamReadError("Block not found")
|
||||||
|
trace "Reading bytes from store stream", blockNum, cid = blk.cid, bytes = readBytes, blockOffset
|
||||||
|
|
||||||
readBytes =
|
# Copy `readBytes` bytes starting at `blockOffset` from the block into the outbuf
|
||||||
if (nbytes - read) >= (self.manifest.blockSize - blockOffset):
|
|
||||||
self.manifest.blockSize - blockOffset
|
|
||||||
else:
|
|
||||||
min(nbytes - read, self.manifest.blockSize)
|
|
||||||
|
|
||||||
trace "Reading bytes from store stream", pos, cid = blk.cid, bytes = readBytes, blockOffset = blockOffset
|
|
||||||
copyMem(
|
copyMem(
|
||||||
pbytes.offset(read),
|
pbytes.offset(read),
|
||||||
if blk.isEmpty:
|
if blk.isEmpty:
|
||||||
|
@ -90,14 +96,12 @@ method readOnce*(
|
||||||
blk.data[blockOffset].addr,
|
blk.data[blockOffset].addr,
|
||||||
readBytes)
|
readBytes)
|
||||||
|
|
||||||
|
# Update current positions in the stream and outbuf
|
||||||
self.offset += readBytes
|
self.offset += readBytes
|
||||||
read += readBytes
|
read += readBytes
|
||||||
|
|
||||||
return read
|
return read
|
||||||
|
|
||||||
method atEof*(self: StoreStream): bool =
|
|
||||||
self.offset >= self.manifest.len * self.manifest.blockSize
|
|
||||||
|
|
||||||
method closeImpl*(self: StoreStream) {.async.} =
|
method closeImpl*(self: StoreStream) {.async.} =
|
||||||
try:
|
try:
|
||||||
trace "Closing StoreStream"
|
trace "Closing StoreStream"
|
||||||
|
|
|
@ -82,7 +82,7 @@ suite "Block Advertising and Discovery":
|
||||||
|
|
||||||
blockDiscovery.findBlockProvidersHandler =
|
blockDiscovery.findBlockProvidersHandler =
|
||||||
proc(d: MockDiscovery, cid: Cid): Future[seq[SignedPeerRecord]] {.async.} =
|
proc(d: MockDiscovery, cid: Cid): Future[seq[SignedPeerRecord]] {.async.} =
|
||||||
engine.resolveBlocks(blocks.filterIt( it.cid == cid ))
|
await engine.resolveBlocks(blocks.filterIt( it.cid == cid ))
|
||||||
|
|
||||||
await allFuturesThrowing(
|
await allFuturesThrowing(
|
||||||
allFinished(pendingBlocks))
|
allFinished(pendingBlocks))
|
||||||
|
|
|
@ -106,7 +106,7 @@ suite "NetworkStore engine - 2 nodes":
|
||||||
|
|
||||||
test "Should send want-have for block":
|
test "Should send want-have for block":
|
||||||
let blk = bt.Block.new("Block 1".toBytes).tryGet()
|
let blk = bt.Block.new("Block 1".toBytes).tryGet()
|
||||||
check await nodeCmps2.localStore.putBlock(blk)
|
(await nodeCmps2.localStore.putBlock(blk)).tryGet()
|
||||||
|
|
||||||
let entry = Entry(
|
let entry = Entry(
|
||||||
`block`: blk.cid.data.buffer,
|
`block`: blk.cid.data.buffer,
|
||||||
|
@ -121,12 +121,12 @@ suite "NetworkStore engine - 2 nodes":
|
||||||
.taskQueue
|
.taskQueue
|
||||||
.pushOrUpdateNoWait(peerCtx1).isOk
|
.pushOrUpdateNoWait(peerCtx1).isOk
|
||||||
|
|
||||||
check eventually nodeCmps1.localStore.hasBlock(blk.cid)
|
check eventually (await nodeCmps1.localStore.hasBlock(blk.cid)).tryGet()
|
||||||
|
|
||||||
test "Should get blocks from remote":
|
test "Should get blocks from remote":
|
||||||
let blocks = await allFinished(
|
let blocks = await allFinished(
|
||||||
blocks2.mapIt( nodeCmps1.networkStore.getBlock(it.cid) ))
|
blocks2.mapIt( nodeCmps1.networkStore.getBlock(it.cid) ))
|
||||||
check blocks.mapIt( !it.read ) == blocks2
|
check blocks.mapIt( it.read().tryGet().get() ) == blocks2
|
||||||
|
|
||||||
test "Remote should send blocks when available":
|
test "Remote should send blocks when available":
|
||||||
let blk = bt.Block.new("Block 1".toBytes).tryGet()
|
let blk = bt.Block.new("Block 1".toBytes).tryGet()
|
||||||
|
@ -137,7 +137,7 @@ suite "NetworkStore engine - 2 nodes":
|
||||||
|
|
||||||
# second trigger blockexc to resolve any pending requests
|
# second trigger blockexc to resolve any pending requests
|
||||||
# for the block
|
# for the block
|
||||||
check await nodeCmps2.networkStore.putBlock(blk)
|
(await nodeCmps2.networkStore.putBlock(blk)).tryGet()
|
||||||
|
|
||||||
# should succeed retrieving block from remote
|
# should succeed retrieving block from remote
|
||||||
check await nodeCmps1.networkStore.getBlock(blk.cid)
|
check await nodeCmps1.networkStore.getBlock(blk.cid)
|
||||||
|
@ -196,14 +196,8 @@ suite "NetworkStore - multiple nodes":
|
||||||
pendingBlocks1 = blocks[0..3].mapIt( engine.pendingBlocks.getWantHandle( it.cid ) )
|
pendingBlocks1 = blocks[0..3].mapIt( engine.pendingBlocks.getWantHandle( it.cid ) )
|
||||||
pendingBlocks2 = blocks[12..15].mapIt( engine.pendingBlocks.getWantHandle( it.cid ))
|
pendingBlocks2 = blocks[12..15].mapIt( engine.pendingBlocks.getWantHandle( it.cid ))
|
||||||
|
|
||||||
await allFutures(
|
for i in 0..15:
|
||||||
blocks[0..3].mapIt( networkStore[0].engine.localStore.putBlock(it) ))
|
(await networkStore[i div 4].engine.localStore.putBlock(blocks[i])).tryGet()
|
||||||
await allFutures(
|
|
||||||
blocks[4..7].mapIt( networkStore[1].engine.localStore.putBlock(it) ))
|
|
||||||
await allFutures(
|
|
||||||
blocks[8..11].mapIt( networkStore[2].engine.localStore.putBlock(it) ))
|
|
||||||
await allFutures(
|
|
||||||
blocks[12..15].mapIt( networkStore[3].engine.localStore.putBlock(it) ))
|
|
||||||
|
|
||||||
await connectNodes(switch)
|
await connectNodes(switch)
|
||||||
await sleepAsync(1.seconds)
|
await sleepAsync(1.seconds)
|
||||||
|
@ -232,14 +226,8 @@ suite "NetworkStore - multiple nodes":
|
||||||
pendingBlocks1 = blocks[0..3].mapIt( engine.pendingBlocks.getWantHandle( it.cid ) )
|
pendingBlocks1 = blocks[0..3].mapIt( engine.pendingBlocks.getWantHandle( it.cid ) )
|
||||||
pendingBlocks2 = blocks[12..15].mapIt( engine.pendingBlocks.getWantHandle( it.cid ))
|
pendingBlocks2 = blocks[12..15].mapIt( engine.pendingBlocks.getWantHandle( it.cid ))
|
||||||
|
|
||||||
await allFutures(
|
for i in 0..15:
|
||||||
blocks[0..3].mapIt( networkStore[0].engine.localStore.putBlock(it) ))
|
(await networkStore[i div 4].engine.localStore.putBlock(blocks[i])).tryGet()
|
||||||
await allFutures(
|
|
||||||
blocks[4..7].mapIt( networkStore[1].engine.localStore.putBlock(it) ))
|
|
||||||
await allFutures(
|
|
||||||
blocks[8..11].mapIt( networkStore[2].engine.localStore.putBlock(it) ))
|
|
||||||
await allFutures(
|
|
||||||
blocks[12..15].mapIt( networkStore[3].engine.localStore.putBlock(it) ))
|
|
||||||
|
|
||||||
await connectNodes(switch)
|
await connectNodes(switch)
|
||||||
await sleepAsync(1.seconds)
|
await sleepAsync(1.seconds)
|
||||||
|
|
|
@ -227,8 +227,8 @@ suite "NetworkStore engine handlers":
|
||||||
sendPresence: sendPresence
|
sendPresence: sendPresence
|
||||||
))
|
))
|
||||||
|
|
||||||
check await engine.localStore.putBlock(blocks[0])
|
(await engine.localStore.putBlock(blocks[0])).tryGet()
|
||||||
check await engine.localStore.putBlock(blocks[1])
|
(await engine.localStore.putBlock(blocks[1])).tryGet()
|
||||||
await engine.wantListHandler(peerId, wantList)
|
await engine.wantListHandler(peerId, wantList)
|
||||||
|
|
||||||
await done
|
await done
|
||||||
|
@ -242,7 +242,8 @@ suite "NetworkStore engine handlers":
|
||||||
let resolved = await allFinished(pending)
|
let resolved = await allFinished(pending)
|
||||||
check resolved.mapIt( it.read ) == blocks
|
check resolved.mapIt( it.read ) == blocks
|
||||||
for b in blocks:
|
for b in blocks:
|
||||||
check engine.localStore.hasBlock(b.cid)
|
let present = await engine.localStore.hasBlock(b.cid)
|
||||||
|
check present.tryGet()
|
||||||
|
|
||||||
test "Should send payments for received blocks":
|
test "Should send payments for received blocks":
|
||||||
let account = Account(address: EthAddress.example)
|
let account = Account(address: EthAddress.example)
|
||||||
|
@ -355,7 +356,7 @@ suite "Task Handler":
|
||||||
blks[0].cid == blocks[1].cid
|
blks[0].cid == blocks[1].cid
|
||||||
|
|
||||||
for blk in blocks:
|
for blk in blocks:
|
||||||
check await engine.localStore.putBlock(blk)
|
(await engine.localStore.putBlock(blk)).tryGet()
|
||||||
engine.network.request.sendBlocks = sendBlocks
|
engine.network.request.sendBlocks = sendBlocks
|
||||||
|
|
||||||
# second block to send by priority
|
# second block to send by priority
|
||||||
|
@ -393,7 +394,7 @@ suite "Task Handler":
|
||||||
]
|
]
|
||||||
|
|
||||||
for blk in blocks:
|
for blk in blocks:
|
||||||
check await engine.localStore.putBlock(blk)
|
(await engine.localStore.putBlock(blk)).tryGet()
|
||||||
engine.network.request.sendPresence = sendPresence
|
engine.network.request.sendPresence = sendPresence
|
||||||
|
|
||||||
# have block
|
# have block
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import std/options
|
||||||
|
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
import pkg/libp2p
|
import pkg/libp2p
|
||||||
import pkg/libp2p/varint
|
import pkg/libp2p/varint
|
||||||
|
@ -17,7 +19,7 @@ export randomchunker, nodeutils, mockdiscovery, eventually
|
||||||
# is changed here, because blocks are now `ref`
|
# is changed here, because blocks are now `ref`
|
||||||
# types. This is only in tests!!!
|
# types. This is only in tests!!!
|
||||||
func `==`*(a, b: bt.Block): bool =
|
func `==`*(a, b: bt.Block): bool =
|
||||||
(a.cid == b.cid) and (a.data == b. data)
|
(a.cid == b.cid) and (a.data == b.data)
|
||||||
|
|
||||||
proc lenPrefix*(msg: openArray[byte]): seq[byte] =
|
proc lenPrefix*(msg: openArray[byte]): seq[byte] =
|
||||||
## Write `msg` with a varint-encoded length prefix
|
## Write `msg` with a varint-encoded length prefix
|
||||||
|
@ -45,7 +47,7 @@ proc corruptBlocks*(
|
||||||
|
|
||||||
pos.add(i)
|
pos.add(i)
|
||||||
var
|
var
|
||||||
blk = (await store.getBlock(manifest[i])).tryGet()
|
blk = (await store.getBlock(manifest[i])).tryGet().get()
|
||||||
bytePos: seq[int]
|
bytePos: seq[int]
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
|
|
|
@ -65,12 +65,9 @@ suite "Storage Proofs Network":
|
||||||
let chunk = await chunker.getBytes();
|
let chunk = await chunker.getBytes();
|
||||||
chunk.len > 0):
|
chunk.len > 0):
|
||||||
|
|
||||||
let
|
let blk = bt.Block.new(chunk).tryGet()
|
||||||
blk = bt.Block.new(chunk).tryGet()
|
|
||||||
|
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
if not (await store.putBlock(blk)):
|
(await store.putBlock(blk)).tryGet()
|
||||||
raise newException(CatchableError, "Unable to store block " & $blk.cid)
|
|
||||||
|
|
||||||
cid = manifest.cid.tryGet()
|
cid = manifest.cid.tryGet()
|
||||||
por = await PoR.init(
|
por = await PoR.init(
|
||||||
|
|
|
@ -36,12 +36,9 @@ suite "BLS PoR":
|
||||||
let chunk = await chunker.getBytes();
|
let chunk = await chunker.getBytes();
|
||||||
chunk.len > 0):
|
chunk.len > 0):
|
||||||
|
|
||||||
let
|
let blk = bt.Block.new(chunk).tryGet()
|
||||||
blk = bt.Block.new(chunk).tryGet()
|
|
||||||
|
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
if not (await store.putBlock(blk)):
|
(await store.putBlock(blk)).tryGet()
|
||||||
raise newException(CatchableError, "Unable to store block " & $blk.cid)
|
|
||||||
|
|
||||||
test "Test PoR without corruption":
|
test "Test PoR without corruption":
|
||||||
let
|
let
|
||||||
|
@ -97,12 +94,9 @@ suite "Test Serialization":
|
||||||
let chunk = await chunker.getBytes();
|
let chunk = await chunker.getBytes();
|
||||||
chunk.len > 0):
|
chunk.len > 0):
|
||||||
|
|
||||||
let
|
let blk = bt.Block.new(chunk).tryGet()
|
||||||
blk = bt.Block.new(chunk).tryGet()
|
|
||||||
|
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
if not (await store.putBlock(blk)):
|
(await store.putBlock(blk)).tryGet()
|
||||||
raise newException(CatchableError, "Unable to store block " & $blk.cid)
|
|
||||||
|
|
||||||
(spk, ssk) = st.keyGen()
|
(spk, ssk) = st.keyGen()
|
||||||
por = await PoR.init(
|
por = await PoR.init(
|
||||||
|
|
|
@ -44,12 +44,9 @@ suite "Test PoR store":
|
||||||
let chunk = await chunker.getBytes();
|
let chunk = await chunker.getBytes();
|
||||||
chunk.len > 0):
|
chunk.len > 0):
|
||||||
|
|
||||||
let
|
let blk = bt.Block.new(chunk).tryGet()
|
||||||
blk = bt.Block.new(chunk).tryGet()
|
|
||||||
|
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
if not (await store.putBlock(blk)):
|
(await store.putBlock(blk)).tryGet()
|
||||||
raise newException(CatchableError, "Unable to store block " & $blk.cid)
|
|
||||||
|
|
||||||
cid = manifest.cid.tryGet()
|
cid = manifest.cid.tryGet()
|
||||||
por = await PoR.init(
|
por = await PoR.init(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import std/strutils
|
import std/strutils
|
||||||
|
import std/options
|
||||||
|
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
import pkg/asynctest
|
import pkg/asynctest
|
||||||
|
@ -48,13 +49,13 @@ suite "Cache Store tests":
|
||||||
|
|
||||||
test "putBlock":
|
test "putBlock":
|
||||||
|
|
||||||
check:
|
(await store.putBlock(newBlock1)).tryGet()
|
||||||
await store.putBlock(newBlock1)
|
check (await store.hasBlock(newBlock1.cid)).tryGet()
|
||||||
newBlock1.cid in store
|
|
||||||
|
|
||||||
# block size bigger than entire cache
|
# block size bigger than entire cache
|
||||||
store = CacheStore.new(cacheSize = 99, chunkSize = 98)
|
store = CacheStore.new(cacheSize = 99, chunkSize = 98)
|
||||||
check not await store.putBlock(newBlock1)
|
(await store.putBlock(newBlock1)).tryGet()
|
||||||
|
check not (await store.hasBlock(newBlock1.cid)).tryGet()
|
||||||
|
|
||||||
# block being added causes removal of LRU block
|
# block being added causes removal of LRU block
|
||||||
store = CacheStore.new(
|
store = CacheStore.new(
|
||||||
|
@ -62,60 +63,63 @@ suite "Cache Store tests":
|
||||||
cacheSize = 200,
|
cacheSize = 200,
|
||||||
chunkSize = 1)
|
chunkSize = 1)
|
||||||
check:
|
check:
|
||||||
not store.hasBlock(newBlock1.cid)
|
not (await store.hasBlock(newBlock1.cid)).tryGet()
|
||||||
store.hasBlock(newBlock2.cid)
|
(await store.hasBlock(newBlock2.cid)).tryGet()
|
||||||
store.hasBlock(newBlock3.cid)
|
(await store.hasBlock(newBlock2.cid)).tryGet()
|
||||||
store.currentSize == newBlock2.data.len + newBlock3.data.len # 200
|
store.currentSize == newBlock2.data.len + newBlock3.data.len # 200
|
||||||
|
|
||||||
test "getBlock":
|
test "getBlock":
|
||||||
store = CacheStore.new(@[newBlock])
|
store = CacheStore.new(@[newBlock])
|
||||||
|
|
||||||
let blk = await store.getBlock(newBlock.cid)
|
let blk = await store.getBlock(newBlock.cid)
|
||||||
|
check blk.tryGet().get() == newBlock
|
||||||
check:
|
|
||||||
blk.isOk
|
|
||||||
blk.get == newBlock
|
|
||||||
|
|
||||||
test "fail getBlock":
|
test "fail getBlock":
|
||||||
let blk = await store.getBlock(newBlock.cid)
|
let blk = await store.getBlock(newBlock.cid)
|
||||||
|
check blk.tryGet().isNone()
|
||||||
check:
|
|
||||||
blk.isErr
|
|
||||||
blk.error of system.KeyError
|
|
||||||
|
|
||||||
test "hasBlock":
|
test "hasBlock":
|
||||||
let store = CacheStore.new(@[newBlock])
|
let store = CacheStore.new(@[newBlock])
|
||||||
|
check:
|
||||||
check store.hasBlock(newBlock.cid)
|
(await store.hasBlock(newBlock.cid)).tryGet()
|
||||||
|
await newBlock.cid in store
|
||||||
|
|
||||||
test "fail hasBlock":
|
test "fail hasBlock":
|
||||||
check not store.hasBlock(newBlock.cid)
|
check:
|
||||||
|
not (await store.hasBlock(newBlock.cid)).tryGet()
|
||||||
|
not (await newBlock.cid in store)
|
||||||
|
|
||||||
test "delBlock":
|
test "delBlock":
|
||||||
# empty cache
|
# empty cache
|
||||||
(await store.delBlock(newBlock1.cid)).tryGet()
|
(await store.delBlock(newBlock1.cid)).tryGet()
|
||||||
|
check not (await store.hasBlock(newBlock1.cid)).tryGet()
|
||||||
|
|
||||||
|
(await store.putBlock(newBlock1)).tryGet()
|
||||||
|
check (await store.hasBlock(newBlock1.cid)).tryGet()
|
||||||
|
|
||||||
# successfully deleted
|
# successfully deleted
|
||||||
discard await store.putBlock(newBlock1)
|
|
||||||
(await store.delBlock(newBlock1.cid)).tryGet()
|
(await store.delBlock(newBlock1.cid)).tryGet()
|
||||||
|
check not (await store.hasBlock(newBlock1.cid)).tryGet()
|
||||||
|
|
||||||
# deletes item should decrement size
|
# deletes item should decrement size
|
||||||
store = CacheStore.new(@[newBlock1, newBlock2, newBlock3])
|
store = CacheStore.new(@[newBlock1, newBlock2, newBlock3])
|
||||||
check:
|
check:
|
||||||
store.currentSize == 300
|
store.currentSize == 300
|
||||||
|
|
||||||
(await store.delBlock(newBlock2.cid)).tryGet()
|
(await store.delBlock(newBlock2.cid)).tryGet()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
store.currentSize == 200
|
store.currentSize == 200
|
||||||
newBlock2.cid notin store
|
not (await store.hasBlock(newBlock2.cid)).tryGet()
|
||||||
|
|
||||||
test "listBlocks":
|
test "listBlocks":
|
||||||
discard await store.putBlock(newBlock1)
|
(await store.putBlock(newBlock1)).tryGet()
|
||||||
|
|
||||||
var listed = false
|
var listed = false
|
||||||
await store.listBlocks(
|
(await store.listBlocks(
|
||||||
proc(cid: Cid) {.gcsafe, async.} =
|
proc(cid: Cid) {.gcsafe, async.} =
|
||||||
check cid in store
|
check (await store.hasBlock(cid)).tryGet()
|
||||||
listed = true
|
listed = true
|
||||||
)
|
)).tryGet()
|
||||||
|
|
||||||
check listed
|
check listed
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import std/os
|
import std/os
|
||||||
|
import std/options
|
||||||
|
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
import pkg/questionable/results
|
import pkg/questionable/results
|
||||||
|
@ -33,36 +34,43 @@ suite "FS Store":
|
||||||
removeDir(repoDir)
|
removeDir(repoDir)
|
||||||
|
|
||||||
test "putBlock":
|
test "putBlock":
|
||||||
check await store.putBlock(newBlock)
|
(await store.putBlock(newBlock)).tryGet()
|
||||||
check fileExists(store.blockPath(newBlock.cid))
|
check:
|
||||||
check newBlock.cid in store
|
fileExists(store.blockPath(newBlock.cid))
|
||||||
|
(await store.hasBlock(newBlock.cid)).tryGet()
|
||||||
|
await newBlock.cid in store
|
||||||
|
|
||||||
test "getBlock":
|
test "getBlock":
|
||||||
createDir(store.blockPath(newBlock.cid).parentDir)
|
createDir(store.blockPath(newBlock.cid).parentDir)
|
||||||
writeFile(store.blockPath(newBlock.cid), newBlock.data)
|
writeFile(store.blockPath(newBlock.cid), newBlock.data)
|
||||||
let blk = await store.getBlock(newBlock.cid)
|
let blk = await store.getBlock(newBlock.cid)
|
||||||
check blk.option == newBlock.some
|
check blk.tryGet().get() == newBlock
|
||||||
|
|
||||||
test "fail getBlock":
|
test "fail getBlock":
|
||||||
let blk = await store.getBlock(newBlock.cid)
|
let blk = await store.getBlock(newBlock.cid)
|
||||||
check blk.isErr
|
check blk.tryGet().isNone
|
||||||
|
|
||||||
test "hasBlock":
|
test "hasBlock":
|
||||||
createDir(store.blockPath(newBlock.cid).parentDir)
|
createDir(store.blockPath(newBlock.cid).parentDir)
|
||||||
writeFile(store.blockPath(newBlock.cid), newBlock.data)
|
writeFile(store.blockPath(newBlock.cid), newBlock.data)
|
||||||
|
|
||||||
check store.hasBlock(newBlock.cid)
|
check:
|
||||||
|
(await store.hasBlock(newBlock.cid)).tryGet()
|
||||||
|
await newBlock.cid in store
|
||||||
|
|
||||||
|
test "fail hasBlock":
|
||||||
|
check:
|
||||||
|
not (await store.hasBlock(newBlock.cid)).tryGet()
|
||||||
|
not (await newBlock.cid in store)
|
||||||
|
|
||||||
test "listBlocks":
|
test "listBlocks":
|
||||||
createDir(store.blockPath(newBlock.cid).parentDir)
|
createDir(store.blockPath(newBlock.cid).parentDir)
|
||||||
writeFile(store.blockPath(newBlock.cid), newBlock.data)
|
writeFile(store.blockPath(newBlock.cid), newBlock.data)
|
||||||
|
|
||||||
await store.listBlocks(
|
(await store.listBlocks(
|
||||||
proc(cid: Cid) {.gcsafe, async.} =
|
proc(cid: Cid) {.gcsafe, async.} =
|
||||||
check cid == newBlock.cid)
|
check cid == newBlock.cid
|
||||||
|
)).tryGet()
|
||||||
test "fail hasBlock":
|
|
||||||
check not store.hasBlock(newBlock.cid)
|
|
||||||
|
|
||||||
test "delBlock":
|
test "delBlock":
|
||||||
createDir(store.blockPath(newBlock.cid).parentDir)
|
createDir(store.blockPath(newBlock.cid).parentDir)
|
||||||
|
|
|
@ -37,7 +37,7 @@ suite "Erasure encode/decode":
|
||||||
|
|
||||||
let blk = bt.Block.new(chunk).tryGet()
|
let blk = bt.Block.new(chunk).tryGet()
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
check (await store.putBlock(blk))
|
(await store.putBlock(blk)).tryGet()
|
||||||
|
|
||||||
proc encode(buffers, parity: int): Future[Manifest] {.async.} =
|
proc encode(buffers, parity: int): Future[Manifest] {.async.} =
|
||||||
let
|
let
|
||||||
|
@ -78,7 +78,8 @@ suite "Erasure encode/decode":
|
||||||
decoded.len == encoded.originalLen
|
decoded.len == encoded.originalLen
|
||||||
|
|
||||||
for d in dropped:
|
for d in dropped:
|
||||||
check d in store
|
let present = await store.hasBlock(d)
|
||||||
|
check present.tryGet()
|
||||||
|
|
||||||
test "Should not tolerate loosing more than M data blocks in a single random column":
|
test "Should not tolerate loosing more than M data blocks in a single random column":
|
||||||
const
|
const
|
||||||
|
@ -103,7 +104,8 @@ suite "Erasure encode/decode":
|
||||||
decoded = (await erasure.decode(encoded)).tryGet()
|
decoded = (await erasure.decode(encoded)).tryGet()
|
||||||
|
|
||||||
for d in dropped:
|
for d in dropped:
|
||||||
check d notin store
|
let present = await store.hasBlock(d)
|
||||||
|
check not present.tryGet()
|
||||||
|
|
||||||
test "Should tolerate loosing M data blocks in M random columns":
|
test "Should tolerate loosing M data blocks in M random columns":
|
||||||
const
|
const
|
||||||
|
@ -130,7 +132,8 @@ suite "Erasure encode/decode":
|
||||||
discard (await erasure.decode(encoded)).tryGet()
|
discard (await erasure.decode(encoded)).tryGet()
|
||||||
|
|
||||||
for d in manifest:
|
for d in manifest:
|
||||||
check d in store
|
let present = await store.hasBlock(d)
|
||||||
|
check present.tryGet()
|
||||||
|
|
||||||
test "Should not tolerate loosing more than M data blocks in M random columns":
|
test "Should not tolerate loosing more than M data blocks in M random columns":
|
||||||
const
|
const
|
||||||
|
@ -179,7 +182,8 @@ suite "Erasure encode/decode":
|
||||||
discard (await erasure.decode(encoded)).tryGet()
|
discard (await erasure.decode(encoded)).tryGet()
|
||||||
|
|
||||||
for d in manifest:
|
for d in manifest:
|
||||||
check d in store
|
let present = await store.hasBlock(d)
|
||||||
|
check present.tryGet()
|
||||||
|
|
||||||
test "Should tolerate loosing M (a.k.a row) contiguous parity blocks":
|
test "Should tolerate loosing M (a.k.a row) contiguous parity blocks":
|
||||||
const
|
const
|
||||||
|
@ -194,7 +198,8 @@ suite "Erasure encode/decode":
|
||||||
discard (await erasure.decode(encoded)).tryGet()
|
discard (await erasure.decode(encoded)).tryGet()
|
||||||
|
|
||||||
for d in manifest:
|
for d in manifest:
|
||||||
check d in store
|
let present = await store.hasBlock(d)
|
||||||
|
check present.tryGet()
|
||||||
|
|
||||||
test "handles edge case of 0 parity blocks":
|
test "handles edge case of 0 parity blocks":
|
||||||
const
|
const
|
||||||
|
|
|
@ -85,10 +85,10 @@ suite "Test Node":
|
||||||
manifestCid = (await storeFut).tryGet()
|
manifestCid = (await storeFut).tryGet()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
manifestCid in localStore
|
(await localStore.hasBlock(manifestCid)).tryGet()
|
||||||
|
|
||||||
var
|
var
|
||||||
manifestBlock = (await localStore.getBlock(manifestCid)).tryGet()
|
manifestBlock = (await localStore.getBlock(manifestCid)).tryGet().get()
|
||||||
localManifest = Manifest.decode(manifestBlock.data).tryGet()
|
localManifest = Manifest.decode(manifestBlock.data).tryGet()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
|
@ -104,20 +104,18 @@ suite "Test Node":
|
||||||
let chunk = await chunker.getBytes();
|
let chunk = await chunker.getBytes();
|
||||||
chunk.len > 0):
|
chunk.len > 0):
|
||||||
|
|
||||||
let
|
let blk = bt.Block.new(chunk).tryGet()
|
||||||
blk = bt.Block.new(chunk).tryGet()
|
|
||||||
|
|
||||||
original &= chunk
|
original &= chunk
|
||||||
check await localStore.putBlock(blk)
|
(await localStore.putBlock(blk)).tryGet()
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
|
|
||||||
let
|
let
|
||||||
manifestBlock = bt.Block.new(
|
manifestBlock = bt.Block.new(
|
||||||
manifest.encode().tryGet(),
|
manifest.encode().tryGet(),
|
||||||
codec = DagPBCodec)
|
codec = DagPBCodec
|
||||||
.tryGet()
|
).tryGet()
|
||||||
|
|
||||||
check await localStore.putBlock(manifestBlock)
|
(await localStore.putBlock(manifestBlock)).tryGet()
|
||||||
|
|
||||||
let stream = (await node.retrieve(manifestBlock.cid)).tryGet()
|
let stream = (await node.retrieve(manifestBlock.cid)).tryGet()
|
||||||
var data: seq[byte]
|
var data: seq[byte]
|
||||||
|
@ -125,9 +123,7 @@ suite "Test Node":
|
||||||
var
|
var
|
||||||
buf = newSeq[byte](BlockSize)
|
buf = newSeq[byte](BlockSize)
|
||||||
res = await stream.readOnce(addr buf[0], BlockSize div 2)
|
res = await stream.readOnce(addr buf[0], BlockSize div 2)
|
||||||
|
|
||||||
buf.setLen(res)
|
buf.setLen(res)
|
||||||
|
|
||||||
data &= buf
|
data &= buf
|
||||||
|
|
||||||
check data == original
|
check data == original
|
||||||
|
@ -137,7 +133,7 @@ suite "Test Node":
|
||||||
testString = "Block 1"
|
testString = "Block 1"
|
||||||
blk = bt.Block.new(testString.toBytes).tryGet()
|
blk = bt.Block.new(testString.toBytes).tryGet()
|
||||||
|
|
||||||
check (await localStore.putBlock(blk))
|
(await localStore.putBlock(blk)).tryGet()
|
||||||
let stream = (await node.retrieve(blk.cid)).tryGet()
|
let stream = (await node.retrieve(blk.cid)).tryGet()
|
||||||
|
|
||||||
var data = newSeq[byte](testString.len)
|
var data = newSeq[byte](testString.len)
|
||||||
|
|
|
@ -16,6 +16,13 @@ suite "StoreStream":
|
||||||
store: BlockStore
|
store: BlockStore
|
||||||
stream: StoreStream
|
stream: StoreStream
|
||||||
|
|
||||||
|
# Check that `buf` contains `size` bytes with values start, start+1...
|
||||||
|
proc sequential_bytes(buf: seq[byte], size: int, start: int): bool =
|
||||||
|
for i in 0..<size:
|
||||||
|
if int(buf[i]) != start+i:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
let
|
let
|
||||||
data = [
|
data = [
|
||||||
[byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
[byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||||
|
@ -36,59 +43,63 @@ suite "StoreStream":
|
||||||
stream = StoreStream.new(store, manifest)
|
stream = StoreStream.new(store, manifest)
|
||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
let
|
let blk = bt.Block.new(d).tryGet()
|
||||||
blk = bt.Block.new(d).tryGet()
|
|
||||||
|
|
||||||
manifest.add(blk.cid)
|
manifest.add(blk.cid)
|
||||||
if not (await store.putBlock(blk)):
|
(await store.putBlock(blk)).tryGet()
|
||||||
raise newException(CatchableError, "Unable to store block " & $blk.cid)
|
|
||||||
|
|
||||||
test "Read all blocks < blockSize":
|
test "Read all blocks < blockSize":
|
||||||
var
|
var
|
||||||
buf = newSeq[byte](8)
|
buf = newSeq[byte](8)
|
||||||
|
n = 0
|
||||||
|
|
||||||
while not stream.atEof:
|
while not stream.atEof:
|
||||||
let
|
let read = (await stream.readOnce(addr buf[0], buf.len))
|
||||||
read = (await stream.readOnce(addr buf[0], buf.len))
|
|
||||||
|
|
||||||
if stream.atEof.not:
|
if not stream.atEof:
|
||||||
check read == 8
|
check read == 8
|
||||||
else:
|
else:
|
||||||
check read == 4
|
check read == 4
|
||||||
|
|
||||||
|
check sequential_bytes(buf,read,n)
|
||||||
|
n += read
|
||||||
|
|
||||||
test "Read all blocks == blockSize":
|
test "Read all blocks == blockSize":
|
||||||
var
|
var
|
||||||
buf = newSeq[byte](10)
|
buf = newSeq[byte](10)
|
||||||
|
n = 0
|
||||||
|
|
||||||
while not stream.atEof:
|
while not stream.atEof:
|
||||||
let
|
let read = (await stream.readOnce(addr buf[0], buf.len))
|
||||||
read = (await stream.readOnce(addr buf[0], buf.len))
|
|
||||||
|
|
||||||
check read == 10
|
check read == 10
|
||||||
|
check sequential_bytes(buf,read,n)
|
||||||
|
n += read
|
||||||
|
|
||||||
test "Read all blocks > blockSize":
|
test "Read all blocks > blockSize":
|
||||||
var
|
var
|
||||||
buf = newSeq[byte](11)
|
buf = newSeq[byte](11)
|
||||||
|
n = 0
|
||||||
|
|
||||||
while not stream.atEof:
|
while not stream.atEof:
|
||||||
let
|
let read = (await stream.readOnce(addr buf[0], buf.len))
|
||||||
read = (await stream.readOnce(addr buf[0], buf.len))
|
|
||||||
|
|
||||||
if stream.atEof.not:
|
if not stream.atEof:
|
||||||
check read == 11
|
check read == 11
|
||||||
else:
|
else:
|
||||||
check read == 1
|
check read == 1
|
||||||
|
|
||||||
|
check sequential_bytes(buf,read,n)
|
||||||
|
n += read
|
||||||
|
|
||||||
test "Read exact bytes within block boundary":
|
test "Read exact bytes within block boundary":
|
||||||
var
|
var
|
||||||
buf = newSeq[byte](5)
|
buf = newSeq[byte](5)
|
||||||
|
|
||||||
await stream.readExactly(addr buf[0], 5)
|
await stream.readExactly(addr buf[0], 5)
|
||||||
check buf == [byte 0, 1, 2, 3, 4]
|
check sequential_bytes(buf,5,0)
|
||||||
|
|
||||||
test "Read exact bytes outside of block boundary":
|
test "Read exact bytes outside of block boundary":
|
||||||
var
|
var
|
||||||
buf = newSeq[byte](15)
|
buf = newSeq[byte](15)
|
||||||
|
|
||||||
await stream.readExactly(addr buf[0], 15)
|
await stream.readExactly(addr buf[0], 15)
|
||||||
check buf == [byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
|
check sequential_bytes(buf,15,0)
|
||||||
|
|
Loading…
Reference in New Issue