add option to use the "new" round constants for Poseidon2 (see #25)

This commit is contained in:
Balazs Komuves 2026-04-22 18:09:25 +02:00
parent eb29fca52b
commit 7ae5c9a22a
No known key found for this signature in database
GPG Key ID: F63B7AEF18435562
19 changed files with 594 additions and 114 deletions

2
.gitignore vendored
View File

@ -3,4 +3,4 @@
!*.*
.nimble
poseidon2.out
.DS_store
.DS_store

View File

@ -39,6 +39,23 @@ let right = Sponge.digest([4'u8, 5'u8, 6'u8])
let combination = compress(left, right)
```
Compatbility
------------
For Poseidon2, because of a historical accident, there are unfortunately TWO
different sets of "standard" parameters, which is obviously bad for cross-project compatibility.
The switchover happened in commit #bb476b9ca38198cf5092487283c8b8c5d4317c4e in
HorizenLab's reference repo. Both versions are safe to use though.
To resolve this issue, since version 0.1.1, we implement both sets (though the default
is the "old" set of round constants, so that we are backward compatible. This may
or may not change in the future.)
Links
-----
[1]: https://eprint.iacr.org/2023/323.pdf
[2]: https://github.com/mratsim/constantine
[3]: https://github.com/nim-lang/nimble

View File

@ -1,4 +1,4 @@
version = "0.1.0"
version = "0.1.1"
author = "nim-poseidon2 authors"
description = "Poseidon2 hash function"
license = "MIT"

View File

@ -2,9 +2,9 @@ import ./types
import ./permutation
# 2-to-1 compression
func compress*(a, b : F, key = zero) : F =
func compression*(a, b : F, key = zero, which: static Flavour = HorizenLabsOld) : F =
var x = a
var y = b
var z = key
permInPlace(x, y, z)
permInPlace(x, y, z, which = which)
return x

View File

@ -4,41 +4,44 @@ import ./types
import ./io
import ./compress
const KeyNone = F.fromHex("0x0")
const KeyBottomLayer = F.fromHex("0x1")
const KeyOdd = F.fromHex("0x2")
const KeyNone = F.fromHex("0x0")
const KeyBottomLayer = F.fromHex("0x1")
const KeyOdd = F.fromHex("0x2")
const KeyOddAndBottomLayer = F.fromhex("0x3")
type Merkle* = object
todo: seq[F] # nodes that haven't been combined yet
width: int # width of the current subtree
leafs: int # amount of leafs processed
type Merkle*[flavour: static Flavour] = object
todo: seq[F] # nodes that haven't been combined yet
width: int # width of the current subtree
leafs: int # amount of leafs processed
func init*(_: type Merkle): Merkle =
Merkle(width: 2)
func init[which](merkle: var Merkle[which]) =
merkle.width = 2
func compress(merkle: var Merkle, odd: static bool) =
func init*(_: type Merkle, which: static Flavour = HorizenLabsOld): Merkle[which] =
result.init
func compress[which](merkle: var Merkle[which], odd: static bool) =
when odd:
let a = merkle.todo.pop()
let b = zero
let key = if merkle.width == 2: KeyOddAndBottomLayer else: KeyOdd
merkle.todo.add(compress(a, b, key = key))
merkle.todo.add(compression(a, b, key = key, which = which))
merkle.leafs += merkle.width div 2 # zero node represents this many leafs
else:
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.todo.add(compression(a, b, key = key, which = which))
merkle.width *= 2
func update*(merkle: var Merkle, element: F) =
func update*[which](merkle: var Merkle[which], element: F) =
merkle.todo.add(element)
inc merkle.leafs
merkle.width = 2
while merkle.width <= merkle.leafs and merkle.leafs mod merkle.width == 0:
merkle.compress(odd = false)
func finish*(merkle: var Merkle): F =
func finish*[which](merkle: var Merkle[which]): F =
assert merkle.todo.len > 0, "merkle root of empty sequence is not defined"
if merkle.leafs == 1:
@ -52,14 +55,14 @@ func finish*(merkle: var Merkle): F =
return merkle.todo[0]
func digest*(_: type Merkle, elements: openArray[F]): F =
var merkle = Merkle.init()
func digest*(_: type Merkle, elements: openArray[F], which: static Flavour = HorizenLabsOld): F =
var merkle = Merkle.init(which = which)
for element in elements:
merkle.update(element)
return merkle.finish()
func digest*(_: type Merkle, bytes: openArray[byte]): F =
var merkle = Merkle.init()
func digest*(_: type Merkle, bytes: openArray[byte], which: static Flavour = HorizenLabsOld): F =
var merkle = Merkle.init(which = which)
for element in bytes.elements(F):
merkle.update(element)
return merkle.finish()

View File

@ -1,18 +1,57 @@
import ./types
import ./roundfun
# the Poseidon2 permutation (mutable, in-place version)
proc permInPlace*(x, y, z : var F) =
#-------------------------------------------------------------------------------
# the Poseidon2 permutation, "old" round constants (mutable, in-place version)
proc permInPlaceOld(x, y, z : var F) =
linearLayer(x, y, z)
for j in 0..3:
externalRound(j, x, y, z)
externalRoundOld(j, x, y, z)
for j in 0..55:
internalRound(j, x, y, z)
internalRoundOld(j, x, y, z)
for j in 4..7:
externalRound(j, x, y, z)
externalRoundOld(j, x, y, z)
# the Poseidon2 permutation
func perm*(xyz: S) : S =
# the Poseidon2 permutation, "old" ronud constants
func permOld(xyz: S) : S =
var (x,y,z) = xyz
permInPlace(x, y, z)
permInPlaceOld(x, y, z)
return (x,y,z)
#-------------------------------------------------------------------------------
# the Poseidon2 permutation, "new" round constants (mutable, in-place version)
proc permInPlaceNew(x, y, z : var F) =
linearLayer(x, y, z)
for j in 0..3:
externalRoundNew(j, x, y, z)
for j in 0..55:
internalRoundNew(j, x, y, z)
for j in 4..7:
externalRoundNew(j, x, y, z)
# the Poseidon2 permutation, "new" ronud constants
func permNew(xyz: S) : S =
var (x,y,z) = xyz
permInPlaceNew(x, y, z)
return (x,y,z)
#-------------------------------------------------------------------------------
# selectable round constants
# the Poseidon2 permutation (mutable, in-place version)
proc permInPlace*(x, y, z: var F, which: static Flavour = HorizenLabsOld) =
case which
of HorizenLabsOld: permInPlaceOld(x,y,z)
of HorizenLabsNew: permInPlaceNew(x,y,z)
# the Poseidon2 permutation, "old" ronud constants
func perm*(xyz: S, which: static Flavour = HorizenLabsOld) : S =
var (x,y,z) = xyz
case which
of HorizenLabsOld: permInPlaceOld(x,y,z)
of HorizenLabsNew: permInPlaceNew(x,y,z)
return (x,y,z)
#-------------------------------------------------------------------------------

View File

@ -0,0 +1,99 @@
#-------------------------------------------------------------------------------
const externalRoundConstNewStr* : array[24, string] =
[ "0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816" ,
"0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610" ,
"0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1" ,
#
"0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5" ,
"0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28" ,
"0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735" ,
#
"0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a" ,
"0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d" ,
"0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce" ,
#
"0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4" ,
"0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e" ,
"0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e" ,
#
"0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0" ,
"0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5" ,
"0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893" ,
#
"0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d" ,
"0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e" ,
"0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc" ,
#
"0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1" ,
"0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe" ,
"0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166" ,
#
"0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce" ,
"0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57" ,
"0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6" ,
]
#-------------------------------------------------------------------------------
const internalRoundConstNewStr* : array[56, string] =
[ "0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5" ,
"0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37" ,
"0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8" ,
"0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495" ,
"0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109" ,
"0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e" ,
"0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc" ,
"0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6" ,
"0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15" ,
"0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74" ,
"0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381" ,
"0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687" ,
"0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620" ,
"0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f" ,
"0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64" ,
"0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f" ,
"0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd" ,
"0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9" ,
"0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c" ,
"0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94" ,
"0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8" ,
"0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd" ,
"0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b" ,
"0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7" ,
"0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e" ,
"0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e" ,
"0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c" ,
"0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78" ,
"0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591" ,
"0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94" ,
"0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b" ,
"0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd" ,
"0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76" ,
"0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488" ,
"0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5" ,
"0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b" ,
"0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc" ,
"0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a" ,
"0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098" ,
"0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077" ,
"0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe" ,
"0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe" ,
"0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d" ,
"0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e" ,
"0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f" ,
"0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f" ,
"0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59" ,
"0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6" ,
"0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d" ,
"0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54" ,
"0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce" ,
"0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b" ,
"0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f" ,
"0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e" ,
"0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f" ,
"0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9" ,
]
#-------------------------------------------------------------------------------

View File

@ -1,8 +1,7 @@
#-------------------------------------------------------------------------------
const externalRoundConstStr* : array[24, string] =
const externalRoundConstOldStr* : array[24, string] =
[ "0x2c4c51fd1bb9567c27e99f5712b49e0574178b41b6f0a476cddc41d242cf2b43" ,
"0x1c5f8d18acb9c61ec6fcbfcda5356f1b3fdee7dc22c99a5b73a2750e5b054104" ,
"0x2d3c1988b4541e4c045595b8d574e98a7c2820314a82e67a4e380f1c4541ba90" ,
@ -38,7 +37,7 @@ const externalRoundConstStr* : array[24, string] =
#-------------------------------------------------------------------------------
const internalRoundConstStr* : array[56, string] =
const internalRoundConstOldStr* : array[56, string] =
[ "0x15ce7e5ae220e8623a40b3a3b22d441eff0c9be1ae1d32f1b777af84eea7e38c" ,
"0x1bf60ac8bfff0f631983c93e218ca0d4a4059c254b4299b1d9984a07edccfaf0" ,
"0x0fab0c9387cb2bec9dc11b2951088b9e1e1d2978542fc131f74a8f8fdac95b40" ,

View File

@ -3,12 +3,16 @@ import
constantine/named/algebras
import ./types
import ./roundconst
import ./roundconst_old
import ./roundconst_new
#-------------------------------------------------------------------------------
const externalRoundConst : array[24, F] = arrayFromHex( externalRoundConstStr )
const internalRoundConst : array[56, F] = arrayFromHex( internalRoundConstStr )
const externalRoundConstOld : array[24, F] = arrayFromHex( externalRoundConstOldStr )
const internalRoundConstOld : array[56, F] = arrayFromHex( internalRoundConstOldStr )
const externalRoundConstNew : array[24, F] = arrayFromHex( externalRoundConstNewStr )
const internalRoundConstNew : array[56, F] = arrayFromHex( internalRoundConstNewStr )
#-------------------------------------------------------------------------------
@ -25,8 +29,10 @@ func linearLayer*(x, y, z : var F) =
y += s
z += s
func internalRound*(j: int; x, y, z: var F) =
x += internalRoundConst[j]
#-------------------------------------------------------------------------------
func internalRoundOld*(j: int; x, y, z: var F) =
x += internalRoundConstOld[j]
sbox(x)
var s = x ; s += y ; s += z
double(z)
@ -34,10 +40,10 @@ func internalRound*(j: int; x, y, z: var F) =
y += s
z += s
func externalRound*(j: int; x, y, z : var F) =
x += externalRoundConst[3*j+0]
y += externalRoundConst[3*j+1]
z += externalRoundConst[3*j+2]
func externalRoundOld*(j: int; x, y, z : var F) =
x += externalRoundConstOld[3*j+0]
y += externalRoundConstOld[3*j+1]
z += externalRoundConstOld[3*j+2]
sbox(x) ; sbox(y) ; sbox(z)
var s = x ; s += y ; s += z
x += s
@ -46,3 +52,23 @@ func externalRound*(j: int; x, y, z : var F) =
#-------------------------------------------------------------------------------
func internalRoundNew*(j: int; x, y, z: var F) =
x += internalRoundConstNew[j]
sbox(x)
var s = x ; s += y ; s += z
double(z)
x += s
y += s
z += s
func externalRoundNew*(j: int; x, y, z : var F) =
x += externalRoundConstNew[3*j+0]
y += externalRoundConstNew[3*j+1]
z += externalRoundConstNew[3*j+2]
sbox(x) ; sbox(y) ; sbox(z)
var s = x ; s += y ; s += z
x += s
y += s
z += s
#-------------------------------------------------------------------------------

View File

@ -5,31 +5,37 @@ import constantine/math/io/io_fields
import constantine/math/arithmetic
type
Sponge*[rate: static int] = object
Sponge*[rate: static int, flavour: static Flavour] = object
s0: F
s1: F
s2: F
when rate == 2:
even: bool
func init(sponge: var Sponge[1]) =
#-------------------------------------------------------------------------------
# rate = 1
func init[which](sponge: var Sponge[1,which]) =
# domain separation IV := (2^64 + 256*t + r)
const IV = F.fromHex("0x10000000000000301")
sponge.s0 = zero
sponge.s1 = zero
sponge.s2 = IV
func update*(sponge: var Sponge[1], element: F) =
func update*[which](sponge: var Sponge[1,which], element: F) = # , which: static Flavour = HorizenLabsOld) =
sponge.s0 += element
permInPlace(sponge.s0, sponge.s1, sponge.s2)
permInPlace(sponge.s0, sponge.s1, sponge.s2, which = which)
func finish*(sponge: var Sponge[1]): F =
func finish*[which](sponge: var Sponge[1,which]): F = # , which: static Flavour = HorizenLabsOld): F =
# padding
sponge.s0 += one
permInPlace(sponge.s0, sponge.s1, sponge.s2)
permInPlace(sponge.s0, sponge.s1, sponge.s2, which = which)
return sponge.s0
func init(sponge: var Sponge[2]) =
#-------------------------------------------------------------------------------
# rate = 2
func init[which](sponge: var Sponge[2,which]) =
# domain separation IV := (2^64 + 256*t + r)
const IV = F.fromHex("0x10000000000000302")
sponge.s0 = zero
@ -37,15 +43,15 @@ func init(sponge: var Sponge[2]) =
sponge.s2 = IV
sponge.even = true
func update*(sponge: var Sponge[2], element: F) =
func update*[which](sponge: var Sponge[2,which], element: F) = # , which: static Flavour = HorizenLabsOld) =
if sponge.even:
sponge.s0 += element
else:
sponge.s1 += element
permInPlace(sponge.s0, sponge.s1, sponge.s2)
permInPlace(sponge.s0, sponge.s1, sponge.s2, which = which)
sponge.even = not sponge.even
func finish*(sponge: var Sponge[2]): F =
func finish*[which](sponge: var Sponge[2,which]): F = #: static Flavour = HorizenLabsOld): F =
if sponge.even:
# padding even input
sponge.s0 += one
@ -53,22 +59,25 @@ func finish*(sponge: var Sponge[2]): F =
else:
# padding odd input
sponge.s1 += one
permInPlace(sponge.s0, sponge.s1, sponge.s2)
permInPlace(sponge.s0, sponge.s1, sponge.s2, which = which)
return sponge.s0
func init*(_: type Sponge, rate: static int = 2): Sponge[rate] =
#-------------------------------------------------------------------------------
# generic
func init*(_: type Sponge, rate: static int = 2, which: static Flavour = HorizenLabsOld): Sponge[rate,which] =
when rate notin {1, 2}:
{.error: "only rate 1 and 2 are supported".}
result.init
func digest*(_: type Sponge, elements: openArray[F], rate: static int = 2): F =
var sponge = Sponge.init(rate)
func digest*(_: type Sponge, elements: openArray[F], rate: static int = 2, which: static Flavour = HorizenLabsOld): F =
var sponge = Sponge.init(rate = rate, which = which)
for element in elements:
sponge.update(element)
return sponge.finish()
func digest*(_: type Sponge, bytes: openArray[byte], rate: static int = 2): F =
var sponge = Sponge.init(rate)
func digest*(_: type Sponge, bytes: openArray[byte], rate: static int = 2, which: static Flavour = HorizenLabsOld): F =
var sponge = Sponge.init(rate = rate, which = which)
for element in bytes.elements(F):
sponge.update(element)
return sponge.finish()

View File

@ -2,23 +2,26 @@ import ./types
import ./merkle
import ./sponge
type SpongeMerkle* = object
merkle: Merkle
type SpongeMerkle*[flavour: static Flavour] = object
merkle: Merkle[flavour]
func init*(_: type SpongeMerkle): SpongeMerkle =
SpongeMerkle(merkle: Merkle.init())
func init[which](spongemerkle: var SpongeMerkle[which]) =
spongemerkle.merkle = Merkle.init(which = which)
func update*(spongemerkle: var SpongeMerkle, chunk: openArray[byte]) =
let digest = Sponge.digest(chunk, rate = 2)
func init*(_: type SpongeMerkle, which: static Flavour = HorizenLabsOld): SpongeMerkle[which] =
result.init
func update*[which](spongemerkle: var SpongeMerkle[which], chunk: openArray[byte]) =
let digest = Sponge.digest(chunk, rate = 2, which = which)
spongemerkle.merkle.update(digest)
func finish*(spongemerkle: var SpongeMerkle): F =
func finish*[which](spongemerkle: var SpongeMerkle[which]): F =
return spongemerkle.merkle.finish()
func digest*(_: type SpongeMerkle, bytes: openArray[byte], chunkSize: int): F =
func digest*(_: type SpongeMerkle, bytes: openArray[byte], chunkSize: int, which: static Flavour = HorizenLabsOld): F =
## Hashes chunks of data with a sponge of rate 2, and combines the
## resulting chunk hashes in a merkle root.
var spongemerkle = SpongeMerkle.init()
var spongemerkle = SpongeMerkle.init(which = which)
var index = 0
while index < bytes.len:
let start = index

View File

@ -7,6 +7,25 @@ import
#-------------------------------------------------------------------------------
#
# Note: Because of a historical accident, there are unfortunately TWO different sets
# of "standard" parameters, which is obviously bad for cross-project compatibility.
#
# the switchover happened at 2023/06/23 in the commit
# <https://github.com/HorizenLabs/poseidon2/commit/bb476b9ca38198cf5092487283c8b8c5d4317c4e>
#
# You can use this type to select between the two. The default is the "old" set.
#
type Flavour* = enum
HorizenLabsOld # the "old" round constants
HorizenLabsNew # the "new" round constants
type SpongeInput* = enum
ByteString # the input of the hash is a sequence of bytes
FieldElements # the input of the hash is a sequence of BN254 field elements
#-------------------------------------------------------------------------------
type B* = BigInt[254]
type F* = Fr[BN254_Snarks]
type S* = (F,F,F)

View File

@ -5,11 +5,15 @@ import poseidon2/types
import poseidon2/io
import poseidon2/compress
const KeyNone = F.fromHex("0x0")
const KeyBottomLayer = F.fromHex("0x1")
const KeyOdd = F.fromHex("0x2")
#-------------------------------------------------------------------------------
const KeyNone = F.fromHex("0x0")
const KeyBottomLayer = F.fromHex("0x1")
const KeyOdd = F.fromHex("0x2")
const KeyOddAndBottomLayer = F.fromhex("0x3")
#-------------------------------------------------------------------------------
# Reference implementation of merkle root algorithm
# Only used in tests
func merkleRoot(xs: openArray[F], isBottomLayer: static bool) : F =
@ -36,10 +40,10 @@ func merkleRoot(xs: openArray[F], isBottomLayer: static bool) : F =
for i in 0..<halfn:
const key = when isBottomLayer: KeyBottomLayer else: KeyNone
ys[i] = compress( xs[a+2*i], xs[a+2*i+1], key = key )
ys[i] = compression( xs[a+2*i], xs[a+2*i+1], key = key )
if isOdd:
const key = when isBottomLayer: KeyOddAndBottomLayer else: KeyOdd
ys[halfn] = compress( xs[n], zero, key = key )
ys[halfn] = compression( xs[n], zero, key = key )
return merkleRoot(ys, isBottomLayer = false)
@ -48,3 +52,5 @@ func merkleRoot*(xs: openArray[F]) : F =
func merkleRoot*(bytes: openArray[byte]): F =
merkleRoot(toSeq bytes.elements(F))
#-------------------------------------------------------------------------------

View File

@ -7,9 +7,9 @@ import poseidon2/compress
suite "compress":
test "uses permutation to compress two elements":
check bool(compress(1.toF, 2.toF) == (1.toF, 2.toF, 0.toF).perm[0])
check bool(compress(3.toF, 4.toF) == (3.toF, 4.toF, 0.toF).perm[0])
check bool(compression(1.toF, 2.toF) == (1.toF, 2.toF, 0.toF).perm[0])
check bool(compression(3.toF, 4.toF) == (3.toF, 4.toF, 0.toF).perm[0])
test "allows for keyed compression":
check bool(compress(1.toF, 2.toF, key=3.toF) == (1.toF, 2.toF, 3.toF).perm[0])
check bool(compress(4.toF, 5.toF, key=6.toF) == (4.toF, 5.toF, 6.toF).perm[0])
check bool(compression(1.toF, 2.toF, key=3.toF) == (1.toF, 2.toF, 3.toF).perm[0])
check bool(compression(4.toF, 5.toF, key=6.toF) == (4.toF, 5.toF, 6.toF).perm[0])

View File

@ -17,7 +17,9 @@ import poseidon2/merkle
import ./fuzzing
import ./reference
suite "merkle root":
#-------------------------------------------------------------------------------
suite "merkle root (old round constants)":
const isBottomLayer = 1
const isOddNode = 2
@ -34,17 +36,17 @@ suite "merkle root":
test "merkle root of even elements":
let elements = toSeq(1..4).mapIt(toF(it))
let expected = compress(
compress(1.toF, 2.toF, key = isBottomLayer.toF),
compress(3.toF, 4.toF, key = isBottomLayer.toF),
let expected = compression(
compression(1.toF, 2.toF, key = isBottomLayer.toF),
compression(3.toF, 4.toF, key = isBottomLayer.toF),
)
check bool(Merkle.digest(elements) == expected)
test "merkle root of odd elements":
let elements = toSeq(1..3).mapIt(toF(it))
let expected = compress(
compress(1.toF, 2.toF, key = isBottomLayer.toF),
compress(3.toF, 0.toF, key = (isBottomLayer + isOddNode).toF)
let expected = compression(
compression(1.toF, 2.toF, key = isBottomLayer.toF),
compression(3.toF, 0.toF, key = (isBottomLayer + isOddNode).toF)
)
check bool(Merkle.digest(elements) == expected)
@ -81,7 +83,30 @@ suite "merkle root":
let empty = seq[byte].default
check Merkle.digest(empty).toBytes.toHex == "0xcc8da1d157900e611b89e258d95450e707f4f9eec169422d7c26aba54f803c08"
suite "merkle root test vectors":
#-------------------------------------------------------------------------------
suite "merkle root probabilistic test (seed: " & $fuzzing.seed & ")":
test "merkle root algorithm matches reference implementation":
proc checkInput(input: seq[byte]) =
let expected = reference.merkleRoot(input)
check bool(Merkle.digest(input) == expected)
# a couple of tests with small input
for _ in 0..<1000:
let len = rand(1024)
let input = newSeqwith(len, byte.rand())
checkInput(input)
# one test with larger input
let len = rand(1024 * 1024)
let input = newSeqwith(len, byte.rand())
checkInput(input)
#-------------------------------------------------------------------------------
suite "merkle root test vectors (old round constants)":
test "field elements":
@ -224,21 +249,151 @@ suite "merkle root test vectors":
let root = Merkle.digest(input)
check root.toDecimal == expected[n]
suite "merkle root fuzzing (seed: " & $fuzzing.seed & ")":
#-------------------------------------------------------------------------------
test "merkle root algorithm matches reference implementation":
suite "merkle root test vectors (new round constants)":
proc checkInput(input: seq[byte]) =
let expected = reference.merkleRoot(input)
check bool(Merkle.digest(input) == expected)
test "field elements":
const expected = [
"18732683119498402220900273862328991850071867550460350301906225211930820497246",
"20044485856448945219360893405968170246210313782806884524691942318885527485620",
"06879156331453065561412171404688536694163751033054206900049615566816845240638",
"10573156824103477315213167903522304842945545228260194505071041398610725747027",
"06384441629309812517061530283473905459834139895523812419737874603741167883751",
"01971250361953534862485856004061479126145269783367209715512197349658074388513",
"07880478051115466397651760556452850966415510478374824691540522067079012008848",
"01588427217707334311194748465055445447386590515144208471159395360876596354896",
"09845833165176316017310739746926711006998448994374103176753950446959241544949",
"05401080693582495042221036612711192243397119342275570141068847210454146432971",
"14979541340966695088021593909504702984608154332274504665382688317350089624983",
"06607457335668542852281630717663887713758119594332918998385113363122923272454",
"16944531731230336601405766048044116440488450625877275170244752673232813024368",
"10505627725719291383716848804809239161132464233693421875021763993166626150955",
"04903777673741595493881538453920061011471375126044729119229615357995429924907",
"21181035192839738728235444431495555418889580879577305428953424496049840916480",
"11221834706496996790865390579246001625017536676754935789453166684523452746294",
"12238404452391357862009969266413532836848829207794340659891644015375290333575",
"15708155082009131580754579627793300299188575609367253204313006197006278683451",
"05517504412493805370300513465650653794195620677809004623411733389887578459282",
"08080378887214146109958442876960165426094127534771411925984754489769775639602",
"21516654090516357344488827195395654437927570305499499742967859768435792307023",
"16287982214682712037347824797340080237681108979031916798200981723277747155615",
"05685694773867255011952458685571219976401216436606262788193180342867328292265",
"19526583356928859196877033020146794422218124413645406452689254965333914794652",
"10848675323512546969760110872496371397468046019826794338866209820503017218094",
"13098526947453076378732134736412645888623373165290562658402930812057369083031",
"15807622910178811742015251785595269484031937827970627692880877492771583344913",
"20947539376392019997058776157314032851916875769916485553613013444732706809995",
"06753553250505666793155665320322498674458995022023435974225627770488883070427",
"12913052272470117275730885267996789708916674339661588942669672432637118634497",
"07140273523155025758656457959906703903322517942895250064763067836921533208395",
"05096662479734546861171656420468400853646484716237537698088073264461873771495",
"21847244193600328566028581147884321487732092052029068139956887865853549271044",
"04009300188097479536921010252828669329555099893330885488914174883959723200864",
"01108208796576758491952732089790509068885986934320479897801398052416369133702",
"12022673388847471818443290403136143220719926934175085921434129203855416578111",
"05819404042502021014554003343904170186243658973014335704550865178124828029416",
"03431622978992796113672165915766558274905154096989369505325925792960764575098",
"21197221572137839559575525886358498768936158674846428380707117122383064595523",
]
for n in 1..40:
let input = collect(newSeq, (for i in 1..n: i.toF))
let root = Merkle.digest(input, which = HorizenLabsNew)
check root.toDecimal == expected[n-1]
test "byte sequences":
const expected = [
"18732683119498402220900273862328991850071867550460350301906225211930820497246",
"13735371274317768426952305440092275471527240089113890985205909417533082052161",
"01311071961332533862186884301632696142957410402596019316669626736502537000889",
"02732096009327439956212066624821392049652901875129572813384220656784351269382",
"15423519270040218946738369930755030045776747564707203916774547968486143247994",
"16587777788369780003816229389975083731089468255710141313765204301889946667321",
"15467495943722300538335131871441595157717335944259983129940442333130468249046",
"12527384548927470515835422807488513691936613642840447225072462058897980963278",
"01139622705334920884613960529662561931290307895998519853900505522690434305790",
"16142107018442488373169352233589562161473473199353653887317707621603496902013",
"01218040156344910251164083693083660612241041250293971471697358991836822822934",
"14539670015608455813987910907856931443374437516459905847769109889483291930300",
"13353935346055642659293878610542225936509964651938066706616162909789461560750",
"19495252482428174907319709455764531263442594194178108741867624978172092574227",
"17055391791505391005929832241289369835820574650534395529091050296974173444277",
"10417815964043008188415968748256260933806143076096615775214277808070524853959",
"17945990364004604436096932654837214363142551400871535149566763159356745021512",
"05378474743511544609549364900907631460628411295768446396969606581840271320568",
"10450818732527136839687818779067442575415758953761064564282786172271412091587",
"16847813351440402327481696819486626114403025989665751639642247984422675096326",
"16080420769027449065496681277508228557905448140947486066822638715407253102529",
"08366919306715136985214953211615165017170848892014972526274387139637478691525",
"19593395325880869057374643800035865356675830852145079290814753209650856975359",
"00384182859815513385839420639647792149090846462738920946060726294059360780469",
"02335931047212737616310219435914099319569437579961124983734763256150543971083",
"17349766852945185312093195812550823004516165462687415856428045793735407024063",
"01235143425552210458738643007111256946993802458731558186823661227715761468007",
"20693990524627292843499617812340491230302068522547584302944531530021746113496",
"18543804617969174119455058085898304136748236983116873040515022303109404769741",
"13926603399214407946814826425691118516102928458497586045312244959900994096091",
"16078981339237308383481231577786535454215521941758594174487496883608824711558",
"17950345918051992105102682100290048318931240905222777741695436091652103456344",
"09678874147582164379140871763209654777121789739582531437661370366499830892962",
"13383628345880432589378013903652289170303171988979847033019029235910629261984",
"06399788198102776237116949116572858190891055708099550677688992960248178791158",
"12285638521053457620056734369275700401462483768944358177245989895025490940187",
"11023910112243505257057851359408986450784351472530106441269959978534967412352",
"10624778787578691366563571801850560225032213384911218497837418378958818487516",
"12535534013320082535133438806894226954140008263818565396776040640374989498871",
"19933288905658378276606393016274134348291434435644657680465855174415264542633",
"06261150612607282233804570001150890371423227569588115985188186624749598110314",
"14485422958846313827372029273530929972217133403908862421592563787636725012150",
"12655195977124373992411099368455139921901944309142105714605662732356389040161",
"14559778810504702456139696792145743036648283769314917401383198598644089445099",
"12228495056709587360202879660468842743300166058897448810505310746824724390060",
"20864102288287485825758511230410936572570242864092892632836899001360469365611",
"08036843746026651704885811598854785242917910516698035692125001933569172974778",
"02473994388543678509867095655309280072095060173737314045638436237839871815768",
"16041637039328926785476935338433435740725771162819735004294804154626351596513",
"17521465549586968141705974423416467389385988509692267225226615599418570155107",
"09997532745735533430076017865915454770069450189257227272088477706570383058659",
"12919418194728984703475519845421880597297152049285440089486588762412519942191",
"13523007876706751682472404373209870560818417675183789073245402883976279224422",
"07515347837520015754253656443014231071504485759795690381509678821970294600877",
"10970160441525076388099216058743216953442053470534974095873799469283344672188",
"06614735377319377745838393344721797713058517520450885046647342813882495568532",
"12094237356602114373376154102675080427659391910583951611366612971336520849631",
"15686434090519025535750972146665146115600132377233307706954662444115261939363",
"07978521044324521560597166077142499104630513155826239482108808618557034953266",
"16186118178373488391355704339907628420623148341096142998906468353458994363684",
"01616094194920248549749929898850128903967843332816845679093076180633343237405",
"12067961685825689214910905495339998018807791993775742376625563551520432086402",
"16767265153622889582195925643547714403142560016144897632022578837961291923974",
"13367520571996359147909127534086997240880862282175334655451694708276490166744",
"12156833442707971504005454687813656406430681366873986160585845607076770190553",
"06817123282892899799924074757891673569493005903737254117749908558580413251083",
"11924134165483538600926542059306903275937269601056341080874171163482134362871",
"16819859849486914771075103002949963250180871968861875173105453574215316415974",
"03161143639269186112857240543778773953847165344056775149046085908262449132057",
"01631545362633925293488685148251774574817863241018946006468038576386573965597",
"01971328662008380114733067397701758302961082702641272074460886259250850887397",
"17695224358631154506978339513738652852648999874890890040034188091482229051269",
"19520521221908952578325184397091529060305659518835790012032359979413117963489",
"13727476748410294673083242759556666609110006331156301177194324266617028346471",
"14698827014679446278250011669216363784537291491609420066054426392958630097415",
"10535097647307983569862114062272489681840622918699371842851022899585266172656",
"11910779613629071438346203159111801031581811581576134374236444390950212635205",
"11680156118951907362527459456638646286695732659238408806089172691700870170551",
"07991451678780803665957836113028903779849744500818389985500383822437655878933",
"09728454236166458320093247365334095846984379976435597324759765167979530078428",
"03784046300336611178891289500943863446674516060395139310827961127099192748117",
]
for n in 0..80:
let input = collect(newSeq, (for i in 1..n: byte(i)))
let root = Merkle.digest(input, which = HorizenLabsNew)
check root.toDecimal == expected[n]
#-------------------------------------------------------------------------------
# a couple of tests with small input
for _ in 0..<1000:
let len = rand(1024)
let input = newSeqwith(len, byte.rand())
checkInput(input)
# one test with larger input
let len = rand(1024 * 1024)
let input = newSeqwith(len, byte.rand())
checkInput(input)

View File

@ -10,13 +10,29 @@ import poseidon2/permutation
suite "permutation":
test "permutation in place":
test "permutation in place (default version)":
var x: F = toF(0)
var y: F = toF(1)
var z: F = toF(2)
permInPlace(x, y, z)
check toDecimal(x) == "21882471761025344482456282050943515707267606647948403374880378562101343146243"
check toDecimal(y) == "09030699330013392132529464674294378792132780497765201297316864012141442630280"
check toDecimal(z) == "09137931384593657624554037900714196568304064431583163402259937475584578975855"
test "permutation in place (old version)":
var x: F = toF(0)
var y: F = toF(1)
var z: F = toF(2)
permInPlace(x, y, z, HorizenLabsOld)
check toDecimal(x) == "21882471761025344482456282050943515707267606647948403374880378562101343146243"
check toDecimal(y) == "09030699330013392132529464674294378792132780497765201297316864012141442630280"
check toDecimal(z) == "09137931384593657624554037900714196568304064431583163402259937475584578975855"
test "permutation in place (new version)":
var x: F = toF(0)
var y: F = toF(1)
var z: F = toF(2)
permInPlace(x, y, z, HorizenLabsNew)
check toDecimal(x) == "05297208644449048816064511434384511824916970985131888684874823260532015509555"
check toDecimal(y) == "21816030159894113985964609355246484851575571273661473159848781012394295965040"
check toDecimal(z) == "13940986381491601233448981668101586453321811870310341844570924906201623195336"

View File

@ -9,6 +9,6 @@ let digest: F = Sponge.digest(input) # a field element
let output: array[32, byte] = digest.toBytes
let left = Sponge.digest([1'u8, 2'u8, 3'u8])
let left = Sponge.digest([1'u8, 2'u8, 3'u8])
let right = Sponge.digest([4'u8, 5'u8, 6'u8])
let combination = compress(left, right)
let combination = compression(left, right)

View File

@ -10,7 +10,7 @@ import poseidon2
#-------------------------------------------------------------------------------
const expectedSpongeResultsRate1 : array[8, string] =
const expectedSpongeResultsRate1_oldConstants : array[9, string] =
[ "11474111961551684932675539562074905375756669035986300321099733737886849683321"
, "12075737409606154890751050839468327529267137715708285489737384891841319770833"
, "01607478768131843313297310704782442615640380643931196052095347138434114571392"
@ -19,9 +19,10 @@ const expectedSpongeResultsRate1 : array[8, string] =
, "16646216251577650555646508049064625507758601195307236539843683725095763921505"
, "11914716034377431890952169039751213443286692885071871704776127977841051829452"
, "20798492850731331785912281726856492405884190236464781409482377236764537088662"
, "06540627055407175851799217748185038564362037151837725911893079868194265409140"
]
const expectedSpongeResultsRate2 : array[8, string] =
const expectedSpongeResultsRate2_oldConstants : array[9, string] =
[ "15335097698975718583905618186682475632756177170667436996250626760551196078076"
, "05101758095924000127790537496504070769319625501671400349336709520206095219618"
, "07306734450287348725566606192910189982345130476287345231433021147457815478255"
@ -30,30 +31,85 @@ const expectedSpongeResultsRate2 : array[8, string] =
, "04630821736691665506072583795473163860465039714428126246168623896083265248907"
, "02020506076765964149531002674962673761843846094901604358961533722934321735239"
, "11732533243633999579592740965735640217427639382365959787508754341969556105663"
, "10595863140043317128604467126415689825650822395227997061806436243673828413111"
]
#-------------------------------------------------------------------------------
suite "sponge":
const expectedSpongeResultsRate1_newConstants : array[9, string] =
[ "16266571538186917378274620794715615871819075505012642331831740166402113589444"
, "01491928274163660244001918443018649243011497181896253665026327205550900025133"
, "02412839127042446155240125134683455485652987653749713765008668784861163705152"
, "13542922135264044648018285414420351411417846004082653245814369808833670302952"
, "07076213890494569790170137210157320757601820601990570639519400872306624103450"
, "14352524674388170989103113995374854592404343519856765997715183437545150946627"
, "09569766023135969668725409572485043887042089063331497639246627061832860243694"
, "07085371443491523419928170734321525595867567352592425091735470317423982305101"
, "08760489972278360817463581930800343241641769319369768058091167957311211828855"
]
const expectedSpongeResultsRate2_newConstants : array[9, string] =
[ "09046401272760514841637528652889990409326782162327754989145589039437684339646"
, "15241474315923430535056537847443353616620358302434799334548590414322880571588"
, "21072215602003532013851767027226990768339943605363951211799157616055680003388"
, "01170974936269513349886180046259796461929600320496736075445632961164642599434"
, "01558215443543426787363296590025566689264472540550531923588672734783818772596"
, "09807480697990556434580623882838196982062075470026453062706673159266728569584"
, "02822720935161078698932842003982797241316889894694232376768120398512081493835"
, "02351886751449393975320062064400658126198560006081569452290595784036978037282"
, "18286883995665455411199910174038826133816802309376680038108548762581403366251"
]
#-------------------------------------------------------------------------------
suite "sponge (old round constants)":
test "sponge with rate=1":
for n in 0..7:
for n in 0..8:
var xs: seq[F]
for i in 1..n:
xs.add( toF(i) )
let h = Sponge.digest(xs, rate = 1)
check toDecimal(h) == expectedSpongeResultsRate1[n]
let h = Sponge.digest(xs, rate = 1, which = HorizenLabsOld)
check toDecimal(h) == expectedSpongeResultsRate1_oldConstants[n]
test "sponge with rate=2":
for n in 0..7:
for n in 0..8:
var xs: seq[F]
for i in 1..n:
xs.add( toF(i) )
let h = Sponge.digest(xs, rate = 2)
check toDecimal(h) == expectedSpongeResultsRate2[n]
let h = Sponge.digest(xs, rate = 2, which = HorizenLabsOld)
check toDecimal(h) == expectedSpongeResultsRate2_oldConstants[n]
test "sponge with byte array as input":
let bytes = toSeq 1'u8..80'u8
let elements = toSeq bytes.elements(F)
let expected = Sponge.digest(elements, rate = 2)
let expected = Sponge.digest(elements, rate = 2, which = HorizenLabsOld)
check bool(Sponge.digest(bytes, rate = 2) == expected)
#-------------------------------------------------------------------------------
suite "sponge (new round constants)":
test "sponge with rate=1":
for n in 0..8:
var xs: seq[F]
for i in 1..n:
xs.add( toF(i) )
let h = Sponge.digest(xs, rate = 1, which = HorizenLabsNew)
check toDecimal(h) == expectedSpongeResultsRate1_newConstants[n]
test "sponge with rate=2":
for n in 0..8:
var xs: seq[F]
for i in 1..n:
xs.add( toF(i) )
let h = Sponge.digest(xs, rate = 2, which = HorizenLabsNew)
check toDecimal(h) == expectedSpongeResultsRate2_newConstants[n]
test "sponge with byte array as input":
let bytes = toSeq 1'u8..80'u8
let elements = toSeq bytes.elements(F)
let expected = Sponge.digest(elements, rate = 2, which = HorizenLabsNew)
check bool(Sponge.digest(bytes, rate = 2, which = HorizenLabsNew) == expected)
#-------------------------------------------------------------------------------

View File

@ -5,8 +5,11 @@ import constantine/math/arithmetic
import poseidon2/sponge
import poseidon2/merkle
import poseidon2/spongemerkle
import poseidon2/types
suite "sponge - merkle root":
#-------------------------------------------------------------------------------
suite "sponge - merkle root (old round constants)":
const KB = 1024
@ -31,3 +34,33 @@ suite "sponge - merkle root":
merkle.update(Sponge.digest(partialChunk, rate = 2))
let expected = merkle.finish()
check bool(SpongeMerkle.digest(bytes, chunkSize = 2*KB) == expected)
#-------------------------------------------------------------------------------
suite "sponge - merkle root (new round constants)":
const KB = 1024
test "hashes chunks of data with sponge, and combines them in merkle root":
let bytes = newSeqWith(64*KB, rand(byte))
var merkle = Merkle.init(which = HorizenLabsNew)
for i in 0..<32:
let chunk = bytes[(i*2*KB)..<((i+1)*2*KB)]
let digest = Sponge.digest(chunk, rate = 2, which = HorizenLabsNew)
merkle.update(digest)
let expected = merkle.finish()
check bool(SpongeMerkle.digest(bytes, chunkSize = 2*KB, which = HorizenLabsNew) == expected)
test "handles partial chunk at the end":
let bytes = newSeqWith(63*KB, rand(byte))
var merkle = Merkle.init(which = HorizenLabsNew)
for i in 0..<31:
let chunk = bytes[(i*2*KB)..<((i+1)*2*KB)]
let digest = Sponge.digest(chunk, rate = 2, which = HorizenLabsNew)
merkle.update(digest)
let partialChunk = bytes[(62*KB)..<(63*KB)]
merkle.update(Sponge.digest(partialChunk, rate = 2, which = HorizenLabsNew))
let expected = merkle.finish()
check bool(SpongeMerkle.digest(bytes, chunkSize = 2*KB, which = HorizenLabsNew) == expected)
#-------------------------------------------------------------------------------