Skip to content

Commit

Permalink
feat: export poseidon2_permutation and add to foundation/crypto (#5706)
Browse files Browse the repository at this point in the history
  • Loading branch information
fcarreiro committed Apr 12, 2024
1 parent 7f8b09f commit 6b91e27
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 34 deletions.
32 changes: 25 additions & 7 deletions barretenberg/cpp/src/barretenberg/crypto/poseidon2/c_bind.cpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
#include "c_bind.hpp"
#include "barretenberg/common/mem.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp"
#include "poseidon2.hpp"
#include "poseidon2_permutation.hpp"

using namespace bb;

WASM_EXPORT void poseidon_hash(fr::vec_in_buf inputs_buffer, fr::out_buf output)
WASM_EXPORT void poseidon2_hash(fr::vec_in_buf inputs_buffer, fr::out_buf output)
{
std::vector<grumpkin::fq> to_hash;
std::vector<fr> to_hash;
read(inputs_buffer, to_hash);
auto r = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>::hash(to_hash);
fr::serialize_to_buffer(r, output);
}

WASM_EXPORT void poseidon_hashes(fr::vec_in_buf inputs_buffer, fr::out_buf output)
WASM_EXPORT void poseidon2_hashes(fr::vec_in_buf inputs_buffer, fr::out_buf output)
{
std::vector<grumpkin::fq> to_hash;
std::vector<fr> to_hash;
read(inputs_buffer, to_hash);
const size_t numHashes = to_hash.size() / 2;
std::vector<grumpkin::fq> results;
std::vector<fr> results;
size_t count = 0;
while (count < numHashes) {
auto r = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>::hash(
Expand All @@ -28,4 +28,22 @@ WASM_EXPORT void poseidon_hashes(fr::vec_in_buf inputs_buffer, fr::out_buf outpu
++count;
}
write(output, results);
}
}

WASM_EXPORT void poseidon2_permutation(fr::vec_in_buf inputs_buffer, fr::vec_out_buf output)
{
using Permutation = crypto::Poseidon2Permutation<crypto::Poseidon2Bn254ScalarFieldParams>;

// Serialise input vector.
std::vector<fr> to_permute;
read(inputs_buffer, to_permute);

// Copy input vector into Permutation::State (which is an std::array).
Permutation::State input_state;
std::copy(to_permute.begin(), to_permute.end(), input_state.data());

Permutation::State results_array = Permutation::permutation(input_state);

const std::vector<fr> results(results_array.begin(), results_array.end());
*output = to_heap_buffer(results);
}
5 changes: 3 additions & 2 deletions barretenberg/cpp/src/barretenberg/crypto/poseidon2/c_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extern "C" {

using namespace bb;

WASM_EXPORT void poseidon_hash(fr::vec_in_buf inputs_buffer, fr::out_buf output);
WASM_EXPORT void poseidon_hashes(fr::vec_in_buf inputs_buffer, fr::out_buf output);
WASM_EXPORT void poseidon2_hash(fr::vec_in_buf inputs_buffer, fr::out_buf output);
WASM_EXPORT void poseidon2_hashes(fr::vec_in_buf inputs_buffer, fr::out_buf output);
WASM_EXPORT void poseidon2_permutation(fr::vec_in_buf inputs_buffer, fr::vec_out_buf output);
}
4 changes: 2 additions & 2 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const std::unordered_map<OpCode, size_t> Bytecode::OPERANDS_NUM = {

//// Gadgets
//{ OpCode::KECCAK, },
//{ OpCode::POSEIDON, },
//{ OpCode::POSEIDON2, },
//{ OpCode::SHA256, },
//{ OpCode::PEDERSEN, },
};
Expand All @@ -102,7 +102,7 @@ const std::unordered_map<OpCode, size_t> Bytecode::OPERANDS_NUM = {
*/
bool Bytecode::is_valid(const uint8_t byte)
{
return byte <= static_cast<uint8_t>(OpCode::POSEIDON);
return byte < static_cast<uint8_t>(OpCode::LAST_OPCODE_SENTINEL);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ enum class OpCode : uint8_t {

// Gadgets
KECCAK,
POSEIDON,
POSEIDON2,

// Sentinel
LAST_OPCODE_SENTINEL,
};

class Bytecode {
Expand Down
20 changes: 18 additions & 2 deletions barretenberg/exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"isAsync": false
},
{
"functionName": "poseidon_hash",
"functionName": "poseidon2_hash",
"inArgs": [
{
"name": "inputs_buffer",
Expand All @@ -92,7 +92,7 @@
"isAsync": false
},
{
"functionName": "poseidon_hashes",
"functionName": "poseidon2_hashes",
"inArgs": [
{
"name": "inputs_buffer",
Expand All @@ -107,6 +107,22 @@
],
"isAsync": false
},
{
"functionName": "poseidon2_permutation",
"inArgs": [
{
"name": "input_state",
"type": "fr::vec_in_buf"
}
],
"outArgs": [
{
"name": "output_state",
"type": "fr::vec_out_buf"
}
],
"isAsync": false
},
{
"functionName": "blake2s",
"inArgs": [
Expand Down
Empty file removed barretenberg/scripts/exports.json
Empty file.
6 changes: 3 additions & 3 deletions barretenberg/ts/src/barretenberg/poseidon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('poseidon sync', () => {
});

it('poseidonHash', () => {
const result = api.poseidonHash([new Fr(4n), new Fr(8n)]);
const result = api.poseidon2Hash([new Fr(4n), new Fr(8n)]);
expect(result).toMatchSnapshot();
});

Expand All @@ -19,7 +19,7 @@ describe('poseidon sync', () => {
const fields = Array.from({ length: loops * 2 }).map(() => Fr.random());
const t = new Timer();
for (let i = 0; i < loops; ++i) {
api.poseidonHash([fields[i * 2], fields[i * 2 + 1]]);
api.poseidon2Hash([fields[i * 2], fields[i * 2 + 1]]);
}
const us = t.us() / loops;
console.log(`Executed ${loops} hashes at an average ${us}us / hash`);
Expand All @@ -31,7 +31,7 @@ describe('poseidon sync', () => {
const fields = Array.from({ length: numHashesPerLoop * 2 }).map(() => Fr.random());
const t = new Timer();
for (let i = 0; i < loops; ++i) {
api.poseidonHashes(fields);
api.poseidon2Hashes(fields);
}
const us = t.us() / (numHashesPerLoop * loops);
console.log(`Executed ${numHashesPerLoop * loops} hashes at an average ${us}us / hash`);
Expand Down
40 changes: 32 additions & 8 deletions barretenberg/ts/src/barretenberg_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,35 @@ export class BarretenbergApi {
return out[0];
}

async poseidonHash(inputsBuffer: Fr[]): Promise<Fr> {
async poseidon2Hash(inputsBuffer: Fr[]): Promise<Fr> {
const inArgs = [inputsBuffer].map(serializeBufferable);
const outTypes: OutputType[] = [Fr];
const result = await this.wasm.callWasmExport(
'poseidon_hash',
'poseidon2_hash',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

async poseidonHashes(inputsBuffer: Fr[]): Promise<Fr> {
async poseidon2Hashes(inputsBuffer: Fr[]): Promise<Fr> {
const inArgs = [inputsBuffer].map(serializeBufferable);
const outTypes: OutputType[] = [Fr];
const result = await this.wasm.callWasmExport(
'poseidon_hashes',
'poseidon2_hashes',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

async poseidon2Permutation(inputState: Fr[]): Promise<Fr[]> {
const inArgs = [inputState].map(serializeBufferable);
const outTypes: OutputType[] = [VectorDeserializer(Fr)];
const result = await this.wasm.callWasmExport(
'poseidon2_permutation',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
Expand Down Expand Up @@ -607,23 +619,35 @@ export class BarretenbergApiSync {
return out[0];
}

poseidonHash(inputsBuffer: Fr[]): Fr {
poseidon2Hash(inputsBuffer: Fr[]): Fr {
const inArgs = [inputsBuffer].map(serializeBufferable);
const outTypes: OutputType[] = [Fr];
const result = this.wasm.callWasmExport(
'poseidon_hash',
'poseidon2_hash',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

poseidonHashes(inputsBuffer: Fr[]): Fr {
poseidon2Hashes(inputsBuffer: Fr[]): Fr {
const inArgs = [inputsBuffer].map(serializeBufferable);
const outTypes: OutputType[] = [Fr];
const result = this.wasm.callWasmExport(
'poseidon_hashes',
'poseidon2_hashes',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

poseidon2Permutation(inputState: Fr[]): Fr[] {
const inArgs = [inputState].map(serializeBufferable);
const outTypes: OutputType[] = [VectorDeserializer(Fr)];
const result = this.wasm.callWasmExport(
'poseidon2_permutation',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
Expand Down
24 changes: 24 additions & 0 deletions yarn-project/foundation/src/crypto/poseidon/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Fr } from '../../fields/fields.js';
import { poseidon2Permutation } from './index.js';

describe('poseidon2Permutation', () => {
it('test vectors from cpp should match', () => {
const init = [0, 1, 2, 3].map(i => new Fr(i));
expect(poseidon2Permutation(init)).toEqual([
new Fr(0x01bd538c2ee014ed5141b29e9ae240bf8db3fe5b9a38629a9647cf8d76c01737n),
new Fr(0x239b62e7db98aa3a2a8f6a0d2fa1709e7a35959aa6c7034814d9daa90cbac662n),
new Fr(0x04cbb44c61d928ed06808456bf758cbf0c18d1e15a7b6dbc8245fa7515d5e3cbn),
new Fr(0x2e11c5cff2a22c64d01304b778d78f6998eff1ab73163a35603f54794c30847an),
]);
});

it('test vectors from Noir should match', () => {
const init = [1n, 2n, 3n, 0x0a0000000000000000n].map(i => new Fr(i));
expect(poseidon2Permutation(init)).toEqual([
new Fr(0x0369007aa630f5dfa386641b15416ecb16fb1a6f45b1acb903cb986b221a891cn),
new Fr(0x1919fd474b4e2e0f8e0cf8ca98ef285675781cbd31aa4807435385d28e4c02a5n),
new Fr(0x0810e7e9a1c236aae4ebff7d3751d9f7346dc443d1de863977d2b81fe8c557f4n),
new Fr(0x1f4a188575e29985b6f8ad03afc1f0759488f8835aafb6e19e06160fb64d3d4an),
]);
});
});
18 changes: 16 additions & 2 deletions yarn-project/foundation/src/crypto/poseidon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,30 @@ import { type Fieldable, serializeToFields } from '../../serialize/serialize.js'
* @returns The poseidon hash.
* TODO(#5714): enable index once barretenberg API supports it
*/
export function poseidonHash(input: Fieldable[], _index = 0): Fr {
export function poseidon2Hash(input: Fieldable[], _index = 0): Fr {
const inputFields = serializeToFields(input);
return Fr.fromBuffer(
Buffer.from(
BarretenbergSync.getSingleton()
.poseidonHash(
.poseidon2Hash(
inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion
// index, // TODO: enable once the barretenberg API supports it
)
.toBuffer(),
),
);
}

/**
* Runs a Poseidon2 permutation.
* @param input the input state. Expected to be of size 4.
* @returns the output state, size 4.
*/
export function poseidon2Permutation(input: Fr[]): Fr[] {
// We'd like this assertion but it's not possible to use it in the browser.
// assert(input.length === 4, 'Input state must be of size 4');
const res = BarretenbergSync.getSingleton().poseidon2Permutation(input.map(i => new FrBarretenberg(i.toBuffer())));
// We'd like this assertion but it's not possible to use it in the browser.
// assert(res.length === 4, 'Output state must be of size 4');
return res.map(o => Fr.fromBuffer(Buffer.from(o.toBuffer())));
}
4 changes: 2 additions & 2 deletions yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { UnencryptedL2Log } from '@aztec/circuit-types';
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
import { EventSelector, FunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto';
import { keccak, pedersenHash, poseidon2Hash, sha256 } from '@aztec/foundation/crypto';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { type Fieldable } from '@aztec/foundation/serialize';
Expand Down Expand Up @@ -139,7 +139,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
});

describe.each([
['poseidon_hash', poseidonHash],
['poseidon_hash', poseidon2Hash],
['pedersen_hash', pedersenHash],
['pedersen_hash_with_index', (m: Fieldable[]) => pedersenHash(m, 20)],
])('Hashes with field returned in noir contracts', (name: string, hashFunction: (data: Fieldable[]) => Fr) => {
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/simulator/src/avm/opcodes/hashing.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto';
import { keccak, pedersenHash, poseidon2Hash, sha256 } from '@aztec/foundation/crypto';

import { type AvmContext } from '../avm_context.js';
import { Field, Uint32 } from '../avm_memory_types.js';
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('Hashing Opcodes', () => {

const dstOffset = 3;

const expectedHash = poseidonHash(args);
const expectedHash = poseidon2Hash(args);
await new Poseidon2(indirect, dstOffset, messageOffset, args.length).execute(context);

const result = context.machineState.memory.get(dstOffset);
Expand All @@ -62,7 +62,7 @@ describe('Hashing Opcodes', () => {

const dstOffset = 3;

const expectedHash = poseidonHash(args);
const expectedHash = poseidon2Hash(args);
await new Poseidon2(indirect, dstOffset, messageOffset, args.length).execute(context);

const result = context.machineState.memory.get(dstOffset);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/simulator/src/avm/opcodes/hashing.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto';
import { keccak, pedersenHash, poseidon2Hash, sha256 } from '@aztec/foundation/crypto';

import { type AvmContext } from '../avm_context.js';
import { Field } from '../avm_memory_types.js';
Expand Down Expand Up @@ -43,7 +43,7 @@ export class Poseidon2 extends Instruction {
// Memory pointer will be indirect
const hashData = memory.getSlice(messageOffset, this.messageSize);

const hash = poseidonHash(hashData);
const hash = poseidon2Hash(hashData);
memory.set(dstOffset, new Field(hash));

memory.assert(memoryOperations);
Expand Down

0 comments on commit 6b91e27

Please sign in to comment.