Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
sha256_ethereum.tcc
Go to the documentation of this file.
1 #ifndef __ZETH_CIRCUITS_SHA256_ETHEREUM_TCC__
2 #define __ZETH_CIRCUITS_SHA256_ETHEREUM_TCC__
3 
4 // DISCLAIMER:
5 // Content taken and adapted from:
6 // https://gist.github.com/kobigurk/24c25e68219df87c348f1a78db51bb52
7 
8 // Get the from_bits function
9 #include "libzeth/circuits/circuit_utils.hpp"
10 
11 // This gadget implements the interface of the HashT template
12 
13 namespace libzeth
14 {
15 
16 // See:
17 // https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts.go#L115
18 // For the implementation of the sha256 precompiled on ethereum, which basically
19 // calls the functions from the crypto/sha256 go package:
20 // https://golang.org/src/crypto/sha256/sha256.go?s=5778:5813#L263
21 // Where we see that, the function that interests us is "func (d *digest)
22 // checkSum() [Size]byte"
23 
24 template<typename FieldT>
25 sha256_ethereum<FieldT>::sha256_ethereum(
26  libsnark::protoboard<FieldT> &pb,
27  const libsnark::block_variable<FieldT> &input_block,
28  const libsnark::digest_variable<FieldT> &output,
29  const std::string &annotation_prefix)
30  : libsnark::gadget<FieldT>(pb, annotation_prefix)
31 {
32  intermediate_hash.reset(new libsnark::digest_variable<FieldT>(
33  pb, 256, FMT(this->annotation_prefix, " intermediate_hash")));
34 
35  // Padding
36  // Equivalent to the lines
37  // -- Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
38  // -- var tmp [64]byte
39  // -- tmp[0] = 0x80
40  // written in the checkSum function of the crypto/sha256 go package
41  // Total size of this vector = 512bits
42  libsnark::pb_variable_array<FieldT> length_padding =
43  pb_variable_array_from_bit_vector(
44  pb,
45  {
46  1, 0, 0, 0, 0, 0, 0, 0, // First part: 448bits <-> 56bytes
47  0, 0, 0, 0, 0, 0, 0, 0, //
48  0, 0, 0, 0, 0, 0, 0, 0, //
49  0, 0, 0, 0, 0, 0, 0, 0, //
50  0, 0, 0, 0, 0, 0, 0, 0, //
51  0, 0, 0, 0, 0, 0, 0, 0, //
52  0, 0, 0, 0, 0, 0, 0, 0, //
53  0, 0, 0, 0, 0, 0, 0, 0, //
54  0, 0, 0, 0, 0, 0, 0, 0, //
55  0, 0, 0, 0, 0, 0, 0, 0, //
56  0, 0, 0, 0, 0, 0, 0, 0, //
57  0, 0, 0, 0, 0, 0, 0, 0, //
58  0, 0, 0, 0, 0, 0, 0, 0, //
59  0, 0, 0, 0, 0, 0, 0, 0, //
60  0, 0, 0, 0, 0, 0, 0, 0, //
61  0, 0, 0, 0, 0, 0, 0, 0, //
62  0, 0, 0, 0, 0, 0, 0, 0, //
63  0, 0, 0, 0, 0, 0, 0, 0, //
64  0, 0, 0, 0, 0, 0, 0, 0, //
65  0, 0, 0, 0, 0, 0, 0, 0, //
66  0, 0, 0, 0, 0, 0, 0, 0, //
67  0, 0, 0, 0, 0, 0, 0, 0, //
68  0, 0, 0, 0, 0, 0, 0, 0, //
69  0, 0, 0, 0, 0, 0, 0, 0, //
70  0, 0, 0, 0, 0, 0, 0, 0, //
71  0, 0, 0, 0, 0, 0, 0, 0, //
72  0, 0, 0, 0, 0, 0, 0, 0, //
73  0, 0, 0, 0, 0, 0, 0, 0, //
74  0, 0, 0, 0, 0, 0, 0, 0, //
75  0, 0, 0, 0, 0, 0, 0, 0, //
76  0, 0, 0, 0, 0, 0, 0, 0, //
77  0, 0, 0, 0, 0, 0, 0, 0, //
78  0, 0, 0, 0, 0, 0, 0, 0, //
79  0, 0, 0, 0, 0, 0, 0, 0, //
80  0, 0, 0, 0, 0, 0, 0, 0, //
81  0, 0, 0, 0, 0, 0, 0, 0, //
82  0, 0, 0, 0, 0, 0, 0, 0, //
83  0, 0, 0, 0, 0, 0, 0, 0, //
84  0, 0, 0, 0, 0, 0, 0, 0, //
85  0, 0, 0, 0, 0, 0, 0, 0, //
86  0, 0, 0, 0, 0, 0, 0, 0, //
87  0, 0, 0, 0, 0, 0, 0, 0, //
88  0, 0, 0, 0, 0, 0, 0, 0, //
89  0, 0, 0, 0, 0, 0, 0, 0, //
90  0, 0, 0, 0, 0, 0, 0, 0, //
91  0, 0, 0, 0, 0, 0, 0, 0, //
92  0, 0, 0, 0, 0, 0, 0, 0, //
93  0, 0, 0, 0, 0, 0, 0, 0, //
94  0, 0, 0, 0, 0, 0, 0, 0, //
95  0, 0, 0, 0, 0, 0, 0, 0, //
96  0, 0, 0, 0, 0, 0, 0, 0, //
97  0, 0, 0, 0, 0, 0, 0, 0, //
98  0, 0, 0, 0, 0, 0, 0, 0, //
99  0, 0, 0, 0, 0, 0, 0, 0, //
100  0, 0, 0, 0, 0, 0, 0, 0, //
101  0, 0, 0, 0, 0, 0, 0, 0, //
102  0, 0, 0, 0, 0, 0, 0, 0, // Last part: 64bits <-> 8bytes
103  0, 0, 0, 0, 0, 0, 0, 0, //
104  0, 0, 0, 0, 0, 0, 0, 0, //
105  0, 0, 0, 0, 0, 0, 0, 0, //
106  0, 0, 0, 0, 0, 0, 0, 0, //
107  0, 0, 0, 0, 0, 0, 0, 0, //
108  0, 0, 0, 0, 0, 0, 1, 0, //
109  0, 0, 0, 0, 0, 0, 0, 0, //
110  },
111  FMT(annotation_prefix, " padding"));
112 
113  // https://github.com/scipr-lab/libsnark/blob/master/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc#L35
114  // Note: The IV defined in libsnark is made of:
115  // "0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
116  // 0x1f83d9ab, 0x5be0cd19" See:
117  // https://github.com/scipr-lab/libsnark/blob/master/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc#L31
118  //
119  // This IV, is the same as the one used in the crypto/sha256 (and thus in
120  // ethereum), as we can see here:
121  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256.go#L30-L38
122  //
123  // This instruction sets the IV for the first round of the hash, which is
124  // equivalent to the function
125  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256.go#L152
126  libsnark::pb_linear_combination_array<FieldT> IV =
127  libsnark::SHA256_default_IV(pb);
128 
129  // Gadget for the SHA256 compression function.
130  // https://github.com/scipr-lab/libsnark/blob/master/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp#L26
131  // Hasher hashes a first time the input data with the IV
132  //
133  // Note: Looking at the golang implementation of sha256, we see that, after
134  // the initialization IV We call the d.Write(data) function, and then we
135  // call the d.checkSum() function, that itself call d.Write internally See:
136  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256.go#L276-L277
137  // Looking into d.Write(), we see that the function calls the block()
138  // function Defined here:
139  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256block_decl.go
140  // which operates on a pointer of digest (returns nothing, because it only
141  // modifies the pointed memory) Then we have:
142  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256block_generic.go
143  // that tells us that block is in fact blockGeneric (Remember that Golang
144  // supports first class functions!) Then in
145  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256block.go#L78
146  // we see the implementation of blockGeneric (and thus of block()).
147  //
148  // Back to
149  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256.go#L276-L277
150  // We see that we first call "d.Write(data)" that corresponds to the first
151  // round of hashing we do with the hasher1 Then looking into
152  // https://github.com/golang/go/blob/master/src/crypto/sha256/sha256.go#L236
153  // We see that d.checkSum() calls d.Write() again, but this time, with the
154  // padding! Thus, this corresponds to the second round of hashing we do here
155  // with the hasher2.
156  hasher1.reset(new libsnark::sha256_compression_function_gadget<FieldT>(
157  pb, // protoboard
158  IV, // previous output - Here the IV
159  input_block.bits, // new block
160  *intermediate_hash, // output
161  FMT(this->annotation_prefix, " hasher1_gadget")));
162 
163  // The intermediate hash obtained as a result of the first hashing round is
164  // then used as IV for the second hashing round
165  libsnark::pb_linear_combination_array<FieldT> IV2(intermediate_hash->bits);
166 
167  // We hash the intermediate hash with the padding.
168  hasher2.reset(new libsnark::sha256_compression_function_gadget<FieldT>(
169  pb,
170  IV2,
171  length_padding,
172  output,
173  FMT(this->annotation_prefix, " hasher2_gadget")));
174 }
175 
176 template<typename FieldT>
177 void sha256_ethereum<FieldT>::generate_r1cs_constraints(
178  const bool ensure_output_bitness)
179 {
180  libff::UNUSED(ensure_output_bitness);
181  hasher1->generate_r1cs_constraints();
182  hasher2->generate_r1cs_constraints();
183 }
184 
185 template<typename FieldT> void sha256_ethereum<FieldT>::generate_r1cs_witness()
186 {
187  hasher1->generate_r1cs_witness();
188  hasher2->generate_r1cs_witness();
189 }
190 
191 template<typename FieldT> size_t sha256_ethereum<FieldT>::get_digest_len()
192 {
193  return SHA256_ETH_digest_size;
194 }
195 
196 template<typename FieldT> size_t sha256_ethereum<FieldT>::get_block_len()
197 {
198  return SHA256_ETH_block_size;
199 }
200 
201 template<typename FieldT>
202 size_t sha256_ethereum<FieldT>::expected_constraints(
203  const bool ensure_output_bitness)
204 {
205  libff::UNUSED(ensure_output_bitness);
206  return 54560;
207 }
208 
209 template<typename FieldT>
210 libff::bit_vector sha256_ethereum<FieldT>::get_hash(
211  const libff::bit_vector &input)
212 {
213  libsnark::protoboard<FieldT> pb;
214 
215  libsnark::block_variable<FieldT> input_block(
216  pb, libsnark::SHA256_block_size, "input_block");
217  libsnark::digest_variable<FieldT> output_variable(
218  pb, libsnark::SHA256_digest_size, "output_variable");
219  sha256_ethereum<FieldT> eth_hasher(
220  pb, input_block, output_variable, "eth_hasher_gadget");
221 
222  input_block.generate_r1cs_witness(input);
223  eth_hasher.generate_r1cs_witness();
224 
225  return output_variable.get_digest();
226 }
227 
228 } // namespace libzeth
229 
230 #endif // __ZETH_CIRCUITS_SHA256_ETHEREUM_TCC__