circom-goldilocks/circuit/poseidon.circom

177 lines
4.5 KiB
Plaintext

//
// Plonky2's Poseidon hash instance over the Goldilocks field
//
pragma circom 2.2.0;
include "goldilocks.circom";
include "constants.circom";
//------------------------------------------------------------------------------
template SBOX() {
input Goldilocks() inp;
output Goldilocks() out;
Goldilocks() x3;
x3 <== Mul3()( inp , inp , inp );
out <== Mul3()( x3 , x3 , inp );
}
//------------------------------------------------------------------------------
// numbers in the range `0 <= x < 2*P`
bus AtMostTwoP() {
signal val;
}
// If `x < P` then of course `x < 2*P` too
template Embed() {
input Goldilocks() inp;
output AtMostTwoP() out;
out.val <== inp.val;
}
//------------------------------------------------------------------------------
template ExternalNonlinearLayer(round) {
input Goldilocks() inp[12];
output Goldilocks() out[12];
var rc[12] = poseidon_round_constants(round);
Goldilocks RC [12];
Goldilocks tmp[12];
for(var i=0; i<12; i++) {
RC[i].val <== rc[i]; // we know that the constants are in the corrrect range
tmp[i] <== Add()( inp[i] , RC[i] ); // this range check could be probably optimized away with a more general Mul3
out[i] <== SBOX()( tmp[i] );
}
// log("\nAFTER external nonlinear layer | round=",round);
// for(var i=0; i<12; i++) {
// log("out[",i,"] = ",out[i].val);
// }
}
//------------------------------------------------------------------------------
//
// NOTE: we don't reduce the output, as it will be multiplied by the MDS matrix next,
// and it's enough to reduce after that
//
template InternalNonlinearLayer(round) {
input Goldilocks() inp[12];
output AtMostTwoP() out[12];
var rc[12] = poseidon_round_constants(round);
Goldilocks RC0;
RC0.val <== rc[0]; // we know that the constants are in the corrrect range
Goldilocks tmp <== Add()( inp[0] , RC0 );
Goldilocks sbox <== SBOX()( tmp );
out[0] <== Embed()( sbox );
for(var i=1; i<12; i++) {
out[i].val <== inp[i].val + rc[i];
}
}
//------------------------------------------------------------------------------
//
// NOTE: the sum of the coefficients of a row of the circulant matrix is
//
// > sum [17, 15, 41, 16, 2, 28, 13, 13, 39, 18, 34, 20] = 256
//
// So if each input is less than `B`, then the (unreduced) linear combination is less than `256*B`
//
// The only exception is the first row, becuase the diagonal
// matrix `[8,0,0,...,0]` is added to the circulant matrix
//
template LinearDiffusionLayer(extra) {
input AtMostTwoP inp[12];
output Goldilocks() out[12];
var coeffs[12] = circular_matrix_coeffs();
var ys[12];
for(var k=0; k<12; k++) {
var y = 0;
for(var j=0; j<12; j++) {
var diag = ((k==0)&&(j==0)) ? 8 : 0;
y += inp[j].val * ( diag + coeffs[ (j-k+12) % 12 ] );
}
ys[k] = y;
}
out[0] <== ReduceModP( 9+extra )( ys[0] );
for(var k=1; k<12; k++) { out[k] <== ReduceModP( 8+extra )( ys[k] ); }
}
//------------------------------------------------------------------------------
template ExternalRound(round) {
input Goldilocks() inp[12];
output Goldilocks() out[12];
AtMostTwoP() tmp[12];
Goldilocks() gls[12];
gls <== ExternalNonlinearLayer(round)( inp );
for(var k=0; k<12; k++) { tmp[k] <== Embed()( gls[k] ); }
out <== LinearDiffusionLayer(0)( tmp );
}
template InternalRound(round) {
input Goldilocks() inp[12];
output Goldilocks() out[12];
AtMostTwoP() tmp[12];
tmp <== InternalNonlinearLayer(round)( inp );
out <== LinearDiffusionLayer(1)( tmp );
}
//------------------------------------------------------------------------------
template Permutation() {
input Goldilocks() inp[12];
output Goldilocks() out[12];
Goldilocks() aux[31][12];
aux[0] <== inp;
for(var i=0 ; i<4 ; i++) { aux[i+1] <== ExternalRound(i)( aux[i] ); }
for(var i=4 ; i<26; i++) { aux[i+1] <== InternalRound(i)( aux[i] ); }
for(var i=26; i<30; i++) { aux[i+1] <== ExternalRound(i)( aux[i] ); }
aux[30] ==> out;
}
//------------------------------------------------------------------------------
template CheckPermutationKAT() {
signal input dummy; // hack around circom
Goldilocks() inp[12];
Goldilocks() out[12];
for(var i=0; i<12; i++) { inp[i].val <== i; }
out <== Permutation()( inp );
var kat[12] = poseidon_kat();
log("\nfinal permutation output:");
for(var i=0; i<12; i++) {
log("out[",i,"] = ",out[i].val, " vs. expected = ",kat[i]);
}
for(var i=0; i<12; i++) {
out[i].val === kat[i];
}
}