diff --git a/poseidon2.nim b/poseidon2.nim index 7959392..74b5379 100644 --- a/poseidon2.nim +++ b/poseidon2.nim @@ -3,10 +3,12 @@ import poseidon2/io import poseidon2/sponge import poseidon2/compress import poseidon2/merkle +import poseidon2/spongemerkle export sponge export compress export merkle +export spongemerkle export toBytes export toF export elements diff --git a/poseidon2/spongemerkle.nim b/poseidon2/spongemerkle.nim new file mode 100644 index 0000000..db45dd8 --- /dev/null +++ b/poseidon2/spongemerkle.nim @@ -0,0 +1,28 @@ +import ./types +import ./merkle +import ./sponge + +type SpongeMerkle* = object + merkle: Merkle + +func init*(_: type SpongeMerkle): SpongeMerkle = + SpongeMerkle(merkle: Merkle.init()) + +func update*(spongemerkle: var SpongeMerkle, chunk: openArray[byte]) = + let digest = Sponge.digest(chunk, rate = 2) + spongemerkle.merkle.update(digest) + +func finish*(spongemerkle: var SpongeMerkle): F = + return spongemerkle.merkle.finish() + +func digest*(_: type SpongeMerkle, bytes: openArray[byte], chunkSize: int): 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 index = 0 + while index < bytes.len: + let start = index + let finish = min(index + chunkSize, bytes.len) + spongemerkle.update(bytes.toOpenArray(start, finish - 1)) + index += chunkSize + return spongemerkle.finish() diff --git a/tests/poseidon2/testSpongeMerkle.nim b/tests/poseidon2/testSpongeMerkle.nim new file mode 100644 index 0000000..412d05c --- /dev/null +++ b/tests/poseidon2/testSpongeMerkle.nim @@ -0,0 +1,33 @@ +import std/unittest +import std/sequtils +import std/random +import constantine/math/arithmetic +import poseidon2/sponge +import poseidon2/merkle +import poseidon2/spongemerkle + +suite "sponge - merkle root": + + 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() + for i in 0..<32: + let chunk = bytes[(i*2*KB)..<((i+1)*2*KB)] + let digest = Sponge.digest(chunk, rate = 2) + merkle.update(digest) + let expected = merkle.finish() + check bool(SpongeMerkle.digest(bytes, chunkSize = 2*KB) == expected) + + test "handles partial chunk at the end": + let bytes = newSeqWith(63*KB, rand(byte)) + var merkle = Merkle.init() + for i in 0..<31: + let chunk = bytes[(i*2*KB)..<((i+1)*2*KB)] + let digest = Sponge.digest(chunk, rate = 2) + merkle.update(digest) + let partialChunk = bytes[(62*KB)..<(63*KB)] + merkle.update(Sponge.digest(partialChunk, rate = 2)) + let expected = merkle.finish() + check bool(SpongeMerkle.digest(bytes, chunkSize = 2*KB) == expected) diff --git a/tests/test.nim b/tests/test.nim index dea2668..76ca9ff 100644 --- a/tests/test.nim +++ b/tests/test.nim @@ -2,6 +2,7 @@ import ./poseidon2/testPermutation import ./poseidon2/testSponge import ./poseidon2/testCompress import ./poseidon2/testMerkle +import ./poseidon2/testSpongeMerkle import ./poseidon2/testIo import ./poseidon2/testReadme