diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 493f0c2dc..3095239e9 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -273,3 +273,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 ---TOTAL--- OK: 145/154 Fail: 0/154 Skip: 9/154 + diff --git a/beacon_chain/merkle_minimal.nim b/beacon_chain/merkle_minimal.nim index 2762ce803..fb0ac7d7c 100644 --- a/beacon_chain/merkle_minimal.nim +++ b/beacon_chain/merkle_minimal.nim @@ -21,87 +21,12 @@ import # TODO All tests need to be moved to the test suite. -func round_step_down(x: Natural, step: static Natural): int {.inline.} = - ## Round the input to the previous multiple of "step" - when (step and (step - 1)) == 0: - # Step is a power of 2. (If compiler cannot prove that x>0 it does not make the optim) - x and not(step - 1) - else: - x - x mod step - -type SparseMerkleTree*[Depth: static int] = object - ## Sparse Merkle tree - # There is an extra "depth" layer to store leaf nodes - # This stores leaves at depth = 0 - # and the root hash at the last depth - nnznodes*: array[Depth+1, seq[Eth2Digest]] # nodes that leads to non-zero leaves - -type - MerkleTreeFragment* = object - depth: int - elements: seq[Eth2Digest] - -func merkleTreeFromLeaves*( - values: openarray[Eth2Digest], - Depth: static[int] = DEPOSIT_CONTRACT_TREE_DEPTH - ): SparseMerkleTree[Depth] = - ## Depth should be the same as is_valid_merkle_branch - - result.nnznodes[0] = @values - - for depth in 1 .. Depth: # Inclusive range - let prev_depth_len = result.nnznodes[depth-1].len - let stop = round_step_down(prev_depth_len, 2) - for i in countup(0, stop-1, 2): - # hash by pair of previous nodes - let nodeHash = withEth2Hash: - h.update result.nnznodes[depth-1][i] - h.update result.nnznodes[depth-1][i+1] - result.nnznodes[depth].add nodeHash - - if prev_depth_len != stop: - # If length is odd, the last one was skipped, - # we need to combine it - # with the zeroHash corresponding to the current depth - let nodeHash = withEth2Hash: - h.update result.nnznodes[depth-1][^1] - h.update zeroHashes[depth-1] - result.nnznodes[depth].add nodeHash - -func getMerkleProof*[Depth: static int](tree: SparseMerkleTree[Depth], - index: int, - depositMode = false): array[Depth, Eth2Digest] = - # Descend down the tree according to the bit representation - # of the index: - # - 0 --> go left - # - 1 --> go right - let path = uint32(index) - - # This is what the nnznodes[depth].len would be if `index` had been the last - # deposit on the Merkle tree - var depthLen = index + 1 - - for depth in 0 ..< Depth: - let nodeIdx = int((path shr depth) xor 1) - - # depositMode simulates only having constructed SparseMerkleTree[Depth] - # through exactly deposit specified. - if nodeIdx < tree.nnznodes[depth].len and - (nodeIdx < depthLen or not depositMode): - result[depth] = tree.nnznodes[depth][nodeIdx] - else: - result[depth] = zeroHashes[depth] - - # Round up, i.e. a half-pair of Merkle nodes/leaves still requires a node - # in the next Merkle tree layer calculated - depthLen = (depthLen + 1) div 2 - func attachMerkleProofs*(deposits: var openarray[Deposit]) = let depositsRoots = mapIt(deposits, hash_tree_root(it.data)) const depositContractLimit = Limit(1'u64 shl (DEPOSIT_CONTRACT_TREE_DEPTH - 1'u64)) var incrementalMerkleProofs = createMerkleizer(depositContractLimit) - + for i in 0 ..< depositsRoots.len: incrementalMerkleProofs.addChunkAndGenMerkleProof(depositsRoots[i], deposits[i].proof) deposits[i].proof[32].data[0..7] = toBytesLE uint64(i + 1) diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 6ea8c9c4e..520439ad6 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -19,7 +19,6 @@ import # Unit test ./test_block_pool, ./test_datatypes, ./test_helpers, - ./test_mocking, ./test_mainchain_monitor, ./test_ssz, ./test_state_transition, diff --git a/tests/test_mocking.nim b/tests/test_mocking.nim deleted file mode 100644 index 422ca15b1..000000000 --- a/tests/test_mocking.nim +++ /dev/null @@ -1,16 +0,0 @@ -# beacon_chain -# Copyright (c) 2020 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -{.used.} - -import - unittest, ./testutil, ../beacon_chain/merkle_minimal - -suiteReport "Mocking utilities": - timedTest "merkle_minimal": - check: - testMerkleMinimal() diff --git a/tests/test_ssz_merkleization.nim b/tests/test_ssz_merkleization.nim index 3c1676abe..9419ce22c 100644 --- a/tests/test_ssz_merkleization.nim +++ b/tests/test_ssz_merkleization.nim @@ -5,6 +5,76 @@ import ../beacon_chain/[ssz, merkle_minimal], mocking/mock_deposits +func round_step_down(x: Natural, step: static Natural): int {.inline.} = + ## Round the input to the previous multiple of "step" + when (step and (step - 1)) == 0: + # Step is a power of 2. (If compiler cannot prove that x>0 it does not make the optim) + x and not(step - 1) + else: + x - x mod step + +type SparseMerkleTree[Depth: static int] = object + ## Sparse Merkle tree + # There is an extra "depth" layer to store leaf nodes + # This stores leaves at depth = 0 + # and the root hash at the last depth + nnznodes*: array[Depth+1, seq[Eth2Digest]] # nodes that leads to non-zero leaves + +func merkleTreeFromLeaves( + values: openarray[Eth2Digest], + Depth: static[int] = DEPOSIT_CONTRACT_TREE_DEPTH + ): SparseMerkleTree[Depth] = + ## Depth should be the same as is_valid_merkle_branch + + result.nnznodes[0] = @values + + for depth in 1 .. Depth: # Inclusive range + let prev_depth_len = result.nnznodes[depth-1].len + let stop = round_step_down(prev_depth_len, 2) + for i in countup(0, stop-1, 2): + # hash by pair of previous nodes + let nodeHash = withEth2Hash: + h.update result.nnznodes[depth-1][i] + h.update result.nnznodes[depth-1][i+1] + result.nnznodes[depth].add nodeHash + + if prev_depth_len != stop: + # If length is odd, the last one was skipped, + # we need to combine it + # with the zeroHash corresponding to the current depth + let nodeHash = withEth2Hash: + h.update result.nnznodes[depth-1][^1] + h.update zeroHashes[depth-1] + result.nnznodes[depth].add nodeHash + +func getMerkleProof[Depth: static int](tree: SparseMerkleTree[Depth], + index: int, + depositMode = false): array[Depth, Eth2Digest] = + # Descend down the tree according to the bit representation + # of the index: + # - 0 --> go left + # - 1 --> go right + let path = uint32(index) + + # This is what the nnznodes[depth].len would be if `index` had been the last + # deposit on the Merkle tree + var depthLen = index + 1 + + for depth in 0 ..< Depth: + let nodeIdx = int((path shr depth) xor 1) + + # depositMode simulates only having constructed SparseMerkleTree[Depth] + # through exactly deposit specified. + if nodeIdx < tree.nnznodes[depth].len and + (nodeIdx < depthLen or not depositMode): + result[depth] = tree.nnznodes[depth][nodeIdx] + else: + result[depth] = zeroHashes[depth] + + # Round up, i.e. a half-pair of Merkle nodes/leaves still requires a node + # in the next Merkle tree layer calculated + depthLen = (depthLen + 1) div 2 + proc testMerkleMinimal*(): bool = proc toDigest[N: static int](x: array[N, byte]): Eth2Digest = result.data[0 .. N-1] = x @@ -97,9 +167,6 @@ proc testMerkleMinimal*(): bool = doAssert testMerkleMinimal() -let - digests = mapIt(1..65, eth2digest toBytesLE(uint64 it)) - proc compareTreeVsMerkleizer(hashes: openarray[Eth2Digest], limit: static Limit) = const treeHeight = binaryTreeHeight(limit) let tree = merkleTreeFromLeaves(hashes, treeHeight) @@ -180,6 +247,9 @@ func attachMerkleProofsReferenceImpl(deposits: var openarray[Deposit]) = DEPOSIT_CONTRACT_TREE_DEPTH + 1, val_idx.uint64, deposit_data_sums[val_idx]) +let + digests = mapIt(1..65, eth2digest toBytesLE(uint64 it)) + proc testMerkleizer = for i in 0 ..< digests.len: compareTreeVsMerkleizer(digests.toOpenArray(0, i), 128)