Skip to content

Commit

Permalink
Merge branch 'key-mgmt' into kyc
Browse files Browse the repository at this point in the history
  • Loading branch information
jimthematrix committed Aug 3, 2024
2 parents a68dae5 + 735de97 commit 069ceb1
Show file tree
Hide file tree
Showing 47 changed files with 190,021 additions and 148,806 deletions.
2 changes: 1 addition & 1 deletion solidity/contracts/erc20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import "hardhat/console.sol";
/// @title A sample implementation of a Zeto based fungible token with anonymity and no encryption
/// @author Kaleido, Inc.
/// @dev The proof has the following statements:
/// - each value in the output commitments must be a positive number in the range 0 ~ (2\*\*40 - 1)
/// - each value in the output commitments must be a positive number in the range 0 ~ (2^40 - 1)
/// - the sum of the input values match the sum of output values
/// - the hashes in the input and output match the `hash(value, salt, owner public key)` formula
/// - the sender possesses the private BabyJubjub key, whose public key is part of the pre-image of the input commitment hashes
Expand Down
219 changes: 219 additions & 0 deletions solidity/contracts/lib/verifier_anon_nullifier_kyc.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-3.0
/*
Copyright 2021 0KIMS association.
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
snarkJS is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
snarkJS is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.7.0 <0.9.0;

contract Groth16Verifier {
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;

// Verification Key data
uint256 constant alphax = 20491192805390485299153009773594534940189261866228447918068658471970481763042;
uint256 constant alphay = 9383485363053290200918347156157836566562967994039712273449902621266178545958;
uint256 constant betax1 = 4252822878758300859123897981450591353533073413197771768651442665752259397132;
uint256 constant betax2 = 6375614351688725206403948262868962793625744043794305715222011528459656738731;
uint256 constant betay1 = 21847035105528745403288232691147584728191162732299865338377159692350059136679;
uint256 constant betay2 = 10505242626370262277552901082094356697409835680220590971873171140371331206856;
uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant deltax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant deltax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant deltay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 constant deltay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;


uint256 constant IC0x = 15894451574377776512828899822631291668771577388294000086031636794401495745108;
uint256 constant IC0y = 20500448077471199298748085716990652313441855863081423369962236790299615428929;

uint256 constant IC1x = 20443159004875723874539838705410257721540879912284412356749567977659903444911;
uint256 constant IC1y = 6665486236079333380974192300286824838457364136917664093397672618694121088151;

uint256 constant IC2x = 10165991286665292508270439031111897031430740961452613000505237863420993798921;
uint256 constant IC2y = 9136420244822775537780456157556473424250276753802018153325130368437335488961;

uint256 constant IC3x = 20281597887882800919800535705908114405524156983380180098782819247741572489398;
uint256 constant IC3y = 21607785824705256552752191795973183116557074570496562616281978326295728837624;

uint256 constant IC4x = 1650512345884490589024034798398413920131787599888143340099721452288283568309;
uint256 constant IC4y = 17762263572124004609952079441932587287950558256360373694997362504497823608435;

uint256 constant IC5x = 19386768506626830282628824919708052542859521671332220831107354474836133656316;
uint256 constant IC5y = 16910894476841255025828316106755038218889475958213840126357807581898033175271;

uint256 constant IC6x = 19307586423967294928893199260202402479766234412752252005480672310147892735668;
uint256 constant IC6y = 11588134326531324221543008937081841914768042290177169066420262408448462215432;

uint256 constant IC7x = 19139469296120924050387585927788723421260614491257547987007007022510688394863;
uint256 constant IC7y = 9288080661952604669905314745907900909227032270567060567695766882828890238075;

uint256 constant IC8x = 14398271946161621939632562577274465836963412570377151128691039469213756214983;
uint256 constant IC8y = 5789907474461302660775855537997949676969273270738559295681426193011863858424;


// Memory data
uint16 constant pVk = 0;
uint16 constant pPairing = 128;

uint16 constant pLastMem = 896;

function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[8] calldata _pubSignals) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, q)) {
mstore(0, 0)
return(0, 0x20)
}
}

// G1 function to multiply a G1 value(x,y) to value in an address
function g1_mulAccC(pR, x, y, s) {
let success
let mIn := mload(0x40)
mstore(mIn, x)
mstore(add(mIn, 32), y)
mstore(add(mIn, 64), s)

success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)

if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}

mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))

success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)

if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}

function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
let _pPairing := add(pMem, pPairing)
let _pVk := add(pMem, pVk)

mstore(_pVk, IC0x)
mstore(add(_pVk, 32), IC0y)

// Compute the linear combination vk_x

g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0)))

g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32)))

g1_mulAccC(_pVk, IC3x, IC3y, calldataload(add(pubSignals, 64)))

g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96)))

g1_mulAccC(_pVk, IC5x, IC5y, calldataload(add(pubSignals, 128)))

g1_mulAccC(_pVk, IC6x, IC6y, calldataload(add(pubSignals, 160)))

g1_mulAccC(_pVk, IC7x, IC7y, calldataload(add(pubSignals, 192)))

g1_mulAccC(_pVk, IC8x, IC8y, calldataload(add(pubSignals, 224)))


// -A
mstore(_pPairing, calldataload(pA))
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))

// B
mstore(add(_pPairing, 64), calldataload(pB))
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))

// alpha1
mstore(add(_pPairing, 192), alphax)
mstore(add(_pPairing, 224), alphay)

// beta2
mstore(add(_pPairing, 256), betax1)
mstore(add(_pPairing, 288), betax2)
mstore(add(_pPairing, 320), betay1)
mstore(add(_pPairing, 352), betay2)

// vk_x
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))


// gamma2
mstore(add(_pPairing, 448), gammax1)
mstore(add(_pPairing, 480), gammax2)
mstore(add(_pPairing, 512), gammay1)
mstore(add(_pPairing, 544), gammay2)

// C
mstore(add(_pPairing, 576), calldataload(pC))
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))

// delta2
mstore(add(_pPairing, 640), deltax1)
mstore(add(_pPairing, 672), deltax2)
mstore(add(_pPairing, 704), deltay1)
mstore(add(_pPairing, 736), deltay2)


let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)

isOk := and(success, mload(_pPairing))
}

let pMem := mload(0x40)
mstore(0x40, add(pMem, pLastMem))

// Validate that all evaluations ∈ F

checkField(calldataload(add(_pubSignals, 0)))

checkField(calldataload(add(_pubSignals, 32)))

checkField(calldataload(add(_pubSignals, 64)))

checkField(calldataload(add(_pubSignals, 96)))

checkField(calldataload(add(_pubSignals, 128)))

checkField(calldataload(add(_pubSignals, 160)))

checkField(calldataload(add(_pubSignals, 192)))

checkField(calldataload(add(_pubSignals, 224)))

checkField(calldataload(add(_pubSignals, 256)))


// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)

mstore(0, isValid)
return(0, 0x20)
}
}
}
4 changes: 2 additions & 2 deletions solidity/contracts/zeto_anon_enc_nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ contract Zeto_AnonEncNullifier is
publicInputs[2] = nullifiers[0];
publicInputs[3] = nullifiers[1];
publicInputs[4] = root;
publicInputs[5] = (nullifiers[0] == 0) ? 0 : 1; // enable MT proof for the first nullifier
publicInputs[6] = (nullifiers[1] == 0) ? 0 : 1; // enable MT proof for the second nullifier
publicInputs[5] = (nullifiers[0] == 0) ? 0 : 1; // if the first nullifier is empty, disable its MT proof verification
publicInputs[6] = (nullifiers[1] == 0) ? 0 : 1; // if the second nullifier is empty, disable its MT proof verification
publicInputs[7] = outputs[0];
publicInputs[8] = outputs[1];
publicInputs[9] = encryptionNonce;
Expand Down
4 changes: 2 additions & 2 deletions solidity/contracts/zeto_anon_nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ contract Zeto_AnonNullifier is
publicInputs[0] = nullifiers[0];
publicInputs[1] = nullifiers[1];
publicInputs[2] = root;
publicInputs[3] = (nullifiers[0] == 0) ? 0 : 1; // enable MT proof for the first nullifier
publicInputs[4] = (nullifiers[1] == 0) ? 0 : 1; // enable MT proof for the second nullifier
publicInputs[3] = (nullifiers[0] == 0) ? 0 : 1; // if the first nullifier is empty, disable its MT proof verification
publicInputs[4] = (nullifiers[1] == 0) ? 0 : 1; // if the second nullifier is empty, disable its MT proof verification
publicInputs[5] = outputs[0];
publicInputs[6] = outputs[1];

Expand Down
1 change: 0 additions & 1 deletion solidity/test/zeto_anon_enc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ describe("Zeto based fungible token with anonymity and encryption", function ()
let Charlie: User;
let erc20: any;
let zeto: any;
let registry: any;
let utxo100: UTXO;
let utxo1: UTXO;
let utxo2: UTXO;
Expand Down
128 changes: 128 additions & 0 deletions zkp/circuits/anon_nullifier_kyc.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pragma circom 2.1.4;

include "./lib/check-positive.circom";
include "./lib/check-hashes.circom";
include "./lib/check-sum.circom";
include "./lib/check-nullifiers.circom";
include "./lib/check-smt-proof.circom";
include "./node_modules/circomlib/circuits/babyjub.circom";
include "./node_modules/circomlib/circuits/poseidon.circom";

// This version of the circuit performs the following operations:
// - derive the sender's public key from the sender's private key
// - check the input and output commitments match the expected hashes
// - check the input and output values sum to the same amount
// - check the nullifiers are derived from the input commitments and the sender's private key
// - check the nullifiers are included in the Merkle tree
// - check the owner public keys for inputs and outputs are included in the identities merkle tree
template Zeto(nInputs, nOutputs, nUTXOSMTLevels, nIdentitiesSMTLevels) {
signal input nullifiers[nInputs];
signal input inputCommitments[nInputs];
signal input inputValues[nInputs];
signal input inputSalts[nInputs];
// must be properly hashed and trimmed to be compatible with the BabyJub curve.
// Reference: https://github.com/iden3/circomlib/blob/master/test/babyjub.js#L103
signal input inputOwnerPrivateKey;
signal input utxoRoot;
signal input utxoMerkleProof[nInputs][nUTXOSMTLevels];
// allows merkle proof verifications for empty input elements to be skipped
signal input enabled[nInputs];
signal input identitiesRoot;
signal input identitiesMerkleProof[nOutputs + 1][nIdentitiesSMTLevels];
signal input outputCommitments[nOutputs];
signal input outputValues[nOutputs];
signal input outputOwnerPublicKeys[nOutputs][2];
signal input outputSalts[nOutputs];

// derive the sender's public key from the secret input
// for the sender's private key. This step demonstrates
// the sender really owns the private key for the input
// UTXOs
var inputOwnerPublicKey[2];
component pub = BabyPbk();
pub.in <== inputOwnerPrivateKey;
inputOwnerPublicKey[0] = pub.Ax;
inputOwnerPublicKey[1] = pub.Ay;
var inputOwnerPublicKeys[nInputs][2];
for (var i = 0; i < nInputs; i++) {
inputOwnerPublicKeys[i][0] = inputOwnerPublicKey[0];
inputOwnerPublicKeys[i][1] = inputOwnerPublicKey[1];
}

component checkPositives = CheckPositive(nOutputs);
checkPositives.outputValues <== outputValues;

component checkInputHashes = CheckHashes(nInputs);
checkInputHashes.commitments <== inputCommitments;
checkInputHashes.values <== inputValues;
checkInputHashes.salts <== inputSalts;
checkInputHashes.ownerPublicKeys <== inputOwnerPublicKeys;

component checkOutputHashes = CheckHashes(nOutputs);
checkOutputHashes.commitments <== outputCommitments;
checkOutputHashes.values <== outputValues;
checkOutputHashes.salts <== outputSalts;
checkOutputHashes.ownerPublicKeys <== outputOwnerPublicKeys;

component checkNullifiers = CheckNullifiers(nInputs);
checkNullifiers.nullifiers <== nullifiers;
checkNullifiers.values <== inputValues;
checkNullifiers.salts <== inputSalts;
checkNullifiers.ownerPrivateKey <== inputOwnerPrivateKey;

component checkSum = CheckSum(nInputs, nOutputs);
checkSum.inputValues <== inputValues;
checkSum.outputValues <== outputValues;

// With the above steps, we demonstrated that the nullifiers
// are securely bound to the input commitments. Now we need to
// demonstrate that the input commitments belong to the Sparse
// Merkle Tree with the root `root`.
component checkUTXOSMTProof = CheckSMTProof(nInputs, nUTXOSMTLevels);
checkUTXOSMTProof.root <== utxoRoot;
checkUTXOSMTProof.merkleProof <== utxoMerkleProof;
checkUTXOSMTProof.enabled <== enabled;
checkUTXOSMTProof.leafNodeIndexes <== inputCommitments;

// Finally, we need to demonstrate that the owner public keys
// for the inputs and outputs are included in the identities
// Sparse Merkle Tree with the root `identitiesRoot`.
var ownerPublicKeyHashes[nOutputs + 1];
component hash1 = Poseidon(2);
hash1.inputs[0] <== inputOwnerPublicKey[0];
hash1.inputs[1] <== inputOwnerPublicKey[1];
ownerPublicKeyHashes[0] = hash1.out;

component hashes[nOutputs];
var identitiesMTPCheckEnabled[nOutputs + 1];
for (var i = 0; i < nOutputs; i++) {
hashes[i] = Poseidon(2);
hashes[i].inputs[0] <== outputOwnerPublicKeys[i][0];
hashes[i].inputs[1] <== outputOwnerPublicKeys[i][1];
ownerPublicKeyHashes[i+1] = hashes[i].out;
identitiesMTPCheckEnabled[i] = 1;
}

component checkIdentitiesSMTProof = CheckSMTProof(nOutputs + 1, nIdentitiesSMTLevels);
checkIdentitiesSMTProof.root <== identitiesRoot;
checkIdentitiesSMTProof.merkleProof <== identitiesMerkleProof;
checkIdentitiesSMTProof.enabled <== identitiesMTPCheckEnabled;
checkIdentitiesSMTProof.leafNodeIndexes <== ownerPublicKeyHashes;
}

component main { public [ nullifiers, outputCommitments, utxoRoot, identitiesRoot, enabled ] } = Zeto(2, 2, 64, 10);
2 changes: 1 addition & 1 deletion zkp/circuits/check_hashes_value.circom
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ template Zeto(nOutputs) {
out <== sumOutputs;
}

component main {public [ outputCommitments ]} = Zeto(1);
component main {public [ outputCommitments ]} = Zeto(1);
Loading

0 comments on commit 069ceb1

Please sign in to comment.