From fffb674bbad1cad8d117af34ac1fdbf72275c90b Mon Sep 17 00:00:00 2001 From: Dmitriy Ryajov Date: Mon, 8 Jan 2024 16:52:46 -0600 Subject: [PATCH] Integrate slot builder (#666) * rework merkle tree support * rename merkletree -> codexmerkletree * treed and proof encoding/decoding * style * adding codex merkle and coders tests * use default hash codec * proof size changed * add from nodes test * shorte file names * wip poseidon tree * shorten file names * root returns a result * import poseidon tests * fix merge issues and cleanup a few warnings * setting up slot builder * Getting cids in slot * ensures blocks are devisable by number of slots * wip * Implements indexing strategies * Swaps in indexing strategy into erasure. * wires slot and indexing tests up * Fixes issue where indexing strategy stepped gives wrong values for smallest of ranges * debugs indexing strategies * Can select slot blocks * finding number of pad cells * Implements building slot tree * finishes implementing slot builder * Adds check that block size is a multiple of cell size * Cleanup slotbuilder * Review comments by Tomasz * Fixes issue where ecK was used as numberOfSlots. * rework merkle tree support * deps * rename merkletree -> codexmerkletree * treed and proof encoding/decoding * style * adding codex merkle and coders tests * remove new codecs for now * proof size changed * add from nodes test * shorte file names * wip poseidon tree * shorten file names * fix bad `elements` iter * bump * bump * wip * reworking slotbuilder * move out of manifest * expose getCidAndProof * import index strat... * remove getMHash * remove unused artifacts * alias zero * add digest for multihash * merge issues * remove unused hashes * add option to result converter * misc * fix tests * add helper to derive EC block count * rename method * misc * bump * extract slot root building into own proc * revert to manifest to accessor --------- Co-authored-by: benbierens --- codex/blockexchange/engine/engine.nim | 7 +- codex/codextypes.nim | 7 +- codex/erasure/erasure.nim | 20 +- codex/errors.nim | 8 + codex/indexingstrategy.nim | 61 ++++ codex/manifest/manifest.nim | 5 + codex/merkletree/codex/codex.nim | 44 ++- codex/merkletree/merkletree.nim | 7 - codex/merkletree/poseidon2.nim | 7 +- codex/node.nim | 2 +- codex/slots/slotbuilder.nim | 275 ++++++++++++++++ codex/stores/blockstore.nim | 20 +- codex/stores/cachestore.nim | 15 +- codex/stores/networkstore.nim | 4 +- codex/stores/repostore.nim | 12 +- codex/stores/treehelper.nim | 2 +- codex/utils/digest.nim | 13 + tests/codex/helpers.nim | 10 +- tests/codex/merkletree/testcodextree.nim | 8 +- tests/codex/merkletree/testposeidon2tree.nim | 9 +- tests/codex/slotbuilder/testslotbuilder.nim | 324 +++++++++++++++++++ tests/codex/testerasure.nim | 2 +- tests/codex/testindexingstrategy.nim | 65 ++++ tests/codex/testslotbuilder.nim | 3 + tests/testCodex.nim | 2 + vendor/nim-libp2p | 2 +- vendor/nim-poseidon2 | 2 +- 27 files changed, 862 insertions(+), 74 deletions(-) create mode 100644 codex/indexingstrategy.nim create mode 100644 codex/slots/slotbuilder.nim create mode 100644 tests/codex/slotbuilder/testslotbuilder.nim create mode 100644 tests/codex/testindexingstrategy.nim create mode 100644 tests/codex/testslotbuilder.nim diff --git a/codex/blockexchange/engine/engine.nim b/codex/blockexchange/engine/engine.nim index 23683718..649edec6 100644 --- a/codex/blockexchange/engine/engine.nim +++ b/codex/blockexchange/engine/engine.nim @@ -361,7 +361,12 @@ proc blocksDeliveryHandler*( without proof =? bd.proof: error "Proof expected for a leaf block delivery" continue - if err =? (await b.localStore.putBlockCidAndProof(bd.address.treeCid, bd.address.index, bd.blk.cid, proof)).errorOption: + if err =? (await b.localStore.putCidAndProof( + bd.address.treeCid, + bd.address.index, + bd.blk.cid, + proof)).errorOption: + error "Unable to store proof and cid for a block" continue diff --git a/codex/codextypes.nim b/codex/codextypes.nim index ddf0ddff..b2d63225 100644 --- a/codex/codextypes.nim +++ b/codex/codextypes.nim @@ -39,6 +39,7 @@ const BlockCodec* = multiCodec("codex-block") SlotRootCodec* = multiCodec("codex-slot-root") SlotProvingRootCodec* = multiCodec("codex-proving-root") + CodexSlotCell* = multiCodec("codex-slot-cell") CodexHashesCodecs* = [ Sha256HashCodec, @@ -52,15 +53,15 @@ const BlockCodec, SlotRootCodec, SlotProvingRootCodec, + CodexSlotCell, ] - proc initEmptyCidTable(): ?!Table[(CidVersion, MultiCodec, MultiCodec), Cid] = ## Initialize padding blocks table ## ## TODO: Ideally this is done at compile time, but for now ## we do it at runtime because of an `importc` error that is - ## coming from somwhere in MultiHash that I can't track down. + ## coming from somewhere in MultiHash that I can't track down. ## let @@ -68,8 +69,6 @@ proc initEmptyCidTable(): ?!Table[(CidVersion, MultiCodec, MultiCodec), Cid] = PadHashes = { Sha256HashCodec: ? MultiHash.digest($Sha256HashCodec, emptyData).mapFailure, Sha512HashCodec: ? MultiHash.digest($Sha512HashCodec, emptyData).mapFailure, - Pos2Bn128SpngCodec: ? MultiHash.digest($Pos2Bn128SpngCodec, emptyData).mapFailure, - Pos2Bn128MrklCodec: ? MultiHash.digest($Pos2Bn128SpngCodec, emptyData).mapFailure, }.toTable var diff --git a/codex/erasure/erasure.nim b/codex/erasure/erasure.nim index 19013b4d..cd10b53a 100644 --- a/codex/erasure/erasure.nim +++ b/codex/erasure/erasure.nim @@ -25,6 +25,7 @@ import ../stores import ../blocktype as bt import ../utils import ../utils/asynciter +import ../indexingstrategy import pkg/stew/byteutils @@ -127,7 +128,12 @@ proc prepareEncodingData( ## let - indicies = toSeq(countup(step, params.rounded - 1, params.steps)) + strategy = SteppedIndexingStrategy.new( + firstIndex = 0, + lastIndex = params.rounded - 1, + numberOfIterations = params.steps + ) + indicies = strategy.getIndicies(step) pendingBlocksIter = self.getPendingBlocks(manifest, indicies.filterIt(it < manifest.blocksCount)) var resolved = 0 @@ -171,7 +177,12 @@ proc prepareDecodingData( ## let - indicies = toSeq(countup(step, encoded.blocksCount - 1, encoded.steps)) + strategy = SteppedIndexingStrategy.new( + firstIndex = 0, + lastIndex = encoded.blocksCount - 1, + numberOfIterations = encoded.steps + ) + indicies = strategy.getIndicies(step) pendingBlocksIter = self.getPendingBlocks(encoded, indicies) var @@ -213,7 +224,10 @@ proc prepareDecodingData( return success (dataPieces, parityPieces) -proc init(_: type EncodingParams, manifest: Manifest, ecK: int, ecM: int): ?!EncodingParams = +proc init*( + _: type EncodingParams, + manifest: Manifest, + ecK: int, ecM: int): ?!EncodingParams = if ecK > manifest.blocksCount: return failure("Unable to encode manifest, not enough blocks, ecK = " & $ecK & ", blocksCount = " & $manifest.blocksCount) diff --git a/codex/errors.nim b/codex/errors.nim index ebb07f04..ff2789b9 100644 --- a/codex/errors.nim +++ b/codex/errors.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +import std/options + import pkg/stew/results import pkg/chronos import pkg/questionable/results @@ -29,6 +31,12 @@ template mapFailure*[T, V, E]( template mapFailure*[T, V](exp: Result[T, V]): Result[T, ref CatchableError] = mapFailure(exp, CodexError) +template toResult*[T](exp: Option[T]): Result[T, ref CatchableError] = + if exp.isSome: + success exp.get + else: + T.failure("Option is None") + proc allFutureResult*[T](fut: seq[Future[T]]): Future[?!void] {.async.} = try: await allFuturesThrowing(fut) diff --git a/codex/indexingstrategy.nim b/codex/indexingstrategy.nim new file mode 100644 index 00000000..b7774765 --- /dev/null +++ b/codex/indexingstrategy.nim @@ -0,0 +1,61 @@ +import std/sequtils +import ./utils + +# I'm choosing to use an assert here because: +# 1. These are a programmer errors and *should not* happen during application runtime. +# 2. Users don't have to deal with Result types. + +type + # Representing a strategy for grouping indices (of blocks usually) + # Given an interation-count as input, will produce a seq of + # selected indices. + IndexingStrategy* = ref object of RootObj + firstIndex*: int # Lowest index that can be returned + lastIndex*: int # Highest index that can be returned + numberOfIterations*: int # getIndices(iteration) will run from 0 ..< numberOfIterations + step*: int + + # Simplest approach: + # 0 => 0, 1, 2 + # 1 => 3, 4, 5 + # 2 => 6, 7, 8 + LinearIndexingStrategy* = ref object of IndexingStrategy + + # Stepped indexing: + # 0 => 0, 3, 6 + # 1 => 1, 4, 7 + # 2 => 2, 5, 8 + SteppedIndexingStrategy* = ref object of IndexingStrategy + +proc assertIteration(self: IndexingStrategy, iteration: int): void = + if iteration >= self.numberOfIterations: + raiseAssert("Indexing iteration can't be greater than or equal to numberOfIterations.") + +method getIndicies*(self: IndexingStrategy, iteration: int): seq[int] {.base.} = + raiseAssert("Not implemented") + +proc new*(T: type IndexingStrategy, firstIndex, lastIndex, numberOfIterations: int): T = + if firstIndex > lastIndex: + raiseAssert("firstIndex (" & $firstIndex & ") can't be greater than lastIndex (" & $lastIndex & ")") + if numberOfIterations <= 0: + raiseAssert("numberOfIteration (" & $numberOfIterations & ") must be greater than zero.") + + T( + firstIndex: firstIndex, + lastIndex: lastIndex, + numberOfIterations: numberOfIterations, + step: divUp((lastIndex - firstIndex), numberOfIterations) + ) + +method getIndicies*(self: LinearIndexingStrategy, iteration: int): seq[int] = + self.assertIteration(iteration) + + let + first = self.firstIndex + iteration * (self.step + 1) + last = min(first + self.step, self.lastIndex) + + toSeq(countup(first, last, 1)) + +method getIndicies*(self: SteppedIndexingStrategy, iteration: int): seq[int] = + self.assertIteration(iteration) + toSeq(countup(self.firstIndex + iteration, self.lastIndex, self.numberOfIterations)) diff --git a/codex/manifest/manifest.nim b/codex/manifest/manifest.nim index fcc2a704..525d4f0e 100644 --- a/codex/manifest/manifest.nim +++ b/codex/manifest/manifest.nim @@ -98,6 +98,11 @@ proc verificationRoot*(self: Manifest): Cid = proc slotRoots*(self: Manifest): seq[Cid] = self.slotRoots +proc numSlots*(self: Manifest): int = + if not self.protected: + 0 + else: + self.ecK + self.ecM ############################################################ # Operations on block list ############################################################ diff --git a/codex/merkletree/codex/codex.nim b/codex/merkletree/codex/codex.nim index 887bb1e1..ea9790d0 100644 --- a/codex/merkletree/codex/codex.nim +++ b/codex/merkletree/codex/codex.nim @@ -13,7 +13,6 @@ push: {.upraises: [].} import std/bitops import std/sequtils - import pkg/questionable import pkg/questionable/results import pkg/libp2p/[cid, multicodec, multihash] @@ -40,15 +39,15 @@ type ByteHash* = seq[byte] ByteTree* = MerkleTree[ByteHash, ByteTreeKey] - ByteTreeProof* = MerkleProof[ByteHash, ByteTreeKey] + ByteProof* = MerkleProof[ByteHash, ByteTreeKey] CodexTree* = ref object of ByteTree - mhash: MHash + mcodec*: MultiCodec - CodexProof* = ref object of ByteTreeProof - mhash: MHash + CodexProof* = ref object of ByteProof + mcodec*: MultiCodec -func getMhash*(mcodec: MultiCodec): ?!MHash = +func mhash*(mcodec: MultiCodec): ?!MHash = let mhash = CodeHashes.getOrDefault(mcodec) @@ -63,21 +62,15 @@ func digestSize*(self: (CodexTree or CodexProof)): int = self.mhash.size -func mcodec*(self: (CodexTree or CodexProof)): MultiCodec = - ## Multicodec - ## - - self.mhash.mcodec - -func bytes*(mhash: MultiHash): seq[byte] = - ## Extract hash bytes +func digestBytes*(mhash: MultiHash): seq[byte] = + ## Extract hash digestBytes ## mhash.data.buffer[mhash.dpos..