Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
blake2s_comp.tcc
Go to the documentation of this file.
1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
2 //
3 // SPDX-License-Identifier: LGPL-3.0+
4 
5 #ifndef __ZETH_CIRCUITS_BLAKE2S_COMP_TCC__
6 #define __ZETH_CIRCUITS_BLAKE2S_COMP_TCC__
7 
8 #include "libzeth/circuits/blake2s/blake2s_comp.hpp"
9 
10 namespace libzeth
11 {
12 
13 /// This gadget implements the interface of the HashT template
14 template<typename FieldT>
15 BLAKE2s_256_comp<FieldT>::BLAKE2s_256_comp(
16  libsnark::protoboard<FieldT> &pb,
17  const libsnark::digest_variable<FieldT> &h,
18  const libsnark::block_variable<FieldT> &input_block,
19  const libsnark::digest_variable<FieldT> &output,
20  const std::string &annotation_prefix)
21  : libsnark::gadget<FieldT>(pb, annotation_prefix)
22  , h(h)
23  , input_block(input_block)
24  , output(output)
25 {
26  // Allocate and format the 16 input block variable
27  for (size_t i = 0; i < BLAKE2s_word_number; i++) {
28  block[i].allocate(
29  pb,
30  BLAKE2s_word_size,
31  FMT(this->annotation_prefix, " block_%zu", i));
32  }
33 
34  // Allocate the init state variables and output bytes (before swapping
35  // endianness and appending)
36  for (size_t i = 0; i < 8; i++) {
37  h_array[i].allocate(
38  this->pb,
39  BLAKE2s_word_size,
40  FMT(this->annotation_prefix, " h_%zu", i));
41 
42  out_temp[i].allocate(
43  pb,
44  BLAKE2s_word_size,
45  FMT(this->annotation_prefix, " out_temp_%zu", i));
46 
47  output_bytes[i].allocate(
48  pb,
49  BLAKE2s_word_size,
50  FMT(this->annotation_prefix, " output_byte_%zu", i));
51  }
52 
53  // Allocate the state variables
54  for (size_t i = 0; i < rounds + 1; i++) {
55  for (size_t j = 0; j < BLAKE2s_word_number; j++) {
56  v[i][j].allocate(
57  this->pb,
58  BLAKE2s_word_size,
59  FMT(this->annotation_prefix, " v_%zu", i * rounds + j));
60  }
61  }
62  for (size_t i = 0; i < rounds; i++) {
63  for (size_t j = 0; j < BLAKE2s_word_number; j++) {
64  v_temp[i][j].allocate(
65  this->pb,
66  BLAKE2s_word_size,
67  FMT(this->annotation_prefix, " v_temp_%zu", i * rounds + j));
68  }
69  }
70 
71  // Set up the g_primitive gadgets used in the compression function
72  setup_mixing_gadgets();
73 };
74 
75 template<typename FieldT>
76 void BLAKE2s_256_comp<FieldT>::generate_r1cs_constraints(
77  const bool ensure_output_bitness)
78 {
79  libff::UNUSED(ensure_output_bitness);
80 
81  for (size_t i = 0; i < rounds; i++) {
82  for (auto &gadget : g_arrays[i]) {
83  gadget.generate_r1cs_constraints();
84  }
85  }
86 
87  for (auto &gadget : xor_vector) {
88  gadget.generate_r1cs_constraints();
89  }
90 };
91 
92 template<typename FieldT>
93 void BLAKE2s_256_comp<FieldT>::generate_r1cs_witness(
94  size_t len_byte_total, bool is_last_block)
95 {
96  // Format two 256-bit long big endian inputs into one 512 long little endian
97  // input (with padding if necessary)
98  size_t input_size = input_block.bits.size();
99  // We do not use block_size because the value might not be entered
100  // (c.f. block_variable<FieldT>::block_variable(
101  // protoboard<FieldT> &pb,
102  // const std::vector<pb_variable_array<FieldT>> &parts,
103  // const std::string &annotation_prefix))
104 
105  // Push the block variable in local to be swapped
106  std::vector<FieldT> padded_input;
107  for (size_t i = 0; i < input_size; i++) {
108  padded_input.push_back(this->pb.val(input_block.bits[i]));
109  }
110 
111  // [SANITY CHECK] Pad if necessary (if input_size < BLAKE2s_block_size)
112  for (size_t i = 0; i < BLAKE2s_block_size - input_size; i++) {
113  padded_input.push_back(FieldT("0"));
114  }
115 
116  // Allocate and format the 16 input block variable
117  for (size_t i = 0; i < BLAKE2s_word_number; i++) {
118  std::vector<FieldT> temp_vector(
119  padded_input.begin() + BLAKE2s_word_size * i,
120  padded_input.begin() + BLAKE2s_word_size * (i + 1));
121  temp_vector = swap_byte_endianness(temp_vector);
122  block[i].fill_with_field_elements(this->pb, temp_vector);
123  }
124 
125  BLAKE2s_256_comp<FieldT>::setup_h();
126  BLAKE2s_256_comp<FieldT>::setup_counter(len_byte_total);
127  BLAKE2s_256_comp<FieldT>::setup_v(is_last_block);
128 
129  for (size_t i = 0; i < rounds; i++) {
130  for (auto &gadget : g_arrays[i]) {
131  gadget.generate_r1cs_witness();
132  }
133  }
134 
135  // TODO: batch equality constraints (should save ~200 constraints (~1%))
136 
137  for (auto &gadget : xor_vector) {
138  gadget.generate_r1cs_witness();
139  }
140 
141  // Retrieve values, swap endianness of each bit32 and append them to get
142  // final output
143  std::vector<FieldT> output_conversion;
144  for (size_t i = 0; i < 8; i++) {
145  std::vector<FieldT> output_byte_value =
146  output_bytes[i].get_vals(this->pb);
147 
148  // We swap to big endian if it is the last call.
149  if (is_last_block) {
150  output_byte_value = swap_byte_endianness(output_byte_value);
151  }
152  output_conversion.insert(
153  output_conversion.end(),
154  output_byte_value.begin(),
155  output_byte_value.end());
156  }
157 
158  output.bits.fill_with_field_elements(this->pb, output_conversion);
159 };
160 
161 template<typename FieldT> size_t BLAKE2s_256_comp<FieldT>::get_digest_len()
162 {
163  return BLAKE2s_digest_size;
164 }
165 
166 template<typename FieldT> size_t BLAKE2s_256_comp<FieldT>::get_block_len()
167 {
168  return BLAKE2s_block_size;
169 }
170 
171 template<typename FieldT>
172 size_t BLAKE2s_256_comp<FieldT>::expected_constraints(
173  const bool ensure_output_bitness)
174 {
175  libff::UNUSED(ensure_output_bitness);
176  return 21472;
177  // ~38.89% of sha256_ethereum
178 }
179 
180 template<typename FieldT>
181 libff::bit_vector BLAKE2s_256_comp<FieldT>::get_hash(
182  const libff::bit_vector &input)
183 {
184  libsnark::protoboard<FieldT> pb;
185 
186  libsnark::block_variable<FieldT> input_block(
187  pb, BLAKE2s_block_size, "input_block");
188  libsnark::digest_variable<FieldT> output_variable(
189  pb, BLAKE2s_digest_size, "output_variable");
190  BLAKE2s_256_comp<FieldT> blake2s_hasher(
191  pb, input_block, output_variable, "blake2s_hasher_gadget");
192 
193  input_block.generate_r1cs_witness(input);
194  blake2s_hasher.generate_r1cs_witness();
195 
196  return output_variable.get_digest();
197 }
198 
199 } // namespace libzeth
200 
201 #endif // __ZETH_CIRCUITS_BLAKE2S_COMP_TCC__