From 7ae5c9a22ae7de3f382c3b9798cccebd4b1082f4 Mon Sep 17 00:00:00 2001 From: Balazs Komuves Date: Wed, 22 Apr 2026 18:09:25 +0200 Subject: [PATCH] add option to use the "new" round constants for Poseidon2 (see #25) --- .gitignore | 2 +- README.md | 17 ++ poseidon2.nimble | 2 +- poseidon2/compress.nim | 4 +- poseidon2/merkle.nim | 39 ++-- poseidon2/permutation.nim | 55 ++++- poseidon2/roundconst_new.nim | 99 +++++++++ .../{roundconst.nim => roundconst_old.nim} | 5 +- poseidon2/roundfun.nim | 44 +++- poseidon2/sponge.nim | 41 ++-- poseidon2/spongemerkle.nim | 21 +- poseidon2/types.nim | 19 ++ tests/poseidon2/reference.nim | 16 +- tests/poseidon2/testCompress.nim | 8 +- tests/poseidon2/testMerkle.nim | 199 ++++++++++++++++-- tests/poseidon2/testPermutation.nim | 22 +- tests/poseidon2/testReadme.nim | 4 +- tests/poseidon2/testSponge.nim | 76 ++++++- tests/poseidon2/testSpongeMerkle.nim | 35 ++- 19 files changed, 594 insertions(+), 114 deletions(-) create mode 100644 poseidon2/roundconst_new.nim rename poseidon2/{roundconst.nim => roundconst_old.nim} (98%) diff --git a/.gitignore b/.gitignore index 2871150..705e339 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ !*.* .nimble poseidon2.out -.DS_store \ No newline at end of file +.DS_store diff --git a/README.md b/README.md index b81b175..40eb6c8 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/poseidon2.nimble b/poseidon2.nimble index c710332..4072650 100644 --- a/poseidon2.nimble +++ b/poseidon2.nimble @@ -1,4 +1,4 @@ -version = "0.1.0" +version = "0.1.1" author = "nim-poseidon2 authors" description = "Poseidon2 hash function" license = "MIT" diff --git a/poseidon2/compress.nim b/poseidon2/compress.nim index 5fc9dd0..71b1b9d 100644 --- a/poseidon2/compress.nim +++ b/poseidon2/compress.nim @@ -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 diff --git a/poseidon2/merkle.nim b/poseidon2/merkle.nim index 48c8e91..3f7ec15 100644 --- a/poseidon2/merkle.nim +++ b/poseidon2/merkle.nim @@ -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() diff --git a/poseidon2/permutation.nim b/poseidon2/permutation.nim index 6e41f15..c1eb6d5 100644 --- a/poseidon2/permutation.nim +++ b/poseidon2/permutation.nim @@ -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) + +#------------------------------------------------------------------------------- diff --git a/poseidon2/roundconst_new.nim b/poseidon2/roundconst_new.nim new file mode 100644 index 0000000..3124b74 --- /dev/null +++ b/poseidon2/roundconst_new.nim @@ -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" , + ] + +#------------------------------------------------------------------------------- diff --git a/poseidon2/roundconst.nim b/poseidon2/roundconst_old.nim similarity index 98% rename from poseidon2/roundconst.nim rename to poseidon2/roundconst_old.nim index 15e43ec..b1629f0 100644 --- a/poseidon2/roundconst.nim +++ b/poseidon2/roundconst_old.nim @@ -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" , diff --git a/poseidon2/roundfun.nim b/poseidon2/roundfun.nim index dc087aa..7ce85ae 100644 --- a/poseidon2/roundfun.nim +++ b/poseidon2/roundfun.nim @@ -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 + +#------------------------------------------------------------------------------- diff --git a/poseidon2/sponge.nim b/poseidon2/sponge.nim index a4160d2..08e4ce4 100644 --- a/poseidon2/sponge.nim +++ b/poseidon2/sponge.nim @@ -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() diff --git a/poseidon2/spongemerkle.nim b/poseidon2/spongemerkle.nim index db45dd8..967d03a 100644 --- a/poseidon2/spongemerkle.nim +++ b/poseidon2/spongemerkle.nim @@ -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 diff --git a/poseidon2/types.nim b/poseidon2/types.nim index 0a24f41..d032b20 100644 --- a/poseidon2/types.nim +++ b/poseidon2/types.nim @@ -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 +# +# +# 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) diff --git a/tests/poseidon2/reference.nim b/tests/poseidon2/reference.nim index 0e4376b..30e0665 100644 --- a/tests/poseidon2/reference.nim +++ b/tests/poseidon2/reference.nim @@ -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..