Skip to content

Commit

Permalink
feat!: add opcode for poseidon2 permutation (#4214)
Browse files Browse the repository at this point in the history
Related to issue: #4037

The PR adds the opcode to ACIR and updates BB and Noir accordingly.
Furthermore you can use it via a foreign function in the stdlib. This
will generate the proper ACIR opcode but the solver will not be able to
solve it and BB will skip it.
  • Loading branch information
guipublic authored and AztecBot committed Jan 25, 2024
1 parent af203d6 commit 435d041
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 7 deletions.
112 changes: 110 additions & 2 deletions acvm-repo/acir/codegen/acir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,17 @@ namespace Circuit {
static BigIntToLeBytes bincodeDeserialize(std::vector<uint8_t>);
};

std::variant<AND, XOR, RANGE, SHA256, Blake2s, Blake3, SchnorrVerify, PedersenCommitment, PedersenHash, EcdsaSecp256k1, EcdsaSecp256r1, FixedBaseScalarMul, EmbeddedCurveAdd, Keccak256, Keccak256VariableLength, Keccakf1600, RecursiveAggregation, BigIntAdd, BigIntNeg, BigIntMul, BigIntDiv, BigIntFromLeBytes, BigIntToLeBytes> value;
struct Poseidon2Permutation {
std::vector<Circuit::FunctionInput> inputs;
std::vector<Circuit::Witness> outputs;
uint32_t len;

friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&);
std::vector<uint8_t> bincodeSerialize() const;
static Poseidon2Permutation bincodeDeserialize(std::vector<uint8_t>);
};

std::variant<AND, XOR, RANGE, SHA256, Blake2s, Blake3, SchnorrVerify, PedersenCommitment, PedersenHash, EcdsaSecp256k1, EcdsaSecp256r1, FixedBaseScalarMul, EmbeddedCurveAdd, Keccak256, Keccak256VariableLength, Keccakf1600, RecursiveAggregation, BigIntAdd, BigIntNeg, BigIntMul, BigIntDiv, BigIntFromLeBytes, BigIntToLeBytes, Poseidon2Permutation> value;

friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&);
std::vector<uint8_t> bincodeSerialize() const;
Expand Down Expand Up @@ -641,7 +651,17 @@ namespace Circuit {
static BigIntToLeBytes bincodeDeserialize(std::vector<uint8_t>);
};

std::variant<Sha256, Blake2s, Blake3, Keccak256, Keccakf1600, EcdsaSecp256k1, EcdsaSecp256r1, SchnorrVerify, PedersenCommitment, PedersenHash, FixedBaseScalarMul, EmbeddedCurveAdd, BigIntAdd, BigIntNeg, BigIntMul, BigIntDiv, BigIntFromLeBytes, BigIntToLeBytes> value;
struct Poseidon2Permutation {
Circuit::HeapVector message;
Circuit::HeapArray output;
Circuit::RegisterIndex len;

friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&);
std::vector<uint8_t> bincodeSerialize() const;
static Poseidon2Permutation bincodeDeserialize(std::vector<uint8_t>);
};

std::variant<Sha256, Blake2s, Blake3, Keccak256, Keccakf1600, EcdsaSecp256k1, EcdsaSecp256r1, SchnorrVerify, PedersenCommitment, PedersenHash, FixedBaseScalarMul, EmbeddedCurveAdd, BigIntAdd, BigIntNeg, BigIntMul, BigIntDiv, BigIntFromLeBytes, BigIntToLeBytes, Poseidon2Permutation> value;

friend bool operator==(const BlackBoxOp&, const BlackBoxOp&);
std::vector<uint8_t> bincodeSerialize() const;
Expand Down Expand Up @@ -2784,6 +2804,50 @@ Circuit::BlackBoxFuncCall::BigIntToLeBytes serde::Deserializable<Circuit::BlackB
return obj;
}

namespace Circuit {

inline bool operator==(const BlackBoxFuncCall::Poseidon2Permutation &lhs, const BlackBoxFuncCall::Poseidon2Permutation &rhs) {
if (!(lhs.inputs == rhs.inputs)) { return false; }
if (!(lhs.outputs == rhs.outputs)) { return false; }
if (!(lhs.len == rhs.len)) { return false; }
return true;
}

inline std::vector<uint8_t> BlackBoxFuncCall::Poseidon2Permutation::bincodeSerialize() const {
auto serializer = serde::BincodeSerializer();
serde::Serializable<BlackBoxFuncCall::Poseidon2Permutation>::serialize(*this, serializer);
return std::move(serializer).bytes();
}

inline BlackBoxFuncCall::Poseidon2Permutation BlackBoxFuncCall::Poseidon2Permutation::bincodeDeserialize(std::vector<uint8_t> input) {
auto deserializer = serde::BincodeDeserializer(input);
auto value = serde::Deserializable<BlackBoxFuncCall::Poseidon2Permutation>::deserialize(deserializer);
if (deserializer.get_buffer_offset() < input.size()) {
throw serde::deserialization_error("Some input bytes were not read");
}
return value;
}

} // end of namespace Circuit

template <>
template <typename Serializer>
void serde::Serializable<Circuit::BlackBoxFuncCall::Poseidon2Permutation>::serialize(const Circuit::BlackBoxFuncCall::Poseidon2Permutation &obj, Serializer &serializer) {
serde::Serializable<decltype(obj.inputs)>::serialize(obj.inputs, serializer);
serde::Serializable<decltype(obj.outputs)>::serialize(obj.outputs, serializer);
serde::Serializable<decltype(obj.len)>::serialize(obj.len, serializer);
}

template <>
template <typename Deserializer>
Circuit::BlackBoxFuncCall::Poseidon2Permutation serde::Deserializable<Circuit::BlackBoxFuncCall::Poseidon2Permutation>::deserialize(Deserializer &deserializer) {
Circuit::BlackBoxFuncCall::Poseidon2Permutation obj;
obj.inputs = serde::Deserializable<decltype(obj.inputs)>::deserialize(deserializer);
obj.outputs = serde::Deserializable<decltype(obj.outputs)>::deserialize(deserializer);
obj.len = serde::Deserializable<decltype(obj.len)>::deserialize(deserializer);
return obj;
}

namespace Circuit {

inline bool operator==(const BlackBoxOp &lhs, const BlackBoxOp &rhs) {
Expand Down Expand Up @@ -3624,6 +3688,50 @@ Circuit::BlackBoxOp::BigIntToLeBytes serde::Deserializable<Circuit::BlackBoxOp::
return obj;
}

namespace Circuit {

inline bool operator==(const BlackBoxOp::Poseidon2Permutation &lhs, const BlackBoxOp::Poseidon2Permutation &rhs) {
if (!(lhs.message == rhs.message)) { return false; }
if (!(lhs.output == rhs.output)) { return false; }
if (!(lhs.len == rhs.len)) { return false; }
return true;
}

inline std::vector<uint8_t> BlackBoxOp::Poseidon2Permutation::bincodeSerialize() const {
auto serializer = serde::BincodeSerializer();
serde::Serializable<BlackBoxOp::Poseidon2Permutation>::serialize(*this, serializer);
return std::move(serializer).bytes();
}

inline BlackBoxOp::Poseidon2Permutation BlackBoxOp::Poseidon2Permutation::bincodeDeserialize(std::vector<uint8_t> input) {
auto deserializer = serde::BincodeDeserializer(input);
auto value = serde::Deserializable<BlackBoxOp::Poseidon2Permutation>::deserialize(deserializer);
if (deserializer.get_buffer_offset() < input.size()) {
throw serde::deserialization_error("Some input bytes were not read");
}
return value;
}

} // end of namespace Circuit

template <>
template <typename Serializer>
void serde::Serializable<Circuit::BlackBoxOp::Poseidon2Permutation>::serialize(const Circuit::BlackBoxOp::Poseidon2Permutation &obj, Serializer &serializer) {
serde::Serializable<decltype(obj.message)>::serialize(obj.message, serializer);
serde::Serializable<decltype(obj.output)>::serialize(obj.output, serializer);
serde::Serializable<decltype(obj.len)>::serialize(obj.len, serializer);
}

template <>
template <typename Deserializer>
Circuit::BlackBoxOp::Poseidon2Permutation serde::Deserializable<Circuit::BlackBoxOp::Poseidon2Permutation>::deserialize(Deserializer &deserializer) {
Circuit::BlackBoxOp::Poseidon2Permutation obj;
obj.message = serde::Deserializable<decltype(obj.message)>::deserialize(deserializer);
obj.output = serde::Deserializable<decltype(obj.output)>::deserialize(deserializer);
obj.len = serde::Deserializable<decltype(obj.len)>::deserialize(deserializer);
return obj;
}

namespace Circuit {

inline bool operator==(const BlockId &lhs, const BlockId &rhs) {
Expand Down
4 changes: 4 additions & 0 deletions acvm-repo/acir/src/circuit/black_box_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub enum BlackBoxFunc {
BigIntFromLeBytes,
/// BigInt to le bytes
BigIntToLeBytes,
/// Permutation function of Poseidon2
Poseidon2Permutation,
}

impl std::fmt::Display for BlackBoxFunc {
Expand Down Expand Up @@ -92,6 +94,7 @@ impl BlackBoxFunc {
BlackBoxFunc::BigIntDiv => "bigint_div",
BlackBoxFunc::BigIntFromLeBytes => "bigint_from_le_bytes",
BlackBoxFunc::BigIntToLeBytes => "bigint_to_le_bytes",
BlackBoxFunc::Poseidon2Permutation => "poseidon2_permutation",
}
}

Expand Down Expand Up @@ -119,6 +122,7 @@ impl BlackBoxFunc {
"bigint_div" => Some(BlackBoxFunc::BigIntDiv),
"bigint_from_le_bytes" => Some(BlackBoxFunc::BigIntFromLeBytes),
"bigint_to_le_bytes" => Some(BlackBoxFunc::BigIntToLeBytes),
"poseidon2_permutation" => Some(BlackBoxFunc::Poseidon2Permutation),
_ => None,
}
}
Expand Down
20 changes: 17 additions & 3 deletions acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@ pub enum BlackBoxFuncCall {
input: u32,
outputs: Vec<Witness>,
},
/// Applies the Poseidon2 permutation function to the given state,
/// outputting the permuted state.
Poseidon2Permutation {
/// Input state for the permutation of Poseidon2
inputs: Vec<FunctionInput>,
/// Permuted state
outputs: Vec<Witness>,
/// State length (in number of field elements)
/// It is the length of inputs and outputs vectors
len: u32,
},
}

impl BlackBoxFuncCall {
Expand Down Expand Up @@ -171,7 +182,8 @@ impl BlackBoxFuncCall {
BlackBoxFuncCall::BigIntMul { .. } => BlackBoxFunc::BigIntMul,
BlackBoxFuncCall::BigIntDiv { .. } => BlackBoxFunc::BigIntDiv,
BlackBoxFuncCall::BigIntFromLeBytes { .. } => BlackBoxFunc::BigIntFromLeBytes,
&BlackBoxFuncCall::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes,
BlackBoxFuncCall::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes,
BlackBoxFuncCall::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation,
}
}

Expand All @@ -188,7 +200,8 @@ impl BlackBoxFuncCall {
| BlackBoxFuncCall::Keccakf1600 { inputs, .. }
| BlackBoxFuncCall::PedersenCommitment { inputs, .. }
| BlackBoxFuncCall::PedersenHash { inputs, .. }
| BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } => inputs.to_vec(),
| BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. }
| BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(),
BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => {
vec![*lhs, *rhs]
}
Expand Down Expand Up @@ -282,7 +295,8 @@ impl BlackBoxFuncCall {
| BlackBoxFuncCall::Blake3 { outputs, .. }
| BlackBoxFuncCall::Keccak256 { outputs, .. }
| BlackBoxFuncCall::Keccakf1600 { outputs, .. }
| BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } => outputs.to_vec(),
| BlackBoxFuncCall::Keccak256VariableLength { outputs, .. }
| BlackBoxFuncCall::Poseidon2Permutation { outputs, .. } => outputs.to_vec(),
BlackBoxFuncCall::AND { output, .. }
| BlackBoxFuncCall::XOR { output, .. }
| BlackBoxFuncCall::SchnorrVerify { output, .. }
Expand Down
4 changes: 4 additions & 0 deletions acvm-repo/acvm/src/compiler/transformers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ pub(super) fn transform_internal(
| acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. }
| acir::circuit::opcodes::BlackBoxFuncCall::BigIntToLeBytes {
outputs, ..
}
| acir::circuit::opcodes::BlackBoxFuncCall::Poseidon2Permutation {
outputs,
..
} => {
for witness in outputs {
transformer.mark_solvable(*witness);
Expand Down
1 change: 1 addition & 0 deletions acvm-repo/acvm/src/pwg/blackbox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,6 @@ pub(crate) fn solve(
BlackBoxFuncCall::BigIntDiv { .. } => todo!(),
BlackBoxFuncCall::BigIntFromLeBytes { .. } => todo!(),
BlackBoxFuncCall::BigIntToLeBytes { .. } => todo!(),
BlackBoxFuncCall::Poseidon2Permutation { .. } => todo!(),
}
}
5 changes: 5 additions & 0 deletions acvm-repo/brillig/src/black_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,9 @@ pub enum BlackBoxOp {
input: RegisterIndex,
output: HeapVector,
},
Poseidon2Permutation {
message: HeapVector,
output: HeapArray,
len: RegisterIndex,
},
}
2 changes: 2 additions & 0 deletions acvm-repo/brillig_vm/src/black_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ pub(crate) fn evaluate_black_box<Solver: BlackBoxFunctionSolver>(
BlackBoxOp::BigIntDiv { .. } => todo!(),
BlackBoxOp::BigIntFromLeBytes { .. } => todo!(),
BlackBoxOp::BigIntToLeBytes { .. } => todo!(),
BlackBoxOp::Poseidon2Permutation { .. } => todo!(),
}
}

Expand All @@ -222,6 +223,7 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc {
BlackBoxOp::BigIntDiv { .. } => BlackBoxFunc::BigIntDiv,
BlackBoxOp::BigIntFromLeBytes { .. } => BlackBoxFunc::BigIntFromLeBytes,
BlackBoxOp::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes,
BlackBoxOp::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,22 @@ pub(crate) fn convert_black_box_call(
)
}
}
BlackBoxFunc::Poseidon2Permutation => {
if let (
[message, BrilligVariable::Simple(state_len)],
[BrilligVariable::BrilligArray(result_array)],
) = (function_arguments, function_results)
{
let message_vector = convert_array_or_vector(brillig_context, message, bb_func);
brillig_context.black_box_op_instruction(BlackBoxOp::Poseidon2Permutation {
message: message_vector.to_heap_vector(),
output: result_array.to_heap_array(),
len: *state_len,
});
} else {
unreachable!("ICE: SHA256 expects one array argument and one array result")
}
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,15 @@ impl DebugShow {
output
);
}
BlackBoxOp::Poseidon2Permutation { message, output, len } => {
debug_println!(
self.enable_debug_trace,
" POSEIDON2_PERMUTATION {} {} -> {}",
message,
len,
output
);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,31 @@ impl AcirContext {

(vec![domain_constant], Vec::new())
}
BlackBoxFunc::Poseidon2Permutation => {
// The last argument is the state length, which must be a constant
let state_len = match inputs.pop() {
Some(state_len) => state_len.into_var()?,
None => {
return Err(RuntimeError::InternalError(InternalError::MissingArg {
name: "poseidon_2_permutation call".to_string(),
arg: "length".to_string(),
call_stack: self.get_call_stack(),
}))
}
};

let state_len = match self.vars[&state_len].as_constant() {
Some(state_len) => state_len,
None => {
return Err(RuntimeError::InternalError(InternalError::NotAConstant {
name: "length".to_string(),
call_stack: self.get_call_stack(),
}))
}
};

(vec![state_len], Vec::new())
}
BlackBoxFunc::BigIntAdd
| BlackBoxFunc::BigIntNeg
| BlackBoxFunc::BigIntMul
Expand Down Expand Up @@ -1261,7 +1286,6 @@ impl AcirContext {

// Convert `AcirVar` to `FunctionInput`
let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?;

// Call Black box with `FunctionInput`
let mut results = vecmap(&constant_outputs, |c| self.add_constant(*c));
let outputs = self.acir_ir.call_black_box(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ impl GeneratedAcir {
input: constant_inputs[0].to_u128() as u32,
outputs,
},
BlackBoxFunc::Poseidon2Permutation => BlackBoxFuncCall::Poseidon2Permutation {
inputs: inputs[0].clone(),
outputs,
len: constant_inputs[0].to_u128() as u32,
},
};

self.push_opcode(AcirOpcode::BlackBoxFuncCall(black_box_func_call));
Expand Down Expand Up @@ -609,6 +614,8 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option<usize> {
| BlackBoxFunc::PedersenHash => None,

BlackBoxFunc::Keccakf1600 => Some(25),
// The permutation takes a fixed number of inputs, but the inputs length depends on the proving system implementation.
BlackBoxFunc::Poseidon2Permutation => None,

// Can only apply a range constraint to one
// witness at a time.
Expand Down Expand Up @@ -657,6 +664,8 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option<usize> {
| BlackBoxFunc::Blake3 => Some(32),

BlackBoxFunc::Keccakf1600 => Some(25),
// The permutation returns a fixed number of outputs, equals to the inputs length which depends on the proving system implementation.
BlackBoxFunc::Poseidon2Permutation => None,

// Pedersen commitment returns a point
BlackBoxFunc::PedersenCommitment => Some(2),
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ fn simplify_black_box_func(
_ => SimplifyResult::None,
}
}

BlackBoxFunc::Poseidon2Permutation => SimplifyResult::None, //TODO(Guillaume)
BlackBoxFunc::EcdsaSecp256k1 => {
simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256k1_verify)
}
Expand Down
2 changes: 2 additions & 0 deletions noir_stdlib/src/hash.nr
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ pub fn hash_to_field<N>(_input: [Field; N]) -> Field {
#[foreign(keccak256)]
pub fn keccak256<N>(_input: [u8; N], _message_size: u32) -> [u8; 32] {}

#[foreign(poseidon2_permutation)]
pub fn poseidon2_permutation<N>(_input: [u8; N], _state_length: u32) -> [u8; N] {}

0 comments on commit 435d041

Please sign in to comment.