Streaming API for merkle root calculation

This commit is contained in:
Mark Spanbroek 2023-11-22 09:47:42 +01:00 committed by markspanbroek
parent 16e20ee8f7
commit fe3e3230f4
2 changed files with 59 additions and 42 deletions

View File

@ -1,4 +1,3 @@
import std/sequtils
import constantine/math/arithmetic import constantine/math/arithmetic
import constantine/math/io/io_fields import constantine/math/io/io_fields
import ./types import ./types
@ -10,39 +9,57 @@ const KeyBottomLayer = F.fromHex("0x1")
const KeyOdd = F.fromHex("0x2") const KeyOdd = F.fromHex("0x2")
const KeyOddAndBottomLayer = F.fromhex("0x3") const KeyOddAndBottomLayer = F.fromhex("0x3")
func merkleRoot(xs: openArray[F], isBottomLayer: static bool) : F = type Merkle* = object
let a = low(xs) todo: seq[F] # nodes that haven't been combined yet
let b = high(xs) width: int # width of the current subtree
let m = b-a+1 leafs: int # amount of leafs processed
when isBottomLayer: func init*(_: type Merkle): Merkle =
assert m > 0, "merkle root of empty sequence is not defined" Merkle(width: 2)
when not isBottomLayer: func compress(merkle: var Merkle, odd: static bool) =
if m==1: when odd:
return xs[a] let a = merkle.todo.pop()
let b = zero
let halfn : int = m div 2 let key = if merkle.width == 2: KeyOddAndBottomLayer else: KeyOdd
let n : int = 2*halfn merkle.todo.add(compress(a, b, key = key))
let isOdd : bool = (n != m) merkle.leafs += merkle.width div 2 # zero node represents this many leafs
var ys : seq[F]
if not isOdd:
ys = newSeq[F](halfn)
else: else:
ys = newSeq[F](halfn+1) let b = merkle.todo.pop()
let a = merkle.todo.pop()
let key = if merkle.width == 2: KeyBottomLayer else: KeyNone
merkle.todo.add(compress(a, b, key = key))
merkle.width *= 2
for i in 0..<halfn: func update*(merkle: var Merkle, element: F) =
const key = when isBottomLayer: KeyBottomLayer else: KeyNone merkle.todo.add(element)
ys[i] = compress( xs[a+2*i], xs[a+2*i+1], key = key ) inc merkle.leafs
if isOdd: merkle.width = 2
const key = when isBottomLayer: KeyOddAndBottomLayer else: KeyOdd while merkle.width <= merkle.leafs and merkle.leafs mod merkle.width == 0:
ys[halfn] = compress( xs[n], zero, key = key ) merkle.compress(odd = false)
return merkleRoot(ys, isBottomLayer = false) func finish*(merkle: var Merkle): F =
assert merkle.todo.len > 0, "merkle root of empty sequence is not defined"
func merkleRoot*(xs: openArray[F]) : F = if merkle.leafs == 1:
merkleRoot(xs, isBottomLayer = true) merkle.compress(odd = true)
func merkleRoot*(bytes: openArray[byte]): F = while merkle.todo.len > 1:
merkleRoot(toSeq bytes.elements(F)) if merkle.leafs mod merkle.width == 0:
merkle.compress(odd = false)
else:
merkle.compress(odd = true)
return merkle.todo[0]
func digest*(_: type Merkle, elements: openArray[F]): F =
var merkle = Merkle.init()
for element in elements:
merkle.update(element)
return merkle.finish()
func digest*(_: type Merkle, bytes: openArray[byte]): F =
var merkle = Merkle.init()
for element in bytes.elements(F):
merkle.update(element)
return merkle.finish()

View File

@ -25,7 +25,7 @@ suite "merkle root":
for i in 1..n: for i in 1..n:
xs.add( toF(i) ) xs.add( toF(i) )
let root = merkleRoot(xs) let root = Merkle.digest(xs)
check root.toHex(littleEndian) == "0x593e01f200cb1aee4e75fe2a9206abc3abd2a1216ab75f1061965e97371e8623" check root.toHex(littleEndian) == "0x593e01f200cb1aee4e75fe2a9206abc3abd2a1216ab75f1061965e97371e8623"
test "merkle root of even elements": test "merkle root of even elements":
@ -34,7 +34,7 @@ suite "merkle root":
compress(1.toF, 2.toF, key = isBottomLayer.toF), compress(1.toF, 2.toF, key = isBottomLayer.toF),
compress(3.toF, 4.toF, key = isBottomLayer.toF), compress(3.toF, 4.toF, key = isBottomLayer.toF),
) )
check bool(merkleRoot(elements) == expected) check bool(Merkle.digest(elements) == expected)
test "merkle root of odd elements": test "merkle root of odd elements":
let elements = toSeq(1..3).mapIt(toF(it)) let elements = toSeq(1..3).mapIt(toF(it))
@ -42,40 +42,40 @@ suite "merkle root":
compress(1.toF, 2.toF, key = isBottomLayer.toF), compress(1.toF, 2.toF, key = isBottomLayer.toF),
compress(3.toF, 0.toF, key = (isBottomLayer + isOddNode).toF) compress(3.toF, 0.toF, key = (isBottomLayer + isOddNode).toF)
) )
check bool(merkleRoot(elements) == expected) check bool(Merkle.digest(elements) == expected)
test "data ending with 0 differs from padded data": test "data ending with 0 differs from padded data":
let a = toSeq(1..3).mapIt(it.toF) let a = toSeq(1..3).mapIt(it.toF)
let b = a & @[0.toF] let b = a & @[0.toF]
check not bool(merkleRoot(a) == merkleRoot(b)) check not bool(Merkle.digest(a) == Merkle.digest(b))
test "merkle root of single element does not equal the element": test "merkle root of single element does not equal the element":
check not bool(merkleRoot([1.toF]) == 1.toF) check not bool(Merkle.digest([1.toF]) == 1.toF)
test "merkle root differs from merkle root of merkle root": test "merkle root differs from merkle root of merkle root":
let a = 1.toF let a = 1.toF
let b = 2.toF let b = 2.toF
check not bool(merkleRoot([a, b]) == merkleRoot([merkleRoot([a, b])])) check not bool(Merkle.digest([a, b]) == Merkle.digest([Merkle.digest([a, b])]))
test "merkle root of bytes": test "merkle root of bytes":
let bytes = toSeq 1'u8..80'u8 let bytes = toSeq 1'u8..80'u8
let root = merkleRoot(bytes) let root = Merkle.digest(bytes)
check root.toHex(littleEndian) == "0x40989b63104f39e3331767883381085bcfc46e2202679123371f1ffe53521b16" check root.toHex(littleEndian) == "0x40989b63104f39e3331767883381085bcfc46e2202679123371f1ffe53521b16"
test "merkle root of bytes converted to bytes": test "merkle root of bytes converted to bytes":
let bytes = toSeq 1'u8..80'u8 let bytes = toSeq 1'u8..80'u8
let rootAsBytes = merkleRoot(bytes).toBytes() let rootAsBytes = Merkle.digest(bytes).toBytes()
check rootAsBytes.toHex == "0x40989b63104f39e3331767883381085bcfc46e2202679123371f1ffe53521b16" check rootAsBytes.toHex == "0x40989b63104f39e3331767883381085bcfc46e2202679123371f1ffe53521b16"
test "merkle root of empty sequence of elements": test "merkle root of empty sequence of elements":
let empty = seq[F].default let empty = seq[F].default
expect Exception: expect Exception:
discard merkleRoot(empty) discard Merkle.digest(empty)
test "merkle root of empty sequency of bytes": test "merkle root of empty sequency of bytes":
# merkle root of empty sequence of bytes is uniquely defined through padding # merkle root of empty sequence of bytes is uniquely defined through padding
let empty = seq[byte].default let empty = seq[byte].default
check merkleRoot(empty).toBytes.toHex == "0xcc8da1d157900e611b89e258d95450e707f4f9eec169422d7c26aba54f803c08" check Merkle.digest(empty).toBytes.toHex == "0xcc8da1d157900e611b89e258d95450e707f4f9eec169422d7c26aba54f803c08"
suite "merkle root test vectors": suite "merkle root test vectors":
@ -126,7 +126,7 @@ suite "merkle root test vectors":
for n in 1..40: for n in 1..40:
let input = collect(newSeq, (for i in 1..n: i.toF)) let input = collect(newSeq, (for i in 1..n: i.toF))
let root = merkleRoot(input) let root = Merkle.digest(input)
check root.toDecimal == expected[n-1] check root.toDecimal == expected[n-1]
test "byte sequences": test "byte sequences":
@ -217,5 +217,5 @@ suite "merkle root test vectors":
for n in 0..80: for n in 0..80:
let input = collect(newSeq, (for i in 1..n: byte(i))) let input = collect(newSeq, (for i in 1..n: byte(i)))
let root = merkleRoot(input) let root = Merkle.digest(input)
check root.toDecimal == expected[n] check root.toDecimal == expected[n]