30 bytes/2Ghz core/sec

This commit is contained in:
themighty1
2022-08-03 19:18:05 +03:00
parent bbafcd321d
commit 14810c616e
4 changed files with 89 additions and 21 deletions

50
circuit.circom Normal file
View File

@@ -0,0 +1,50 @@
pragma circom 2.0.0;
include "./poseidon.circom";
include "./utils.circom";
template Main() {
var fe_count = 10;
// the other 130 bits of the last field element contain
// the salt
var last_fe_bits = 123;
signal input plaintext_hash;
signal input label_sum_hash;
signal input plaintext[fe_count];
signal input delta[fe_count-1][253];
signal input delta_last[last_fe_bits];
signal input sum_of_zero_labels;
signal sums[fe_count];
component hash = Poseidon(fe_count);
for (var i = 0; i<fe_count; i++) {
hash.inputs[i] <== plaintext[i];
}
// TODO to pass this assert we'd have to
// use actual values instead of random ones, so commenting out for now
// plaintext_hash === hash.out;
component ip[fe_count-1];
for (var i = 0; i<fe_count-1; i++) {
ip[i] = InnerProd(253);
ip[i].plaintext <== plaintext[i];
for (var j=0; j<253; j++) {
ip[i].deltas[j] <== delta[i][j];
}
sums[i] <== ip[i].out;
}
component ip_last = InnerProd(last_fe_bits);
ip_last.plaintext <== plaintext[fe_count-1];
for (var j=0; j<last_fe_bits; j++) {
ip_last.deltas[j] <== delta_last[j];
}
sums[fe_count-1] <== ip_last.out;
signal sum_of_deltas <== sums[0] + sums[1] + sums[2] + sums[3] + sums[4] + sums[5] + sums[6] + sums[7] + sums[8] + sums[9];
// TODO to pass this assert we'd have to
// use actual values instead of random ones, so commenting out for now
component ls_hash = Poseidon(1);
ls_hash.inputs[0] <== sum_of_zero_labels + sum_of_deltas;
//label_sum_hash === ls_hash.out;
}
component main {public [plaintext_hash, label_sum_hash, delta, delta_last, sum_of_zero_labels]} = Main();

View File

@@ -38,7 +38,8 @@ mod tests {
// we have 16 FE * 253 bits each == 506 bytes
let mut plaintext = [0u8; 512];
rng.fill(&mut plaintext);
let plaintext = &plaintext[0..506];
let plaintext = &plaintext[0..474];
// 3792 bits
// bn254 prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
// in decimal 21888242871839275222246405745257275088548364400416034343698204186575808495617

View File

@@ -18,7 +18,7 @@ pub struct LsumProver {
useful_bits: Option<usize>,
// We will compute a separate Poseidon hash on each chunk of the plaintext.
// Each chunk contains 16 field elements.
chunks: Option<Vec<[BigUint; 16]>>,
chunks: Option<Vec<[BigUint; 15]>>,
// Poseidon hashes of each chunk
hashes_of_chunks: Option<Vec<BigUint>>,
// each chunk's last 128 bits are used for the salt. This is important for
@@ -60,19 +60,22 @@ impl LsumProver {
// The last element's last 128 bits are reserved for the salt of the hash.
// If there is not enough plaintext to fill the whole chunk, we fill the gap
// with zero bits.
fn plaintext_to_chunks(&mut self) -> (Vec<[BigUint; 16]>, Vec<BigUint>) {
fn plaintext_to_chunks(&mut self) -> (Vec<[BigUint; 15]>, Vec<BigUint>) {
let useful_bits = self.useful_bits.unwrap();
// the size of a chunk of plaintext not counting the salt
// let chunk_size = useful_bits * 16 - 128;
// TODO dont use the salt for now
let chunk_size = useful_bits * 16;
// TODO we hard code the chunk size to = 3792
// to be a multiple of 8
// let chunk_size = useful_bits * 16;
let chunk_size = 3792;
// plaintext converted into bits
let mut bits = u8vec_to_boolvec(&self.plaintext);
// chunk count (rounded up)
let chunk_count = (bits.len() + (chunk_size - 1)) / chunk_size;
// extend bits with zeroes to fill the chunk
bits.extend(vec![false; chunk_count * chunk_size - bits.len()]);
let mut chunks: Vec<[BigUint; 16]> = Vec::with_capacity(chunk_count);
let mut chunks: Vec<[BigUint; 15]> = Vec::with_capacity(chunk_count);
let mut salts: Vec<BigUint> = Vec::with_capacity(chunk_count);
// current offset within bits
let mut offset: usize = 0;
@@ -95,16 +98,21 @@ impl LsumProver {
BigUint::default(),
BigUint::default(),
BigUint::default(),
BigUint::default(),
//BigUint::default(),
];
// TODO dont use salt for now, to make debugging easier, later change this to
// for j in 0..15 { and uncomment the lines below //offset and //chunk[15]
for j in 0..16 {
for j in 0..14 {
// convert bits into field element
chunk[j] =
BigUint::from_bytes_be(&boolvec_to_u8vec(&bits[offset..offset + useful_bits]));
offset += useful_bits;
}
// convert bits into field element
chunk[14] =
BigUint::from_bytes_be(&boolvec_to_u8vec(&bits[offset..offset + useful_bits - 3]));
offset += useful_bits;
// last field element's last 128 bits are for the salt
// let mut rng = thread_rng();
// let salt: [u8; 16] = rng.gen();
@@ -125,7 +133,7 @@ impl LsumProver {
}
// hashes each chunk with Poseidon and returns digests for each chunk
fn hash_chunks(&mut self, chunks: Vec<[BigUint; 16]>) -> Vec<BigUint> {
fn hash_chunks(&mut self, chunks: Vec<[BigUint; 15]>) -> Vec<BigUint> {
return chunks
.iter()
.map(|chunk| self.poseidon(chunk.to_vec()))
@@ -166,11 +174,12 @@ impl LsumProver {
.map(|bigint| bigint.to_string())
.collect();
// For now dealing with one chunk only
let deltas_ = &deltas[0..self.useful_bits.unwrap() * 16];
let mut deltas_chunk: Vec<Vec<BigUint>> = Vec::with_capacity(16);
for i in 0..16 {
let deltas_ = &deltas[0..self.useful_bits.unwrap() * 15 - 3];
let mut deltas_chunk: Vec<Vec<BigUint>> = Vec::with_capacity(15);
for i in 0..14 {
deltas_chunk.push(deltas[i * 253..(i + 1) * 253].to_vec());
}
deltas_chunk.push(deltas[14 * 253..15 * 253 - 3].to_vec());
// There are as many deltas as there are bits in the plaintext
let delta_str: Vec<Vec<String>> = deltas_chunk
@@ -265,24 +274,24 @@ mod tests {
prover.setup();
// Check chunk1 correctness
let chunk1: Vec<u128> = prover.chunks.clone().unwrap()[0][0..15]
let chunk1: Vec<u128> = prover.chunks.clone().unwrap()[0][0..14]
.iter()
.map(|bigint| bigint.to_u128().unwrap())
.collect();
assert_eq!(chunk1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
// the last field element must be random salt. We just check that the
// salt has been set, i.e. it is not equal 0
assert!(!prover.chunks.clone().unwrap()[0][15].eq(&BigUint::from_u8(0).unwrap()));
assert!(!prover.chunks.clone().unwrap()[0][14].eq(&BigUint::from_u8(0).unwrap()));
// Check chunk2 correctness
let chunk2: Vec<u128> = prover.chunks.clone().unwrap()[1][0..15]
let chunk2: Vec<u128> = prover.chunks.clone().unwrap()[1][0..14]
.iter()
.map(|bigint| bigint.to_u128().unwrap())
.collect();
assert_eq!(chunk2, [16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
// the last field element must be random salt. We just check that the
// salt has been set, i.e. it is not equal 0
assert!(!prover.chunks.clone().unwrap()[1][15].eq(&BigUint::from_u8(0).unwrap()));
assert!(!prover.chunks.clone().unwrap()[1][14].eq(&BigUint::from_u8(0).unwrap()));
}
#[test]

View File

@@ -1,3 +1,4 @@
pragma circom 2.0.0;
// copied from circomlib/circuits/bitify.circom
template Num2Bits(n) {
@@ -16,20 +17,27 @@ template Num2Bits(n) {
lc1 === in;
}
template InnerProd(){
template InnerProd(count){
signal input plaintext;
signal input deltas[253];
signal input deltas[count];
signal output out;
component n2b = Num2Bits(253);
plaintext ==> n2b.in;
signal sum[253];
for (var i=0; i<253; i++) {
signal sum[count];
for (var i=0; i<count; i++) {
// Num2Bits returns bits in "least bit first" order
// but deltas are in the opposite bit order.
// So, we reverse the bits.
sum[i] <== n2b.out[252-i] * deltas[i];
sum[i] <== n2b.out[count-1-i] * deltas[i];
}
out <== (sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5] + sum[6] + sum[7] + sum[8] + sum[9] + sum[10] + sum[11] + sum[12] + sum[13] + sum[14] + sum[15] + sum[16] + sum[17] + sum[18] + sum[19] + sum[20] + sum[21] + sum[22] + sum[23] + sum[24] + sum[25] + sum[26] + sum[27] + sum[28] + sum[29] + sum[30] + sum[31] + sum[32] + sum[33] + sum[34] + sum[35] + sum[36] + sum[37] + sum[38] + sum[39] + sum[40] + sum[41] + sum[42] + sum[43] + sum[44] + sum[45] + sum[46] + sum[47] + sum[48] + sum[49] + sum[50] + sum[51] + sum[52] + sum[53] + sum[54] + sum[55] + sum[56] + sum[57] + sum[58] + sum[59] + sum[60] + sum[61] + sum[62] + sum[63] + sum[64] + sum[65] + sum[66] + sum[67] + sum[68] + sum[69] + sum[70] + sum[71] + sum[72] + sum[73] + sum[74] + sum[75] + sum[76] + sum[77] + sum[78] + sum[79] + sum[80] + sum[81] + sum[82] + sum[83] + sum[84] + sum[85] + sum[86] + sum[87] + sum[88] + sum[89] + sum[90] + sum[91] + sum[92] + sum[93] + sum[94] + sum[95] + sum[96] + sum[97] + sum[98] + sum[99] + sum[100] + sum[101] + sum[102] + sum[103] + sum[104] + sum[105] + sum[106] + sum[107] + sum[108] + sum[109] + sum[110] + sum[111] + sum[112] + sum[113] + sum[114] + sum[115] + sum[116] + sum[117] + sum[118] + sum[119] + sum[120] + sum[121] + sum[122] + sum[123] + sum[124] + sum[125] + sum[126] + sum[127] + sum[128] + sum[129] + sum[130] + sum[131] + sum[132] + sum[133] + sum[134] + sum[135] + sum[136] + sum[137] + sum[138] + sum[139] + sum[140] + sum[141] + sum[142] + sum[143] + sum[144] + sum[145] + sum[146] + sum[147] + sum[148] + sum[149] + sum[150] + sum[151] + sum[152] + sum[153] + sum[154] + sum[155] + sum[156] + sum[157] + sum[158] + sum[159] + sum[160] + sum[161] + sum[162] + sum[163] + sum[164] + sum[165] + sum[166] + sum[167] + sum[168] + sum[169] + sum[170] + sum[171] + sum[172] + sum[173] + sum[174] + sum[175] + sum[176] + sum[177] + sum[178] + sum[179] + sum[180] + sum[181] + sum[182] + sum[183] + sum[184] + sum[185] + sum[186] + sum[187] + sum[188] + sum[189] + sum[190] + sum[191] + sum[192] + sum[193] + sum[194] + sum[195] + sum[196] + sum[197] + sum[198] + sum[199] + sum[200] + sum[201] + sum[202] + sum[203] + sum[204] + sum[205] + sum[206] + sum[207] + sum[208] + sum[209] + sum[210] + sum[211] + sum[212] + sum[213] + sum[214] + sum[215] + sum[216] + sum[217] + sum[218] + sum[219] + sum[220] + sum[221] + sum[222] + sum[223] + sum[224] + sum[225] + sum[226] + sum[227] + sum[228] + sum[229] + sum[230] + sum[231] + sum[232] + sum[233] + sum[234] + sum[235] + sum[236] + sum[237] + sum[238] + sum[239] + sum[240] + sum[241] + sum[242] + sum[243] + sum[244] + sum[245] + sum[246] + sum[247] + sum[248] + sum[249] + sum[250] + sum[251] + sum[252]);
var total = 0;
for (var i=0; i<count; i++) {
total += sum[i];
}
out <== total;
}