1 #ifndef __ZETH_CIRCUITS_SHA256_ETHEREUM_TCC__
2 #define __ZETH_CIRCUITS_SHA256_ETHEREUM_TCC__
5 // Content taken and adapted from:
6 // https://gist.github.com/kobigurk/24c25e68219df87c348f1a78db51bb52
8 // Get the from_bits function
9 #include "libzeth/circuits/circuit_utils.hpp"
11 // This gadget implements the interface of the HashT template
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"
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)
32 intermediate_hash.reset(new libsnark::digest_variable<FieldT>(
33 pb, 256, FMT(this->annotation_prefix, " intermediate_hash")));
36 // Equivalent to the lines
37 // -- Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
38 // -- var tmp [64]byte
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(
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, //
111 FMT(annotation_prefix, " padding"));
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
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
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);
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
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()).
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
156 hasher1.reset(new libsnark::sha256_compression_function_gadget<FieldT>(
158 IV, // previous output - Here the IV
159 input_block.bits, // new block
160 *intermediate_hash, // output
161 FMT(this->annotation_prefix, " hasher1_gadget")));
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);
167 // We hash the intermediate hash with the padding.
168 hasher2.reset(new libsnark::sha256_compression_function_gadget<FieldT>(
173 FMT(this->annotation_prefix, " hasher2_gadget")));
176 template<typename FieldT>
177 void sha256_ethereum<FieldT>::generate_r1cs_constraints(
178 const bool ensure_output_bitness)
180 libff::UNUSED(ensure_output_bitness);
181 hasher1->generate_r1cs_constraints();
182 hasher2->generate_r1cs_constraints();
185 template<typename FieldT> void sha256_ethereum<FieldT>::generate_r1cs_witness()
187 hasher1->generate_r1cs_witness();
188 hasher2->generate_r1cs_witness();
191 template<typename FieldT> size_t sha256_ethereum<FieldT>::get_digest_len()
193 return SHA256_ETH_digest_size;
196 template<typename FieldT> size_t sha256_ethereum<FieldT>::get_block_len()
198 return SHA256_ETH_block_size;
201 template<typename FieldT>
202 size_t sha256_ethereum<FieldT>::expected_constraints(
203 const bool ensure_output_bitness)
205 libff::UNUSED(ensure_output_bitness);
209 template<typename FieldT>
210 libff::bit_vector sha256_ethereum<FieldT>::get_hash(
211 const libff::bit_vector &input)
213 libsnark::protoboard<FieldT> pb;
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");
222 input_block.generate_r1cs_witness(input);
223 eth_hasher.generate_r1cs_witness();
225 return output_variable.get_digest();
228 } // namespace libzeth
230 #endif // __ZETH_CIRCUITS_SHA256_ETHEREUM_TCC__