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 <cstddef>
5#include <cstdint>
6#include <stdexcept>
7#include <string>
8#include <utility>
9
14
17
18namespace bb::avm2::simulation {
19
20namespace {
21
22class InternalPoseidon2Exception : public std::runtime_error {
23 using std::runtime_error::runtime_error; // Inherit the constructor.
24};
25
26} // namespace
27
35FF Poseidon2::hash(const std::vector<FF>& input)
36{
37 size_t input_size = input.size(); // Will be mutated in the loop below.
38 // The number of permutation events required to process the input
39 auto num_perm_events = (input_size / 3) + static_cast<size_t>(input_size % 3 != 0);
40 std::vector<std::array<FF, 4>> intermediate_states;
41 // We reserve space for the intermediate permutation states and 1 additional space for the initial state
42 intermediate_states.reserve(num_perm_events + 1);
43
44 // The unpadded length of the input is set as the IV
45 // The initial permutation state is seeded with the iv at the last input index
46 const uint256_t iv = static_cast<uint256_t>(input_size) << 64;
47 std::array<FF, 4> perm_state = { 0, 0, 0, iv };
48 intermediate_states.push_back(perm_state);
49
50 for (size_t i = 0; i < num_perm_events; i++) {
51 // We can at most absorb a chunk of 3 elements
52 size_t chunk_size = std::min(input_size, static_cast<size_t>(3));
53 // Mix the input chunk into the previous permutation output state
54 for (size_t j = 0; j < chunk_size; j++) {
55 perm_state[j] += input[(i * 3) + j];
56 }
57 perm_state = permutation(perm_state);
58 intermediate_states.push_back(perm_state);
59
60 input_size -= chunk_size;
61 }
62
63 hash_events.emit(
64 { .inputs = input, .intermediate_states = std::move(intermediate_states), .output = perm_state[0] });
65 return perm_state[0];
66}
67
74std::array<FF, 4> Poseidon2::permutation(const std::array<FF, 4>& input)
75{
77 perm_events.emit({ .input = input, .output = output });
78 return output;
79}
80
93{
94 const auto execution_clk = execution_id_manager.get_execution_id();
95 const auto space_id = memory.get_space_id();
96
97 const auto zero = MemoryValue::from_tag(static_cast<MemoryTag>(0), 0);
98 std::array<MemoryValue, 4> input = { zero, zero, zero, zero };
99
100 // Poseidon2Perm reads and writes 4 sequential elements each. We need to ensure that these memory addresses are
101 // within the memory address bounds.
102 // Read Addressess: { src_address, src_address + 1, src_address + 2, src_address + 3 }
103 // Write Addresses: { dst_address, dst_address + 1, dst_address + 2, dst_address + 3 }
104 // So we check that src_address + 3 and dst_address + 3 are within the bounds
105 uint64_t max_read_address = static_cast<uint64_t>(src_address) + 3;
106 uint64_t max_write_address = static_cast<uint64_t>(dst_address) + 3;
107 bool read_out_of_range = gt.gt(max_read_address, AVM_HIGHEST_MEM_ADDRESS);
108 bool write_out_of_range = gt.gt(max_write_address, AVM_HIGHEST_MEM_ADDRESS);
109
110 try {
111 if (read_out_of_range || write_out_of_range) {
112 throw InternalPoseidon2Exception("src or dst address out of range");
113 }
114
115 // Read 4 elements from memory starting at src_address
116 for (uint32_t i = 0; i < 4; i++) {
117 input[i] = memory.get(src_address + i);
118 }
119
120 // If any of the memory values are not tagged as FF, we throw an error. This is only tested after all elements
121 // are loaded as the circuit expects reading and tagging checking to be different temporality groups
122 if (std::ranges::any_of(
123 input.begin(), input.end(), [](const MemoryValue& val) { return val.get_tag() != MemoryTag::FF; })) {
124 throw InternalPoseidon2Exception("An input tag is not FF");
125 }
126
127 // This calls the Poseidon2 gadget permutation function and so generates events
128 std::array<FF, 4> output = permutation({
129 input[0].as_ff(),
130 input[1].as_ff(),
131 input[2].as_ff(),
132 input[3].as_ff(),
133 });
134
135 // Write the output back to memory starting at dst_address
136 for (uint32_t i = 0; i < 4; i++) {
138 }
139 perm_mem_events.emit({ .space_id = space_id,
140 .execution_clk = execution_clk,
141 .src_address = src_address,
142 .dst_address = dst_address,
143 .input = input,
144 .output = output });
145
146 } catch (const InternalPoseidon2Exception& e) {
147 perm_mem_events.emit({ .space_id = space_id,
148 .execution_clk = execution_clk,
149 .src_address = src_address,
150 .dst_address = dst_address,
151 .input = input,
152 .output = { 0, 0, 0, 0 } });
153 throw Poseidon2Exception("Permutation failed, " + std::string(e.what()));
154 }
155}
156
157} // 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
Applies the Poseidon2 permutation function to a single input state.
Definition poseidon2.cpp:74
EventEmitterInterface< Poseidon2PermutationEvent > & perm_events
Definition poseidon2.hpp:39
EventEmitterInterface< Poseidon2HashEvent > & hash_events
Definition poseidon2.hpp:38
ExecutionIdManagerInterface & execution_id_manager
Definition poseidon2.hpp:36
EventEmitterInterface< Poseidon2PermutationMemoryEvent > & perm_mem_events
Definition poseidon2.hpp:40
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.
AVM range check gadget for witness generation.
AvmFlavorSettings::FF FF
Definition field.hpp:10
uint32_t MemoryAddress
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13