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 std::array<MemoryValue, AVM_KECCAKF1600_STATE_SIZE> src_mem_values{ MemoryValue::from<uint64_t>(0) };
99
100 // Slice read and tag check
101 for (size_t k = 0; k < AVM_KECCAKF1600_STATE_SIZE; k++) {
102 const auto addr = src_addr + static_cast<MemoryAddress>(k);
103 const MemoryValue& mem_val = memory.get(addr);
104 const MemoryTag tag = mem_val.get_tag();
105 src_mem_values[k] = mem_val;
106
107 if (tag != MemoryTag::U64) {
108 keccakf1600_event.tag_error = true;
109 keccakf1600_event.src_mem_values = src_mem_values;
110
112 format("Read slice tag invalid - addr: ", addr, " tag: ", static_cast<uint32_t>(tag)));
113 }
114 }
115
116 keccakf1600_event.src_mem_values = src_mem_values;
117
118 // Initialize state input values with values read from memory.
119 // Standard Keccak layout: memory[(y * 5) + x] = A[x][y], so linear index k maps to (x=k%5, y=k/5)
120 KeccakF1600StateMemValues state_input_values;
121 for (size_t k = 0; k < AVM_KECCAKF1600_STATE_SIZE; k++) {
122 state_input_values[k % 5][k / 5] = src_mem_values[k];
123 }
124
126
127 for (uint8_t round_idx = 0; round_idx < AVM_KECCAKF1600_NUM_ROUNDS; round_idx++) {
128 std::array<std::array<MemoryValue, 4>, 5> theta_xor_values;
129
130 // Theta xor computations
131 for (size_t i = 0; i < 5; ++i) {
132 MemoryValue xor_accumulator = state_input_values[i][0];
133 for (size_t j = 0; j < 4; ++j) {
134 xor_accumulator = bitwise.xor_op(xor_accumulator, state_input_values[i][j + 1]);
135 theta_xor_values[i][j] = xor_accumulator;
136 }
137 }
138
139 // Theta xor values left rotated by 1
140 std::array<MemoryValue, 5> theta_xor_row_rotl1_values;
141 for (size_t i = 0; i < 5; ++i) {
142 theta_xor_row_rotl1_values.at(i) = unconstrained_rotate_left(theta_xor_values[i][3], 1);
143 }
144
145 // Theta combined xor computation
146 std::array<MemoryValue, 5> theta_combined_xor_values;
147 for (size_t i = 0; i < 5; ++i) {
148 theta_combined_xor_values.at(i) =
149 bitwise.xor_op(theta_xor_values[(i + 4) % 5][3], theta_xor_row_rotl1_values.at((i + 1) % 5));
150 }
151
152 // State theta values
153 std::array<std::array<MemoryValue, 5>, 5> state_theta_values;
154 for (size_t i = 0; i < 5; ++i) {
155 for (size_t j = 0; j < 5; ++j) {
156 state_theta_values[i][j] =
157 bitwise.xor_op(state_input_values[i][j], theta_combined_xor_values.at(i));
158 }
159 }
160
161 // State rho values
162 KeccakF1600StateMemValues state_rho_values;
163
164 // Handle range checks related to Rho round function.
165 // For i,j, such that 0 < rotation_len[i][j] <= 32, we range check
166 // the highest rotation_len[i][j] number of bits of state_theta_values[i][j].
167 // Otherwise, we range check the lowest 64 - rotation_len[i][j] bits.
168 for (size_t i = 0; i < 5; ++i) {
169 for (size_t j = 0; j < 5; ++j) {
170 const uint8_t& len = keccak_rotation_len[i][j];
171 // Compute state values after Rho function.
172 state_rho_values[i][j] = unconstrained_rotate_left(state_theta_values[i][j], len);
173 if (len > 0 && len <= 32) {
174 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() >> (64 - len), len);
175 } else if (len > 32) {
176 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() & ((1U << (64 - len)) - 1),
177 64 - len);
178 }
179 }
180 }
181
182 // state pi values
183 // state "not pi" values
184 KeccakF1600StateMemValues state_pi_values;
185 KeccakF1600StateMemValues state_pi_not_values;
186 for (size_t i = 0; i < 5; ++i) {
187 for (size_t j = 0; j < 5; ++j) {
188 state_pi_values[i][j] = state_rho_values[keccak_pi_rho_x_coords[i][j]][i];
189 state_pi_not_values[i][j] = ~state_pi_values[i][j];
190 }
191 }
192
193 // state "pi and" values
194 KeccakF1600StateMemValues state_pi_and_values;
195 // state chi values
196 KeccakF1600StateMemValues state_chi_values;
197 for (size_t i = 0; i < 5; ++i) {
198 for (size_t j = 0; j < 5; ++j) {
199 state_pi_and_values[i][j] =
200 bitwise.and_op(state_pi_not_values[(i + 1) % 5][j], state_pi_values[(i + 2) % 5][j]);
201 state_chi_values[i][j] = bitwise.xor_op(state_pi_values[i][j], state_pi_and_values[i][j]);
202 }
203 }
204
205 // state iota_00 value
206 // Recall that round starts with 1
207 MemoryValue iota_00_value =
208 bitwise.xor_op(state_chi_values[0][0], MemoryValue::from(keccak_round_constants.at(round_idx)));
209
210 rounds_data.at(round_idx) = {
211 .state = two_dim_array_to_uint64(state_input_values),
212 .theta_xor = two_dim_array_to_uint64(theta_xor_values),
213 .theta_xor_row_rotl1 = array_to_uint64(theta_xor_row_rotl1_values),
214 .theta_combined_xor = array_to_uint64(theta_combined_xor_values),
215 .state_theta = two_dim_array_to_uint64(state_theta_values),
216 .state_rho = two_dim_array_to_uint64(state_rho_values),
217 .state_pi_not = two_dim_array_to_uint64(state_pi_not_values),
218 .state_pi_and = two_dim_array_to_uint64(state_pi_and_values),
219 .state_chi = two_dim_array_to_uint64(state_chi_values),
220 .state_iota_00 = iota_00_value.as<uint64_t>(),
221 };
222
223 state_input_values = state_chi_values;
224 state_input_values[0][0] = iota_00_value;
225 }
226
227 // Slice write
228 for (size_t i = 0; i < 5; i++) {
229 for (size_t j = 0; j < 5; j++) {
230 memory.set(dst_addr + static_cast<MemoryAddress>((j * 5) + i), state_input_values[i][j]);
231 }
232 }
233
234 keccakf1600_event.rounds = rounds_data;
235 perm_events.emit(KeccakF1600Event(keccakf1600_event));
236 } catch (const KeccakF1600Exception& e) {
237 perm_events.emit(KeccakF1600Event(keccakf1600_event));
238 throw e;
239 }
240}
241
242} // namespace bb::avm2::simulation
#define AVM_KECCAKF1600_STATE_SIZE
#define AVM_HIGHEST_MEM_ADDRESS
#define AVM_KECCAKF1600_NUM_ROUNDS
static TaggedValue from(T value)
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:22
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< MemoryValue, AVM_KECCAKF1600_STATE_SIZE > src_mem_values
std::array< KeccakF1600RoundData, AVM_KECCAKF1600_NUM_ROUNDS > rounds