From 66de36a9ecc67c98ee858faf555b8a8dd2ea2b5f Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 11 Jan 2024 16:22:10 +0100 Subject: [PATCH] Use tree height instead of chunk limit for merkleizer (#74) More closely matches implementation and avoids generic bloat. Renamed to avoid accidental limit-vs-height confusion. --- ssz_serialization/merkleization.nim | 133 ++++++++++++++++------------ 1 file changed, 74 insertions(+), 59 deletions(-) diff --git a/ssz_serialization/merkleization.nim b/ssz_serialization/merkleization.nim index 727c045..5922501 100644 --- a/ssz_serialization/merkleization.nim +++ b/ssz_serialization/merkleization.nim @@ -51,9 +51,9 @@ func binaryTreeHeight*(totalElements: Limit): int = bitWidth nextPow2(uint64 totalElements) type - SszMerkleizer*[limit: static[Limit]] = object + SszMerkleizer2*[height: static[int]] = object ## The Merkleizer incrementally computes the SSZ-style Merkle root of a tree - ## with `limit` leaf nodes. + ## with `2**(height-1)` leaf nodes. ## ## As chunks are added, the combined hash of each pair of chunks is computed ## and partially propagated up the tree in the `combinedChunks` array - @@ -65,17 +65,26 @@ type # `sha256.update` otherwise would do. # The two digests represent the left and right nodes that get combined to # a parent node in the tree. + # `SszMerkleizer` used chunk count as limit # TODO it's possible to further parallelize by using even wider buffers here - combinedChunks: array[binaryTreeHeight limit, (Digest, Digest)] + combinedChunks: array[height, (Digest, Digest)] totalChunks*: uint64 # Public for historical reasons topIndex: int internal: bool + # Avoid copying chunk data into merkleizer when not needed - maw result + # in an incomplete root-to-leaf proof -template getChunkCount*(m: SszMerkleizer): uint64 = +template limit*(T: type SszMerkleizer2): Limit = + if T.height == 0: 0'i64 else: 1'i64 shl (T.height - 1) + +template limit*(v: SszMerkleizer2): Limit = + typeof(v).limit + +template getChunkCount*(m: SszMerkleizer2): uint64 = m.totalChunks -func getCombinedChunks*(m: SszMerkleizer): seq[Digest] = +func getCombinedChunks*(m: SszMerkleizer2): seq[Digest] = mapIt(toOpenArray(m.combinedChunks, 0, m.topIndex), it[0]) when USE_BLST_SHA256: @@ -182,7 +191,7 @@ func computeZeroHashes: array[sizeof(Limit) * 8, Digest] = const zeroHashes* = computeZeroHashes() -template combineChunks(merkleizer: var SszMerkleizer, start: int) = +func combineChunks(merkleizer: var SszMerkleizer2, start: int) = for i in start.. 0 and data.len <= bytesPerChunk when merkleizer.limit > 0: @@ -233,7 +246,7 @@ func addChunk*(merkleizer: var SszMerkleizer, data: openArray[byte]) = template isOdd(x: SomeNumber): bool = (x and 1) != 0 -func addChunks*(merkleizer: var SszMerkleizer, data: openArray[byte]) = +func addChunks*(merkleizer: var SszMerkleizer2, data: openArray[byte]) = doAssert merkleizer.totalChunks == 0 doAssert merkleizer.limit * bytesPerChunk >= data.len, "Adding chunks would exceed merklelizer limit " & $merkleizer.limit @@ -275,7 +288,7 @@ func addChunks*(merkleizer: var SszMerkleizer, data: openArray[byte]) = merkleizer.addChunk(data.toOpenArray(done, data.high)) break -func addChunkAndGenMerkleProof*(merkleizer: var SszMerkleizer, +func addChunkAndGenMerkleProof*(merkleizer: var SszMerkleizer2, hash: Digest, outProof: var openArray[Digest]) = var @@ -297,7 +310,7 @@ func addChunkAndGenMerkleProof*(merkleizer: var SszMerkleizer, merkleizer.totalChunks += 1 -func completeStartedChunk(merkleizer: var SszMerkleizer, +func completeStartedChunk(merkleizer: var SszMerkleizer2, hash: Digest, atLevel: int) = when false: let @@ -313,7 +326,7 @@ func completeStartedChunk(merkleizer: var SszMerkleizer, merkleizer.combinedChunks[i][0] = hash break -func addChunksAndGenMerkleProofs*(merkleizer: var SszMerkleizer, +func addChunksAndGenMerkleProofs*(merkleizer: var SszMerkleizer2, chunks: openArray[Digest]): seq[Digest] = doAssert chunks.len > 0 and merkleizer.topIndex > 0 @@ -465,36 +478,38 @@ func addChunksAndGenMerkleProofs*(merkleizer: var SszMerkleizer, merkleizer.totalChunks = newTotalChunks -func init*(S: type SszMerkleizer): S = +func init*(S: type SszMerkleizer2): S = S( - topIndex: binaryTreeHeight(S.limit) - 1, + topIndex: S.height - 1, totalChunks: 0) -func init*(S: type SszMerkleizer, +func init*(S: type SszMerkleizer2, combinedChunks: openArray[Digest], totalChunks: uint64): S = for i in 0..