-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Asyncify the Tree Building #975
base: master
Are you sure you want to change the base?
Changes from 10 commits
cad74cc
599a522
c560720
9f94536
f8e713f
33a37c6
00fe07a
3934671
7e0328d
1bc915e
8aaff32
1801120
668eabc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,16 +145,18 @@ | |
mhash.coder(@x & @y & @[ key.byte ], digest) | ||
success digest | ||
|
||
func init*( | ||
proc init*( | ||
_: type CodexTree, | ||
mcodec: MultiCodec = Sha256HashCodec, | ||
leaves: openArray[ByteHash]): ?!CodexTree = | ||
leaves: seq[ByteHash]): Future[?!CodexTree] {.async.} = | ||
|
||
if leaves.len == 0: | ||
return failure "Empty leaves" | ||
|
||
without mhash =? mcodec.mhash(), error: | ||
return failure error | ||
|
||
let | ||
mhash = ? mcodec.mhash() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What the heck did this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 OK this raises an exception, so this change is actually modifying behavior. I suppose this is OK if it doesn't break expectations at callers, but we could simply preserve this behavior and propagate the exception using a Chronos raises clause. I see you've done this change in several places, and I'm assuming you did the due diligence to ensure this doesn't cause surprises, so I won't block your PR for this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That would actually change the behaviour. Imo, the proposed change here does not alter the behaviour. |
||
compressor = proc(x, y: seq[byte], key: ByteTreeKey): ?!ByteHash {.noSideEffect.} = | ||
compress(x, y, key, mhash) | ||
Zero: ByteHash = newSeq[byte](mhash.size) | ||
|
@@ -165,33 +167,42 @@ | |
var | ||
self = CodexTree(mcodec: mcodec, compress: compressor, zero: Zero) | ||
|
||
self.layers = ? merkleTreeWorker(self, leaves, isBottomLayer = true) | ||
without layers =? (await merkleTreeWorker(self, leaves, isBottomLayer = true)), error: | ||
return failure error | ||
|
||
self.layers = layers | ||
success self | ||
|
||
func init*( | ||
proc init*( | ||
_: type CodexTree, | ||
leaves: openArray[MultiHash]): ?!CodexTree = | ||
leaves: seq[MultiHash]): Future[?!CodexTree] {.async.} = | ||
|
||
if leaves.len == 0: | ||
return failure "Empty leaves" | ||
|
||
let | ||
mcodec = leaves[0].mcodec | ||
leaves = leaves.mapIt( it.digestBytes ) | ||
leafBytes = leaves.mapIt( it.digestBytes ) | ||
|
||
CodexTree.init(mcodec, leaves) | ||
await CodexTree.init(mcodec, leafBytes) | ||
|
||
func init*( | ||
proc init*( | ||
_: type CodexTree, | ||
leaves: openArray[Cid]): ?!CodexTree = | ||
leaves: seq[Cid]): Future[?!CodexTree] {.async.} = | ||
|
||
if leaves.len == 0: | ||
return failure "Empty leaves" | ||
|
||
let | ||
mcodec = (? leaves[0].mhash.mapFailure).mcodec | ||
leaves = leaves.mapIt( (? it.mhash.mapFailure).digestBytes ) | ||
without mhash =? leaves[0].mhash.mapFailure, error: | ||
return failure error | ||
|
||
var hashes = newSeq[seq[byte]]() | ||
for leaf in leaves: | ||
without hash =? leaf.mhash.mapFailure, error: | ||
return failure error | ||
hashes.add(hash.digestBytes) | ||
Comment on lines
+196
to
+203
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did this really need a rewrite? In other words, was there a compilation issue that forced this re-write? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I couldn't figure out a cleaner way to unroll the results. If you've got one, I'm eager to learn. |
||
|
||
CodexTree.init(mcodec, leaves) | ||
await CodexTree.init(mhash.mcodec, hashes) | ||
|
||
proc fromNodes*( | ||
_: type CodexTree, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -161,8 +161,7 @@ | |
if blk.isEmpty: | ||
success (self.emptyBlock, self.emptyDigestTree) | ||
else: | ||
without tree =? | ||
T.digestTree(blk.data, self.cellSize.int), err: | ||
without tree =? (await T.digestTree(blk.data, self.cellSize.int)), err: | ||
error "Failed to create digest for block", err = err.msg | ||
return failure(err) | ||
|
||
|
@@ -213,7 +212,7 @@ | |
error "Failed to select slot blocks", err = err.msg | ||
return failure(err) | ||
|
||
T.init(cellHashes) | ||
await T.init(cellHashes) | ||
|
||
proc buildSlot*[T, H]( | ||
self: SlotsBuilder[T, H], | ||
|
@@ -251,8 +250,8 @@ | |
|
||
tree.root() | ||
|
||
func buildVerifyTree*[T, H](self: SlotsBuilder[T, H], slotRoots: openArray[H]): ?!T = | ||
T.init(@slotRoots) | ||
proc buildVerifyTree*[T, H](self: SlotsBuilder[T, H], slotRoots: seq[H]): Future[?!T] {.async.} = | ||
await T.init(@slotRoots) | ||
|
||
proc buildSlots*[T, H](self: SlotsBuilder[T, H]): Future[?!void] {.async.} = | ||
## Build all slot trees and store them in the block store. | ||
|
@@ -272,7 +271,7 @@ | |
return failure(err) | ||
slotRoot | ||
|
||
without tree =? self.buildVerifyTree(self.slotRoots) and root =? tree.root, err: | ||
without tree =? (await self.buildVerifyTree(self.slotRoots)) and root =? tree.root, err: | ||
error "Failed to build slot roots tree", err = err.msg | ||
return failure(err) | ||
|
||
|
@@ -305,6 +304,29 @@ | |
self.cellSize, | ||
self.strategy.strategyType) | ||
|
||
proc init*[T, H](self: SlotsBuilder[T, H]): Future[?!void] {.async.} = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Hm... I think the fact that you need to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree that an Maybe an easy fix is to call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A lot of this seems like it was replacing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't have changed them if I didn't have to. |
||
without emptyTree =? (await T.digestTree(self.emptyBlock, self.cellSize.int)), err: | ||
return failure err | ||
self.emptyDigestTree = emptyTree | ||
|
||
if self.manifest.verifiable: | ||
without tree =? (await self.buildVerifyTree(self.slotRoots)), err: | ||
return failure err | ||
|
||
without verifyRoot =? tree.root, err: | ||
return failure err | ||
|
||
without expectedRoot =? self.manifest.verifyRoot.fromVerifyCid(), err: | ||
return failure err | ||
|
||
if verifyRoot != expectedRoot: | ||
return failure "Existing slots root doesn't match reconstructed root." | ||
|
||
self.verifiableTree = some tree | ||
|
||
trace "Slots builder initialized" | ||
success() | ||
|
||
proc new*[T, H]( | ||
_: type SlotsBuilder[T, H], | ||
store: BlockStore, | ||
|
@@ -346,7 +368,6 @@ | |
numBlocksTotal = numSlotBlocksTotal * manifest.numSlots # number of blocks per slot | ||
|
||
emptyBlock = newSeq[byte](manifest.blockSize.int) | ||
emptyDigestTree = ? T.digestTree(emptyBlock, cellSize.int) | ||
|
||
strategy = ? strategy.init( | ||
0, | ||
|
@@ -372,24 +393,13 @@ | |
strategy: strategy, | ||
cellSize: cellSize, | ||
emptyBlock: emptyBlock, | ||
numSlotBlocks: numSlotBlocksTotal, | ||
emptyDigestTree: emptyDigestTree) | ||
numSlotBlocks: numSlotBlocksTotal) | ||
|
||
if manifest.verifiable: | ||
if manifest.slotRoots.len == 0 or | ||
manifest.slotRoots.len != manifest.numSlots: | ||
return failure "Manifest is verifiable but slot roots are missing or invalid." | ||
|
||
let | ||
slotRoots = manifest.slotRoots.mapIt( (? it.fromSlotCid() )) | ||
tree = ? self.buildVerifyTree(slotRoots) | ||
expectedRoot = ? manifest.verifyRoot.fromVerifyCid() | ||
verifyRoot = ? tree.root | ||
|
||
if verifyRoot != expectedRoot: | ||
return failure "Existing slots root doesn't match reconstructed root." | ||
|
||
self.slotRoots = slotRoots | ||
self.verifiableTree = some tree | ||
self.slotRoots = self.manifest.slotRoots.mapIt( (? it.fromSlotCid() )) | ||
|
||
success self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might need to avoid making this
init
async so we can avoid usingwaitFor
later on... instead this is a good pattern to follow:https://github.com/codex-storage/nim-ethers/blob/80b2ead97ce32cca74cef4a4501ab97106cb578b/ethers/providers/jsonrpc.nim#L50-L86
Then later, we can use
CodexTree.init
synchronously, and when needed, we canlet layers = await tree.layers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this pattern. If I applied it, SlotBuilder verifyTree and verifyRoot funcs would have to become async procs to await for the internal field. This would propagate the asycing through the codebase further. Is this desirable? Or is there a way around this?