From 7a45fe8592786b0ba15c740357c6e38b2bd6c7e1 Mon Sep 17 00:00:00 2001 From: Tomasz Bekas Date: Tue, 15 Aug 2023 13:37:14 +0200 Subject: [PATCH] Refine merkle tree construction --- codex/merkletree/merkletree.nim | 262 ++++++++++++---------- tests/codex/merkletree/testmerkletree.nim | 225 ++++++++++++++----- 2 files changed, 310 insertions(+), 177 deletions(-) diff --git a/codex/merkletree/merkletree.nim b/codex/merkletree/merkletree.nim index 6fcb4682..2a052bfb 100644 --- a/codex/merkletree/merkletree.nim +++ b/codex/merkletree/merkletree.nim @@ -1,5 +1,5 @@ ## Nim-Codex -## Copyright (c) 2022 Status Research & Development GmbH +## Copyright (c) 2023 Status Research & Development GmbH ## Licensed under either of ## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) ## * MIT license ([LICENSE-MIT](LICENSE-MIT)) @@ -7,41 +7,29 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. -import std/sequtils import std/math import std/bitops -import std/sugar +import std/strutils -import pkg/libp2p -import pkg/stew/byteutils -import pkg/questionable import pkg/questionable/results +import pkg/nimcrypto/sha2 + +const digestSize = sha256.sizeDigest type - MerkleHash* = MultiHash + MerkleHash* = array[digestSize, byte] MerkleTree* = object - leavesCount: int + leavesCount: Natural nodes: seq[MerkleHash] MerkleProof* = object - index: int + index: Natural path: seq[MerkleHash] + MerkleTreeBuilder* = object + buffer: seq[MerkleHash] -# Tree constructed from leaves H0..H2 is -# H5=H(H3 & H4) -# / \ -# H3=H(H0 & H1) H4=H(H2 & H2) -# / \ / -# H0=H(A) H1=H(B) H2=H(C) -# | | | -# A B C -# -# Memory layout is [H0, H1, H2, H3, H4, H5] -# -# Proofs of inclusion are -# - [H1, H4] for A -# - [H0, H4] for B -# - [H2, H3] for C - +########################################################### +# Helper functions +########################################################### func computeTreeHeight(leavesCount: int): int = if isPowerOfTwo(leavesCount): @@ -49,48 +37,100 @@ func computeTreeHeight(leavesCount: int): int = else: fastLog2(leavesCount) + 2 -func getLowHigh(leavesCount, level: int): (int, int) = - var width = leavesCount - var low = 0 - for _ in 0..= self.leavesCount: + return failure("Index " & $index & " out of range [0.." & $self.leaves.high & "]" ) + + let levels = computeLevels(self.leavesCount) + var path = newSeq[MerkleHash](levels.len - 1) + for levelIndex, level in levels[0..^2]: + let i = index div (1 shl levelIndex) + let siblingIndex = if i mod 2 == 0: + level.offset + i + 1 + else: + level.offset + i - 1 + + if siblingIndex < level.offset + level.width: + path[levelIndex] = self.nodes[siblingIndex] + else: + path[levelIndex] = zeroHash + + success(MerkleProof(index: index, path: path)) + proc `$`*(self: MerkleTree): string = result &= "leavesCount: " & $self.leavesCount result &= "\nnodes: " & $self.nodes -proc getProof*(self: MerkleTree, index: int): ?!MerkleProof = - if index >= self.leavesCount or index < 0: - return failure("Index " & $index & " out of range [0.." & $self.leaves.high & "]" ) +########################################################### +# MerkleProof +########################################################### - var path = newSeq[MerkleHash](self.height - 1) - for level in 0.. newException(CatchableError, "Error calculating hash using codec " & $mcodec & ": " & $c) - ) - - # copy leaves - for i in 0..