Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
poseidon2.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <array>
5#include <cstdint>
6
11
14
15namespace bb::avm2::simulation {
16
17FF Poseidon2::hash(const std::vector<FF>& input)
18{
19 size_t input_size = input.size();
20 // The number of permutation events required to process the input
21 auto num_perm_events = (input_size / 3) + static_cast<size_t>(input_size % 3 != 0);
22 std::vector<std::array<FF, 4>> intermediate_states;
23 // We reserve space for the intermediate permutation states and 1 additional space for the initial state
24 intermediate_states.reserve(num_perm_events + 1);
25
26 // The unpadded length of the input is set as the IV
27 // The initial permutation state is seeded with the iv at the last input index
28 const uint256_t iv = static_cast<uint256_t>(input_size) << 64;
29 std::array<FF, 4> perm_state = { 0, 0, 0, iv };
30 intermediate_states.push_back(perm_state);
31
32 // Also referred to as cache but is the inputs that will be passed to the permutation function
34
35 for (size_t i = 0; i < num_perm_events; i++) {
36 // We can at most absorb a chunk of 3 elements
37 size_t chunk_size = std::min(input_size, static_cast<size_t>(3));
38 // Mix the input chunk into the previous permutation output state
39 for (size_t j = 0; j < chunk_size; j++) {
40 perm_state[j] += input[(i * 3) + j];
41 }
42 perm_state = permutation(perm_state);
43 intermediate_states.push_back(perm_state);
44
45 input_size -= chunk_size;
46 }
47
48 hash_events.emit(
49 { .inputs = input, .intermediate_states = std::move(intermediate_states), .output = perm_state[0] });
50 return perm_state[0];
51}
52
53std::array<FF, 4> Poseidon2::permutation(const std::array<FF, 4>& input)
54{
56 perm_events.emit({ .input = input, .output = output });
57 return output;
58}
59
60void Poseidon2::permutation(MemoryInterface& memory, MemoryAddress src_address, MemoryAddress dst_address)
61{
62 uint32_t execution_clk = execution_id_manager.get_execution_id();
63 uint32_t space_id = memory.get_space_id();
64
65 auto zero = MemoryValue::from<FF>(0);
66 std::array<MemoryValue, 4> input = { zero, zero, zero, zero };
67
68 // Poseidon2Perm reads and writes 4 sequential elements each. We need to ensure that these memory addresses are
69 // within the memory address bounds.
70 // Read Addressess: { src_address, src_address + 1, src_address + 2, src_address + 3 }
71 // Write Addresses: { dst_address, dst_address + 1, dst_address + 2, dst_address + 3 }
72 // So we check that src_address + 3 and dst_address + 3 are within the bounds
73 uint64_t max_read_address = static_cast<uint64_t>(src_address) + 3;
74 uint64_t max_write_address = static_cast<uint64_t>(dst_address) + 3;
75 bool read_out_of_range = gt.gt(max_read_address, AVM_HIGHEST_MEM_ADDRESS);
76 bool write_out_of_range = gt.gt(max_write_address, AVM_HIGHEST_MEM_ADDRESS);
77
78 try {
79 if (read_out_of_range || write_out_of_range) {
80 throw std::runtime_error("src or dst address out of range");
81 }
82
83 // Read 4 elements from memory starting at src_address
84 for (uint32_t i = 0; i < 4; i++) {
85 input[i] = memory.get(src_address + i);
86 }
87
88 // If any of the memory values are not tagged as FF, we throw an error. This is only tested after all elements
89 // are loaded as the circuit expects reading and tagging checking to be different temporality groups
90 if (std::ranges::any_of(
91 input.begin(), input.end(), [](const MemoryValue& val) { return val.get_tag() != MemoryTag::FF; })) {
92 throw std::runtime_error("An input tag is not FF");
93 }
94
95 // This calls the Poseidon2 gadget permutation function and so generates events
96 std::array<FF, 4> output = permutation({
97 input[0].as_ff(),
98 input[1].as_ff(),
99 input[2].as_ff(),
100 input[3].as_ff(),
101 });
102
103 // Write the output back to memory starting at dst_address
104 for (uint32_t i = 0; i < 4; i++) {
105 memory.set(dst_address + i, MemoryValue::from_tag(MemoryTag::FF, output[i]));
106 }
107 perm_mem_events.emit({ .space_id = space_id,
108 .execution_clk = execution_clk,
109 .src_address = src_address,
110 .dst_address = dst_address,
111 .input = input,
112 .output = output });
113
114 } catch (const std::exception& e) {
115 perm_mem_events.emit({ .space_id = space_id,
116 .execution_clk = execution_clk,
117 .src_address = src_address,
118 .dst_address = dst_address,
119 .input = input,
120 .output = { 0, 0, 0, 0 } });
121 throw Poseidon2Exception("Permutation failed, " + std::string(e.what()));
122 }
123}
124
125} // namespace bb::avm2::simulation
#define AVM_HIGHEST_MEM_ADDRESS
static TaggedValue from_tag(ValueTag tag, FF value)
virtual uint32_t get_execution_id() const =0
std::array< FF, 4 > permutation(const std::array< FF, 4 > &input) override
Definition poseidon2.cpp:53
EventEmitterInterface< Poseidon2PermutationEvent > & perm_events
Definition poseidon2.hpp:45
EventEmitterInterface< Poseidon2HashEvent > & hash_events
Definition poseidon2.hpp:44
ExecutionIdManagerInterface & execution_id_manager
Definition poseidon2.hpp:42
EventEmitterInterface< Poseidon2PermutationMemoryEvent > & perm_mem_events
Definition poseidon2.hpp:46
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
Applies the Poseidon2 permutation function from https://eprint.iacr.org/2023/323 ....
uint32_t MemoryAddress
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13