Skip to content

Commit

Permalink
anemoi: implemented gadget for the full anemoi permutation (WIP) (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vesselin Velichkov committed Feb 7, 2023
1 parent 1b96808 commit eb2a1cf
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 8 deletions.
48 changes: 46 additions & 2 deletions libsnark/gadgetlib1/gadgets/hashes/anemoi/anemoi_components.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,9 @@ class anemoi_permutation_round_prime_field_gadget

anemoi_permutation_round_prime_field_gadget(
protoboard<FieldT> &pb,
const std::vector<FieldT> &C_const,
const std::vector<FieldT> &D_const,
// TODO: add round index
const std::vector<FieldT> &C_const, // remove
const std::vector<FieldT> &D_const, // remove
const pb_linear_combination_array<FieldT> &X_left_input,
const pb_linear_combination_array<FieldT> &X_right_input,
const pb_variable_array<FieldT> &Y_left_output,
Expand Down Expand Up @@ -263,6 +264,49 @@ template<typename ppT> class anemoi_permutation_mds<ppT, 4>
static anemoi_mds_matrix_t permutation_mds(const libff::Fr<ppT> g);
};

/// Full Anemoi permutation mapping (Fr)^{2L} -> (Fr)^{2L}
/// see anemoi_permutation_round_prime_field_gadget
template<
typename ppT,
size_t NumStateColumns_L,
class parameters = anemoi_parameters<libff::Fr<ppT>>>
class anemoi_permutation_prime_field_gadget : public gadget<libff::Fr<ppT>>
{
using FieldT = libff::Fr<ppT>;

private:
// C round constants for all rounds
std::vector<std::vector<FieldT>> C_const_vec;
// D round constants for all rounds
std::vector<std::vector<FieldT>> D_const_vec;
// vector of round gadgets
std::vector<anemoi_permutation_round_prime_field_gadget<
ppT,
NumStateColumns_L,
parameters>>
Round;

public:
const pb_linear_combination_array<FieldT> X_left_input;
const pb_linear_combination_array<FieldT> X_right_input;
const pb_variable_array<FieldT> Y_left_output;
const pb_variable_array<FieldT> Y_right_output;

anemoi_permutation_prime_field_gadget(
protoboard<FieldT> &pb,
// TODO: remove constants
const std::vector<std::vector<FieldT>> &C_const,
const std::vector<std::vector<FieldT>> &D_const,
const pb_linear_combination_array<FieldT> &X_left_input,
const pb_linear_combination_array<FieldT> &X_right_input,
const pb_variable_array<FieldT> &Y_left_output,
const pb_variable_array<FieldT> &Y_right_output,
const std::string &annotation_prefix);

void generate_r1cs_constraints();
void generate_r1cs_witness();
};

} // namespace libsnark

#include "libsnark/gadgetlib1/gadgets/hashes/anemoi/anemoi_components.tcc"
Expand Down
181 changes: 179 additions & 2 deletions libsnark/gadgetlib1/gadgets/hashes/anemoi/anemoi_components.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -621,10 +621,16 @@ void anemoi_permutation_round_prime_field_gadget<
Flystel[i].generate_r1cs_witness();
}

#if 1 // DEBUG vpv-20230207
for (size_t i = 0; i < NumStateColumns_L; i++) {
this->pb.val(Y_left_output[i]) = this->pb.val(Flystel[i].output_y0);
this->pb.val(Y_right_output[i]) = this->pb.val(Flystel[i].output_y1);
assert(
this->pb.val(Y_left_output[i]) ==
this->pb.val(Flystel[i].output_y0));
assert(
this->pb.val(Y_right_output[i]) ==
this->pb.val(Flystel[i].output_y1));
}
#endif // DEBUG
}

// TODO: consdier applying the following changes to all
Expand Down Expand Up @@ -673,6 +679,177 @@ std::array<std::array<libff::Fr<ppT>, 4>, 4> anemoi_permutation_mds<ppT, 4>::
return M;
}

template<typename ppT, size_t NumStateColumns_L, class parameters>
anemoi_permutation_prime_field_gadget<ppT, NumStateColumns_L, parameters>::
anemoi_permutation_prime_field_gadget(
protoboard<libff::Fr<ppT>> &pb,
const std::vector<std::vector<FieldT>> &C,
const std::vector<std::vector<FieldT>> &D,
const pb_linear_combination_array<FieldT> &X_left,
const pb_linear_combination_array<FieldT> &X_right,
const pb_variable_array<FieldT> &Y_left,
const pb_variable_array<FieldT> &Y_right,
const std::string &annotation_prefix)
: gadget<libff::Fr<ppT>>(pb, annotation_prefix)
, C_const_vec(C)
, D_const_vec(D)
, X_left_input(X_left)
, X_right_input(X_right)
, Y_left_output(Y_left)
, Y_right_output(Y_right)
{
// Number of columns can not be larger than rounds128 size
assert(NumStateColumns_L <= parameters::nrounds128.size());
// Number of columns can not be larger than rounds256 size
assert(NumStateColumns_L <= parameters::nrounds256.size());

// Get the number of rounds for the given Anemoi instance
// (i.e. given number of columns in the state). Note: currently
// using 256-bit security instance by default. TODO add support
// for 128-bit security e.g. by adding a Boolean flag b_sec_128 in
// the tamplate parameters.
const size_t nrounds = parameters::nrounds256[NumStateColumns_L - 1];

// Left and right input to round i, outputs from round i-1
std::vector<pb_variable_array<FieldT>> round_results_left;
round_results_left.resize(nrounds);
std::vector<pb_variable_array<FieldT>> round_results_right;
round_results_right.resize(nrounds);

// Initialize Round[0] with input X_left_input, X_right_input and
// output round_results_left[0], round_results_right[0]
round_results_left[0].allocate(
pb,
NumStateColumns_L,
FMT(this->annotation_prefix, " round_results_left[0]"));
round_results_right[0].allocate(
pb,
NumStateColumns_L,
FMT(this->annotation_prefix, " round_results_right[0]"));

Round.emplace_back(anemoi_permutation_round_prime_field_gadget<
ppT,
NumStateColumns_L,
parameters>(
pb,
C[0],
D[0],
X_left_input,
X_right_input,
round_results_left[0],
round_results_right[0],
FMT(this->annotation_prefix, " Round[0]")));

printf("[%s:%d] CHECKPOINT! i = 0\n", __FILE__, __LINE__);

// Initialize Round[i>0] gadget with input round_results_left[i -
// 1], round_results_right[i - 1] and output
// round_results_left[i], round_results_right[i]
for (size_t i = 1; i < nrounds - 1; i++) {

printf("[%s:%d] CHECKPOINT! i = %zu\n", __FILE__, __LINE__, i);

round_results_left[i].allocate(
pb,
NumStateColumns_L,
FMT(this->annotation_prefix, " round_results_left[%zu]", i));
round_results_right[i].allocate(
pb,
NumStateColumns_L,
FMT(this->annotation_prefix, " round_results_right[%zu]", i));

Round.emplace_back(anemoi_permutation_round_prime_field_gadget<
ppT,
NumStateColumns_L,
parameters>(
pb,
C[i],
D[i],
round_results_left[i - 1],
round_results_right[i - 1],
round_results_left[i],
round_results_right[i],
FMT(this->annotation_prefix, " Round[%zu]", i)));
}

printf(
"[%s:%d] CHECKPOINT! final round %zu\n",
__FILE__,
__LINE__,
nrounds - 1);

round_results_left[nrounds - 1].allocate(
pb,
NumStateColumns_L,
FMT(this->annotation_prefix, " round_results_left[%zu]", nrounds - 1));
round_results_right[nrounds - 1].allocate(
pb,
NumStateColumns_L,
FMT(this->annotation_prefix, " round_results_right[%zu]", nrounds - 1));

// For last round, copy the output as given by the caller
// Y_left_output, Y_right_output
round_results_left[nrounds - 1] = Y_left_output;
round_results_right[nrounds - 1] = Y_right_output;

// Initialize the last round gadget
Round.emplace_back(anemoi_permutation_round_prime_field_gadget<
ppT,
NumStateColumns_L,
parameters>(
pb,
C[nrounds - 1],
D[nrounds - 1],
round_results_left[nrounds - 2],
round_results_right[nrounds - 2],
round_results_left[nrounds - 1],
round_results_right[nrounds - 1],
FMT(this->annotation_prefix, " Round[%zu]", nrounds - 1)));

printf("[%s:%d] Exit %s()\n", __FILE__, __LINE__, __FUNCTION__);
}

template<typename ppT, size_t NumStateColumns_L, class parameters>
void anemoi_permutation_prime_field_gadget<ppT, NumStateColumns_L, parameters>::
generate_r1cs_constraints()
{
printf("[%s:%d] Enter %s()\n", __FILE__, __LINE__, __FUNCTION__);
size_t nrounds = parameters::nrounds256[NumStateColumns_L - 1];
for (size_t i = 0; i < nrounds; i++) {
Round[i].generate_r1cs_constraints();
}
}

template<typename ppT, size_t NumStateColumns_L, class parameters>
void anemoi_permutation_prime_field_gadget<ppT, NumStateColumns_L, parameters>::
generate_r1cs_witness()
{
printf("[%s:%d] Enter %s()\n", __FILE__, __LINE__, __FUNCTION__);
size_t nrounds = parameters::nrounds256[NumStateColumns_L - 1];
for (size_t i = 0; i < nrounds; i++) {
Round[i].generate_r1cs_witness();
}

#if 0 // DEBUG
for (size_t i = 0; i < nrounds; i++) {
pb_variable_array<FieldT> Y_left_i = Round[i].Y_left_output;
pb_variable_array<FieldT> Y_right_i = Round[i].Y_right_output;

printf("[%s:%d] Round %2zu\n", __FILE__, __LINE__, i);
// Print left output from round i
for (size_t j = 0; j < NumStateColumns_L; j++) {
FieldT Y_left_i_val_j = this->pb.val(Y_left_i[j]);
Y_left_i_val_j.print();
}
// Print right output from round i
for (size_t j = 0; j < NumStateColumns_L; j++) {
FieldT Y_right_i_val_j = this->pb.val(Y_right_i[j]);
Y_right_i_val_j.print();
}
}
#endif // #if 1 // DEBUG
}

} // namespace libsnark

#endif // LIBSNARK_GADGETLIB1_GADGETS_HASHES_ANEMOI_COMPONENTS_TCC_
60 changes: 60 additions & 0 deletions libsnark/gadgetlib1/gadgets/hashes/anemoi/tests/anemoi_outputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,64 @@ std::vector<libff::Fr<libff::bls12_381_pp>> anemoi_expected_output_one_round(
return Y_expect_one_round;
}

std::vector<libff::Fr<libff::bls12_381_pp>> anemoi_expected_output(
const size_t &NumStateColumns_L)
{
std::vector<libff::Fr<libff::bls12_381_pp>> Y_expect;

assert(
((NumStateColumns_L == 1) || (NumStateColumns_L == 2) ||
(NumStateColumns_L == 3) || (NumStateColumns_L == 4)));

// Expected output for X rounds, L=1: Y_left || Y_right
if (NumStateColumns_L == 1) {
// TBD
Y_expect = {
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
};
}

// Expected output for X rounds, L=2: Y_left || Y_right
if (NumStateColumns_L == 2) {
// TBD
Y_expect = {
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
};
}

// Expected output for X rounds, L=3: Y_left || Y_right
if (NumStateColumns_L == 3) {
// TBD
Y_expect = {
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
};
}

// Expected output for X rounds, L=4: Y_left || Y_right
if (NumStateColumns_L == 4) {
// TBD
Y_expect = {
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
libff::Fr<libff::bls12_381_pp>("0"),
};
}

return Y_expect;
}

} // namespace libsnark
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ template<typename ppT>
using expected_round_values_fn_t =
std::function<std::vector<libff::Fr<ppT>>(const size_t)>;

// Returns the expected outputs from the full Anemoi permutation for
// BLS12_381
std::vector<libff::Fr<libff::bls12_381_pp>> anemoi_expected_output(
const size_t &NumStateColumns_L);

template<typename ppT>
using expected_values_fn_t =
std::function<std::vector<libff::Fr<ppT>>(const size_t)>;

} // namespace libsnark

#endif // LIBSNARK_GADGETLIB1_GADGETS_HASHES_ANEMOI_TESTS_ANEMOI_OUTPUTS_HPP_
Loading

0 comments on commit eb2a1cf

Please sign in to comment.