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
17namespace {
18
19class InternalPoseidon2Exception : public std::runtime_error {
20 using std::runtime_error::runtime_error; // Inherit the constructor.
21};
22
23} // namespace
24
25FF Poseidon2::hash(const std::vector<FF>& input)
26{
27 size_t input_size = input.size();
28 // The number of permutation events required to process the input
29 auto num_perm_events = (input_size / 3) + static_cast<size_t>(input_size % 3 != 0);
30 std::vector<std::array<FF, 4>> intermediate_states;
31 // We reserve space for the intermediate permutation states and 1 additional space for the initial state
32 intermediate_states.reserve(num_perm_events + 1);
33
34 // The unpadded length of the input is set as the IV
35 // The initial permutation state is seeded with the iv at the last input index
36 const uint256_t iv = static_cast<uint256_t>(input_size) << 64;
37 std::array<FF, 4> perm_state = { 0, 0, 0, iv };
38 intermediate_states.push_back(perm_state);
39
40 // Also referred to as cache but is the inputs that will be passed to the permutation function
42
43 for (size_t i = 0; i < num_perm_events; i++) {
44 // We can at most absorb a chunk of 3 elements
45 size_t chunk_size = std::min(input_size, static_cast<size_t>(3));
46 // Mix the input chunk into the previous permutation output state
47 for (size_t j = 0; j < chunk_size; j++) {
48 perm_state[j] += input[(i * 3) + j];
49 }
50 perm_state = permutation(perm_state);
51 intermediate_states.push_back(perm_state);
52
53 input_size -= chunk_size;
54 }
55
56 hash_events.emit(
57 { .inputs = input, .intermediate_states = std::move(intermediate_states), .output = perm_state[0] });
58 return perm_state[0];
59}
60
61std::array<FF, 4> Poseidon2::permutation(const std::array<FF, 4>& input)
62{
64 perm_events.emit({ .input = input, .output = output });
65 return output;
66}
67
69{
70 uint32_t execution_clk = execution_id_manager.get_execution_id();
71 uint16_t space_id = memory.get_space_id();
72
73 auto zero = MemoryValue::from<FF>(0);
74 std::array<MemoryValue, 4> input = { zero, zero, zero, zero };
75
76 // Poseidon2Perm reads and writes 4 sequential elements each. We need to ensure that these memory addresses are
77 // within the memory address bounds.
78 // Read Addressess: { src_address, src_address + 1, src_address + 2, src_address + 3 }
79 // Write Addresses: { dst_address, dst_address + 1, dst_address + 2, dst_address + 3 }
80 // So we check that src_address + 3 and dst_address + 3 are within the bounds
81 uint64_t max_read_address = static_cast<uint64_t>(src_address) + 3;
82 uint64_t max_write_address = static_cast<uint64_t>(dst_address) + 3;
83 bool read_out_of_range = gt.gt(max_read_address, AVM_HIGHEST_MEM_ADDRESS);
84 bool write_out_of_range = gt.gt(max_write_address, AVM_HIGHEST_MEM_ADDRESS);
85
86 try {
87 if (read_out_of_range || write_out_of_range) {
88 throw InternalPoseidon2Exception("src or dst address out of range");
89 }
90
91 // Read 4 elements from memory starting at src_address
92 for (uint32_t i = 0; i < 4; i++) {
93 input[i] = memory.get(src_address + i);
94 }
95
96 // If any of the memory values are not tagged as FF, we throw an error. This is only tested after all elements
97 // are loaded as the circuit expects reading and tagging checking to be different temporality groups
98 if (std::ranges::any_of(
99 input.begin(), input.end(), [](const MemoryValue& val) { return val.get_tag() != MemoryTag::FF; })) {
100 throw InternalPoseidon2Exception("An input tag is not FF");
101 }
102
103 // This calls the Poseidon2 gadget permutation function and so generates events
104 std::array<FF, 4> output = permutation({
105 input[0].as_ff(),
106 input[1].as_ff(),
107 input[2].as_ff(),
108 input[3].as_ff(),
109 });
110
111 // Write the output back to memory starting at dst_address
112 for (uint32_t i = 0; i < 4; i++) {
114 }
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 = output });
121
122 } catch (const InternalPoseidon2Exception& e) {
123 perm_mem_events.emit({ .space_id = space_id,
124 .execution_clk = execution_clk,
125 .src_address = src_address,
126 .dst_address = dst_address,
127 .input = input,
128 .output = { 0, 0, 0, 0 } });
129 throw Poseidon2Exception("Permutation failed, " + std::string(e.what()));
130 }
131}
132
133} // 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:61
EventEmitterInterface< Poseidon2PermutationEvent > & perm_events
Definition poseidon2.hpp:37
EventEmitterInterface< Poseidon2HashEvent > & hash_events
Definition poseidon2.hpp:36
ExecutionIdManagerInterface & execution_id_manager
Definition poseidon2.hpp:34
EventEmitterInterface< Poseidon2PermutationMemoryEvent > & perm_mem_events
Definition poseidon2.hpp:38
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