Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] add selective disclosure circuit #47

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
6062mb 45.243
5596mb 40.872
5536mb 41.214
7 changes: 7 additions & 0 deletions packages/circom-circuit/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ gov-sig-setup/public.json
gov-sig-setup/verification_key.json
gov-sig-setup/input.json
gov-sig-build/*
setup-disclosure/circuit*
setup-disclosure/witness.wtns
setup-disclosure/proof.json
setup-disclosure/public.json
setup-disclosure/verification_key.json
setup-disclosure/input.json
build-disclosure/*
29 changes: 28 additions & 1 deletion packages/circom-circuit/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ user-sig-build-circuit:
circom src/verify-user-sig.circom --r1cs --wasm --sym -o ./user-sig-build
node user-sig-build/verify-user-sig_js/generate_witness.js ./user-sig-build/verify-user-sig_js/verify-user-sig.wasm ./user-sig-setup/input.json ./user-sig-setup/witness.wtns

build-disclosure-circuit:
circom src/selective-disclosure.circom --r1cs --wasm --sym -o ./build-disclosure
node build-disclosure/selective-disclosure_js/generate_witness.js ./build-disclosure/selective-disclosure_js/selective-disclosure.wasm ./setup-disclosure/input.json ./setup-disclosure/witness.wtns

gov-sig-setup-groth16:
snarkjs groth16 setup ./gov-sig-build/verify-gov-sig.r1cs ./powersOfTau28_hez_final_20.ptau ./gov-sig-setup/circuit_0000.zkey
snarkjs zkey contribute ./gov-sig-setup/circuit_0000.zkey ./gov-sig-setup/circuit_0001.zkey --name="1st Contributor Name 1" -v -e="1st random entropy"
Expand All @@ -34,26 +38,49 @@ user-sig-setup-groth16:
snarkjs zkey verify ./user-sig-build/verify-user-sig.r1cs ./powersOfTau28_hez_final_20.ptau ./user-sig-setup/circuit_final.zkey
snarkjs zkey export verificationkey ./user-sig-setup/circuit_final.zkey ./user-sig-setup/verification_key.json

setup-disclosure-groth16:
snarkjs groth16 setup ./build-disclosure/selective-disclosure.r1cs ./powersOfTau28_hez_final_21.ptau ./setup-disclosure/circuit_0000.zkey
snarkjs zkey contribute ./setup-disclosure/circuit_0000.zkey ./setup-disclosure/circuit_0001.zkey --name="1st Contributor Name 1" -v -e="1st random entropy"
snarkjs zkey contribute ./setup-disclosure/circuit_0001.zkey ./setup-disclosure/circuit_0002.zkey --name="2st Contributor Name 2" -v -e="2st random entropy"
snarkjs zkey contribute ./setup-disclosure/circuit_0002.zkey ./setup-disclosure/circuit_0003.zkey --name="3st Contributor Name 3" -v -e="3st random entropy"
snarkjs zkey verify ./build-disclosure/selective-disclosure.r1cs ./powersOfTau28_hez_final_21.ptau ./setup-disclosure/circuit_0003.zkey
snarkjs zkey beacon ./setup-disclosure/circuit_0003.zkey ./setup-disclosure/circuit_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2"
snarkjs zkey verify ./build-disclosure/selective-disclosure.r1cs ./powersOfTau28_hez_final_21.ptau ./setup-disclosure/circuit_final.zkey
snarkjs zkey export verificationkey ./setup-disclosure/circuit_final.zkey ./setup-disclosure/verification_key.json

gov-sig-create-proof:
snarkjs groth16 prove ./gov-sig-setup/circuit_final.zkey ./gov-sig-setup/witness.wtns ./gov-sig-setup/proof.json ./gov-sig-setup/public.json

user-sig-create-proof:
snarkjs groth16 prove ./user-sig-setup/circuit_final.zkey ./user-sig-setup/witness.wtns ./user-sig-setup/proof.json ./user-sig-setup/public.json

create-disclosure-proof:
node build-disclosure/selective-disclosure_js/generate_witness.js ./build-disclosure/selective-disclosure_js/selective-disclosure.wasm ./setup-disclosure/input.json ./setup-disclosure/witness.wtns
snarkjs groth16 prove ./setup-disclosure/circuit_final.zkey ./setup-disclosure/witness.wtns ./setup-disclosure/proof.json ./setup-disclosure/public.json

gov-sig-verify-proof:
snarkjs groth16 verify ./gov-sig-setup/verification_key.json ./gov-sig-setup/public.json ./gov-sig-setup/proof.json

user-sig-verify-proof:
snarkjs groth16 verify ./user-sig-setup/verification_key.json ./user-sig-setup/public.json ./user-sig-setup/proof.json

verify-disclosure-proof:
snarkjs groth16 verify ./setup-disclosure/verification_key.json ./setup-disclosure/public.json ./setup-disclosure/proof.json

gov-sig-export-verifier:
snarkjs zkey export solidityverifier ./gov-sig-setup/circuit_final.zkey ../contracts/src/circom-verifier/govSigVerifier.sol

user-sig-export-verifier:
snarkjs zkey export solidityverifier ./user-sig-setup/circuit_final.zkey ../contracts/src/circom-verifier/userSigVerifier.sol

export-disclosure-verifier:
snarkjs zkey export solidityverifier ./setup-disclosure/circuit_final.zkey ../contracts/src/circom-verifier/SelectiveDisclosure.sol

gov-sig-generate-calldata:
snarkjs generatecall ./gov-sig-setup/public.json ./gov-sig-setup/proof.json >> ./gov-sig-build/solidity-input.json

user-sig-generate-calldata:
snarkjs generatecall ./user-sig-setup/public.json ./user-sig-setup/proof.json >> ./user-sig-build/solidity-input.json
snarkjs generatecall ./user-sig-setup/public.json ./user-sig-setup/proof.json >> ./user-sig-build/solidity-input.json

generate-disclosure-calldata:
snarkjs generatecall ./setup-disclosure/public.json ./setup-disclosure/proof.json >> ./build-disclosure/solidity-input.json
173 changes: 173 additions & 0 deletions packages/circom-circuit/src/helpers/extract.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
pragma circom 2.1.2;
include "./utils.circom";

// A set of utils for shifting and packing signal arrays
// Performs extraction of reveal signals and packed signals

// From https://github.com/iden3/circomlib/blob/master/circuits/multiplexer.circom
function log2(a) {
if (a == 0) {
return 0;
}
var n = 1;
var r = 1;
while (n<a) {
r++;
n *= 2;
}
return r;
}

// Pack size is # of chunks i.e. number of char signals that fit into a signal (default 7 but can be 30)
template PackBytes(max_in_signals, max_out_signals, pack_size) {
assert(max_out_signals == ((max_in_signals - 1) \ pack_size + 1)); // Packing constant is wrong

signal input in[max_in_signals];
signal output out[max_out_signals];

component packer[max_out_signals];
for (var i = 0; i < max_out_signals; i++) {
packer[i] = Bytes2Packed(pack_size);
for (var j = 0; j < pack_size; j++) {
var reveal_idx = i * pack_size + j;
if (reveal_idx < max_in_signals) {
packer[i].in[j] <== in[i * pack_size + j];
} else {
packer[i].in[j] <== 0;
}
}
out[i] <== packer[i].out;
}
}

// Shift the input left by variable size of bytes.
// From https://demo.hedgedoc.org/s/Le0R3xUhB
// Note that if len_bits < max_substr * C, C around 1, then
// it's more efficient to use Sampriti's O(nk) solution instead
template VarShiftLeft(in_array_len, out_array_len) {
var len_bits = log2(in_array_len);
assert(in_array_len <= (1 << len_bits));
signal input in[in_array_len]; // x
signal input shift; // k

signal output out[out_array_len]; // y

component n2b = Num2Bits(len_bits);
n2b.in <== shift;

signal tmp[len_bits][in_array_len];
for (var j = 0; j < len_bits; j++) {
for (var i = 0; i < in_array_len; i++) {
var offset = (i + (1 << j)) % in_array_len;
// Shift left by 2^j indices if bit is 1
if (j == 0) {
tmp[j][i] <== n2b.out[j] * (in[offset] - in[i]) + in[i];
} else {
tmp[j][i] <== n2b.out[j] * (tmp[j-1][offset] - tmp[j-1][i]) + tmp[j-1][i];
}
}
}

// Return last row
// TODO: Assert the rest of the values are 0
for (var i = 0; i < out_array_len; i++) {
out[i] <== tmp[len_bits - 1][i];
}
}

// Shift the input left by variable size of bytes.
// Its input and output are the same as those of VarShiftLeft.
// However, it assumes the input is the masked bytes and checks that shift is the first index of the non-masked bytes.
template VarShiftMaskedStr(in_array_len, out_array_len) {
signal input in[in_array_len]; // x
signal input shift; // k
signal output out[out_array_len] <== VarShiftLeft(in_array_len, out_array_len)(in, shift);

signal is_target_idx[in_array_len];
signal prev_byte[in_array_len];
signal is_this_zero[in_array_len];
signal is_prev_zero[in_array_len];
for(var i = 0; i < in_array_len; i++) {
is_target_idx[i] <== IsEqual()([i, shift]);
is_this_zero[i] <== IsZero()(in[i]);
is_target_idx[i] * is_this_zero[i] === 0;
if(i == 0) {
is_prev_zero[i] <== 1;
} else {
is_prev_zero[i] <== IsZero()(in[i-1]);
}
is_target_idx[i] * (1 - is_prev_zero[i]) === 0;
}
}

// From https://demo.hedgedoc.org/s/Le0R3xUhB -- unused
template ClearSubarrayAfterEndIndex(n, nBits) {
signal input in[n]; // x
signal input end; // k

signal output out[n]; // y

component lt[n];
for (var i = 0; i < n; i++) {
lt[i] = LessThan(nBits);
lt[i].in[0] <== i;
lt[i].in[1] <== end;

// y[i] = (i < k) * x[i]
out[i] <== lt[i].out * in[i];
}
}

// Lengths here are in signals, even though the final output array is 1/7 the size of max_substr_len
// TODO: Maybe a better architectural decision to avoid mistakes is to require both values and assert their equality
template ShiftAndPack(in_array_len, max_substr_len, pack_size) {
var max_substr_len_packed = ((max_substr_len - 1) \ pack_size + 1);

component shifter = VarShiftLeft(in_array_len, max_substr_len);
component packer = PackBytes(max_substr_len, max_substr_len_packed, pack_size);

signal input in[in_array_len];
signal input shift;
signal output out[max_substr_len_packed];

for (var i = 0; i < in_array_len; i++) {
shifter.in[i] <== in[i];
}
shifter.shift <== shift;

// Note that this technically doesn't constrain the rest øf the bits after the max_substr_len to be 0/unmatched/unrevealed
// Because of the constraints on signed inputs, it seems this should be OK security wise
// But still, TODO unconstrained assert to double check they are 0
for (var i = 0; i < max_substr_len; i++) {
packer.in[i] <== shifter.out[i];
}
for (var i = 0; i < max_substr_len_packed; i++) {
out[i] <== packer.out[i];
}
}

// Shift the input left by variable size of bytes and pack the shifted bytes into fields under pack_size.
// Its input and output are the same as those of ShiftAndPack.
// However, it assumes the input is the masked bytes and checks that shift is the first index of the non-masked bytes.
template ShiftAndPackMaskedStr(in_array_len, max_substr_len, pack_size) {
var max_substr_len_packed = ((max_substr_len - 1) \ pack_size + 1);

component shifter = VarShiftMaskedStr(in_array_len, max_substr_len);
component packer = PackBytes(max_substr_len, max_substr_len_packed, pack_size);

signal input in[in_array_len];
signal input shift;
signal output out[max_substr_len_packed];

for (var i = 0; i < in_array_len; i++) {
shifter.in[i] <== in[i];
}
shifter.shift <== shift;

for (var i = 0; i < max_substr_len; i++) {
packer.in[i] <== shifter.out[i];
}
for (var i = 0; i < max_substr_len_packed; i++) {
out[i] <== packer.out[i];
}
}
104 changes: 104 additions & 0 deletions packages/circom-circuit/src/helpers/sha.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
pragma circom 2.1.5;

include "../../../../node_modules/circomlib/circuits/bitify.circom";
include "./sha256general.circom";
include "./sha256partial.circom";

template Sha256Bytes(max_num_bytes) {
signal input in_padded[max_num_bytes];
signal input in_len_padded_bytes;
signal output out[256];

var num_bits = max_num_bytes * 8;
component sha = Sha256General(num_bits);

component bytes[max_num_bytes];
for (var i = 0; i < max_num_bytes; i++) {
bytes[i] = Num2Bits(8);
bytes[i].in <== in_padded[i];
for (var j = 0; j < 8; j++) {
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
}
}
sha.in_len_padded_bits <== in_len_padded_bytes * 8;

for (var i = 0; i < 256; i++) {
out[i] <== sha.out[i];
}
}

template Sha256BytesPartial(max_num_bytes) {
assert(max_num_bytes % 32 == 0);
signal input in_padded[max_num_bytes];
signal input in_len_padded_bytes;
signal input pre_hash[32];
signal output out[256];

var num_bits = max_num_bytes * 8;
component sha = Sha256Partial(num_bits);

component bytes[max_num_bytes];
for (var i = 0; i < max_num_bytes; i++) {
bytes[i] = Num2Bits(8);
bytes[i].in <== in_padded[i];
for (var j = 0; j < 8; j++) {
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
}
}
sha.in_len_padded_bits <== in_len_padded_bytes * 8;

component states[32];
for (var i = 0; i < 32; i++) {
states[i] = Num2Bits(8);
states[i].in <== pre_hash[i];
for (var j = 0; j < 8; j++) {
sha.pre_state[8*i+j] <== states[i].out[7-j];
}
}

for (var i = 0; i < 256; i++) {
out[i] <== sha.out[i];
}
}

// Takes in 2^(8 * 31)-sized integers, not bytes, to save calldata. n is usually 31.
// max_num_n_bytes is the number of n-byte size inputs we have. expected to be max_num_bytes / (n + 1)
// template Sha256NBytes(max_num_n_bytes, n) {
// assert(1 << log_ceil(max_num_n_bytes) == max_num_n_bytes); // max_num_n_bytes is a power of 2
// assert(1 << log_ceil(n+1) == n+1); // n+1 is a power of 2

// // assert(1 << log_ceil(in_len_padded_bytes) == in_len_padded_bytes); // in_len_padded_bytes is a power of 2
// // assert(in_len_padded_bytes <= max_num_n_bytes * n)
// signal input in_padded[max_num_n_bytes];
// signal input in_len_padded_bytes; // Keep this in bytes for now. Can make n_bytes later.
// signal output out[256];

// var num_bits = max_num_n_bytes * 8 * (n + 1); // makes it a power of 2, though we waste 3% of constraints
// assert(1 << log_ceil(num_bits) == num_bits); // num_bits is a power of 2

// component sha = Sha256General(num_bits);

// component n_bytes[max_num_n_bytes];
// for (var i = 0; i < max_num_n_bytes; i++) {
// n_bytes[i] = Num2Bits(8 * n);
// n_bytes[i].in <== in_padded[i];
// for (var k = 0; k < n; k++){
// for (var j = 0; j < 8; j++) {
// sha.paddedIn[i * 8 * n + k * 8 + j] <== n_bytes[i].out[k * 8 + 7 - j]; // Big/little endian handled here
// }
// }
// }
// for (var j = 0; j < 8; j++) {
// sha.paddedIn[max_num_n_bytes * 8 * n + j] <== 0;
// }

// sha.in_len_padded_bits <== in_len_padded_bytes * 8;

// for (var i = 0; i < 256; i++) {
// out[i] <== sha.out[i];
// }
// }

// component main { public [ in_padded, in_len_padded_bytes ] } = Sha256Bytes(448);
// Note that Sha256NBytes is an unnecesary optimization.
// component main { public [ in_padded, in_len_padded_bytes ] } = Sha256NBytes(64, 31);
Loading
Loading