Skip to content
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

feat: new Poseidon2 relations #3406

Merged
merged 28 commits into from
Dec 1, 2023
Merged
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e1c732d
initial commit
zac-williamson Oct 27, 2023
ea12f99
mmmmm constexpr
zac-williamson Oct 27, 2023
95e6572
wip
zac-williamson Oct 27, 2023
8c30828
basic poseidon hash working
zac-williamson Oct 27, 2023
86cf018
added tests + comments
zac-williamson Oct 30, 2023
6dd2511
comments
zac-williamson Oct 30, 2023
3dbd8df
removed string_view constructors
zac-williamson Oct 30, 2023
3631294
poseidon2 parameters now consistent with definition in paper
zac-williamson Oct 31, 2023
910b303
fixed bug where constructing uint from a string that is too big!
zac-williamson Oct 31, 2023
c7b4157
added cast to fix build error
lucasxia01 Nov 7, 2023
7bcee20
benchmarking file for native impl
lucasxia01 Nov 7, 2023
752bd14
Merge branch 'master' into zw/poseidon2
lucasxia01 Nov 7, 2023
7d0fe79
Merge branch 'master' into zw/poseidon2
lucasxia01 Nov 20, 2023
20a7eb2
Merge branch 'master' into zw/poseidon2
lucasxia01 Nov 22, 2023
f09ed91
external poseidon2 round relation
lucasxia01 Nov 20, 2023
d679581
added ARC and SBox rounds to external
lucasxia01 Nov 20, 2023
35ab7d9
created internal round relation
lucasxia01 Nov 20, 2023
5e94698
naming update
lucasxia01 Nov 22, 2023
14157c1
external relation consistency test
lucasxia01 Nov 22, 2023
e04037e
internal round consistency test
lucasxia01 Nov 22, 2023
6da3335
initial manual relation tests
lucasxia01 Nov 22, 2023
0c961b1
variable renaming
lucasxia01 Nov 24, 2023
8ef04db
added manual tests for internal
lucasxia01 Nov 24, 2023
b144cce
Merge branch 'master' into lx/poseidon2-relations
lucasxia01 Nov 28, 2023
80958c5
updated based on review
lucasxia01 Nov 30, 2023
a3c8eed
naming update
lucasxia01 Nov 30, 2023
52d260b
comment and scoping update
lucasxia01 Dec 1, 2023
a53e861
Merge branch 'master' into lx/poseidon2-relations
lucasxia01 Dec 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
added tests + comments
zac-williamson committed Oct 30, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 86cf018009fc14c60b971313f18be64f42b2ba98
Original file line number Diff line number Diff line change
@@ -29,4 +29,21 @@ TEST(Poseidon2, BasicTests)
EXPECT_NE(r0, r2);
}

// N.B. these hardcoded values were extracted from the algorithm being tested. These are NOT independent test vectors!
// TODO(@zac-williamson): find independent test vectors we can compare against! (very hard to find given flexibility of
// Poseidon's parametrisation)
TEST(Poseidon2, ConsistencyCheck)
{
barretenberg::fr a(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));
barretenberg::fr b(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));
barretenberg::fr c(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));
barretenberg::fr d(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));

std::array<barretenberg::fr, 4> input{ a, b, c, d };
auto result = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>::hash(input);

barretenberg::fr expected(std::string_view("0x2f8f825d4ba3c0461bd4804e7e9490775b6f107e2eea15d6c74d370d465f7e6d"));

EXPECT_EQ(result, expected);
}
} // namespace poseidon2_tests
Original file line number Diff line number Diff line change
@@ -11,26 +11,39 @@

namespace crypto {

/**
* @brief Applies the Poseidon2 permutation function from https://eprint.iacr.org/2023/323 .
* This algorithm was implemented using https://github.com/HorizenLabs/poseidon2 as a reference.
*
* @tparam Params
*/
template <typename Params> class Poseidon2Permutation {
public:
// todo make parameters configurable?
// t = sponge permutation size (in field elements)
// t = rate + capacity
// capacity = 1 field element (256 bits)
// rate = number of field elements that can be compressed per permutation
static constexpr size_t t = Params::t;
// d = degree of s-box polynomials. For a given field, `d` is the smallest element of `p` such that gdc(d, p - 1) =
// 1 (excluding 1) For bn254/grumpkin, d = 5
static constexpr size_t d = Params::d;
static constexpr size_t sbox_size = Params::sbox_size; // logp
// sbox size = number of bits in p
static constexpr size_t sbox_size = Params::sbox_size;
// number of full sbox rounds
static constexpr size_t rounds_f = Params::rounds_f;
// number of partial sbox rounds
static constexpr size_t rounds_p = Params::rounds_p;
static constexpr size_t NUM_ROUNDS = Params::rounds_f + Params::rounds_p;

using FF = typename Params::FF;
using State = std::vector<FF>;
using State = std::array<FF, t>;
using RoundConstants = std::array<FF, t>;
using MatrixDiagonal = std::array<FF, t>;
using RoundConstantsContainer = std::array<RoundConstants, NUM_ROUNDS>;

static constexpr MatrixDiagonal internal_matrix_diagonal =
Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal;
static constexpr RoundConstantsContainer round_constants = Poseidon2Bn254ScalarFieldParams::round_constants;
// derive_round_constants();

static constexpr void matrix_multiplication_4x4(State& input)
{
Original file line number Diff line number Diff line change
@@ -18,8 +18,8 @@ TEST(Poseidon2Permutation, BasicTests)
barretenberg::fr c = barretenberg::fr::random_element(&engine);
barretenberg::fr d = barretenberg::fr::random_element(&engine);

std::vector<barretenberg::fr> input1{ a, b, c, d };
std::vector<barretenberg::fr> input2{ d, c, b, a };
std::array<barretenberg::fr, 4> input1{ a, b, c, d };
std::array<barretenberg::fr, 4> input2{ d, c, b, a };

auto r0 = crypto::Poseidon2Permutation<crypto::Poseidon2Bn254ScalarFieldParams>::permutation(input1);
auto r1 = crypto::Poseidon2Permutation<crypto::Poseidon2Bn254ScalarFieldParams>::permutation(input1);
@@ -29,4 +29,26 @@ TEST(Poseidon2Permutation, BasicTests)
EXPECT_NE(r0, r2);
}

// N.B. these hardcoded values were extracted from the algorithm being tested. These are NOT independent test vectors!
// TODO(@zac-williamson): find independent test vectors we can compare against! (very hard to find given flexibility of
// Poseidon's parametrisation)
TEST(Poseidon2Permutation, ConsistencyCheck)
{
barretenberg::fr a(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));
barretenberg::fr b(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));
barretenberg::fr c(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));
barretenberg::fr d(std::string("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"));

std::array<barretenberg::fr, 4> input{ a, b, c, d };
auto result = crypto::Poseidon2Permutation<crypto::Poseidon2Bn254ScalarFieldParams>::permutation(input);

std::array<barretenberg::fr, 4> expected{
barretenberg::fr(std::string_view("0x0514d38493ec8da89f9e2b599bc20f96206ad0c94bc2751e6df03003009aa2ea")),
barretenberg::fr(std::string_view("0x0757d335371eacea287976a7b26729a74801720418bfdac37d852ac198b585ed")),
barretenberg::fr(std::string_view("0x19f5168edd96d2c8800d460908dde37c5dd36d56ae905faa8660182a2803c56c")),
barretenberg::fr(std::string_view("0x0096047284f80a35f2f9f95101a9287e99e1afb0866f19e86286a09bdb203685")),
};
EXPECT_EQ(result, expected);
}

} // namespace poseidon2_tests
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ namespace crypto {
* see https://github.com/C2SP/C2SP/blob/792c1254124f625d459bfe34417e8f6bdd02eb28/poseidon-sponge.md
* (Note: this spec was not accepted into the C2SP repo, we might want to reference something else!)
*
* Note: If we ever use this sponge class for more than 1 hash functions, we should move this out of `poseidon2`
* and into its own directory
* @tparam FF
* @tparam rate
* @tparam capacity
@@ -23,11 +25,22 @@ namespace crypto {
*/
template <typename FF, size_t rate, size_t capacity, size_t t, typename Permutation> class FieldSponge {
public:
/**
* @brief Defines what phase of the sponge algorithm we are in.
*
* ABSORB: 'absorbing' field elements into the sponge
* SQUEEZE: compressing the sponge and extracting a field element
*
*/
enum Mode {
ABSORB,
SQUEEZE,
};

// sponge state. t = rate + capacity. capacity = 1 field element (~256 bits)
std::array<FF, t> state;

// cached elements that have been absorbed.
std::array<FF, rate> cache;
size_t cache_size = 0;
Mode mode = Mode::ABSORB;
@@ -42,36 +55,37 @@ template <typename FF, size_t rate, size_t capacity, size_t t, typename Permutat

std::array<FF, rate> perform_duplex()
{
// zero-pad the cache
for (size_t i = cache_size; i < rate; ++i) {
cache[i] = 0;
}
// add the cache into sponge state
for (size_t i = 0; i < rate; ++i) {
state[i] += cache[i];
}
// todo unify types between SPonge and permutation
std::vector<FF> in(state.begin(), state.end());
auto res = Permutation::permutation(in);
state = Permutation::permutation(state);
// return `rate` number of field elements from the sponge state.
std::array<FF, rate> output;
for (size_t i = 0; i < t; ++i) {
state[i] = res[i];
}
for (size_t i = 0; i < rate; ++i) {
output[i] = state[i];
}

return output;
}

void absorb(const FF& input)
{
if (mode == Mode::ABSORB && cache_size == rate) {
// If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache
perform_duplex();
cache[0] = input;
cache_size = 1;
} else if (mode == Mode::ABSORB && cache_size < rate) {
// If we're absorbing, and the cache is not full, add the input into the cache
cache[cache_size] = input;
cache_size += 1;
} else if (mode == Mode::SQUEEZE) {
// If we're in squeeze mode, switch to absorb mode and add the input into the cache.
// N.B. I don't think this code path can be reached?!
cache[0] = input;
cache_size = 1;
mode = Mode::ABSORB;
@@ -81,17 +95,23 @@ template <typename FF, size_t rate, size_t capacity, size_t t, typename Permutat
FF squeeze()
{
if (mode == Mode::SQUEEZE && cache_size == 0) {
// If we're in squeze mode and the cache is empty, there is nothing left to squeeze out of the sponge!
// Switch to absorb mode.
mode = Mode::ABSORB;
cache_size = 0;
}
if (mode == Mode::ABSORB) {
// If we're in absorb mode, apply sponge permutation to compress the cache, populate cache with compressed
// state and switch to squeeze mode. Note: this code block will execute if the previous `if` condition was
// matched
auto new_output_elements = perform_duplex();
mode = Mode::SQUEEZE;
for (size_t i = 0; i < rate; ++i) {
cache[i] = new_output_elements[i];
}
cache_size = rate;
}
// By this point, we should have a non-empty cache. Pop one item off the top of the cache and return it.
FF result = cache[0];
for (size_t i = 1; i < cache_size; ++i) {
cache[i - 1] = cache[i];
@@ -100,6 +120,14 @@ template <typename FF, size_t rate, size_t capacity, size_t t, typename Permutat
return result;
}

/**
* @brief Use the sponge to hash an input string
*
* @tparam out_len
* @tparam is_variable_length. Distinguishes between hashes where the preimage length is constant/not constant
* @param input
* @return std::array<FF, out_len>
*/
template <size_t out_len, bool is_variable_length> static std::array<FF, out_len> hash_internal(std::span<FF> input)
{
size_t in_len = input.size();
@@ -110,6 +138,9 @@ template <typename FF, size_t rate, size_t capacity, size_t t, typename Permutat
sponge.absorb(input[i]);
}

// In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish
// from fixed-length hashes. (the combination of this additional field element + the hash IV ensures
// fixed-length and variable-length hashes do not collide)
if constexpr (is_variable_length) {
sponge.absorb(1);
}