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

Identity VAnchor #182

Merged
merged 63 commits into from
Oct 3, 2022
Merged

Identity VAnchor #182

merged 63 commits into from
Oct 3, 2022

Conversation

semaraugusto
Copy link
Contributor

@semaraugusto semaraugusto commented Sep 9, 2022

Identity-VAnchor Spec

Overview

The identity-VAnchor protocol works exactly the same as VAnchor, with the addition of an identity check to prove that the user using the system has been registered into a Semaphore Group previous to start transacting and having to prove that the outputs (if relevant) are being sent to another registered user

User flow

  • User requests the group Admin for a registration onto the Semaphore Group related to VAnchor
  • System registers user’s public key as the leaf onto the SemaphoreGroup
  • Once registered, user can submit a proof with the same information that is currently required on VAnchor and some extra data related to proving membership onto the SemaphoreGroup. This information would be:
    • PrivateKey for the registered publicKey
    • Merkle-path to the user’s public key
    • Merkle-path to output UTXO public keys
    • Set of merkle-roots on the linked-tree

Security

  • User needs to have privateKey in order to prove membership onto the SemaphoreGroup. Merkle-Paths would not be secret and we assume this isn’t a problem
  • There’s no double interaction prevention on the semaphore circuit or smart-contract. We’re passing this responsability up to the VAnchor circuit as it already prevents double spending.

Edit:

Add merkle verification of output UTXO publicKeys if amount > 0

Circuits

Identity check

pragma circom 2.0.0;

include "../../node_modules/circomlib/circuits/poseidon.circom";
include "./manyMerkleProof.circom";

// nLevels must be < 32.
template Semaphore(nLevels, length) {
    signal input privateKey;
    signal input treePathIndices[nLevels];
    signal input treeSiblings[nLevels];

    // roots for interoperability, one-of-many merkle membership proof
    signal input roots[length];
    signal input chainID;

    component poseidon = Poseidon(1);
    poseidon.inputs[0] <== privateKey;

    signal publicKey;
    publicKey <== poseidon.out;

    component inclusionProof = ManyMerkleProofIdentity(nLevels, length);
    inclusionProof.leaf <== publicKey;
    // transformed value into list of values due to semaphore usage
    /* inclusionProof.pathIndices <== inPathIndices[tx]; */

    // add the roots and diffs signals to the bridge circuit
    for (var i = 0; i < length; i++) {
        inclusionProof.roots[i] <== roots[i];
    }

    for (var i = 0; i < nLevels; i++) {
        inclusionProof.pathElements[i] <== treeSiblings[i];
        inclusionProof.pathIndices[i] <== treePathIndices[i];
    }

    // Dummy square to prevent tampering chainID.
    signal chainIDSquared;
    chainIDSquared <== chainID * chainID;
}

Identity-VAnchor

pragma circom 2.0.0;

include "../vanchor/transaction.circom";
include "./semaphore.circom";

template IdentityVAnchor(levels, nIns, nOuts, zeroLeaf, length) {
    // Semaphore inputs
    signal input privateKey;
    signal input semaphoreTreePathIndices[levels];
    signal input semaphoreTreeSiblings[levels];

    // roots for interoperability, one-of-many merkle membership proof
    signal input semaphoreRoots[length];
    signal input chainID;

    // VAnchor inputs
    signal input publicAmount;
    signal input extDataHash; // arbitrary

    // data for transaction inputs
    signal input inputNullifier[nIns];
    signal input inAmount[nIns];
    signal input inPrivateKey[nIns];
    signal input inBlinding[nIns];
    signal input inPathIndices[nIns];
    signal input inPathElements[nIns][levels];

    // data for transaction outputs
    signal input outputCommitment[nOuts];
    signal input outChainID[nOuts];
    signal input outAmount[nOuts];
    signal input outPubkey[nOuts];
    signal input outSemaphoreTreePathIndices[nOuts][levels];
    // TODO: can we reduce this to a single index per nOut?
    signal input outSemaphoreTreeElements[nOuts][levels];
    signal input outBlinding[nOuts];

    // roots for interoperability, one-of-many merkle membership proof
    signal input vanchorRoots[length];

    component semaphore =  Semaphore(levels, length);

    semaphore.privateKey <== privateKey;
    for (var i = 0; i < levels; i++) {
        semaphore.treePathIndices[i] <== semaphoreTreePathIndices[i];
        semaphore.treeSiblings[i] <== semaphoreTreeSiblings[i];
    }
    semaphore.chainID <== chainID;

    for (var i = 0; i < length; i++) {
        semaphore.roots[i] <== semaphoreRoots[i];
    }

    component vanchor =  Transaction(levels, nIns, nOuts, zeroLeaf, length);

    vanchor.publicAmount <== publicAmount;
    vanchor.extDataHash <== extDataHash;
    vanchor.chainID <== chainID;
    for (var i = 0; i < nIns; i++) {
        vanchor.inputNullifier[i] <== inputNullifier[i];
        vanchor.inAmount[i] <== inAmount[i];
        vanchor.inPrivateKey[i] <== inPrivateKey[i];
        vanchor.inBlinding[i] <== inBlinding[i];
        vanchor.inPathIndices[i] <== inPathIndices[i];
        for (var j = 0; j < levels; j++) {
            vanchor.inPathElements[i][j] <== inPathElements[i][j];
        }
    }
    for (var i = 0; i < nOuts; i++) {
        vanchor.outputCommitment[i] <== outputCommitment[i];
        vanchor.outChainID[i] <== outChainID[i];
        vanchor.outAmount[i] <== outAmount[i];
        vanchor.outPubkey[i] <== outPubkey[i];
        vanchor.outBlinding[i] <== outBlinding[i];
    }
    for (var i = 0; i < length; i++) {
        vanchor.roots[i] <== vanchorRoots[i];
    }

    component publicSemaphore[nOuts];
    
    for (var n = 0; n < nOuts; n++) {
        publicSemaphore[n] = ManyMerkleProofPublic(levels, length);
        publicSemaphore[n].leaf <== outPubkey[n];
        publicSemaphore[n].enabled <== outAmount[n];
        for (var i = 0; i < length; i++) {
            publicSemaphore[n].roots[i] <== semaphoreRoots[i];
        }
        for (var i = 0; i < levels; i++) {
            publicSemaphore[n].pathIndices[i] <== outSemaphoreTreePathIndices[n][i];
            publicSemaphore[n].pathElements[i] <== outSemaphoreTreeElements[n][i];
        }
    }
}

…phore deploy and method to populate its roots
…Anchor for some reason and was removed to make tests work again
…nd package.json to import published version of semaphore
@semaraugusto semaraugusto changed the title Semar/identity vanchor Identity VAnchor Sep 9, 2022
@drewstone
Copy link
Contributor

drewstone commented Sep 9, 2022

Comments discussed in the meeting:

  • Use a single semaphore private key for all input UTXOs
  • Verify membership of output public keys in the semaphore set if enabled (check if amount is zero, etc.)
  • Update the Semaphore intermediate circuit to really just verify public key membership in the root set (so that it can be reused between input proof and output pubkey proofs)

@drewstone drewstone merged commit faf732a into main Oct 3, 2022
@drewstone drewstone deleted the semar/identity-vanchor branch October 3, 2022 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants