Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
keccakf1600.cpp
Go to the documentation of this file.
2
3#include <array>
4#include <cassert>
5#include <cstddef>
6#include <cstdint>
7
12
13namespace bb::avm2::simulation {
14
15namespace {
16
17MemoryValue unconstrained_rotate_left(MemoryValue x, uint8_t len)
18{
19 // We avoid an undefined behavior in the shift below: "x_uint64_t >> (64 - len)"
20 // if it were evaluated with len = 0. (cpp standard on bitwise shifts requires rhs
21 // to be less than the number of bits in lhs).
22 if (len == 0) {
23 return x;
24 }
25
26 const auto x_uint64_t = x.as<uint64_t>();
27 assert(len < 64);
28 const auto out_uint64_t = (x_uint64_t << len) | x_uint64_t >> (64 - len);
29 return MemoryValue::from(out_uint64_t);
30}
31
32// A function which transforms any two-dimensional array of MemoryValue's into a two-dimensional array of uint64_t.
33template <size_t N, size_t M>
34std::array<std::array<uint64_t, M>, N> two_dim_array_to_uint64(const std::array<std::array<MemoryValue, M>, N>& input)
35{
37 for (size_t i = 0; i < N; i++) {
38 for (size_t j = 0; j < M; j++) {
39 output[i][j] = input[i][j].template as<uint64_t>();
40 }
41 }
42 return output;
43}
44
45// A function which transforms any array of MemoryValue's into an array of uint64_t.
46template <size_t N> std::array<uint64_t, N> array_to_uint64(const std::array<MemoryValue, N>& input)
47{
49 for (size_t i = 0; i < N; i++) {
50 output.at(i) = input.at(i).template as<uint64_t>();
51 }
52 return output;
53}
54
55} // namespace
56
57// TODO: For fast simulation, we might directly call ethash_keccakf1600 from barretenberg, instead of
58// the following with no event emission. In this case, we will probably need two KeccakF1600 classes.
68{
69 KeccakF1600Event keccakf1600_event;
71 keccakf1600_event.dst_addr = dst_addr;
72 keccakf1600_event.src_addr = src_addr;
73
74 try {
75 // We need to perform two bound checks to determine whether dst_addr and src_addr correspond to
76 // a memory slice which is out-of-range.
77 constexpr MemoryAddress HIGHEST_SLICE_ADDRESS = AVM_HIGHEST_MEM_ADDRESS - AVM_KECCAKF1600_STATE_SIZE + 1;
78
79 // We group both possible out-of-range errors in the same temporality group.
80 // Therefore, we perform both bound checks no matter what.
81 bool src_out_of_range = gt.gt(static_cast<uint128_t>(src_addr), static_cast<uint128_t>(HIGHEST_SLICE_ADDRESS));
82 bool dst_out_of_range = gt.gt(static_cast<uint128_t>(dst_addr), static_cast<uint128_t>(HIGHEST_SLICE_ADDRESS));
83
84 keccakf1600_event.src_out_of_range = src_out_of_range;
85 keccakf1600_event.dst_out_of_range = dst_out_of_range;
86 keccakf1600_event.space_id = memory.get_space_id();
87
88 if (src_out_of_range) {
89 throw KeccakF1600Exception(format("Read slice out of range: ", src_addr));
90 }
91 if (dst_out_of_range) {
92 throw KeccakF1600Exception(format("Write slice out of range: ", dst_addr));
93 }
94
95 // We work with MemoryValue as this type is required for bitwise operations handled
96 // by the bitwise sub-trace simulator. We continue by operating over Memory values and convert
97 // them back only at the end (event emission).
98 KeccakF1600StateMemValues src_mem_values;
99 src_mem_values.fill(std::array<MemoryValue, 5>{ MemoryValue::from<uint64_t>(0) });
100
101 // Slice read and tag check
102 for (size_t i = 0; i < 5; i++) {
103 for (size_t j = 0; j < 5; j++) {
104 const auto addr = src_addr + static_cast<MemoryAddress>((i * 5) + j);
105 const MemoryValue& mem_val = memory.get(addr);
106 const MemoryTag tag = mem_val.get_tag();
107 src_mem_values[i][j] = mem_val;
108
109 if (tag != MemoryTag::U64) {
110 keccakf1600_event.tag_error = true;
111 keccakf1600_event.src_mem_values = src_mem_values;
112
114 format("Read slice tag invalid - addr: ", addr, " tag: ", static_cast<uint32_t>(tag)));
115 }
116 }
117 }
118
119 // Initialize state input values with values read from memory.
120 KeccakF1600StateMemValues state_input_values = src_mem_values;
121 keccakf1600_event.src_mem_values = src_mem_values;
122
124
125 for (uint8_t round_idx = 0; round_idx < AVM_KECCAKF1600_NUM_ROUNDS; round_idx++) {
126 std::array<std::array<MemoryValue, 4>, 5> theta_xor_values;
127
128 // Theta xor computations
129 for (size_t i = 0; i < 5; ++i) {
130 MemoryValue xor_accumulator = state_input_values[i][0];
131 for (size_t j = 0; j < 4; ++j) {
132 xor_accumulator = bitwise.xor_op(xor_accumulator, state_input_values[i][j + 1]);
133 theta_xor_values[i][j] = xor_accumulator;
134 }
135 }
136
137 // Theta xor values left rotated by 1
138 std::array<MemoryValue, 5> theta_xor_row_rotl1_values;
139 for (size_t i = 0; i < 5; ++i) {
140 theta_xor_row_rotl1_values.at(i) = unconstrained_rotate_left(theta_xor_values[i][3], 1);
141 }
142
143 // Theta combined xor computation
144 std::array<MemoryValue, 5> theta_combined_xor_values;
145 for (size_t i = 0; i < 5; ++i) {
146 theta_combined_xor_values.at(i) =
147 bitwise.xor_op(theta_xor_values[(i + 4) % 5][3], theta_xor_row_rotl1_values.at((i + 1) % 5));
148 }
149
150 // State theta values
151 std::array<std::array<MemoryValue, 5>, 5> state_theta_values;
152 for (size_t i = 0; i < 5; ++i) {
153 for (size_t j = 0; j < 5; ++j) {
154 state_theta_values[i][j] =
155 bitwise.xor_op(state_input_values[i][j], theta_combined_xor_values.at(i));
156 }
157 }
158
159 // State rho values
160 KeccakF1600StateMemValues state_rho_values;
161
162 // Handle range checks related to Rho round function.
163 // For i,j, such that 0 < rotation_len[i][j] <= 32, we range check
164 // the highest rotation_len[i][j] number of bits of state_theta_values[i][j].
165 // Otherwise, we range check the lowest 64 - rotation_len[i][j] bits.
166 for (size_t i = 0; i < 5; ++i) {
167 for (size_t j = 0; j < 5; ++j) {
168 const uint8_t& len = keccak_rotation_len[i][j];
169 // Compute state values after Rho function.
170 state_rho_values[i][j] = unconstrained_rotate_left(state_theta_values[i][j], len);
171 if (len > 0 && len <= 32) {
172 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() >> (64 - len), len);
173 } else if (len > 32) {
174 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() & ((1U << (64 - len)) - 1),
175 64 - len);
176 }
177 }
178 }
179
180 // state pi values
181 // state "not pi" values
182 KeccakF1600StateMemValues state_pi_values;
183 KeccakF1600StateMemValues state_pi_not_values;
184 for (size_t i = 0; i < 5; ++i) {
185 for (size_t j = 0; j < 5; ++j) {
186 state_pi_values[i][j] = state_rho_values[keccak_pi_rho_x_coords[i][j]][i];
187 state_pi_not_values[i][j] = ~state_pi_values[i][j];
188 }
189 }
190
191 // state "pi and" values
192 KeccakF1600StateMemValues state_pi_and_values;
193 // state chi values
194 KeccakF1600StateMemValues state_chi_values;
195 for (size_t i = 0; i < 5; ++i) {
196 for (size_t j = 0; j < 5; ++j) {
197 state_pi_and_values[i][j] =
198 bitwise.and_op(state_pi_not_values[(i + 1) % 5][j], state_pi_values[(i + 2) % 5][j]);
199 state_chi_values[i][j] = bitwise.xor_op(state_pi_values[i][j], state_pi_and_values[i][j]);
200 }
201 }
202
203 // state iota_00 value
204 // Recall that round starts with 1
205 MemoryValue iota_00_value =
206 bitwise.xor_op(state_chi_values[0][0], MemoryValue::from(keccak_round_constants.at(round_idx)));
207
208 rounds_data.at(round_idx) = {
209 .state = two_dim_array_to_uint64(state_input_values),
210 .theta_xor = two_dim_array_to_uint64(theta_xor_values),
211 .theta_xor_row_rotl1 = array_to_uint64(theta_xor_row_rotl1_values),
212 .theta_combined_xor = array_to_uint64(theta_combined_xor_values),
213 .state_theta = two_dim_array_to_uint64(state_theta_values),
214 .state_rho = two_dim_array_to_uint64(state_rho_values),
215 .state_pi_not = two_dim_array_to_uint64(state_pi_not_values),
216 .state_pi_and = two_dim_array_to_uint64(state_pi_and_values),
217 .state_chi = two_dim_array_to_uint64(state_chi_values),
218 .state_iota_00 = iota_00_value.as<uint64_t>(),
219 };
220
221 state_input_values = state_chi_values;
222 state_input_values[0][0] = iota_00_value;
223 }
224
225 // Slice write
226 for (size_t i = 0; i < 5; i++) {
227 for (size_t j = 0; j < 5; j++) {
228 memory.set(dst_addr + static_cast<MemoryAddress>((i * 5) + j), state_input_values[i][j]);
229 }
230 }
231
232 keccakf1600_event.rounds = rounds_data;
233 perm_events.emit(KeccakF1600Event(keccakf1600_event));
234 } catch (const KeccakF1600Exception& e) {
235 perm_events.emit(KeccakF1600Event(keccakf1600_event));
236 throw e;
237 }
238}
239
240} // namespace bb::avm2::simulation
#define AVM_KECCAKF1600_STATE_SIZE
#define AVM_HIGHEST_MEM_ADDRESS
#define AVM_KECCAKF1600_NUM_ROUNDS
static TaggedValue from(T value)
ValueTag get_tag() const
virtual uint32_t get_execution_id() const =0
EventEmitterInterface< KeccakF1600Event > & perm_events
void permutation(MemoryInterface &memory, MemoryAddress dst_addr, MemoryAddress src_addr) override
Permutation Keccak-f[1600] consisting in AVM_KECCAKF1600_NUM_ROUNDS (24) rounds and a state of 25 64-...
ExecutionIdManagerInterface & execution_id_manager
std::string format(Args... args)
Definition log.hpp:21
uint32_t dst_addr
constexpr std::array< std::array< uint8_t, 5 >, 5 > keccak_pi_rho_x_coords
constexpr std::array< uint64_t, 24 > keccak_round_constants
std::array< std::array< MemoryValue, 5 >, 5 > KeccakF1600StateMemValues
constexpr std::array< std::array< uint8_t, 5 >, 5 > keccak_rotation_len
TaggedValue MemoryValue
uint32_t MemoryAddress
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t len
unsigned __int128 uint128_t
Definition serialize.hpp:44
std::array< KeccakF1600RoundData, AVM_KECCAKF1600_NUM_ROUNDS > rounds