2019-08-28 12:07:00 +00:00
|
|
|
# beacon_chain
|
2020-04-08 14:06:30 +00:00
|
|
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
2019-08-28 12:07:00 +00:00
|
|
|
# Licensed and distributed under either of
|
2019-11-25 15:30:02 +00:00
|
|
|
# * 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).
|
2019-08-28 12:07:00 +00:00
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2020-10-12 14:37:14 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0-rc.0/tests/core/pyspec/eth2spec/utils/merkle_minimal.py
|
2020-04-08 14:06:30 +00:00
|
|
|
|
2019-08-28 12:07:00 +00:00
|
|
|
# Merkle tree helpers
|
|
|
|
# ---------------------------------------------------------------
|
|
|
|
|
2020-04-22 05:53:02 +00:00
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
2019-08-28 12:07:00 +00:00
|
|
|
import
|
2020-10-21 16:24:36 +00:00
|
|
|
sequtils, macros,
|
|
|
|
stew/endians2,
|
2019-08-28 12:07:00 +00:00
|
|
|
# Specs
|
2020-10-21 16:24:36 +00:00
|
|
|
../../beacon_chain/spec/[datatypes, digest],
|
2020-06-03 13:52:02 +00:00
|
|
|
../../beacon_chain/ssz/merkleization
|
2019-08-28 12:07:00 +00:00
|
|
|
|
2020-08-13 23:37:58 +00:00
|
|
|
# TODO All tests need to be moved to the test suite.
|
|
|
|
|
|
|
|
func round_step_down(x: Natural, step: static Natural): int {.inline.} =
|
2019-08-28 12:07:00 +00:00
|
|
|
## 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)
|
2020-08-13 23:37:58 +00:00
|
|
|
x and not(step - 1)
|
2019-08-28 12:07:00 +00:00
|
|
|
else:
|
2020-08-13 23:37:58 +00:00
|
|
|
x - x mod step
|
2019-08-28 12:07:00 +00:00
|
|
|
|
|
|
|
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
|
2020-10-21 16:24:36 +00:00
|
|
|
nnznodes*: array[Depth+1, seq[Eth2Digest]] # nodes that leads to non-zero leaves
|
2019-08-28 12:07:00 +00:00
|
|
|
|
2020-10-21 16:24:36 +00:00
|
|
|
type
|
|
|
|
MerkleTreeFragment* = object
|
|
|
|
depth: int
|
|
|
|
elements: seq[Eth2Digest]
|
|
|
|
|
|
|
|
func merkleTreeFromLeaves*(
|
2019-08-28 12:07:00 +00:00
|
|
|
values: openarray[Eth2Digest],
|
|
|
|
Depth: static[int] = DEPOSIT_CONTRACT_TREE_DEPTH
|
|
|
|
): SparseMerkleTree[Depth] =
|
2019-11-18 16:52:02 +00:00
|
|
|
## Depth should be the same as is_valid_merkle_branch
|
2019-08-28 12:07:00 +00:00
|
|
|
|
|
|
|
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]
|
2020-06-01 19:48:20 +00:00
|
|
|
h.update zeroHashes[depth-1]
|
2019-08-28 12:07:00 +00:00
|
|
|
result.nnznodes[depth].add nodeHash
|
|
|
|
|
2020-10-21 16:24:36 +00:00
|
|
|
func getMerkleProof*[Depth: static int](tree: SparseMerkleTree[Depth],
|
|
|
|
index: int,
|
|
|
|
depositMode = false): array[Depth, Eth2Digest] =
|
2019-08-28 12:07:00 +00:00
|
|
|
# Descend down the tree according to the bit representation
|
|
|
|
# of the index:
|
|
|
|
# - 0 --> go left
|
|
|
|
# - 1 --> go right
|
|
|
|
let path = uint32(index)
|
2020-08-14 12:42:59 +00:00
|
|
|
|
|
|
|
# This is what the nnznodes[depth].len would be if `index` had been the last
|
|
|
|
# deposit on the Merkle tree
|
2020-08-13 23:37:58 +00:00
|
|
|
var depthLen = index + 1
|
2020-08-14 12:42:59 +00:00
|
|
|
|
2019-08-28 12:07:00 +00:00
|
|
|
for depth in 0 ..< Depth:
|
|
|
|
let nodeIdx = int((path shr depth) xor 1)
|
2020-08-13 23:37:58 +00:00
|
|
|
|
|
|
|
# depositMode simulates only having constructed SparseMerkleTree[Depth]
|
|
|
|
# through exactly deposit specified.
|
|
|
|
if nodeIdx < tree.nnznodes[depth].len and
|
|
|
|
(nodeIdx < depthLen or not depositMode):
|
2019-08-28 12:07:00 +00:00
|
|
|
result[depth] = tree.nnznodes[depth][nodeIdx]
|
|
|
|
else:
|
2020-06-01 19:48:20 +00:00
|
|
|
result[depth] = zeroHashes[depth]
|
2019-08-28 12:07:00 +00:00
|
|
|
|
2020-08-14 12:42:59 +00:00
|
|
|
# Round up, i.e. a half-pair of Merkle nodes/leaves still requires a node
|
|
|
|
# in the next Merkle tree layer calculated
|
2020-08-13 23:37:58 +00:00
|
|
|
depthLen = (depthLen + 1) div 2
|
|
|
|
|
2020-06-28 20:17:47 +00:00
|
|
|
func attachMerkleProofs*(deposits: var openarray[Deposit]) =
|
2020-10-21 16:24:36 +00:00
|
|
|
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)
|
|
|
|
|