-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Edmund/continue poseidon #1313
Edmund/continue poseidon #1313
Conversation
Co-authored-by: chessai <chessai@kadena.io>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good so far
src/Pact/Native.hs
Outdated
, "The input consists of 1 to 8 integers." | ||
, "The output is the hash result as an integer." | ||
, "Note: This is a reference version of the Poseidon hash function used by Hack-a-Chain." | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is non-idiomatic of the way we write native docs. There's no need to intro this now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if the new version is idiomatic.
let in2 = mulmod inVal inVal | ||
in4 = mulmod in2 in2 | ||
in mulmod in4 inVal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My group theory is rusty, but can you do mod
just once after all the multiplications?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried some basic tests and that seems to hold true:
module Main where
modulus :: Integer
modulus = 53
mulmod :: Integer -> Integer -> Integer
mulmod a b = (a * b) `mod` modulus
sig :: Integer -> Integer
sig inVal =
let in2 = mulmod inVal inVal
in4 = mulmod in2 in2
in mulmod in4 inVal
mulmod2:: Integer -> Integer -> Integer
mulmod2 a b = (a * b)
sig2 :: Integer -> Integer
sig2 inVal =
let in2 = mulmod2 inVal inVal
in4 = mulmod2 in2 in2
in mulmod2 in4 inVal `mod` modulus
main :: IO ()
main = do
print $ sig 123
print $ sig 456
print $ sig 789
print $ sig2 123
print $ sig2 456
print $ sig2 789
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I grabbed a pen and paper today! So the question effectively is whether ab mod n = (a mod n) × (b mod n) mod n
. Let's use brute force and assume a = k₁n + r₁
, b = k₂n + r₂
(with the usual conditions of r₁, r₂ < n
). Then:
ab mod n = (...) × n + r₁r₂ mod n = r₁r₂ mod n
while for the rhs
(a mod n) × (b mod n) mod n = r₁r₂ mod n
so that clearly holds, and mod
can happen just once. That is assuming nothing funny happens wrt negative signs here and haskell mod
behaves like math mod
(which IIRC it does).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Georg, worth a try.
Just tried it, it's not any faster according to my benchmark to do
(inVal ^ 5) `mod` modulus
than to do what we're doing now. I think that's what you were suggesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that was it.
Well, perhaps it's not that much of a hotspot then! Up to you what expression to keep, although I'd probably vote for inVal ^ 5
— this way it's more obvious what's happening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think sig
is just strangely named (due a the paper reference impl maybe?).
That said (inVal ^ 5)
if the ^
implementation isn't applying modulo may be significantly slower because of the size the integers can grow. The modulus is quite a large prime, so there are numbers quite large there.
Here's where exactly the constants came from: https://github.com/iden3/circomlibjs/blob/main/src/poseidon_constants.js. I've patched the PR to use the exact same constants (in hex, just like they do it) so it's easier to compare. I have verified by hand that the output for these test vectors matches the output from https://github.com/iden3/circomlibjs/blob/main/src/poseidon_reference.js. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fantastic!
- Ran it locally in repl, outputs look reasonable
- Rejects some bad arguments as expected
- Unit tests look good
- Gas tests look good
- Gas analysis matches the benchmarks
- Repl complains when DisablePact410
import qualified Data.Primitive.SmallArray as SmallArray | ||
|
||
modulus :: Integer | ||
modulus = 21888242871839275222246405745257275088548364400416034343698204186575808495617 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constant is in our ZK impl as well. I wonder whether we want a common place for it.
summod :: Integer -> Integer -> Integer | ||
summod a b = (a + b) `mod` modulus | ||
|
||
sig :: Integer -> Integer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sig
is just a^5 mod n
, is the name due to the reference impl?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I think.
in mulmod in4 inVal | ||
|
||
ark :: Integer -> Integer -> Integer | ||
ark inVal cc = summod inVal cc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ark = summod
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I kept the names just in case this makes it easier to compare to the ref. impl.
let in2 = mulmod inVal inVal | ||
in4 = mulmod in2 in2 | ||
in mulmod in4 inVal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think sig
is just strangely named (due a the paper reference impl maybe?).
That said (inVal ^ 5)
if the ^
implementation isn't applying modulo may be significantly slower because of the size the integers can grow. The modulus is quite a large prime, so there are numbers quite large there.
ark inVal cc = summod inVal cc | ||
|
||
data NineRings = | ||
NineRings !Integer !Integer !Integer !Integer !Integer !Integer !Integer !Integer !Integer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same amount Kobe and Shaq have combined.
poseidon :: [Integer] -> Integer | ||
poseidon inputs = | ||
if nInputs > 8 || nInputs == 0 | ||
then error $ "poseidon: number of inputs not between 0 and 8: " <> show nInputs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error
here worries me ever so slightly, only since the function spits out package hashes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We never get to this point anyway, because of the case in poseidonDef
. But I can replace this with a bespoke exception type if you'd prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be okay 👍
This PR supersedes #1274.
Remaining work: check test vectors, check gas.
Edit: both done.
The performance improvements in this PR go from this:
to this:
and from this:
to this:
PR checklist:
cabal run tests
. If they pass locally, docs are generated.pact -t
), make sure pact-lsp is in sync.Additionally, please justify why you should or should not do the following: