From fc7c15fb5c5d374a548197f229a834bd573db12e Mon Sep 17 00:00:00 2001 From: Kobi Gurkan Date: Thu, 1 Aug 2019 08:02:19 +0300 Subject: [PATCH] fix+feat: fixes some aliasing bugs, adds an optional permissioning to broadcast (#23) * fixes aliasing bugs, adds optional permissioning to broadcast * fixes nullifiers hash, makes permissioning work with tests * fix: clears cache * fix: adds another account for ci testing * fix: addresses comments --- .circleci/config.yml | 8 +- semaphorejs/contracts/Semaphore.sol | 24 +-- semaphorejs/package-lock.json | 2 +- semaphorejs/package.json | 1 + semaphorejs/scripts/integration_tests.sh | 2 +- semaphorejs/scripts/run_all_test.sh | 4 +- semaphorejs/snark/semaphore-base.circom | 42 ++++-- semaphorejs/src/client/semaphore.js | 22 +-- semaphorejs/src/server/server.js | 14 ++ semaphorejs/test/contracts/semaphore.js | 179 ++++++++++++++++++++++- 10 files changed, 250 insertions(+), 48 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 025e7bd4..32ec2854 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,14 +19,14 @@ jobs: - restore_cache: name: restore-npm-cache keys: - - v1.8-dependencies-{{ checksum "package-lock.json" }} + - v1.9-dependencies-{{ checksum "package-lock.json" }} - run: npm install - save_cache: paths: - node_modules - key: v1.8-dependencies-{{ checksum "package-lock.json" }} + key: v1.9-dependencies-{{ checksum "package-lock.json" }} # checksum the snarks definitions - run: @@ -37,7 +37,7 @@ jobs: - restore_cache: name: restore-snark-cache keys: - - v1.8-dependencies-{{ checksum "build/.snark_checksum" }} + - v1.9-dependencies-{{ checksum "build/.snark_checksum" }} # build snarks - run: @@ -47,7 +47,7 @@ jobs: # cache generated snark circuit and keys - save_cache: - key: v1.8-dependencies-{{ checksum "build/.snark_checksum" }} + key: v1.9-dependencies-{{ checksum "build/.snark_checksum" }} paths: - build/circuit.json - build/proving_key.bin diff --git a/semaphorejs/contracts/Semaphore.sol b/semaphorejs/contracts/Semaphore.sol index 4cd750ac..3c5dc53b 100644 --- a/semaphorejs/contracts/Semaphore.sol +++ b/semaphorejs/contracts/Semaphore.sol @@ -27,6 +27,7 @@ import "./Ownable.sol"; contract Semaphore is Verifier, MultipleMerkleTree, Ownable { uint256 public external_nullifier; uint8 public id_tree_index; + bool public is_broadcast_permissioned = true; mapping (uint256 => bool) root_history; uint8 current_root_index = 0; @@ -38,17 +39,17 @@ contract Semaphore is Verifier, MultipleMerkleTree, Ownable { event SignalBroadcast(bytes signal, uint256 nullifiers_hash, uint256 external_nullifier); + modifier onlyOwnerIfPermissioned() { + require(!is_broadcast_permissioned || isOwner(), "Semaphore: broadcast permission denied"); + _; + } + constructor(uint8 tree_levels, uint256 zero_value, uint256 external_nullifier_in) Ownable() public { external_nullifier = external_nullifier_in; id_tree_index = init_tree(tree_levels, zero_value); } - event Funded(uint256 amount); - - function fund() public payable { - emit Funded(msg.value); - } function insertIdentity(uint256 leaf) public onlyOwner { insert(id_tree_index, leaf); @@ -89,9 +90,6 @@ contract Semaphore is Verifier, MultipleMerkleTree, Ownable { uint[4] memory input, uint256 signal_hash ) public { - // Note that we only verify the broadcaster's address (input[4]) in the - // snark via verifyProof(). - require(hasNullifier(input[1]) == false, "Semaphore: nullifier already seen"); require(signal_hash == input[2], "Semaphore: signal hash mismatch"); require(external_nullifier == input[3], "Semaphore: external nullifier mismatch"); @@ -104,10 +102,10 @@ contract Semaphore is Verifier, MultipleMerkleTree, Ownable { uint[2] memory a, uint[2][2] memory b, uint[2] memory c, - uint[4] memory input // [root, nullifiers_hash, signal_hash, external_nullifier] - ) public { + uint[4] memory input // (root, nullifiers_hash, signal_hash, external_nullifier) + ) public onlyOwnerIfPermissioned { // Hash the signal - uint256 signal_hash = uint256(sha256(signal)) >> 8; + uint256 signal_hash = uint256(keccak256(signal)) >> 8; // Check the inputs preBroadcastRequire(a, b, c, input, signal_hash); @@ -136,4 +134,8 @@ contract Semaphore is Verifier, MultipleMerkleTree, Ownable { function setExternalNullifier(uint256 new_external_nullifier) public onlyOwner { external_nullifier = new_external_nullifier; } + + function setPermissioning(bool _newPermission) public onlyOwner { + is_broadcast_permissioned = _newPermission; + } } diff --git a/semaphorejs/package-lock.json b/semaphorejs/package-lock.json index 6cc0f357..61bcd705 100644 --- a/semaphorejs/package-lock.json +++ b/semaphorejs/package-lock.json @@ -3505,7 +3505,7 @@ "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" }, "ethereumjs-abi": { - "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb", + "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "requires": { "bn.js": "^4.11.8", diff --git a/semaphorejs/package.json b/semaphorejs/package.json index cfc5d7a5..62afd7a0 100644 --- a/semaphorejs/package.json +++ b/semaphorejs/package.json @@ -24,6 +24,7 @@ "circomlib": "0.0.10", "cors": "^2.8.5", "del": "^4.1.0", + "ethers": "^4.0.33", "file-saver": "^2.0.1", "ganache-cli": "^6.4.1", "ganache-core": "^2.5.3", diff --git a/semaphorejs/scripts/integration_tests.sh b/semaphorejs/scripts/integration_tests.sh index 38cc2dfe..213a8b09 100755 --- a/semaphorejs/scripts/integration_tests.sh +++ b/semaphorejs/scripts/integration_tests.sh @@ -22,7 +22,7 @@ echo "Working directory: `pwd`" npx mocha --recursive ../test/circuits -../node_modules/.bin/ganache-cli -p 7545 -l 8800000 -i 5777 --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21,100000000000000000000000000' --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21,10000000000000000000000000' -q & +../node_modules/.bin/ganache-cli -p 7545 -l 8800000 -i 5777 --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21,100000000000000000000000000' --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd22,10000000000000000000000000' -q & ../node_modules/.bin/truffle migrate --reset --network=local ../node_modules/.bin/truffle test ../test/contracts/* --network=local diff --git a/semaphorejs/scripts/run_all_test.sh b/semaphorejs/scripts/run_all_test.sh index 7f4d9ebb..da53a321 100755 --- a/semaphorejs/scripts/run_all_test.sh +++ b/semaphorejs/scripts/run_all_test.sh @@ -25,7 +25,7 @@ export CHAIN_ID=5777 export NODE_URL=http://localhost:7545 export SERVER_NODE_URL=http://localhost:7545 export DEPLOY_TO=local -export SLEEP_TIME=7 +export SLEEP_TIME=10 if [ "$1" == "goerli" ]; then CHAIN_ID=5 @@ -66,6 +66,6 @@ IDENTITY_COMMITMENT=`cat ./semaphore_identity.json | jq '.identity_commitment' | echo ${IDENTITY_COMMITMENT} tmux \ - new-session "source ~/.bashrc; export CONFIG_ENV=true LOG_LEVEL=${LOG_LEVEL} CHAIN_ID=${CHAIN_ID} CONTRACT_ADDRESS=$ADDRESS NODE_URL=${SERVER_NODE_URL} SEMAPHORE_PORT=3000 FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 TRANSACTION_CONFIRMATION_BLOCKS=1 CREATION_HASH=${CREATION_HASH}; npx semaphorejs-server fund; npx semaphorejs-server; bash -i" \; \ + new-session "source ~/.bashrc; export CONFIG_ENV=true LOG_LEVEL=${LOG_LEVEL} CHAIN_ID=${CHAIN_ID} CONTRACT_ADDRESS=$ADDRESS NODE_URL=${SERVER_NODE_URL} SEMAPHORE_PORT=3000 FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 TRANSACTION_CONFIRMATION_BLOCKS=1 CREATION_HASH=${CREATION_HASH}; npx semaphorejs-server disable_permissioning; npx semaphorejs-server; bash -i" \; \ split-window -h -t 0 "source ~/.bashrc; sleep ${SLEEP_TIME}; LOG_LEVEL=${LOG_LEVEL} curl http://localhost:3000/add_identity -X POST -d'{\"leaf\": \"${IDENTITY_COMMITMENT}\"}' -H 'Content-Type: application/json'; bash -i" \; \ split-window -t 0 "source ~/.bashrc; sleep ${SLEEP_TIME}; ./wait_for_element.sh ${IDENTITY_COMMITMENT}; LOG_LEVEL=${LOG_LEVEL} TRANSACTION_CONFIRMATION_BLOCKS=1 CHAIN_ID=${CHAIN_ID} CONTRACT_ADDRESS=$ADDRESS FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 NODE_URL=${NODE_URL} EXTERNAL_NULLIFIER=12312 SEMAPHORE_SERVER_URL=http://localhost:3000 BROADCASTER_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 CONFIG_ENV=true BASE_DIR=`pwd`/.. npx semaphorejs-client signal `shuf -i 1-100000000 -n 1`; bash -i" diff --git a/semaphorejs/snark/semaphore-base.circom b/semaphorejs/snark/semaphore-base.circom index eba57f91..b0ee6a21 100644 --- a/semaphorejs/snark/semaphore-base.circom +++ b/semaphorejs/snark/semaphore-base.circom @@ -76,17 +76,26 @@ template Semaphore(jubjub_field_size, n_levels, n_rounds) { // END signals - component identity_nullifier_bits = Num2Bits(256); + component identity_nullifier_bits = Num2Bits(248); identity_nullifier_bits.in <== identity_nullifier; - component identity_pk_0_bits = Num2Bits(256); + component identity_pk_0_bits = Num2Bits_strict(); identity_pk_0_bits.in <== dbl3.xout; component identity_commitment = Pedersen(2*256); // BEGIN identity commitment for (var i = 0; i < 256; i++) { - identity_commitment.in[i] <== identity_pk_0_bits.out[i]; - identity_commitment.in[i + 256] <== identity_nullifier_bits.out[i]; + if (i < 254) { + identity_commitment.in[i] <== identity_pk_0_bits.out[i]; + } else { + identity_commitment.in[i] <== 0; + } + + if (i < 248) { + identity_commitment.in[i + 256] <== identity_nullifier_bits.out[i]; + } else { + identity_commitment.in[i + 256] <== 0; + } } // END identity commitment @@ -115,21 +124,24 @@ template Semaphore(jubjub_field_size, n_levels, n_rounds) { // END tree // BEGIN nullifiers - component external_nullifier_bits = Num2Bits(256); + component external_nullifier_bits = Num2Bits(232); external_nullifier_bits.in <== external_nullifier; component nullifiers_hasher = Blake2s(512, 0); - for (var i = 0; i < 256; i++) { + for (var i = 0; i < 248; i++) { nullifiers_hasher.in_bits[i] <== identity_nullifier_bits.out[i]; - if (i < 224) { - nullifiers_hasher.in_bits[256 + i] <== external_nullifier_bits.out[i]; - } else { - if ( (i-224) < n_levels ) { - nullifiers_hasher.in_bits[256 + i] <== identity_path_index[i - 224]; - } else { - nullifiers_hasher.in_bits[256 + i] <== 0; - } - } + } + + for (var i = 0; i < 232; i++) { + nullifiers_hasher.in_bits[248 + i] <== external_nullifier_bits.out[i]; + } + + for (var i = 0; i < n_levels; i++) { + nullifiers_hasher.in_bits[248 + 232 + i] <== identity_path_index[i]; + } + + for (var i = (248 + 232 + n_levels); i < 512; i++) { + nullifiers_hasher.in_bits[i] <== 0; } component nullifiers_hash_num = Bits2Num(253); diff --git a/semaphorejs/src/client/semaphore.js b/semaphorejs/src/client/semaphore.js index be173de9..3303c1d4 100644 --- a/semaphorejs/src/client/semaphore.js +++ b/semaphorejs/src/client/semaphore.js @@ -21,7 +21,6 @@ const crypto = require('crypto'); const path = require('path'); const {unstringifyBigInts, stringifyBigInts} = require('websnark/tools/stringifybigint.js'); -const blake2 = require('blakejs'); const chai = require('chai'); const assert = chai.assert; @@ -40,6 +39,8 @@ const fetch = require('node-fetch'); const Web3 = require('web3'); +const ethers = require('ethers'); + let logger; /* uint8array to hex */ @@ -112,7 +113,6 @@ class SemaphoreClient { this.chain_id = chain_id; this.server_broadcast = server_broadcast; - this.broadcaster_address = server_broadcast_address; } async broadcast_signal(signal_str) { @@ -132,11 +132,16 @@ class SemaphoreClient { external_nullifier = bigInt(this.external_nullifier); } const signal_to_contract = this.web3.utils.asciiToHex(signal_str); - const signal_hash_raw = crypto.createHash('sha256').update(signal_to_contract.slice(2), 'hex').digest(); - const signal_hash = beBuff2int(signal_hash_raw.slice(0, 31)); - const broadcaster_address = bigInt(this.broadcaster_address); + const signal_to_contract_bytes = new Buffer(signal_to_contract.slice(2), 'hex'); - const msg = mimcsponge.multiHash([external_nullifier, signal_hash, broadcaster_address]); + const signal_hash_raw = ethers.utils.solidityKeccak256( + ['bytes'], + [signal_to_contract_bytes], + ); + const signal_hash_raw_bytes = new Buffer(signal_hash_raw.slice(2), 'hex'); + const signal_hash = beBuff2int(signal_hash_raw_bytes.slice(0, 31)); + + const msg = mimcsponge.multiHash([external_nullifier, signal_hash]); const signature = eddsa.signMiMCSponge(prvKey, msg); assert(eddsa.verifyMiMCSponge(msg, signature, pubKey)); @@ -174,7 +179,6 @@ class SemaphoreClient { identity_nullifier, identity_path_elements, identity_path_index, - broadcaster_address, }; const w = this.circuit.calculateWitness(inputs); const witness_bin = proof_util.convertWitness(snarkjs.stringifyBigInts(w)); @@ -196,8 +200,8 @@ class SemaphoreClient { logger.debug(`publicSignals: ${publicSignals}`); - // publicSignals = (root, nullifiers_hash, signal_hash, external_nullifier, broadcaster_address) - const public_signals_to_broadcast = [ publicSignals[0].toString(), publicSignals[1].toString(), publicSignals[2].toString(), publicSignals[3].toString(), publicSignals[4].toString() ]; + // publicSignals = (root, nullifiers_hash, signal_hash, external_nullifier) + const public_signals_to_broadcast = [ publicSignals[0].toString(), publicSignals[1].toString(), publicSignals[2].toString(), publicSignals[3].toString() ]; const proof_to_broadcast = [ [ proof.pi_a[0].toString(), proof.pi_a[1].toString() ], [ [ proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString() ], [ proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString() ] ], diff --git a/semaphorejs/src/server/server.js b/semaphorejs/src/server/server.js index 7f541713..84bc7233 100755 --- a/semaphorejs/src/server/server.js +++ b/semaphorejs/src/server/server.js @@ -350,6 +350,11 @@ async function fund() { ); } +async function disablePermissioning() { + const encoded = await semaphore.contract.methods.disablePermissioning().encodeABI(); + await send_transaction(encoded); +} + if (process.argv[2] == 'fund') { fund() .then(() => { @@ -359,6 +364,15 @@ if (process.argv[2] == 'fund') { logger.error(`error funding: ${err.stack}`); process.exit(1); }); +} else if (process.argv[2] == 'disable_permissioning') { + disablePermissioning() + .then(() => { + process.exit(0); + }) + .catch((err) => { + logger.error(`error funding: ${err.stack}`); + process.exit(1); + }); } else { const express = require('express'); var cors = require('cors'); diff --git a/semaphorejs/test/contracts/semaphore.js b/semaphorejs/test/contracts/semaphore.js index 38991ad8..f5bb5378 100644 --- a/semaphorejs/test/contracts/semaphore.js +++ b/semaphorejs/test/contracts/semaphore.js @@ -29,6 +29,8 @@ const path = require('path'); const snarkjs = require('snarkjs'); const circomlib = require('circomlib'); +const ethers = require('ethers'); + const test_util = require('../../src/test_util'); const bigInt = snarkjs.bigInt; @@ -77,6 +79,7 @@ const cutDownBits = function(b, bits) { contract('Semaphore', function (accounts) { let semaphore; + let identity_commitment1; before(async () => { semaphore = await Semaphore.deployed(); @@ -96,9 +99,16 @@ contract('Semaphore', function (accounts) { const external_nullifier = bigInt('12312'); const signal_str = 'hello!'; - const signal_hash_raw = crypto.createHash('sha256').update(signal_str, 'utf8').digest(); - const signal_hash = beBuff2int(signal_hash_raw.slice(0, 31)); const signal_to_contract = web3.utils.asciiToHex(signal_str); + const signal_to_contract_bytes = new Buffer(signal_to_contract.slice(2), 'hex'); + + const signal_hash_raw = ethers.utils.solidityKeccak256( + ['bytes'], + [signal_to_contract_bytes], + ); + const signal_hash_raw_bytes = new Buffer(signal_hash_raw.slice(2), 'hex'); + const signal_hash = beBuff2int(signal_hash_raw_bytes.slice(0, 31)); + const accounts = await web3.eth.getAccounts(); const msg = mimcsponge.multiHash([bigInt(external_nullifier), bigInt(signal_hash)]); @@ -106,7 +116,7 @@ contract('Semaphore', function (accounts) { assert(eddsa.verifyMiMCSponge(msg, signature, pubKey)); - const identity_nullifier = bigInt('230'); + const identity_nullifier = bigInt('231'); const storage_path = '/tmp/rocksdb_semaphore_test'; if (fs.existsSync(storage_path)) { @@ -134,18 +144,18 @@ contract('Semaphore', function (accounts) { ); const identity_commitment = pedersenHash([bigInt(circomlib.babyJub.mulPointEscalar(pubKey, 8)[0]), bigInt(identity_nullifier)]); + identity_commitment1 = identity_commitment; const semaphore = await Semaphore.deployed(); const receipt = await semaphore.insertIdentity(identity_commitment.toString()); assert.equal(receipt.logs[0].event, 'LeafAdded'); const next_index = parseInt(receipt.logs[0].args.leaf_index.toString()); - await semaphore.fund({value: web3.utils.toWei('10')}); await tree.update(next_index, identity_commitment.toString()); await memTree.update(next_index, identity_commitment.toString()); const identity_path = await tree.path(next_index); const mem_identity_path = await memTree.path(next_index); - + assert.equal(JSON.stringify(identity_path), JSON.stringify(mem_identity_path)) const identity_path_elements = identity_path.path_elements; @@ -244,4 +254,163 @@ contract('Semaphore', function (accounts) { console.log(evs); */ }); + + it('tests permissioning', async () => { + const cirDef = JSON.parse(fs.readFileSync(path.join(__dirname,'../../build/circuit.json')).toString()); + circuit = new snarkjs.Circuit(cirDef); + + const prvKey = Buffer.from('0001020304050607080900010203040506070809000102030405060708080001', 'hex'); + + const pubKey = eddsa.prv2pub(prvKey); + + const external_nullifier = bigInt('12312'); + const signal_str = 'hello!'; + const signal_to_contract = web3.utils.asciiToHex(signal_str); + const signal_to_contract_bytes = new Buffer(signal_to_contract.slice(2), 'hex'); + + const signal_hash_raw = ethers.utils.solidityKeccak256( + ['bytes'], + [signal_to_contract_bytes], + ); + const signal_hash_raw_bytes = new Buffer(signal_hash_raw.slice(2), 'hex'); + const signal_hash = beBuff2int(signal_hash_raw_bytes.slice(0, 31)); + + const accounts = await web3.eth.getAccounts(); + + const msg = mimcsponge.multiHash([bigInt(external_nullifier), bigInt(signal_hash)]); + const signature = eddsa.signMiMCSponge(prvKey, msg); + + assert(eddsa.verifyMiMCSponge(msg, signature, pubKey)); + + const identity_nullifier = bigInt('230'); + + const storage_path = '/tmp/rocksdb_semaphore_test'; + if (fs.existsSync(storage_path)) { + del.sync(storage_path, { force: true }); + } + const default_value = '0'; + const storage = new RocksDb(storage_path); + const memStorage = new MemStorage(); + const hasher = new MimcSpongeHasher(); + const prefix = 'semaphore'; + const tree = new MerkleTree( + prefix, + storage, + hasher, + 20, + default_value, + ); + + const memTree = new MerkleTree( + prefix, + memStorage, + hasher, + 20, + default_value, + ); + + + const identity_commitment = pedersenHash([bigInt(circomlib.babyJub.mulPointEscalar(pubKey, 8)[0]), bigInt(identity_nullifier)]); + + const semaphore = await Semaphore.deployed(); + const receipt = await semaphore.insertIdentity(identity_commitment.toString()); + assert.equal(receipt.logs[0].event, 'LeafAdded'); + const next_index = parseInt(receipt.logs[0].args.leaf_index.toString()); + + await tree.update(0, identity_commitment1.toString()); + await memTree.update(0, identity_commitment1.toString()); + await tree.update(next_index, identity_commitment.toString()); + await memTree.update(next_index, identity_commitment.toString()); + const identity_path = await tree.path(next_index); + const mem_identity_path = await memTree.path(next_index); + + assert.equal(JSON.stringify(identity_path), JSON.stringify(mem_identity_path)) + + const identity_path_elements = identity_path.path_elements; + const identity_path_index = identity_path.path_index; + + //console.log(identity_commitment.toString()); + //console.log(identity_path_elements, identity_path_index, identity_path.root); + + const w = circuit.calculateWitness({ + 'identity_pk[0]': pubKey[0], + 'identity_pk[1]': pubKey[1], + 'auth_sig_r[0]': signature.R8[0], + 'auth_sig_r[1]': signature.R8[1], + auth_sig_s: signature.S, + signal_hash, + external_nullifier, + identity_nullifier, + identity_path_elements, + identity_path_index, + }); + + const root = w[circuit.getSignalIdx('main.root')]; + const nullifiers_hash = w[circuit.getSignalIdx('main.nullifiers_hash')]; + assert(circuit.checkWitness(w)); + assert.equal(w[circuit.getSignalIdx('main.root')].toString(), identity_path.root); + + //console.log(w[circuit.getSignalIdx('main.root')]); + + /* + console.log(tree[0]); + console.log(w[circuit.getSignalIdx('main.signal_hash')]); + console.log(w[circuit.getSignalIdx('main.external_nullifier')]); + console.log(w[circuit.getSignalIdx('main.root')]); + console.log(w[circuit.getSignalIdx('main.nullifiers_hash')]); + console.log(w[circuit.getSignalIdx('main.identity_commitment.out')]); + */ + + const vk_proof = fs.readFileSync(path.join(__dirname,'../../build/proving_key.bin')); + const witness_bin = proof_util.convertWitness(snarkjs.stringifyBigInts(w)); + const publicSignals = w.slice(1, circuit.nPubInputs + circuit.nOutputs+1); + const proof = await proof_util.prove(witness_bin.buffer, vk_proof.buffer); + let failed = false; + let reason = ''; + + await semaphore.transferOwnership(accounts[1]); + assert.equal(await semaphore.owner(), accounts[1]); + + failed = false; + try { + await semaphore.broadcastSignal( + + signal_to_contract, + [ proof.pi_a[0].toString(), proof.pi_a[1].toString() ], + [ [ proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString() ], [ proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString() ] ], + [ proof.pi_c[0].toString(), proof.pi_c[1].toString() ], + [ publicSignals[0].toString(), publicSignals[1].toString(), publicSignals[2].toString(), publicSignals[3].toString() ], + ); + } catch(e) { + failed = true; + reason = e.reason + } + assert.equal(failed, true); + assert.equal(reason, 'Semaphore: broadcast permission denied'); + + await semaphore.setPermissioning(false, { from: accounts[1] }); + + const a = [ proof.pi_a[0].toString(), proof.pi_a[1].toString() ] + const b = [ [ proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString() ], [ proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString() ] ] + const c = [ proof.pi_c[0].toString(), proof.pi_c[1].toString() ] + const input = [ publicSignals[0].toString(), publicSignals[1].toString(), publicSignals[2].toString(), publicSignals[3].toString() ] + + const check = await semaphore.preBroadcastCheck(a, b, c, input, bigInt(signal_hash).toString()) + assert.isTrue(check) + + const broadcastTx = await semaphore.broadcastSignal( + signal_to_contract, + a, b, c, input + ); + + assert.isTrue(broadcastTx.receipt.status) + + /* + const evs = await semaphore.getPastEvents('allEvents', { + fromBlock: 0, + toBlock: 'latest' + }); + console.log(evs); + */ + }); });