├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── client └── automation.js ├── get-libsnark ├── index.js ├── node-proof-generator ├── index.js └── package.json ├── package-lock.json ├── package.json └── src ├── gadget_template.hpp ├── payment_in_out_gadget.hpp ├── payment_in_out_generate_keypair.cpp ├── payment_in_out_generate_proof.cpp ├── payment_in_out_verify_proof.cpp ├── payment_multi_gadget.hpp ├── payment_multi_generate_keypair.cpp ├── payment_multi_generate_proof.cpp ├── payment_multi_verify_proof.cpp ├── snark.hpp ├── test.cpp ├── test.h └── utils.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | *.o 3 | *.d 4 | depinst 5 | depsrc 6 | *.swp 7 | libsnark 8 | payment_* 9 | proof* 10 | proving* 11 | node_modules/ 12 | privateInputParameters* 13 | publicInputParameters* 14 | verificationKey* 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libsnark"] 2 | path = libsnark 3 | url = git@github.com:scipr-lab/libsnark.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INSTALLDIR=libsnark/build/install 2 | LIBFQFFT_INSTALLDIR=libsnark/libfqfft/build/install 3 | LIBSNARK_FLAGS= -DUSE_ASM -DCURVE_ALT_BN128 -DWITH_PROCPS=OFF -DWITH_SUPERCOP=OFF 4 | 5 | OPTFLAGS = -march=native -mtune=native -O2 6 | CXXFLAGS += -g -Wall -Wextra -Wno-unused-parameter -std=c++11 -fPIC -Wno-unused-variable 7 | CXXFLAGS += -I$(INSTALLDIR)/usr/local/include 8 | CXXFLAGS += -I$(LIBFQFFT_INSTALLDIR)/include 9 | LDFLAGS += -flto 10 | 11 | LDLIBS += -L$(INSTALLDIR)/usr/local/lib/ -lsnark -lff -lgmpxx -lgmp 12 | LDLIBS += -lboost_system 13 | 14 | all: 15 | $(CXX) -o payment_in_out_generate_keypair.o src/payment_in_out_generate_keypair.cpp -c $(CXXFLAGS) $(LIBSNARK_FLAGS) $(OPTFLAGS) 16 | $(CXX) -o payment_in_out_generate_keypair payment_in_out_generate_keypair.o $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) 17 | $(CXX) -o payment_in_out_generate_proof.o src/payment_in_out_generate_proof.cpp -c $(CXXFLAGS) $(LIBSNARK_FLAGS) $(OPTFLAGS) 18 | $(CXX) -o payment_in_out_generate_proof payment_in_out_generate_proof.o $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) 19 | $(CXX) -o payment_in_out_verify_proof.o src/payment_in_out_verify_proof.cpp -c $(CXXFLAGS) $(LIBSNARK_FLAGS) $(OPTFLAGS) 20 | $(CXX) -o payment_in_out_verify_proof payment_in_out_verify_proof.o $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) 21 | $(CXX) -o payment_multi_generate_keypair.o src/payment_multi_generate_keypair.cpp -c $(CXXFLAGS) $(LIBSNARK_FLAGS) $(OPTFLAGS) 22 | $(CXX) -o payment_multi_generate_keypair payment_multi_generate_keypair.o $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) 23 | $(CXX) -o payment_multi_generate_proof.o src/payment_multi_generate_proof.cpp -c $(CXXFLAGS) $(LIBSNARK_FLAGS) $(OPTFLAGS) 24 | $(CXX) -o payment_multi_generate_proof payment_multi_generate_proof.o $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) 25 | $(CXX) -o payment_multi_verify_proof.o src/payment_multi_verify_proof.cpp -c $(CXXFLAGS) $(LIBSNARK_FLAGS) $(OPTFLAGS) 26 | $(CXX) -o payment_multi_verify_proof payment_multi_verify_proof.o $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) 27 | 28 | clean: 29 | $(RM) test.o test 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zero-knowledge-proofs 2 | Zero Knowledge Proofs and how they can be implemented in Quorum 3 | 4 | This is a SNARK implementation using libsnark for the following: 5 | 6 | ``ZkPoK{ (R1, R2, R3): Hi = sha256(Ri) and R3 = R1 + R2 }`` 7 | 8 | Read: given `H1`, `H2`, `H3`, prove you know `R1`, `R2`, `R3` such that `R1` is the preimage of `H1`, `R2` is the preimage of `H2`, `R3` is the preimage of `H3`, and `R3` is `R1 + R2`. 9 | 10 | This is an implementation and benchmark of the "Receive" zk-SNARK in the Confidential Transaction scheme from this article: . 11 | 12 | Code based on . 13 | 14 | Some more background here: 15 | 16 | ## howto 17 | 18 | ### Required packages 19 | 20 | * On Ubuntu 16.04 LTS: 21 | 22 | `$ sudo apt-get install build-essential cmake git libgmp3-dev libprocps4-dev python-markdown libboost-all-dev libssl-dev` 23 | 24 | * On Ubuntu 14.04 LTS: 25 | 26 | `$ sudo apt-get install build-essential cmake git libgmp3-dev libprocps3-dev python-markdown libboost-all-dev libssl-dev` 27 | 28 | ### Installation 29 | 30 | `./get-libsnark && make` 31 | 32 | `npm install` 33 | 34 | ### Running 35 | `node index.js startBalance=10000` 36 | 37 | First select 38 | 1. Single payment in and single payment out 39 | 2. Multiple payments in and multiple payments out 40 | 41 | Follow the prompts. 42 | 43 | 1. Generate a key pair 44 | 2. Generate a proof (single or multiple) 45 | 4. Verify proof 46 | 0. Quit 47 | 48 | #### Generate a new key pair 49 | This creates a new proving key and verification key from the circuit. They are saved to the files: 50 | * `provingKey-single` or `provingKey-multi` 51 | * `verificationKey-single` or `verificationKey-multi` 52 | 53 | #### Generate a payment proof 54 | This generates a proof using the proving key as well as the following values: 55 | 56 | * `start balance` 57 | * `incoming payment/s` 58 | * `outgoing payment/s` 59 | * `end balance` (start balance + incoming - outgoing) 60 | 61 | The proof is: 62 | * `start balance` + `incoming payments` = `end balance` + `outgoing payments` 63 | 64 | #### Verify payment proof 65 | Verifies the above proofs 66 | 67 | # License 68 | Copyright (C) 2017 The Quorum Zero Knowledge Proof Authors. 69 | 70 | Licensed under the Apache License, Version 2.0 (the "License"); 71 | you may not use this file except in compliance with the License. 72 | You may obtain a copy of the License at 73 | 74 | http://www.apache.org/licenses/LICENSE-2.0 75 | 76 | Unless required by applicable law or agreed to in writing, software 77 | distributed under the License is distributed on an "AS IS" BASIS, 78 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 79 | See the License for the specific language governing permissions and 80 | limitations under the License. 81 | -------------------------------------------------------------------------------- /client/automation.js: -------------------------------------------------------------------------------- 1 | const {spawn} = require('child_process') 2 | var events = require('events'); 3 | var eventEmitter = new events.EventEmitter(); 4 | 5 | var multiProofGenerator 6 | var singleProofGenerator 7 | var proofCodeBlocking = false 8 | var multiProofGeneratorIsRunning = false 9 | var singleProofGeneratorIsRunning = false 10 | 11 | function getMatches(string, regex) { 12 | var matches = []; 13 | var match; 14 | while (match = regex.exec(string)) { 15 | matches.push(match[0]); 16 | } 17 | return matches; 18 | } 19 | 20 | function getProofCodeBlocking(){ 21 | return proofCodeBlocking 22 | } 23 | 24 | function setProofCodeBlocking(value){ 25 | proofCodeBlocking=value 26 | } 27 | 28 | function shutDown(multiOrSingle){ 29 | if(multiOrSingle=='multi'){ 30 | multiProofGenerator.stdin.write('q\n') 31 | } else { 32 | singleProofGenerator.stdin.write('q\n') 33 | } 34 | } 35 | 36 | function generateProof(multiOrSingle, paymentId){ 37 | if(multiOrSingle=='multi'){ 38 | multiProofGenerator.stdin.write(paymentId + '\n') 39 | } else { 40 | singleProofGenerator.stdin.write(paymentId + '\n') 41 | } 42 | } 43 | 44 | function handleExecuteProgram(programName, args, msgStart, msgEnd, msgError, cb){ 45 | // console.log(msgStart) 46 | 47 | var succesfullyCompleted=true 48 | const runCommand = spawn(programName, args) 49 | 50 | runCommand.stdout.on('data', (data) => { 51 | dataString = data.toString() 52 | 53 | if(data.indexOf('(leave) Call to r1cs_ppzksnark_online_verifier_strong_IC')>-1){ 54 | var matches = getMatches(dataString, /verifier_strong_IC.\[[0-9]{0,2}.[0-9]*s/g) 55 | var noSecs = '' 56 | if(matches && matches.length>0){ 57 | noSecs = matches[0].substring(20,matches[0].length) 58 | } else { 59 | // console.log(dataString) 60 | } 61 | // console.log('\nProof verification ended:', noSecs) 62 | } else { 63 | // process.stdout.write('.') 64 | } 65 | }) 66 | 67 | runCommand.stderr.on('data', (data) => { 68 | console.log(data.toString()) 69 | succesfullyCompleted = false 70 | }) 71 | 72 | runCommand.on('close', (code) => { 73 | // console.log() 74 | setProofCodeBlocking(false) 75 | if(code=='1' || !succesfullyCompleted){ 76 | cb(msgError) 77 | } else { 78 | cb(null) 79 | } 80 | }) 81 | } 82 | 83 | function generateNewKeyPair(multiOrSingle, cb){ 84 | var generateKeyPairProgram = multiOrSingle == 'multi' ? './payment_multi_generate_keypair' : './payment_in_out_generate_keypair' 85 | setProofCodeBlocking(true) 86 | if((multiOrSingle == 'multi' && multiProofGeneratorIsRunning == true) || multiOrSingle == 'single' && singleProofGeneratorIsRunning == true){ 87 | shutDown(multiOrSingle) 88 | } 89 | handleExecuteProgram(generateKeyPairProgram, [], 'Generating key pair...', 'The key pair has been generated and the keys written to files', 'The key pair failed\n\n', function(){ 90 | cb() 91 | }) 92 | } 93 | 94 | function logGeneratorOutput(proofGenerator){ 95 | proofGenerator.stdout.on('data', (data) => { 96 | dataString = data.toString() 97 | 98 | if(dataString.indexOf('to generate a proof or q to quit')>-1){ 99 | setProofCodeBlocking(false) 100 | } 101 | 102 | if(dataString.indexOf('System not satisfied!')>-1){ 103 | console.log('Proof generation unsuccesful: System not satisfied') 104 | setProofCodeBlocking(false) 105 | } 106 | if(dataString.indexOf('Proving key loaded into memory')>-1){ 107 | setProofCodeBlocking(false) 108 | } 109 | 110 | 111 | if(data.indexOf('Starting proof generation for')>-1){ 112 | 113 | var matches = getMatches(dataString, /proof_single_[0-9]{0,4}/g) 114 | var payment_id = '1' 115 | if(matches && matches.length>0){ 116 | payment_id = matches[0].substring(13,matches[0].length) 117 | eventEmitter.emit('proofGenerationStarted', payment_id); 118 | } else { 119 | // console.log(dataString) 120 | } 121 | } 122 | 123 | if(data.indexOf('Proof was generated!!proof_single')>-1){ 124 | 125 | var matches = getMatches(dataString, /proof_single_[0-9]{0,4}/g) 126 | var payment_id = '1' 127 | if(matches && matches.length>0){ 128 | payment_id = matches[0].substring(13,matches[0].length) 129 | eventEmitter.emit('proofGenerationComplete', payment_id); 130 | } else { 131 | // console.log(dataString) 132 | } 133 | } 134 | 135 | /*if(data.indexOf('Compute the proof')>-1){ 136 | if(data.indexOf('(enter)')>-1){ 137 | // console.log('\nGenerating proof') 138 | } else { 139 | var matches = getMatches(dataString, /\[[0-9]{0,2}.[0-9]*s/g) 140 | var noSecs = matches[1].substring(1,matches[1].length) 141 | // console.log('\nProof generation ended:', noSecs) 142 | } 143 | } else { 144 | // process.stdout.write('.') 145 | }*/ 146 | 147 | }) 148 | 149 | proofGenerator.stderr.on('data', (data) => { 150 | console.log(data.toString()) 151 | }) 152 | 153 | proofGenerator.on('exit', function (code) { 154 | generatorIsRunning = false 155 | }) 156 | 157 | } 158 | 159 | function loadProvingKey(multiOrSingle){ 160 | console.log('Loading proving key from file. This will take a few seconds') 161 | setProofCodeBlocking(true) 162 | if(multiOrSingle=='multi'){ 163 | multiProofGenerator = spawn('./payment_multi_generate_proof') 164 | generatorIsRunning = true 165 | logGeneratorOutput(multiProofGenerator) 166 | } else { 167 | singleProofGenerator = spawn('./payment_in_out_generate_proof') 168 | logGeneratorOutput(singleProofGenerator) 169 | generatorIsRunning = true 170 | } 171 | } 172 | 173 | function verifyProof(multiOrSingle, paymentId, cb){ 174 | eventEmitter.emit('proofVerificationStarted', paymentId); 175 | var verifyProofProgram = multiOrSingle == 'multi' ? './payment_multi_verify_proof' : './payment_in_out_verify_proof' 176 | handleExecuteProgram(verifyProofProgram, [paymentId], '', '', 'The proof verification failed\n\n', function(verifyErr){ 177 | eventEmitter.emit('proofVerificationComplete', paymentId); 178 | cb(verifyErr) 179 | }) 180 | } 181 | 182 | exports.LoadProvingKey = loadProvingKey 183 | exports.SetProofCodeBlocking = setProofCodeBlocking 184 | exports.GetProofCodeBlocking = getProofCodeBlocking 185 | exports.ShutDown = shutDown 186 | exports.GenerateNewKeyPair = generateNewKeyPair 187 | exports.GenerateProof = generateProof 188 | exports.VerifyProof = verifyProof 189 | exports.Events = eventEmitter 190 | -------------------------------------------------------------------------------- /get-libsnark: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | 5 | LIBSNARK_FLAGS="-DUSE_ASM=ON -DCURVE=ALT_BN128 -DWITH_PROCPS=OFF -DWITH_SUPERCOP=OFF -DBINARY_OUTPUT=OFF" 6 | LIBFQFFT_SRC=${LIBFQFFT_SRC:-https://github.com/scipr-lab/libfqfft.git} 7 | LIBSNARK_SRC=${LIBSNARK_SRC:-https://github.com/scipr-lab/libsnark.git} 8 | [ ! -d libsnark ] && git clone $LIBSNARK_SRC libsnark 9 | cd libsnark 10 | git pull 11 | git submodule init && git submodule update 12 | [ ! -d build ] && mkdir build 13 | cd build && cmake $LIBSNARK_FLAGS .. 14 | make 15 | [ ! -d install ] && mkdir install 16 | DESTDIR=./install make install 17 | cd .. 18 | [ ! -d libfqfft ] && git clone $LIBFQFFT_SRC libfqfft 19 | cd libfqfft 20 | git pull 21 | git submodule init && git submodule update 22 | [ ! -d build ] && mkdir build 23 | cd build 24 | [ ! -d install ] && mkdir install 25 | cmake .. -DCMAKE_INSTALL_PREFIX=./install 26 | make 27 | make install 28 | 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var prompt = require('prompt') 2 | var colors = require('colors/safe') 3 | var fs = require('fs') 4 | var events = require('events'); 5 | var automation = require('./client/automation.js') 6 | 7 | const sha256 = require('sha256') 8 | 9 | let startTime = null 10 | let proofsVerified = {} 11 | let proofsGenerated = {} 12 | let numberOfProofsVerified = 0 13 | let numberOfProofsGenerated = 0 14 | 15 | var startBalance = 0 16 | var endBalance = 0 17 | var incoming = [0,0,0,0,0,0] 18 | var outgoing = [0,0,0,0,0,0] 19 | var noPayments = 6 20 | 21 | if(process.argv.length!=3){ 22 | console.log("you need to set your start balance. Run the application using node index.js startBalance=1000") 23 | return 1 24 | } 25 | 26 | process.argv.forEach(function (val, index, array) { 27 | if(val.startsWith("startBalance")){ 28 | startBalance = parseInt(val.split("=")[1]) 29 | endBalance = startBalance 30 | } 31 | }); 32 | 33 | longToByteArray = function(valueToConvert) { 34 | // we want to represent the input as a 8-bytes array 35 | var byteArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 36 | 37 | for ( var index = byteArray.length-1; index >=0; index -- ) { 38 | var byte = valueToConvert & 0xff 39 | byteArray [ index ] = byte 40 | valueToConvert = (valueToConvert - byte) / 256 41 | } 42 | 43 | return byteArray; 44 | } 45 | 46 | function getArray(value){ 47 | var r_value = longToByteArray(value) 48 | var arr_salt = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 49 | return r_value.concat(arr_salt) 50 | } 51 | 52 | function getListOfArrays(inputArray){ 53 | returnVal = [] 54 | for(var i=0;i { 154 | cb(exists) 155 | }) 156 | } 157 | } 158 | 159 | function checkForKeypairAndRunGenerateProof(multiOrSingle, cb){ 160 | checkAllFilesExist(multiOrSingle, function(exists){ 161 | if(exists==true){ 162 | automation.LoadProvingKey(multiOrSingle) 163 | cb() 164 | } else { 165 | console.log("\nThe provingKey and verificationKey need to be generated\n") 166 | automation.GenerateNewKeyPair(multiOrSingle, function(){ 167 | automation.LoadProvingKey(multiOrSingle) 168 | cb() 169 | }) 170 | } 171 | }) 172 | } 173 | 174 | function getPayment(incomingOrOutgoing, paymentNo, cb){ 175 | (function getOnePrompt() { 176 | try { 177 | var schema = { 178 | properties: { 179 | payment: { 180 | type: 'integer', 181 | description: incomingOrOutgoing=='Incoming'?colors.green("Incoming payment number " + (paymentNo+1)):colors.red("Outgoing payment number " + (paymentNo+1)), 182 | required: true 183 | } 184 | } 185 | } 186 | prompt.get(schema, function(err, paymentAmountInputs){ 187 | if (err) { cb(err); return } 188 | if(incomingOrOutgoing=='Incoming'){ 189 | incoming[paymentNo] = parseInt(paymentAmountInputs.payment) 190 | } else { 191 | outgoing[paymentNo] = parseInt(paymentAmountInputs.payment) 192 | } 193 | paymentNo++ 194 | if(paymentNo < noPayments){ 195 | getOnePrompt() 196 | } else { 197 | cb() 198 | } 199 | }) 200 | } catch (exception) { 201 | cb(exception); 202 | } 203 | })(); 204 | } 205 | 206 | function handleGenerateMultiPaymentProof(cb){ 207 | fs.unlink('proof_multi', function(error) { 208 | automation.SetProofCodeBlocking(true) 209 | console.log('Please enter the amounts that are being paid') 210 | 211 | var paymentCount = 0; 212 | getPayment('Incoming', 0, function(incomingErr){ 213 | if(incomingErr){ 214 | console.log(incomingErr) 215 | cb() 216 | } else { 217 | getPayment('Outgoing', 0, function(outgoingErr){ 218 | if(outgoingErr){ 219 | console.log(outgoingErr) 220 | cb() 221 | } else { 222 | endBalance = startBalance 223 | for(var i=0;i 1){ //We use ids greater than 1 for the simulator 391 | var unconfirmedPayment = getUnconfirmedPaymentById(payment_id) 392 | unconfirmedPayment.status = 'Generating proof' 393 | } 394 | } 395 | 396 | var onProofGenerationComplete = function (payment_id) { 397 | automation.SetProofCodeBlocking(false) 398 | if(payment_id > 1){ //We use ids greater than 1 for the simulator 399 | automation.VerifyProof('single', payment_id, function(verifyErr){ 400 | if(verifyErr){ 401 | console.log(verifyErr) 402 | } else { 403 | // proof is verified, get the right payment 404 | var unconfirmedPayment = getUnconfirmedPaymentById(payment_id) 405 | unconfirmedPayment.status = 'verifying proof' 406 | // console.log('payment: ', unconfirmedPayment) 407 | if(unconfirmedPayment.direction=='incoming'){ 408 | startBalance = startBalance + unconfirmedPayment.amount 409 | availableLiquidity = availableLiquidity + unconfirmedPayment.amount 410 | } else { 411 | startBalance = startBalance - unconfirmedPayment.amount 412 | } 413 | unconfirmedPayments = removeUnconfimedPayment(payment_id) 414 | } 415 | }) 416 | } 417 | } 418 | 419 | automation.Events.on('proofGenerationStarted', onProofGenerationStarted); 420 | automation.Events.on('proofGenerationComplete', onProofGenerationComplete); 421 | 422 | function generateSinglePaymentProofForSimulation(newPayment){ 423 | unconfirmedPayments.push(newPayment) 424 | if(newPayment.direction=='incoming'){ 425 | incoming[0] = newPayment.amount 426 | outgoing[0] = 0 427 | endBalance = startBalance + newPayment.amount 428 | } else { 429 | incoming[0] = 0 430 | outgoing[0] = newPayment.amount 431 | endBalance = startBalance - newPayment.amount 432 | } 433 | generateProofInputs('single', paymentId, function(msg, err){ 434 | if(err){ 435 | console.log('Error generating proof inputs') 436 | } else { 437 | //write the proof inputs (single proof) 438 | automation.GenerateProof('single', paymentId) 439 | } 440 | }) 441 | } 442 | function createANewPayment(){ 443 | var randomNumberBetween0and2000 = Math.floor(Math.random() * 2000) 444 | var inOut = Math.floor(Math.random() + 0.5) == 0 ? "incoming" : "outgoing" 445 | paymentId++ 446 | if(inOut=="incoming"){ 447 | var queuedRandom = Math.floor(Math.random() + 0.5) == 0 ? "queued" : "not-queued" 448 | if(queuedRandom == "queued"){ 449 | //queuedPayments.push({paymentId: paymentId, direction: inOut, amount: randomNumberBetween0and2000}) 450 | } else { 451 | var newPayment = {paymentId: paymentId, direction: inOut, amount: randomNumberBetween0and2000, status: 'Payment received - unconfirmed'} 452 | generateSinglePaymentProofForSimulation(newPayment) 453 | } 454 | } 455 | 456 | if(inOut=="outgoing" && randomNumberBetween0and2000 <= availableLiquidity){ 457 | var newPayment = {paymentId: paymentId, direction: inOut, amount: randomNumberBetween0and2000, status: 'Payment sent - unconfirmed'} 458 | availableLiquidity -= newPayment.amount 459 | generateSinglePaymentProofForSimulation(newPayment) 460 | } 461 | 462 | if(inOut=="outgoing" && randomNumberBetween0and2000 > startBalance){ 463 | //queuedPayments.push({paymentId: paymentId, direction: inOut, amount: randomNumberBetween0and2000}) 464 | } 465 | 466 | } 467 | 468 | automation.Events.on('proofGenerationStarted', function(paymentId){ 469 | if(startTime === null){ 470 | startTime = new Date().getTime() 471 | } 472 | proofsGenerated[paymentId] = { 473 | startTime: new Date().getTime(), 474 | elapsedTime: null 475 | } 476 | }); 477 | 478 | automation.Events.on('proofGenerationComplete', function(paymentId){ 479 | numberOfProofsGenerated++ 480 | let elapsedTime = new Date().getTime() - proofsGenerated[paymentId].startTime 481 | proofsGenerated[paymentId].elapsedTime = elapsedTime 482 | }); 483 | 484 | automation.Events.on('proofVerificationStarted', function(paymentId){ 485 | if(startTime === null){ 486 | startTime = new Date().getTime() 487 | } 488 | proofsVerified[paymentId] = { 489 | startTime: new Date().getTime(), 490 | elapsedTime: null 491 | } 492 | }); 493 | 494 | automation.Events.on('proofVerificationComplete', function(paymentId){ 495 | numberOfProofsVerified++ 496 | let elapsedTime = new Date().getTime() - proofsVerified[paymentId].startTime 497 | proofsVerified[paymentId].elapsedTime = elapsedTime 498 | }); 499 | 500 | function handleSimulator(){ 501 | if(automation.GetProofCodeBlocking()==true){ 502 | process.stdout.write('.') 503 | setTimeout(handleSimulator, 500) 504 | } else { 505 | console.log('\033[2J') 506 | if(startTime !== null){ 507 | let elapsedTime = 0 508 | for(let key in proofsGenerated){ 509 | let generation = proofsGenerated[key] 510 | if(generation.elapsedTime !== null){ 511 | elapsedTime += generation.elapsedTime 512 | } 513 | } 514 | let proofsPerSecond = numberOfProofsGenerated/(elapsedTime/1000) 515 | console.log('proof per second:', proofsPerSecond.toFixed(3), 516 | '| seconds per proof', (1/proofsPerSecond).toFixed(3)) 517 | elapsedTime = 0 518 | for(let key in proofsVerified){ 519 | let verification = proofsVerified[key] 520 | if(verification.elapsedTime !== null){ 521 | elapsedTime += verification.elapsedTime 522 | } 523 | } 524 | verificationPerSecond = numberOfProofsVerified/(elapsedTime/1000) 525 | console.log('verifications per second:', verificationPerSecond.toFixed(3), 526 | '| seconds per verification', (1/verificationPerSecond).toFixed(3)) 527 | } 528 | console.log('Current balance:', startBalance) 529 | console.log('Available liquidity:', availableLiquidity) 530 | console.log(statusColor('Status: ', simulatorStatus)) 531 | console.log() 532 | console.log(colors.green.underline('Unconfimed payments')) 533 | for(var i=0; i> 56, bitlen >> 48, bitlen >> 40, bitlen >> 32, // message length 20 | bitlen >> 24, bitlen >> 16, bitlen >> 8, bitlen 21 | }; 22 | 23 | std::vector padding_bv(256); 24 | 25 | convertBytesToVector(padding, padding_bv); 26 | 27 | printVector(padding_bv); 28 | */ 29 | bool sha256_padding[256] = {1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 32 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 33 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 34 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 35 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 36 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0}; 37 | 38 | template 39 | class l_gadget : public gadget { 40 | public: 41 | pb_variable_array input_as_field_elements; /* R1CS input */ 42 | pb_variable_array input_as_bits; /* unpacked R1CS input */ 43 | 44 | /* R1CS constraints for computing sum_i 2^i *x_i where [x_i] is bit-array */ 45 | 46 | pb_variable_array intermediate_val1; 47 | pb_variable_array intermediate_val2; 48 | pb_variable_array intermediate_val3; 49 | pb_variable_array intermediate_val4; 50 | 51 | std::shared_ptr > unpack_inputs; /* multipacking gadget */ 52 | 53 | std::shared_ptr> h1_var; /* H(R1) */ 54 | std::shared_ptr> h2_var; /* H(R2) */ 55 | std::shared_ptr> h3_var; /* H(R3) */ 56 | std::shared_ptr> h4_var; /* H(R4) */ 57 | 58 | std::shared_ptr> r1_var; /* R1 */ 59 | std::shared_ptr> r2_var; /* R2 */ 60 | std::shared_ptr> r3_var; /* R3 */ 61 | std::shared_ptr> r4_var; /* R4 */ 62 | 63 | std::shared_ptr> h_r1_block; /* 512 bit block that contains r1 + padding */ 64 | std::shared_ptr> h_r1; /* hashing gadget for r1 */ 65 | 66 | std::shared_ptr> h_r2_block; /* 512 bit block that contains r2 + padding */ 67 | std::shared_ptr> h_r2; /* hashing gadget for r2 */ 68 | 69 | std::shared_ptr> h_r3_block; /* 512 bit block that contains r3 + padding */ 70 | std::shared_ptr> h_r3; /* hashing gadget for r3 */ 71 | 72 | std::shared_ptr> h_r4_block; /* 512 bit block that contains r4 + padding */ 73 | std::shared_ptr> h_r4; /* hashing gadget for r4 */ 74 | 75 | pb_variable zero; 76 | pb_variable_array padding_var; /* SHA256 length padding */ 77 | 78 | 79 | l_gadget(protoboard &pb) : gadget(pb, "l_gadget") 80 | { 81 | // Allocate space for the verifier input. 82 | const size_t input_size_in_bits = sha256_digest_len * 4; 83 | { 84 | // We use a "multipacking" technique which allows us to constrain 85 | // the input bits in as few field elements as possible. 86 | const size_t input_size_in_field_elements = div_ceil(input_size_in_bits, FieldT::capacity()); 87 | 88 | std::cout << "**************** input_size_in_field_elements: " << input_size_in_field_elements << "\n"; 89 | std::cout << "**************** FieldT::capacity(): " << FieldT::capacity() << "\n"; 90 | input_as_field_elements.allocate(pb, input_size_in_field_elements, "input_as_field_elements"); 91 | this->pb.set_input_sizes(input_size_in_field_elements); 92 | } 93 | 94 | zero.allocate(this->pb, FMT(this->annotation_prefix, "zero")); 95 | 96 | intermediate_val1.allocate(this->pb, sha256_digest_len/2, "intermediate_val1"); 97 | intermediate_val2.allocate(this->pb, sha256_digest_len/2, "intermediate_val2"); 98 | intermediate_val3.allocate(this->pb, sha256_digest_len/2, "intermediate_val3"); 99 | intermediate_val4.allocate(this->pb, sha256_digest_len/2, "intermediate_val4"); 100 | 101 | // SHA256's length padding 102 | for (size_t i = 0; i < 256; i++) { 103 | if (sha256_padding[i]) 104 | padding_var.emplace_back(ONE); 105 | else 106 | padding_var.emplace_back(zero); 107 | } 108 | 109 | // Verifier (and prover) inputs: 110 | h1_var.reset(new digest_variable(pb, sha256_digest_len, "h1")); 111 | h2_var.reset(new digest_variable(pb, sha256_digest_len, "h2")); 112 | h3_var.reset(new digest_variable(pb, sha256_digest_len, "h3")); 113 | h4_var.reset(new digest_variable(pb, sha256_digest_len, "h4")); 114 | 115 | input_as_bits.insert(input_as_bits.end(), h1_var->bits.begin(), h1_var->bits.end()); 116 | input_as_bits.insert(input_as_bits.end(), h2_var->bits.begin(), h2_var->bits.end()); 117 | input_as_bits.insert(input_as_bits.end(), h3_var->bits.begin(), h3_var->bits.end()); 118 | input_as_bits.insert(input_as_bits.end(), h4_var->bits.begin(), h4_var->bits.end()); 119 | 120 | // Multipacking 121 | assert(input_as_bits.size() == input_size_in_bits); 122 | unpack_inputs.reset(new multipacking_gadget(this->pb, input_as_bits, input_as_field_elements, FieldT::capacity(), FMT(this->annotation_prefix, " unpack_inputs"))); 123 | 124 | // Prover inputs: 125 | r1_var.reset(new digest_variable(pb, sha256_digest_len, "r1")); 126 | r2_var.reset(new digest_variable(pb, sha256_digest_len, "r2")); 127 | r3_var.reset(new digest_variable(pb, sha256_digest_len, "r3")); 128 | r4_var.reset(new digest_variable(pb, sha256_digest_len, "r4")); 129 | 130 | // IV for SHA256 131 | pb_linear_combination_array IV = SHA256_default_IV(pb); 132 | 133 | // Initialize the block gadget for r1's hash 134 | h_r1_block.reset(new block_variable(pb, { 135 | r1_var->bits, 136 | padding_var 137 | }, "h_r1_block")); 138 | 139 | // Initialize the hash gadget for r1's hash 140 | h_r1.reset(new sha256_compression_function_gadget(pb, 141 | IV, 142 | h_r1_block->bits, 143 | *h1_var, 144 | "h_r1")); 145 | 146 | // Initialize the block gadget for r2's hash 147 | h_r2_block.reset(new block_variable(pb, { 148 | r2_var->bits, 149 | padding_var 150 | }, "h_r2_block")); 151 | 152 | // Initialize the hash gadget for r2's hash 153 | h_r2.reset(new sha256_compression_function_gadget(pb, 154 | IV, 155 | h_r2_block->bits, 156 | *h2_var, 157 | "h_r2")); 158 | 159 | // Initialize the block gadget for r3's hash 160 | h_r3_block.reset(new block_variable(pb, { 161 | r3_var->bits, 162 | padding_var 163 | }, "h_r3_block")); 164 | 165 | // Initialize the hash gadget for r3's hash 166 | h_r3.reset(new sha256_compression_function_gadget(pb, 167 | IV, 168 | h_r3_block->bits, 169 | *h3_var, 170 | "h_r3")); 171 | 172 | // Initialize the block gadget for r4's hash 173 | h_r4_block.reset(new block_variable(pb, { 174 | r4_var->bits, 175 | padding_var 176 | }, "h_r4_block")); 177 | 178 | // Initialize the hash gadget for r4's hash 179 | h_r4.reset(new sha256_compression_function_gadget(pb, 180 | IV, 181 | h_r4_block->bits, 182 | *h4_var, 183 | "h_r4")); 184 | } 185 | void generate_r1cs_constraints() 186 | { 187 | // Multipacking constraints (for input validation) 188 | unpack_inputs->generate_r1cs_constraints(true); 189 | 190 | // Ensure bitness of the digests. Bitness of the inputs 191 | // is established by `unpack_inputs->generate_r1cs_constraints(true)` 192 | r1_var->generate_r1cs_constraints(); 193 | r2_var->generate_r1cs_constraints(); 194 | r3_var->generate_r1cs_constraints(); 195 | r4_var->generate_r1cs_constraints(); 196 | 197 | generate_r1cs_equals_const_constraint(this->pb, zero, FieldT::zero(), "zero"); 198 | 199 | unsigned int NN = sha256_digest_len/2; 200 | // a = intermediate_val 201 | // Constraint a[0] = r[0] 202 | this->pb.add_r1cs_constraint( 203 | r1cs_constraint( 204 | intermediate_val1[0], 205 | 1, 206 | r1_var->bits[0]), 207 | FMT(this->annotation_prefix, " zero1_%zu", 0)); 208 | 209 | this->pb.add_r1cs_constraint( 210 | r1cs_constraint( 211 | intermediate_val2[0], 212 | 1, 213 | r2_var->bits[0]), 214 | FMT(this->annotation_prefix, " zero2_%zu", 0)); 215 | 216 | this->pb.add_r1cs_constraint( 217 | r1cs_constraint( 218 | intermediate_val3[0], 219 | 1, 220 | r3_var->bits[0]), 221 | FMT(this->annotation_prefix, " zero3_%zu", 0)); 222 | 223 | this->pb.add_r1cs_constraint( 224 | r1cs_constraint( 225 | intermediate_val4[0], 226 | 1, 227 | r4_var->bits[0]), 228 | FMT(this->annotation_prefix, " zero4_%zu", 0)); 229 | 230 | for (unsigned int i = 1; i < NN; i++) { 231 | // a[i] = 2*a[i-1] + r[i] 232 | // 233 | // Constraint containing the intermediate steps in the calculation 234 | // a[NN-1] = \sum_{i=0}^{NN-1} 2^i * r[NN-1-i] 235 | this->pb.add_r1cs_constraint( 236 | r1cs_constraint( 237 | { intermediate_val1[i] }, 238 | { ONE }, 239 | { intermediate_val1[i-1] * 2 , r1_var->bits[i] }), 240 | FMT(this->annotation_prefix, " sum1_%zu", i)); 241 | 242 | this->pb.add_r1cs_constraint( 243 | r1cs_constraint( 244 | { intermediate_val2[i] }, 245 | { 1 }, 246 | { intermediate_val2[i-1] * 2, r2_var->bits[i] }), 247 | FMT(this->annotation_prefix, " sum2_%zu", i)); 248 | 249 | this->pb.add_r1cs_constraint( 250 | r1cs_constraint( 251 | { intermediate_val3[i] }, 252 | { 1 }, 253 | { intermediate_val3[i-1] * 2, r3_var->bits[i] }), 254 | FMT(this->annotation_prefix, " sum3_%zu", i)); 255 | 256 | this->pb.add_r1cs_constraint( 257 | r1cs_constraint( 258 | { intermediate_val4[i] }, 259 | { 1 }, 260 | { intermediate_val4[i-1] * 2, r4_var->bits[i] }), 261 | FMT(this->annotation_prefix, " sum4_%zu", i)); 262 | } 263 | 264 | 265 | // Constraint that r1 + r3 = r2 + r4 (start bal + incoming = end bal + outgoing) 266 | this->pb.add_r1cs_constraint( 267 | r1cs_constraint( 268 | { intermediate_val1[NN-1], intermediate_val3[NN-1]}, 269 | { 1 }, 270 | { intermediate_val2[NN-1], intermediate_val4[NN-1]}), 271 | FMT(this->annotation_prefix, "finalsum_%zu", 0)); 272 | 273 | 274 | // These are the constraints to ensure the hashes validate. 275 | h_r1->generate_r1cs_constraints(); 276 | h_r2->generate_r1cs_constraints(); 277 | h_r3->generate_r1cs_constraints(); 278 | h_r4->generate_r1cs_constraints(); 279 | } 280 | 281 | void generate_r1cs_witness(const bit_vector &h1, 282 | const bit_vector &h2, 283 | const bit_vector &h3, 284 | const bit_vector &h4, 285 | const bit_vector &r1, 286 | const bit_vector &r2, 287 | const bit_vector &r3, 288 | const bit_vector &r4 289 | ) 290 | { 291 | // Fill our digests with our witnessed data 292 | r1_var->bits.fill_with_bits(this->pb, r1); 293 | r2_var->bits.fill_with_bits(this->pb, r2); 294 | r3_var->bits.fill_with_bits(this->pb, r3); 295 | r4_var->bits.fill_with_bits(this->pb, r4); 296 | 297 | size_t NN = sha256_digest_len/2; 298 | 299 | std::vector interm1(NN); 300 | std::vector interm2(NN); 301 | std::vector interm3(NN); 302 | std::vector interm4(NN); 303 | 304 | interm1[0] = r1[0] ? FieldT::one() : FieldT::zero(); 305 | interm2[0] = r2[0] ? FieldT::one() : FieldT::zero(); 306 | interm3[0] = r3[0] ? FieldT::one() : FieldT::zero(); 307 | interm4[0] = r4[0] ? FieldT::one() : FieldT::zero(); 308 | 309 | for (size_t i=1; ipb, interm1); 317 | intermediate_val2.fill_with_field_elements(this->pb, interm2); 318 | intermediate_val3.fill_with_field_elements(this->pb, interm3); 319 | intermediate_val4.fill_with_field_elements(this->pb, interm4); 320 | 321 | // Set the zero pb_variable to zero 322 | this->pb.val(zero) = FieldT::zero(); 323 | 324 | // Generate witnesses as necessary in our other gadgets 325 | h_r1->generate_r1cs_witness(); 326 | h_r2->generate_r1cs_witness(); 327 | h_r3->generate_r1cs_witness(); 328 | h_r4->generate_r1cs_witness(); 329 | unpack_inputs->generate_r1cs_witness_from_bits(); 330 | 331 | h1_var->bits.fill_with_bits(this->pb, h1); 332 | h2_var->bits.fill_with_bits(this->pb, h2); 333 | h3_var->bits.fill_with_bits(this->pb, h3); 334 | h4_var->bits.fill_with_bits(this->pb, h4); 335 | } 336 | }; 337 | 338 | template 339 | r1cs_primary_input l_input_map(const bit_vector &h1, 340 | const bit_vector &h2, 341 | const bit_vector &h3, 342 | const bit_vector &h4 343 | ) 344 | { 345 | // Construct the multipacked field points which encode 346 | // the verifier's knowledge. This is the "dual" of the 347 | // multipacking gadget logic in the constructor. 348 | 349 | assert(h1.size() == sha256_digest_len); 350 | assert(h2.size() == sha256_digest_len); 351 | assert(h3.size() == sha256_digest_len); 352 | assert(h4.size() == sha256_digest_len); 353 | 354 | std::cout << "**** After assert(size() == sha256_digest_len) *****" << std::endl; 355 | 356 | bit_vector input_as_bits; 357 | input_as_bits.insert(input_as_bits.end(), h1.begin(), h1.end()); 358 | input_as_bits.insert(input_as_bits.end(), h2.begin(), h2.end()); 359 | input_as_bits.insert(input_as_bits.end(), h3.begin(), h3.end()); 360 | input_as_bits.insert(input_as_bits.end(), h4.begin(), h4.end()); 361 | std::vector input_as_field_elements = pack_bit_vector_into_field_element_vector(input_as_bits); 362 | 363 | std::cout << "**** After pack_bit_vector_into_field_element_vector *****" << std::endl; 364 | 365 | return input_as_field_elements; 366 | } 367 | -------------------------------------------------------------------------------- /src/payment_in_out_gadget.hpp: -------------------------------------------------------------------------------- 1 | #include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" 2 | #include "libff/algebra/fields/field_utils.hpp" 3 | 4 | using namespace libff; 5 | 6 | const size_t sha256_digest_len1 = 256; 7 | 8 | bool sha256_padding1[256] = {1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 9 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 10 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 11 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 12 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0}; 16 | 17 | template 18 | class payment_in_out_gadget : public gadget { 19 | public: 20 | pb_variable_array input_as_field_elements; /* R1CS input */ 21 | pb_variable_array input_as_bits; /* unpacked R1CS input */ 22 | 23 | /* R1CS constraints for computing sum_i 2^i *x_i where [x_i] is bit-array */ 24 | 25 | pb_variable_array intermediate_startBalance; 26 | pb_variable_array intermediate_endBalance; 27 | pb_variable_array intermediate_incoming; 28 | pb_variable_array intermediate_outgoing; 29 | 30 | std::shared_ptr > unpack_inputs; /* multipacking gadget */ 31 | 32 | std::shared_ptr> h1_var; /* H(R1) */ 33 | std::shared_ptr> h2_var; /* H(R2) */ 34 | std::shared_ptr> h3_var; /* H(R3) */ 35 | std::shared_ptr> h4_var; /* H(R4) */ 36 | 37 | std::shared_ptr> r1_var; /* R1 */ 38 | std::shared_ptr> r2_var; /* R2 */ 39 | std::shared_ptr> r3_var; /* R3 */ 40 | std::shared_ptr> r4_var; /* R4 */ 41 | 42 | std::shared_ptr> h_r1_block; /* 512 bit block that contains r1 + padding */ 43 | std::shared_ptr> h_r1; /* hashing gadget for r1 */ 44 | 45 | std::shared_ptr> h_r2_block; /* 512 bit block that contains r2 + padding */ 46 | std::shared_ptr> h_r2; /* hashing gadget for r2 */ 47 | 48 | std::shared_ptr> h_r3_block; /* 512 bit block that contains r3 + padding */ 49 | std::shared_ptr> h_r3; /* hashing gadget for r3 */ 50 | 51 | std::shared_ptr> h_r4_block; /* 512 bit block that contains r4 + padding */ 52 | std::shared_ptr> h_r4; /* hashing gadget for r4 */ 53 | 54 | pb_variable zero; 55 | pb_variable_array padding_var; /* SHA256 length padding */ 56 | 57 | 58 | payment_in_out_gadget(protoboard &pb) : gadget(pb, "payment_in_out_gadget") 59 | { 60 | // Allocate space for the verifier input. 61 | const size_t input_size_in_bits = sha256_digest_len1 * 4; 62 | { 63 | // We use a "multipacking" technique which allows us to constrain 64 | // the input bits in as few field elements as possible. 65 | const size_t input_size_in_field_elements = div_ceil(input_size_in_bits, FieldT::capacity()); 66 | 67 | std::cout << "**************** input_size_in_field_elements: " << input_size_in_field_elements << "\n"; 68 | std::cout << "**************** FieldT::capacity(): " << FieldT::capacity() << "\n"; 69 | input_as_field_elements.allocate(pb, input_size_in_field_elements, "input_as_field_elements"); 70 | this->pb.set_input_sizes(input_size_in_field_elements); 71 | } 72 | 73 | zero.allocate(this->pb, FMT(this->annotation_prefix, "zero")); 74 | 75 | intermediate_startBalance.allocate(this->pb, sha256_digest_len1/2, "intermediate_startBalance"); 76 | intermediate_endBalance.allocate(this->pb, sha256_digest_len1/2, "intermediate_endBalance"); 77 | intermediate_incoming.allocate(this->pb, sha256_digest_len1/2, "intermediate_incoming"); 78 | intermediate_outgoing.allocate(this->pb, sha256_digest_len1/2, "intermediate_outgoing"); 79 | 80 | // SHA256's length padding 81 | for (size_t i = 0; i < 256; i++) { 82 | if (sha256_padding1[i]) 83 | padding_var.emplace_back(ONE); 84 | else 85 | padding_var.emplace_back(zero); 86 | } 87 | 88 | // Verifier (and prover) inputs: 89 | h1_var.reset(new digest_variable(pb, sha256_digest_len1, "h1")); 90 | h2_var.reset(new digest_variable(pb, sha256_digest_len1, "h2")); 91 | h3_var.reset(new digest_variable(pb, sha256_digest_len1, "h3")); 92 | h4_var.reset(new digest_variable(pb, sha256_digest_len1, "h4")); 93 | 94 | input_as_bits.insert(input_as_bits.end(), h1_var->bits.begin(), h1_var->bits.end()); 95 | input_as_bits.insert(input_as_bits.end(), h2_var->bits.begin(), h2_var->bits.end()); 96 | input_as_bits.insert(input_as_bits.end(), h3_var->bits.begin(), h3_var->bits.end()); 97 | input_as_bits.insert(input_as_bits.end(), h4_var->bits.begin(), h4_var->bits.end()); 98 | 99 | // Multipacking 100 | assert(input_as_bits.size() == input_size_in_bits); 101 | unpack_inputs.reset(new multipacking_gadget(this->pb, input_as_bits, input_as_field_elements, FieldT::capacity(), FMT(this->annotation_prefix, " unpack_inputs"))); 102 | 103 | // Prover inputs: 104 | r1_var.reset(new digest_variable(pb, sha256_digest_len1, "r1")); 105 | r2_var.reset(new digest_variable(pb, sha256_digest_len1, "r2")); 106 | r3_var.reset(new digest_variable(pb, sha256_digest_len1, "r3")); 107 | r4_var.reset(new digest_variable(pb, sha256_digest_len1, "r4")); 108 | 109 | // IV for SHA256 110 | pb_linear_combination_array IV = SHA256_default_IV(pb); 111 | 112 | // Initialize the block gadget for r1's hash 113 | h_r1_block.reset(new block_variable(pb, { 114 | r1_var->bits, 115 | padding_var 116 | }, "h_r1_block")); 117 | 118 | // Initialize the hash gadget for r1's hash 119 | h_r1.reset(new sha256_compression_function_gadget(pb, 120 | IV, 121 | h_r1_block->bits, 122 | *h1_var, 123 | "h_r1")); 124 | 125 | // Initialize the block gadget for r2's hash 126 | h_r2_block.reset(new block_variable(pb, { 127 | r2_var->bits, 128 | padding_var 129 | }, "h_r2_block")); 130 | 131 | // Initialize the hash gadget for r2's hash 132 | h_r2.reset(new sha256_compression_function_gadget(pb, 133 | IV, 134 | h_r2_block->bits, 135 | *h2_var, 136 | "h_r2")); 137 | 138 | // Initialize the block gadget for r3's hash 139 | h_r3_block.reset(new block_variable(pb, { 140 | r3_var->bits, 141 | padding_var 142 | }, "h_r3_block")); 143 | 144 | // Initialize the hash gadget for r3's hash 145 | h_r3.reset(new sha256_compression_function_gadget(pb, 146 | IV, 147 | h_r3_block->bits, 148 | *h3_var, 149 | "h_r3")); 150 | 151 | // Initialize the block gadget for r4's hash 152 | h_r4_block.reset(new block_variable(pb, { 153 | r4_var->bits, 154 | padding_var 155 | }, "h_r4_block")); 156 | 157 | // Initialize the hash gadget for r4's hash 158 | h_r4.reset(new sha256_compression_function_gadget(pb, 159 | IV, 160 | h_r4_block->bits, 161 | *h4_var, 162 | "h_r4")); 163 | } 164 | void generate_payment_in_out_constraints() 165 | { 166 | // Multipacking constraints (for input validation) 167 | unpack_inputs->generate_r1cs_constraints(true); 168 | 169 | // Ensure bitness of the digests. Bitness of the inputs 170 | // is established by `unpack_inputs->generate_r1cs_constraints(true)` 171 | r1_var->generate_r1cs_constraints(); 172 | r2_var->generate_r1cs_constraints(); 173 | r3_var->generate_r1cs_constraints(); 174 | r4_var->generate_r1cs_constraints(); 175 | 176 | generate_r1cs_equals_const_constraint(this->pb, zero, FieldT::zero(), "zero"); 177 | 178 | unsigned int NN = sha256_digest_len1/2; 179 | // a = intermediate_val 180 | // Constraint a[0] = r[0] 181 | this->pb.add_r1cs_constraint( 182 | r1cs_constraint( 183 | intermediate_startBalance[0], 184 | 1, 185 | r1_var->bits[0]), 186 | FMT(this->annotation_prefix, " zero1_%zu", 0)); 187 | 188 | this->pb.add_r1cs_constraint( 189 | r1cs_constraint( 190 | intermediate_endBalance[0], 191 | 1, 192 | r2_var->bits[0]), 193 | FMT(this->annotation_prefix, " zero2_%zu", 0)); 194 | 195 | this->pb.add_r1cs_constraint( 196 | r1cs_constraint( 197 | intermediate_incoming[0], 198 | 1, 199 | r3_var->bits[0]), 200 | FMT(this->annotation_prefix, " zero3_%zu", 0)); 201 | 202 | this->pb.add_r1cs_constraint( 203 | r1cs_constraint( 204 | intermediate_outgoing[0], 205 | 1, 206 | r4_var->bits[0]), 207 | FMT(this->annotation_prefix, " zero4_%zu", 0)); 208 | 209 | for (unsigned int i = 1; i < NN; i++) { 210 | // a[i] = 2*a[i-1] + r[i] 211 | // 212 | // Constraint containing the intermediate steps in the calculation 213 | // a[NN-1] = \sum_{i=0}^{NN-1} 2^i * r[NN-1-i] 214 | this->pb.add_r1cs_constraint( 215 | r1cs_constraint( 216 | { intermediate_startBalance[i] }, 217 | { ONE }, 218 | { intermediate_startBalance[i-1] * 2 , r1_var->bits[i] }), 219 | FMT(this->annotation_prefix, " sum1_%zu", i)); 220 | 221 | this->pb.add_r1cs_constraint( 222 | r1cs_constraint( 223 | { intermediate_endBalance[i] }, 224 | { 1 }, 225 | { intermediate_endBalance[i-1] * 2, r2_var->bits[i] }), 226 | FMT(this->annotation_prefix, " sum2_%zu", i)); 227 | 228 | this->pb.add_r1cs_constraint( 229 | r1cs_constraint( 230 | { intermediate_incoming[i] }, 231 | { 1 }, 232 | { intermediate_incoming[i-1] * 2, r3_var->bits[i] }), 233 | FMT(this->annotation_prefix, " sum3_%zu", i)); 234 | 235 | this->pb.add_r1cs_constraint( 236 | r1cs_constraint( 237 | { intermediate_outgoing[i] }, 238 | { 1 }, 239 | { intermediate_outgoing[i-1] * 2, r4_var->bits[i] }), 240 | FMT(this->annotation_prefix, " sum4_%zu", i)); 241 | } 242 | 243 | 244 | // Constraint that start bal + incoming = end bal + outgoing 245 | this->pb.add_r1cs_constraint( 246 | r1cs_constraint( 247 | { intermediate_startBalance[NN-1], intermediate_incoming[NN-1]}, 248 | { 1 }, 249 | { intermediate_endBalance[NN-1], intermediate_outgoing[NN-1]}), 250 | FMT(this->annotation_prefix, "finalsum_%zu", 0)); 251 | 252 | 253 | // These are the constraints to ensure the hashes validate. 254 | h_r1->generate_r1cs_constraints(); 255 | h_r2->generate_r1cs_constraints(); 256 | h_r3->generate_r1cs_constraints(); 257 | h_r4->generate_r1cs_constraints(); 258 | } 259 | 260 | void generate_payment_in_out_witness(const bit_vector &h_startBalance, 261 | const bit_vector &h_endBalance, 262 | const bit_vector &h_incoming, 263 | const bit_vector &h_outgoing, 264 | const bit_vector &r_startBalance, 265 | const bit_vector &r_endBalance, 266 | const bit_vector &r_incoming, 267 | const bit_vector &r_outgoing 268 | ) 269 | { 270 | // Fill our digests with our witnessed data 271 | r1_var->bits.fill_with_bits(this->pb, r_startBalance); 272 | r2_var->bits.fill_with_bits(this->pb, r_endBalance); 273 | r3_var->bits.fill_with_bits(this->pb, r_incoming); 274 | r4_var->bits.fill_with_bits(this->pb, r_outgoing); 275 | 276 | size_t NN = sha256_digest_len1/2; 277 | 278 | std::vector interm1(NN); 279 | std::vector interm2(NN); 280 | std::vector interm3(NN); 281 | std::vector interm4(NN); 282 | 283 | interm1[0] = r_startBalance[0] ? FieldT::one() : FieldT::zero(); 284 | interm2[0] = r_endBalance[0] ? FieldT::one() : FieldT::zero(); 285 | interm3[0] = r_incoming[0] ? FieldT::one() : FieldT::zero(); 286 | interm4[0] = r_outgoing[0] ? FieldT::one() : FieldT::zero(); 287 | 288 | for (size_t i=1; ipb, interm1); 296 | intermediate_endBalance.fill_with_field_elements(this->pb, interm2); 297 | intermediate_incoming.fill_with_field_elements(this->pb, interm3); 298 | intermediate_outgoing.fill_with_field_elements(this->pb, interm4); 299 | 300 | // Set the zero pb_variable to zero 301 | this->pb.val(zero) = FieldT::zero(); 302 | 303 | // Generate witnesses as necessary in our other gadgets 304 | h_r1->generate_r1cs_witness(); 305 | h_r2->generate_r1cs_witness(); 306 | h_r3->generate_r1cs_witness(); 307 | h_r4->generate_r1cs_witness(); 308 | unpack_inputs->generate_r1cs_witness_from_bits(); 309 | 310 | h1_var->bits.fill_with_bits(this->pb, h_startBalance); 311 | h2_var->bits.fill_with_bits(this->pb, h_endBalance); 312 | h3_var->bits.fill_with_bits(this->pb, h_incoming); 313 | h4_var->bits.fill_with_bits(this->pb, h_outgoing); 314 | } 315 | }; 316 | 317 | template 318 | r1cs_primary_input l_input_map(const bit_vector &h1, 319 | const bit_vector &h2, 320 | const bit_vector &h3, 321 | const bit_vector &h4 322 | ) 323 | { 324 | // Construct the multipacked field points which encode 325 | // the verifier's knowledge. This is the "dual" of the 326 | // multipacking gadget logic in the constructor. 327 | 328 | assert(h1.size() == sha256_digest_len1); 329 | assert(h2.size() == sha256_digest_len1); 330 | assert(h3.size() == sha256_digest_len1); 331 | assert(h4.size() == sha256_digest_len1); 332 | 333 | std::cout << "**** After assert(size() == sha256_digest_len) *****" << std::endl; 334 | 335 | bit_vector input_as_bits; 336 | input_as_bits.insert(input_as_bits.end(), h1.begin(), h1.end()); 337 | input_as_bits.insert(input_as_bits.end(), h2.begin(), h2.end()); 338 | input_as_bits.insert(input_as_bits.end(), h3.begin(), h3.end()); 339 | input_as_bits.insert(input_as_bits.end(), h4.begin(), h4.end()); 340 | std::vector input_as_field_elements = pack_bit_vector_into_field_element_vector(input_as_bits); 341 | 342 | std::cout << "**** After pack_bit_vector_into_field_element_vector *****" << std::endl; 343 | 344 | return input_as_field_elements; 345 | } 346 | -------------------------------------------------------------------------------- /src/payment_in_out_generate_keypair.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "snark.hpp" 7 | 8 | using namespace libsnark; 9 | using namespace libff; 10 | using namespace std; 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | // Initialize the curve parameters. 15 | default_r1cs_ppzksnark_pp::init_public_params(); 16 | // Generate the verifying/proving keys. (This is trusted setup!) 17 | auto keypair = generate_keypair(); 18 | 19 | stringstream verificationKey; 20 | verificationKey << keypair.vk; 21 | 22 | ofstream fileOut; 23 | fileOut.open("verificationKey_single"); 24 | 25 | fileOut << verificationKey.rdbuf(); 26 | fileOut.close(); 27 | 28 | stringstream provingKey; 29 | provingKey << keypair.pk; 30 | 31 | fileOut.open("provingKey_single"); 32 | 33 | fileOut << provingKey.rdbuf(); 34 | fileOut.close(); 35 | 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/payment_in_out_generate_proof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "snark.hpp" 11 | #include "utils.cpp" 12 | 13 | using namespace libsnark; 14 | using namespace std; 15 | 16 | int genProof(r1cs_ppzksnark_proving_key provingKey_in, string proofFileName, string publicInputs, string privateInputs) 17 | { 18 | // Initialize bit_vectors for all of the variables involved. 19 | vector h_startBalance_bv(256); 20 | vector h_endBalance_bv(256); 21 | vector h_incoming_bv(256); 22 | vector h_outgoing_bv(256); 23 | vector r_startBalance_bv(256); 24 | vector r_endBalance_bv(256); 25 | vector r_incoming_bv(256); 26 | vector r_outgoing_bv(256); 27 | 28 | vector> publicValues = fillValuesFromfile(publicInputs); 29 | h_startBalance_bv = int_list_to_bits_local(publicValues[0], 8); 30 | h_endBalance_bv = int_list_to_bits_local(publicValues[1], 8); 31 | h_incoming_bv = int_list_to_bits_local(publicValues[2], 8); 32 | h_outgoing_bv = int_list_to_bits_local(publicValues[3], 8); 33 | 34 | vector> privateValues = fillValuesFromfile(privateInputs); 35 | r_startBalance_bv = int_list_to_bits_local(privateValues[0], 8); 36 | r_endBalance_bv = int_list_to_bits_local(privateValues[1], 8); 37 | r_incoming_bv = int_list_to_bits_local(privateValues[2], 8); 38 | r_outgoing_bv = int_list_to_bits_local(privateValues[3], 8); 39 | 40 | cout << "Starting proof generation for " + proofFileName << endl; 41 | boost::optional> proof = generate_payment_in_out_proof(provingKey_in, h_startBalance_bv, h_endBalance_bv, h_incoming_bv, h_outgoing_bv, r_startBalance_bv, r_endBalance_bv, r_incoming_bv, r_outgoing_bv); 42 | 43 | if(proof == boost::none) 44 | { 45 | return 1; 46 | } else { 47 | stringstream proofStream; 48 | proofStream << proof; 49 | 50 | ofstream fileOut; 51 | fileOut.open(proofFileName); 52 | 53 | fileOut << proofStream.rdbuf(); 54 | fileOut.close(); 55 | cout << "Proof was generated!!" << proofFileName << endl; 56 | return 0; 57 | } 58 | } 59 | 60 | int getUserInput(r1cs_ppzksnark_proving_key provingKey_in) 61 | { 62 | string inputTemp = ""; 63 | int result=0; 64 | cout << "Press enter paymentId to generate a proof or q to quit" << endl; 65 | cin >> inputTemp; 66 | cout << "Input from console: " << inputTemp << endl; 67 | if(inputTemp != "q") 68 | { 69 | string proofName = "proof_single_"; 70 | string proofNameWithId = proofName + inputTemp; 71 | string publicInputs = "publicInputParameters_single_"; 72 | string publicInputsWithId = publicInputs + inputTemp; 73 | string privateInputs = "privateInputParameters_single_"; 74 | string privateInputsWithId = privateInputs + inputTemp; 75 | 76 | cout << proofNameWithId << endl; 77 | result = genProof(provingKey_in, proofNameWithId, publicInputsWithId, privateInputsWithId); 78 | if(result!=0) 79 | { 80 | cout << "There was an error generating the proof" << endl; 81 | return getUserInput(provingKey_in); 82 | } 83 | else 84 | { 85 | return getUserInput(provingKey_in); 86 | } 87 | } 88 | else 89 | { 90 | return 0; 91 | } 92 | } 93 | 94 | 95 | int main(int argc, char *argv[]) 96 | { 97 | string keyFileName = "provingKey_single"; 98 | 99 | // Initialize the curve parameters. 100 | default_r1cs_ppzksnark_pp::init_public_params(); 101 | 102 | r1cs_ppzksnark_proving_key provingKey_in; 103 | 104 | ifstream fileIn(keyFileName); 105 | stringstream provingKeyFromFile; 106 | if (fileIn) { 107 | provingKeyFromFile << fileIn.rdbuf(); 108 | fileIn.close(); 109 | } 110 | 111 | provingKeyFromFile >> provingKey_in; 112 | 113 | return getUserInput(provingKey_in); 114 | } 115 | -------------------------------------------------------------------------------- /src/payment_in_out_verify_proof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "snark.hpp" 10 | #include "utils.cpp" 11 | 12 | using namespace libsnark; 13 | using namespace std; 14 | 15 | int verifyProof(r1cs_ppzksnark_verification_key verificationKey_in, string proofFileName, string publicInputs) 16 | { 17 | // Read proof in from file 18 | //libsnark::r1cs_ppzksnark_proof proof_in; 19 | boost::optional> proof_in; 20 | //r1cs_ppzksnark_proof proof_in; 21 | 22 | cout << proofFileName << endl; 23 | ifstream proofFileIn(proofFileName); 24 | stringstream proofFromFile; 25 | if (proofFileIn) { 26 | proofFromFile << proofFileIn.rdbuf(); 27 | proofFileIn.close(); 28 | } else { 29 | cout << "Failed to read from proof file" << endl; 30 | return 1; 31 | } 32 | 33 | proofFromFile >> proof_in; 34 | 35 | // Hashes to validate against 36 | std::vector h_startBalance_bv(256); 37 | std::vector h_endBalance_bv(256); 38 | std::vector h_incoming_bv(256); 39 | std::vector h_outgoing_bv(256); 40 | vector> values = fillValuesFromfile(publicInputs); 41 | h_startBalance_bv = int_list_to_bits_local(values[0], 8); 42 | h_endBalance_bv = int_list_to_bits_local(values[1], 8); 43 | h_incoming_bv = int_list_to_bits_local(values[2], 8); 44 | h_outgoing_bv = int_list_to_bits_local(values[3], 8); 45 | 46 | cout << "proof read ... starting verification" << endl; 47 | // Verify the proof 48 | bool isVerified = verify_payment_in_out_proof(verificationKey_in, *proof_in, h_startBalance_bv, h_endBalance_bv, h_incoming_bv, h_outgoing_bv); 49 | 50 | if(isVerified){ 51 | cout << "Proof was verified!!" << proofFileName << endl; 52 | return 0; 53 | } else { 54 | cout << "Proof was not verified!!" << proofFileName << endl; 55 | return 1; 56 | } 57 | } 58 | 59 | int main(int argc, char *argv[]) 60 | { 61 | // Initialize the curve parameters. 62 | default_r1cs_ppzksnark_pp::init_public_params(); 63 | 64 | // Read verification key in from file 65 | r1cs_ppzksnark_verification_key verificationKey_in; 66 | ifstream fileIn("verificationKey_single"); 67 | stringstream verificationKeyFromFile; 68 | if (fileIn) { 69 | verificationKeyFromFile << fileIn.rdbuf(); 70 | fileIn.close(); 71 | } 72 | verificationKeyFromFile >> verificationKey_in; 73 | 74 | string proofName = "proof_single_"; 75 | string proofNameWithId = proofName + argv[1]; 76 | string publicInputs = "publicInputParameters_single_"; 77 | string publicInputsWithId = publicInputs + argv[1]; 78 | return verifyProof(verificationKey_in, proofNameWithId, publicInputsWithId); 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/payment_multi_gadget.hpp: -------------------------------------------------------------------------------- 1 | #include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" 2 | #include "libff/algebra/fields/field_utils.hpp" 3 | #include 4 | 5 | 6 | using namespace libff; 7 | 8 | const size_t sha256_digest_len = 256; 9 | const int unsigned noIncomingPayments = 6; //Note, you must update the variables in the r1 constraint on line 288 10 | const int unsigned noOutgoingPayments = 6; 11 | int unsigned counter; 12 | 13 | bool sha256_padding[256] = {1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 16 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 17 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 18 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 19 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 20 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0}; 21 | 22 | template 23 | class payment_multi_gadget : public gadget { 24 | public: 25 | pb_variable_array input_as_field_elements; /* R1CS input */ 26 | pb_variable_array input_as_bits; /* unpacked R1CS input */ 27 | 28 | pb_variable_array intermediate_startBalance; 29 | pb_variable_array intermediate_endBalance; 30 | pb_variable_array intermediate_incoming[noIncomingPayments]; 31 | pb_variable_array intermediate_outgoing[noOutgoingPayments]; 32 | 33 | std::shared_ptr > unpack_inputs; /* multipacking gadget */ 34 | 35 | std::shared_ptr> h_startBalance_var; 36 | std::shared_ptr> h_endBalance_var; 37 | std::shared_ptr> h_incoming_var[noIncomingPayments]; 38 | std::shared_ptr> h_outgoing_var[noOutgoingPayments]; 39 | 40 | std::shared_ptr> r_startBalance_var; 41 | std::shared_ptr> r_endBalance_var; 42 | std::shared_ptr> r_incoming_var[noIncomingPayments]; 43 | std::shared_ptr> r_outgoing_var[noOutgoingPayments]; 44 | 45 | std::shared_ptr> h_r_startBalance_block; /* 512 bit block that contains startBalance + padding */ 46 | std::shared_ptr> h_r_startBalance; /* hashing gadget for startBalance */ 47 | 48 | std::shared_ptr> h_r_endBalance_block; /* 512 bit block that contains endBalance + padding */ 49 | std::shared_ptr> h_r_endBalance; /* hashing gadget for endBalance */ 50 | 51 | std::shared_ptr> h_r_incoming_block[noIncomingPayments]; /* 512 bit block that contains incoming + padding */ 52 | std::shared_ptr> h_r_incoming[noIncomingPayments]; /* hashing gadget for incoming */ 53 | 54 | std::shared_ptr> h_r_outgoing_block[noOutgoingPayments]; /* 512 bit block that contains outgoing + padding */ 55 | std::shared_ptr> h_r_outgoing[noOutgoingPayments]; /* hashing gadget for outgoing */ 56 | 57 | pb_variable zero; 58 | pb_variable_array padding_var; /* SHA256 length padding */ 59 | 60 | payment_multi_gadget(protoboard &pb) : gadget(pb, "payment_multi_gadget") 61 | { 62 | // Allocate space for the verifier input. 63 | const size_t input_size_in_bits = sha256_digest_len * (2 + noIncomingPayments + noOutgoingPayments); //The 2 are the beforeBalance and the afterBalance 64 | { 65 | // We use a "multipacking" technique which allows us to constrain 66 | // the input bits in as few field elements as possible. 67 | const size_t input_size_in_field_elements = div_ceil(input_size_in_bits, FieldT::capacity()); 68 | 69 | std::cout << "**************** input_size_in_field_elements: " << input_size_in_field_elements << "\n"; 70 | std::cout << "**************** FieldT::capacity(): " << FieldT::capacity() << "\n"; 71 | input_as_field_elements.allocate(pb, input_size_in_field_elements, "input_as_field_elements"); 72 | this->pb.set_input_sizes(input_size_in_field_elements); 73 | } 74 | 75 | zero.allocate(this->pb, FMT(this->annotation_prefix, "zero")); 76 | 77 | intermediate_startBalance.allocate(this->pb, sha256_digest_len/2, "intermediate_startBalance"); 78 | intermediate_endBalance.allocate(this->pb, sha256_digest_len/2, "intermediate_endBalance"); 79 | 80 | for (counter = 0; counter < noIncomingPayments; counter++) 81 | { 82 | intermediate_incoming[counter].allocate(this->pb, sha256_digest_len/2, "intermediate_incoming" + std::to_string(counter+1)); 83 | } 84 | 85 | for (counter = 0; counter < noOutgoingPayments; counter++) 86 | { 87 | intermediate_outgoing[counter].allocate(this->pb, sha256_digest_len/2, "intermediate_outgoing" + std::to_string(counter+1)); 88 | } 89 | 90 | // SHA256's length padding 91 | for (size_t i = 0; i < 256; i++) { 92 | if (sha256_padding[i]) 93 | padding_var.emplace_back(ONE); 94 | else 95 | padding_var.emplace_back(zero); 96 | } 97 | 98 | // Verifier (and prover) inputs: 99 | h_startBalance_var.reset(new digest_variable(pb, sha256_digest_len, "h_startBalance")); 100 | h_endBalance_var.reset(new digest_variable(pb, sha256_digest_len, "h_endBalance")); 101 | for (counter = 0; counter < noIncomingPayments; counter++) 102 | { 103 | h_incoming_var[counter].reset(new digest_variable(pb, sha256_digest_len, "h_incoming" + std::to_string(counter+1))); 104 | } 105 | for (counter = 0; counter < noOutgoingPayments; counter++) 106 | { 107 | h_outgoing_var[counter].reset(new digest_variable(pb, sha256_digest_len, "h_outgoing" + std::to_string(counter+1))); 108 | } 109 | 110 | input_as_bits.insert(input_as_bits.end(), h_startBalance_var->bits.begin(), h_startBalance_var->bits.end()); 111 | input_as_bits.insert(input_as_bits.end(), h_endBalance_var->bits.begin(), h_endBalance_var->bits.end()); 112 | for (counter = 0; counter < noIncomingPayments; counter++) 113 | { 114 | input_as_bits.insert(input_as_bits.end(), h_incoming_var[counter]->bits.begin(), h_incoming_var[counter]->bits.end()); 115 | } 116 | for (counter = 0; counter < noOutgoingPayments; counter++) 117 | { 118 | input_as_bits.insert(input_as_bits.end(), h_outgoing_var[counter]->bits.begin(), h_outgoing_var[counter]->bits.end()); 119 | } 120 | 121 | // Multipacking 122 | std::cout << "**************** input_size_bits: " << input_size_in_bits << "\n"; 123 | std::cout << "**************** input_as_bits.size(): " << input_as_bits.size() << "\n"; 124 | assert(input_as_bits.size() == input_size_in_bits); 125 | unpack_inputs.reset(new multipacking_gadget(this->pb, input_as_bits, input_as_field_elements, FieldT::capacity(), FMT(this->annotation_prefix, " unpack_inputs"))); 126 | 127 | // Prover inputs: 128 | r_startBalance_var.reset(new digest_variable(pb, sha256_digest_len, "r_startBalance")); 129 | r_endBalance_var.reset(new digest_variable(pb, sha256_digest_len, "r_endBalance")); 130 | for (counter = 0; counter < noIncomingPayments; counter++) 131 | { 132 | r_incoming_var[counter].reset(new digest_variable(pb, sha256_digest_len, "r_incoming" + std::to_string(counter+1))); 133 | } 134 | for (counter = 0; counter < noOutgoingPayments; counter++) 135 | { 136 | r_outgoing_var[counter].reset(new digest_variable(pb, sha256_digest_len, "r_outgoing" + std::to_string(counter+1))); 137 | } 138 | 139 | // IV for SHA256 140 | pb_linear_combination_array IV = SHA256_default_IV(pb); 141 | 142 | h_r_startBalance_block.reset(new block_variable(pb, { 143 | r_startBalance_var->bits, 144 | padding_var 145 | }, "h_r_startBalance_block")); 146 | 147 | h_r_startBalance.reset(new sha256_compression_function_gadget(pb, 148 | IV, 149 | h_r_startBalance_block->bits, 150 | *h_startBalance_var, 151 | "h_r_startBalance")); 152 | 153 | h_r_endBalance_block.reset(new block_variable(pb, { 154 | r_endBalance_var->bits, 155 | padding_var 156 | }, "h_r_endBalance_block")); 157 | 158 | h_r_endBalance.reset(new sha256_compression_function_gadget(pb, 159 | IV, 160 | h_r_endBalance_block->bits, 161 | *h_endBalance_var, 162 | "h_r_endBalance")); 163 | 164 | 165 | for (counter = 0; counter < noIncomingPayments; counter++) 166 | { 167 | h_r_incoming_block[counter].reset(new block_variable(pb, { 168 | r_incoming_var[counter]->bits, 169 | padding_var 170 | }, "h_r_incoming" + std::to_string(counter+1) + "_block")); 171 | 172 | h_r_incoming[counter].reset(new sha256_compression_function_gadget(pb, 173 | IV, 174 | h_r_incoming_block[counter]->bits, 175 | *h_incoming_var[counter], 176 | "h_r_incoming" + std::to_string(counter+1))); 177 | } 178 | 179 | for (counter = 0; counter < noOutgoingPayments; counter++) 180 | { 181 | 182 | h_r_outgoing_block[counter].reset(new block_variable(pb, { 183 | r_outgoing_var[counter]->bits, 184 | padding_var 185 | }, "h_r_outgoing" + std::to_string(counter+1) + "_block")); 186 | 187 | h_r_outgoing[counter].reset(new sha256_compression_function_gadget(pb, 188 | IV, 189 | h_r_outgoing_block[counter]->bits, 190 | *h_outgoing_var[counter], 191 | "h_r_outgoing" + std::to_string(counter+1))); 192 | } 193 | } 194 | 195 | void generate_payment_multi_constraints() 196 | { 197 | // Multipacking constraints (for input validation) 198 | unpack_inputs->generate_r1cs_constraints(true); 199 | 200 | // Ensure bitness of the digests. Bitness of the inputs 201 | // is established by `unpack_inputs->generate_r1cs_constraints(true)` 202 | r_startBalance_var->generate_r1cs_constraints(); 203 | r_endBalance_var->generate_r1cs_constraints(); 204 | for (counter = 0; counter < noIncomingPayments; counter++) 205 | { 206 | r_incoming_var[counter]->generate_r1cs_constraints(); 207 | } 208 | for (counter = 0; counter < noOutgoingPayments; counter++) 209 | { 210 | r_outgoing_var[counter]->generate_r1cs_constraints(); 211 | } 212 | 213 | generate_r1cs_equals_const_constraint(this->pb, zero, FieldT::zero(), "zero"); 214 | 215 | unsigned int NN = sha256_digest_len/2; 216 | 217 | this->pb.add_r1cs_constraint( 218 | r1cs_constraint( 219 | intermediate_startBalance[0], 220 | 1, 221 | r_startBalance_var->bits[0]), 222 | FMT(this->annotation_prefix, " zero1_%zu", 0)); 223 | 224 | this->pb.add_r1cs_constraint( 225 | r1cs_constraint( 226 | intermediate_endBalance[0], 227 | 1, 228 | r_endBalance_var->bits[0]), 229 | FMT(this->annotation_prefix, " zero2_%zu", 0)); 230 | 231 | for (counter = 0; counter < noIncomingPayments; counter++) 232 | { 233 | this->pb.add_r1cs_constraint( 234 | r1cs_constraint( 235 | intermediate_incoming[counter][0], 236 | 1, 237 | r_incoming_var[counter]->bits[0]), 238 | FMT(this->annotation_prefix, " zero" + std::to_string(counter+3) + "_%zu", 0)); 239 | } 240 | 241 | for (counter = 0; counter < noOutgoingPayments; counter++) 242 | { 243 | this->pb.add_r1cs_constraint( 244 | r1cs_constraint( 245 | intermediate_outgoing[counter][0], 246 | 1, 247 | r_outgoing_var[counter]->bits[0]), 248 | FMT(this->annotation_prefix, " zero" + std::to_string(counter+3+noIncomingPayments) + "_%zu", 0)); 249 | } 250 | 251 | for (unsigned int i = 1; i < NN; i++) { 252 | 253 | this->pb.add_r1cs_constraint( 254 | r1cs_constraint( 255 | { intermediate_startBalance[i] }, 256 | { ONE }, 257 | { intermediate_startBalance[i-1] * 2 , r_startBalance_var->bits[i] }), 258 | FMT(this->annotation_prefix, " sum1_%zu", i)); 259 | 260 | this->pb.add_r1cs_constraint( 261 | r1cs_constraint( 262 | { intermediate_endBalance[i] }, 263 | { 1 }, 264 | { intermediate_endBalance[i-1] * 2, r_endBalance_var->bits[i] }), 265 | FMT(this->annotation_prefix, " sum2_%zu", i)); 266 | 267 | for (counter = 0; counter < noIncomingPayments; counter++) 268 | { 269 | this->pb.add_r1cs_constraint( 270 | r1cs_constraint( 271 | { intermediate_incoming[counter][i] }, 272 | { 1 }, 273 | { intermediate_incoming[counter][i-1] * 2, r_incoming_var[counter]->bits[i] }), 274 | FMT(this->annotation_prefix, " sum" + std::to_string(counter+3) + "_%zu", i)); 275 | } 276 | 277 | for (counter = 0; counter < noOutgoingPayments; counter++) 278 | { 279 | this->pb.add_r1cs_constraint( 280 | r1cs_constraint( 281 | { intermediate_outgoing[counter][i] }, 282 | { 1 }, 283 | { intermediate_outgoing[counter][i-1] * 2, r_outgoing_var[counter]->bits[i] }), 284 | FMT(this->annotation_prefix, " sum" + std::to_string(counter+3+noIncomingPayments) + "_%zu", i)); 285 | } 286 | } 287 | 288 | // Constraint that start bal + sum(incoming) = end bal + sum(outgoing) 289 | this->pb.add_r1cs_constraint( 290 | r1cs_constraint( 291 | { 292 | intermediate_startBalance[NN-1], 293 | intermediate_incoming[0][NN-1], 294 | intermediate_incoming[1][NN-1], 295 | intermediate_incoming[2][NN-1], 296 | intermediate_incoming[3][NN-1], 297 | intermediate_incoming[4][NN-1], 298 | intermediate_incoming[5][NN-1] 299 | }, 300 | { 1 }, 301 | { 302 | intermediate_endBalance[NN-1], 303 | intermediate_outgoing[0][NN-1], 304 | intermediate_outgoing[1][NN-1], 305 | intermediate_outgoing[2][NN-1], 306 | intermediate_outgoing[3][NN-1], 307 | intermediate_outgoing[4][NN-1], 308 | intermediate_outgoing[5][NN-1] 309 | }), 310 | FMT(this->annotation_prefix, "finalsum_%zu", 0)); 311 | 312 | 313 | // These are the constraints to ensure the hashes validate. 314 | h_r_startBalance->generate_r1cs_constraints(); 315 | h_r_endBalance->generate_r1cs_constraints(); 316 | for (counter = 0; counter < noIncomingPayments; counter++) 317 | { 318 | h_r_incoming[counter]->generate_r1cs_constraints(); 319 | } 320 | for (counter = 0; counter < noOutgoingPayments; counter++) 321 | { 322 | h_r_outgoing[counter]->generate_r1cs_constraints(); 323 | } 324 | } 325 | 326 | void generate_payment_multi_witness(const bit_vector &h_startBalance, 327 | const bit_vector &h_endBalance, 328 | const bit_vector *h_incoming, 329 | const bit_vector *h_outgoing, 330 | const bit_vector &r_startBalance, 331 | const bit_vector &r_endBalance, 332 | const bit_vector *r_incoming, 333 | const bit_vector *r_outgoing 334 | ) 335 | { 336 | // Fill our digests with our witnessed data 337 | r_startBalance_var->bits.fill_with_bits(this->pb, r_startBalance); 338 | r_endBalance_var->bits.fill_with_bits(this->pb, r_endBalance); 339 | for (counter = 0; counter < noIncomingPayments; counter++) 340 | { 341 | r_incoming_var[counter]->bits.fill_with_bits(this->pb, r_incoming[counter]); 342 | } 343 | for (counter = 0; counter < noOutgoingPayments; counter++) 344 | { 345 | r_outgoing_var[counter]->bits.fill_with_bits(this->pb, r_outgoing[counter]); 346 | } 347 | 348 | size_t NN = sha256_digest_len/2; 349 | 350 | std::vector interm_startBalance(NN); 351 | std::vector interm_endBalance(NN); 352 | 353 | std::array, noIncomingPayments> interm_incoming; 354 | for (counter = 0; counter < noIncomingPayments; counter++) 355 | { 356 | interm_incoming[counter] = std::vector(NN); 357 | } 358 | std::array, noOutgoingPayments> interm_outgoing; 359 | for (counter = 0; counter < noOutgoingPayments; counter++) 360 | { 361 | interm_outgoing[counter] = std::vector(NN); 362 | } 363 | 364 | interm_startBalance[0] = r_startBalance[0] ? FieldT::one() : FieldT::zero(); 365 | interm_endBalance[0] = r_endBalance[0] ? FieldT::one() : FieldT::zero(); 366 | for (counter = 0; counter < noIncomingPayments; counter++) 367 | { 368 | interm_incoming[counter][0] = r_incoming[counter][0] ? FieldT::one() : FieldT::zero(); 369 | } 370 | for (counter = 0; counter < noOutgoingPayments; counter++) 371 | { 372 | interm_incoming[counter][0] = r_incoming[counter][0] ? FieldT::one() : FieldT::zero(); 373 | } 374 | 375 | for (size_t i=1; ipb, interm_startBalance); 389 | intermediate_endBalance.fill_with_field_elements(this->pb, interm_endBalance); 390 | for (counter = 0; counter < noIncomingPayments; counter++) 391 | { 392 | intermediate_incoming[counter].fill_with_field_elements(this->pb, interm_incoming[counter]); 393 | } 394 | for (counter = 0; counter < noOutgoingPayments; counter++) 395 | { 396 | intermediate_outgoing[counter].fill_with_field_elements(this->pb, interm_outgoing[counter]); 397 | } 398 | 399 | // Set the zero pb_variable to zero 400 | this->pb.val(zero) = FieldT::zero(); 401 | 402 | // Generate witnesses as necessary in our other gadgets 403 | h_r_startBalance->generate_r1cs_witness(); 404 | h_r_endBalance->generate_r1cs_witness(); 405 | for (counter = 0; counter < noIncomingPayments; counter++) 406 | { 407 | h_r_incoming[counter]->generate_r1cs_witness(); 408 | } 409 | for (counter = 0; counter < noOutgoingPayments; counter++) 410 | { 411 | h_r_outgoing[counter]->generate_r1cs_witness(); 412 | } 413 | unpack_inputs->generate_r1cs_witness_from_bits(); 414 | 415 | h_startBalance_var->bits.fill_with_bits(this->pb, h_startBalance); 416 | h_endBalance_var->bits.fill_with_bits(this->pb, h_endBalance); 417 | for (counter = 0; counter < noIncomingPayments; counter++) 418 | { 419 | h_incoming_var[counter]->bits.fill_with_bits(this->pb, h_incoming[counter]); 420 | } 421 | for (counter = 0; counter < noOutgoingPayments; counter++) 422 | { 423 | h_outgoing_var[counter]->bits.fill_with_bits(this->pb, h_outgoing[counter]); 424 | } 425 | } 426 | }; 427 | 428 | template 429 | r1cs_primary_input l_input_map_multi(const bit_vector &h_startBalance, 430 | const bit_vector &h_endBalance, 431 | const bit_vector *h_incoming, 432 | const bit_vector *h_outgoing 433 | ) 434 | { 435 | // Construct the multipacked field points which encode 436 | // the verifier's knowledge. This is the "dual" of the 437 | // multipacking gadget logic in the constructor. 438 | 439 | assert(h_startBalance.size() == sha256_digest_len); 440 | assert(h_endBalance.size() == sha256_digest_len); 441 | for (counter = 0; counter < noIncomingPayments; counter++) 442 | { 443 | assert(h_incoming[counter].size() == sha256_digest_len); 444 | } 445 | for (counter = 0; counter < noOutgoingPayments; counter++) 446 | { 447 | assert(h_outgoing[counter].size() == sha256_digest_len); 448 | } 449 | 450 | std::cout << "**** After assert(size() == sha256_digest_len) *****" << std::endl; 451 | 452 | bit_vector input_as_bits; 453 | input_as_bits.insert(input_as_bits.end(), h_startBalance.begin(), h_startBalance.end()); 454 | input_as_bits.insert(input_as_bits.end(), h_endBalance.begin(), h_endBalance.end()); 455 | for (counter = 0; counter < noIncomingPayments; counter++) 456 | { 457 | input_as_bits.insert(input_as_bits.end(), h_incoming[counter].begin(), h_incoming[counter].end()); 458 | } 459 | for (counter = 0; counter < noOutgoingPayments; counter++) 460 | { 461 | input_as_bits.insert(input_as_bits.end(), h_outgoing[counter].begin(), h_outgoing[counter].end()); 462 | } 463 | std::vector input_as_field_elements = pack_bit_vector_into_field_element_vector(input_as_bits); 464 | 465 | std::cout << "**** After pack_bit_vector_into_field_element_vector *****" << std::endl; 466 | 467 | return input_as_field_elements; 468 | } 469 | -------------------------------------------------------------------------------- /src/payment_multi_generate_keypair.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "snark.hpp" 7 | 8 | using namespace libsnark; 9 | using namespace libff; 10 | using namespace std; 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | // Initialize the curve parameters. 15 | default_r1cs_ppzksnark_pp::init_public_params(); 16 | // Generate the verifying/proving keys. (This is trusted setup!) 17 | auto keypair = generate_keypair_multi(); 18 | 19 | stringstream verificationKey; 20 | verificationKey << keypair.vk; 21 | 22 | ofstream fileOut; 23 | fileOut.open("verificationKey_multi"); 24 | 25 | fileOut << verificationKey.rdbuf(); 26 | fileOut.close(); 27 | 28 | stringstream provingKey; 29 | provingKey << keypair.pk; 30 | 31 | fileOut.open("provingKey_multi"); 32 | 33 | fileOut << provingKey.rdbuf(); 34 | fileOut.close(); 35 | 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/payment_multi_generate_proof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "snark.hpp" 11 | #include "utils.cpp" 12 | 13 | using namespace libsnark; 14 | using namespace std; 15 | 16 | int genProof(r1cs_ppzksnark_proving_key provingKey_in, string proofFileName) 17 | { 18 | int unsigned counter; 19 | // Initialize bit_vectors for all of the variables involved. 20 | bit_vector h_startBalance_bv; 21 | bit_vector h_endBalance_bv; 22 | bit_vector h_incoming_bv[noIncomingPayments]; 23 | bit_vector h_outgoing_bv[noOutgoingPayments]; 24 | bit_vector r_startBalance_bv; 25 | bit_vector r_endBalance_bv; 26 | bit_vector r_incoming_bv[noIncomingPayments]; 27 | bit_vector r_outgoing_bv[noOutgoingPayments]; 28 | 29 | vector> publicValues = fillValuesFromfile("publicInputParameters_multi"); 30 | h_startBalance_bv = int_list_to_bits_local(publicValues[0], 8); 31 | h_endBalance_bv = int_list_to_bits_local(publicValues[1], 8); 32 | for (counter = 0; counter < noIncomingPayments; counter++) 33 | { 34 | h_incoming_bv[counter] = int_list_to_bits_local(publicValues[counter+2], 8); 35 | } 36 | 37 | for (counter = 0; counter < noOutgoingPayments; counter++) 38 | { 39 | h_outgoing_bv[counter] = int_list_to_bits_local(publicValues[counter+2+noIncomingPayments], 8); 40 | } 41 | 42 | vector> privateValues = fillValuesFromfile("privateInputParameters_multi"); 43 | r_startBalance_bv = int_list_to_bits_local(privateValues[0], 8); 44 | r_endBalance_bv = int_list_to_bits_local(privateValues[1], 8); 45 | for (counter = 0; counter < noIncomingPayments; counter++) 46 | { 47 | r_incoming_bv[counter] = int_list_to_bits_local(privateValues[counter+2], 8); 48 | } 49 | for (counter = 0; counter < noOutgoingPayments; counter++) 50 | { 51 | r_outgoing_bv[counter] = int_list_to_bits_local(privateValues[counter+2+noIncomingPayments], 8); 52 | } 53 | 54 | boost::optional> proof = generate_payment_multi_proof(provingKey_in, h_startBalance_bv, h_endBalance_bv, h_incoming_bv, h_outgoing_bv, r_startBalance_bv, r_endBalance_bv, r_incoming_bv, r_outgoing_bv); 55 | 56 | if(proof == boost::none) 57 | { 58 | return 1; 59 | } else { 60 | stringstream proofStream; 61 | proofStream << proof; 62 | 63 | ofstream fileOut; 64 | fileOut.open(proofFileName); 65 | 66 | fileOut << proofStream.rdbuf(); 67 | fileOut.close(); 68 | return 0; 69 | } 70 | } 71 | 72 | int getUserInput(r1cs_ppzksnark_proving_key provingKey_in) 73 | { 74 | string inputTemp = ""; 75 | int result=0; 76 | cout << "Press enter paymentId to generate a proof or q to quit" << endl; 77 | cin >> inputTemp; 78 | cout << "Input from console: " << inputTemp << endl; 79 | if(inputTemp != "q") 80 | { 81 | result = genProof(provingKey_in, "proof_multi_" + inputTemp); 82 | if(result!=0) 83 | { 84 | cout << "There was an error generating the proof" << endl; 85 | return getUserInput(provingKey_in); 86 | } 87 | else 88 | { 89 | return getUserInput(provingKey_in); 90 | } 91 | } 92 | else 93 | { 94 | return 0; 95 | } 96 | } 97 | 98 | int main(int argc, char *argv[]) 99 | { 100 | string keyFileName = "provingKey_multi"; 101 | 102 | cout << "Loading proving key" << endl; 103 | 104 | // Initialize the curve parameters. 105 | default_r1cs_ppzksnark_pp::init_public_params(); 106 | 107 | r1cs_ppzksnark_proving_key provingKey_in; 108 | 109 | ifstream fileIn(keyFileName); 110 | stringstream provingKeyFromFile; 111 | if (fileIn) { 112 | provingKeyFromFile << fileIn.rdbuf(); 113 | fileIn.close(); 114 | } 115 | 116 | provingKeyFromFile >> provingKey_in; 117 | 118 | cout << "Proving key loaded into memory" << endl; 119 | 120 | return getUserInput(provingKey_in); 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/payment_multi_verify_proof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "snark.hpp" 10 | #include "utils.cpp" 11 | 12 | using namespace libsnark; 13 | using namespace std; 14 | 15 | 16 | int verifyProof(r1cs_ppzksnark_verification_key verificationKey_in, string proofFileName) 17 | { 18 | boost::optional> proof_in; 19 | 20 | cout << proofFileName << endl; 21 | ifstream proofFileIn(proofFileName); 22 | stringstream proofFromFile; 23 | if (proofFileIn) { 24 | proofFromFile << proofFileIn.rdbuf(); 25 | proofFileIn.close(); 26 | } else { 27 | cout << "Failed to read from proof file" << endl; 28 | return 1; 29 | } 30 | 31 | proofFromFile >> proof_in; 32 | 33 | // Hashes to validate against 34 | bit_vector h_startBalance_bv; 35 | bit_vector h_endBalance_bv; 36 | bit_vector h_incoming_bv[noIncomingPayments]; 37 | bit_vector h_outgoing_bv[noOutgoingPayments]; 38 | vector> values = fillValuesFromfile("publicInputParameters_multi"); 39 | h_startBalance_bv = int_list_to_bits_local(values[0], 8); 40 | h_endBalance_bv = int_list_to_bits_local(values[1], 8); 41 | 42 | for (counter = 0; counter < noIncomingPayments; counter++) 43 | { 44 | h_incoming_bv[counter] = int_list_to_bits_local(values[counter+2], 8); 45 | } 46 | for (counter = 0; counter < noOutgoingPayments; counter++) 47 | { 48 | h_outgoing_bv[counter] = int_list_to_bits_local(values[counter+2+noIncomingPayments], 8); 49 | } 50 | 51 | cout << "proof read ... starting verification" << endl; 52 | // Verify the proof 53 | bool isVerified = verify_payment_multi_proof(verificationKey_in, *proof_in, h_startBalance_bv, h_endBalance_bv, h_incoming_bv, h_outgoing_bv); 54 | 55 | if(isVerified){ 56 | cout << "Proof was verified!!" << endl; 57 | return 0; 58 | } else { 59 | cout << "Proof was not verified!!" << endl; 60 | return 1; 61 | } 62 | } 63 | 64 | int main(int argc, char *argv[]) 65 | { 66 | // Initialize the curve parameters. 67 | default_r1cs_ppzksnark_pp::init_public_params(); 68 | 69 | // Read verification key in from file 70 | r1cs_ppzksnark_verification_key verificationKey_in; 71 | ifstream fileIn("verificationKey_multi"); 72 | stringstream verificationKeyFromFile; 73 | if (fileIn) { 74 | verificationKeyFromFile << fileIn.rdbuf(); 75 | fileIn.close(); 76 | } 77 | verificationKeyFromFile >> verificationKey_in; 78 | 79 | string proofName = "proof_multi_"; 80 | string proofNameWithId = proofName + argv[1]; 81 | return verifyProof(verificationKey_in, proofNameWithId); 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/snark.hpp: -------------------------------------------------------------------------------- 1 | #include "libsnark/gadgetlib1/gadgets/basic_gadgets.hpp" 2 | #include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" 3 | #include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" 4 | #include "libff/common/utils.hpp" 5 | #include 6 | 7 | using namespace libsnark; 8 | using namespace std; 9 | 10 | #include "payment_in_out_gadget.hpp" 11 | #include "payment_multi_gadget.hpp" 12 | 13 | template 14 | r1cs_ppzksnark_keypair generate_keypair() 15 | { 16 | typedef Fr FieldT; 17 | 18 | protoboard pb; 19 | payment_in_out_gadget g(pb); 20 | g.generate_payment_in_out_constraints(); 21 | const r1cs_constraint_system constraint_system = pb.get_constraint_system(); 22 | 23 | cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; 24 | 25 | return r1cs_ppzksnark_generator(constraint_system); 26 | } 27 | 28 | template 29 | r1cs_ppzksnark_keypair generate_keypair_multi() 30 | { 31 | typedef Fr FieldT; 32 | 33 | protoboard pb; 34 | payment_multi_gadget g(pb); 35 | g.generate_payment_multi_constraints(); 36 | const r1cs_constraint_system constraint_system = pb.get_constraint_system(); 37 | 38 | cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl; 39 | 40 | return r1cs_ppzksnark_generator(constraint_system); 41 | } 42 | 43 | template 44 | boost::optional> generate_payment_in_out_proof(r1cs_ppzksnark_proving_key proving_key, 45 | const bit_vector &h_startbalance, 46 | const bit_vector &h_endbalance, 47 | const bit_vector &h_incoming, 48 | const bit_vector &h_outgoing, 49 | const bit_vector &r_startbalance, 50 | const bit_vector &r_endbalance, 51 | const bit_vector &r_incoming, 52 | const bit_vector &r_outgoing 53 | ) 54 | { 55 | typedef Fr FieldT; 56 | 57 | protoboard pb; 58 | payment_in_out_gadget g(pb); 59 | g.generate_payment_in_out_constraints(); 60 | g.generate_payment_in_out_witness(h_startbalance, h_endbalance, h_incoming, h_outgoing, r_startbalance, r_endbalance, r_incoming, r_outgoing); 61 | 62 | if (!pb.is_satisfied()) { 63 | std::cout << "System not satisfied!" << std::endl; 64 | return boost::none; 65 | } 66 | 67 | return r1cs_ppzksnark_prover(proving_key, pb.primary_input(), pb.auxiliary_input()); 68 | } 69 | 70 | 71 | template 72 | boost::optional> generate_payment_multi_proof(r1cs_ppzksnark_proving_key proving_key, 73 | const bit_vector &h_startbalance, 74 | const bit_vector &h_endbalance, 75 | const bit_vector *h_incoming, 76 | const bit_vector *h_outgoing, 77 | const bit_vector &r_startbalance, 78 | const bit_vector &r_endbalance, 79 | const bit_vector *r_incoming, 80 | const bit_vector *r_outgoing 81 | ) 82 | { 83 | typedef Fr FieldT; 84 | 85 | protoboard pb; 86 | payment_multi_gadget g(pb); 87 | g.generate_payment_multi_constraints(); 88 | g.generate_payment_multi_witness(h_startbalance, h_endbalance, h_incoming, h_outgoing, r_startbalance, r_endbalance, r_incoming, r_outgoing); 89 | 90 | if (!pb.is_satisfied()) { 91 | std::cout << "System not satisfied!" << std::endl; 92 | return boost::none; 93 | } 94 | 95 | return r1cs_ppzksnark_prover(proving_key, pb.primary_input(), pb.auxiliary_input()); 96 | } 97 | 98 | template 99 | bool verify_payment_in_out_proof(r1cs_ppzksnark_verification_key verification_key, 100 | r1cs_ppzksnark_proof proof, 101 | const bit_vector &h_startbalance, 102 | const bit_vector &h_endbalance, 103 | const bit_vector &h_incoming, 104 | const bit_vector &h_outgoing 105 | ) 106 | { 107 | typedef Fr FieldT; 108 | 109 | const r1cs_primary_input input = l_input_map(h_startbalance, h_endbalance, h_incoming, h_outgoing); 110 | 111 | std::cout << "**** After l_input_map *****" << std::endl; 112 | 113 | return r1cs_ppzksnark_verifier_strong_IC(verification_key, input, proof); 114 | 115 | } 116 | 117 | template 118 | bool verify_payment_multi_proof(r1cs_ppzksnark_verification_key verification_key, 119 | r1cs_ppzksnark_proof proof, 120 | const bit_vector &h_startbalance, 121 | const bit_vector &h_endbalance, 122 | const bit_vector *h_incoming, 123 | const bit_vector *h_outgoing 124 | ) 125 | { 126 | typedef Fr FieldT; 127 | const r1cs_primary_input input = l_input_map_multi(h_startbalance, h_endbalance, h_incoming, h_outgoing); 128 | 129 | std::cout << "**** After l_input_map_multi *****" << std::endl; 130 | 131 | return r1cs_ppzksnark_verifier_strong_IC(verification_key, input, proof); 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "snark.hpp" 8 | #include "test.h" 9 | 10 | using namespace libsnark; 11 | using namespace std; 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | ifstream pkFile("proverKey"); 16 | ifstream vkFile("verifierKey"); 17 | 18 | // Initialize the curve parameters. 19 | default_r1cs_ppzksnark_pp::init_public_params(); 20 | // Generate the verifying/proving keys. (This is trusted setup!) 21 | r1cs_ppzksnark_keypair keypair = generate_keypair(); 22 | 23 | cout << "verifier key before:" << endl; 24 | cout << keypair.vk << endl; 25 | 26 | if(!pkFile.good() || !vkFile.good()) { 27 | stringstream proverKey; 28 | proverKey << keypair.pk; 29 | 30 | ofstream pOut; 31 | pOut.open("proverKey"); 32 | 33 | pOut << proverKey.rdbuf(); 34 | pOut.close(); 35 | 36 | stringstream verifierKey; 37 | verifierKey << keypair.vk; 38 | 39 | ofstream vOut; 40 | vOut.open("verifierKey"); 41 | 42 | vOut << verifierKey.rdbuf(); 43 | vOut.close(); 44 | } else { 45 | ifstream pIn("proverKey"); 46 | stringstream proverKeyFromFile; 47 | if (pIn) { 48 | proverKeyFromFile << pIn.rdbuf(); 49 | pIn.close(); 50 | } 51 | 52 | proverKeyFromFile >> keypair.pk; 53 | 54 | ifstream vIn("verifierKey"); 55 | stringstream verifierKeyFromFile; 56 | if (vIn) { 57 | verifierKeyFromFile << vIn.rdbuf(); 58 | vIn.close(); 59 | } 60 | 61 | verifierKeyFromFile >> keypair.vk; 62 | cout << "verifier key after:" << endl; 63 | cout << keypair.vk << endl; 64 | } 65 | 66 | cout << "keypair.vk|start:" << keypair.vk << ":end" << endl; 67 | 68 | // Run test vectors. 69 | assert(run_test(keypair, false, false, false)); 70 | //assert(!run_test(keypair, true, false, false)); 71 | //assert(!run_test(keypair, false, true, false)); 72 | //assert(!run_test(keypair, false, false, true)); 73 | return 0; 74 | } 75 | 76 | bool run_test(r1cs_ppzksnark_keypair& keypair, 77 | // These are just for changing behavior 78 | // for testing purposes: 79 | bool use_and_instead_of_xor, 80 | bool swap_r1_r2, 81 | bool goofy_verification_inputs 82 | ) { 83 | 84 | // Initialize bit_vectors for all of the variables involved. 85 | std::vector h1_bv(256); 86 | std::vector h2_bv(256); 87 | std::vector h3_bv(256); 88 | std::vector r1_bv(256); 89 | std::vector r2_bv(256); 90 | std::vector r3_bv(256); 91 | 92 | { 93 | // These are working test vectors. 94 | h1_bv = int_list_to_bits({78,152, 95 | 23, 96 | 135, 97 | 180, 98 | 61, 99 | 171, 100 | 123, 101 | 58, 102 | 147, 103 | 215, 104 | 200, 105 | 83, 106 | 7, 107 | 198, 108 | 244, 109 | 58, 110 | 26, 111 | 58, 112 | 88, 113 | 150, 114 | 57, 115 | 69, 116 | 185, 117 | 62, 118 | 165, 119 | 253, 120 | 53, 121 | 112, 122 | 69, 80, 23}, 8); 123 | h2_bv = int_list_to_bits({182, 124 | 169, 125 | 95, 126 | 91, 127 | 248, 128 | 154, 129 | 156, 130 | 163, 131 | 104, 132 | 18, 133 | 251, 134 | 174, 135 | 68, 136 | 251, 137 | 237, 138 | 249, 139 | 215, 140 | 166, 141 | 135, 142 | 222, 143 | 50, 144 | 133, 145 | 48, 146 | 197, 147 | 197, 148 | 205, 149 | 182, 150 | 20, 151 | 56, 152 | 166, 153 | 108, 154 | 66}, 8); 155 | h3_bv = int_list_to_bits({101, 156 | 119, 157 | 48, 158 | 144, 159 | 165, 160 | 169, 161 | 249, 162 | 100, 163 | 249, 164 | 74, 165 | 13, 166 | 126, 167 | 39, 168 | 34, 169 | 64, 170 | 47, 171 | 238, 172 | 173, 173 | 29, 174 | 72, 175 | 31, 176 | 203, 177 | 7, 178 | 100, 179 | 179, 180 | 20, 181 | 220, 182 | 66, 183 | 172, 184 | 97, 185 | 252, 186 | 223}, 8); 187 | // r = (num, salt) 188 | // Constraint is num3 = num1 + num2 189 | r1_bv = int_list_to_bits({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 203, 6, 191, 16, 141, 210, 73, 136, 65, 136, 152, 60, 117, 24, 101, 18}, 8); 190 | r2_bv = int_list_to_bits({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 6, 178, 210, 43, 243, 10, 217, 251, 246, 248, 0, 21, 86, 194, 100, 94}, 8); 191 | r3_bv = int_list_to_bits({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 203, 6, 191, 16, 141, 210, 73, 136, 65, 136, 152, 60, 117, 24, 101, 18}, 8); 192 | } 193 | 194 | if (swap_r1_r2) { 195 | // This swaps r1 and r2 which should test if the hashing 196 | // constraints work properly. 197 | auto tmp = r2_bv; 198 | r2_bv = r1_bv; 199 | r1_bv = tmp; 200 | } 201 | 202 | cout << "Trying to generate proof..." << endl; 203 | auto proof = generate_proof(keypair.pk, h1_bv, h2_bv, h3_bv, r1_bv, r2_bv, r3_bv); 204 | cout << "Proof generated!" << endl; 205 | cout << "Proof: " << proof << endl; 206 | 207 | if (!proof) { 208 | return false; 209 | } else { 210 | if (goofy_verification_inputs) { 211 | // [test] if we generated the proof but try to validate 212 | // with bogus inputs it shouldn't let us 213 | return verify_proof(keypair.vk, *proof, h2_bv, h1_bv, h3_bv); 214 | } else { 215 | // verification should not fail if the proof is generated! 216 | assert(verify_proof(keypair.vk, *proof, h1_bv, h2_bv, h3_bv)); 217 | return true; 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/test.h: -------------------------------------------------------------------------------- 1 | bool run_test(r1cs_ppzksnark_keypair& keypair, 2 | 3 | // These are just for changing behavior 4 | // for testing purposes: 5 | bool use_and_instead_of_xor, 6 | bool swap_r1_r2, 7 | bool goofy_verification_inputs 8 | ); -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::vector int_list_to_bits_local(const vector &l, const size_t wordsize) 7 | { 8 | std::vector res(wordsize*l.size()); 9 | for (size_t i = 0; i < l.size(); ++i) 10 | { 11 | for (size_t j = 0; j < wordsize; ++j) 12 | { 13 | res[i*wordsize + j] = (*(l.begin()+i) & (1ul<<(wordsize-1-j))); 14 | } 15 | } 16 | return res; 17 | } 18 | 19 | std::vector> fillValuesFromfile(string fileName ) { 20 | { 21 | string line; 22 | 23 | std::vector> outputValues; 24 | ifstream inputParameters(fileName); 25 | while(getline(inputParameters, line)){ 26 | 27 | stringstream iss( line ); 28 | 29 | int number; 30 | vector outputValue; 31 | while ( iss >> number ) 32 | outputValue.push_back( number ); 33 | 34 | outputValues.push_back(outputValue); 35 | } 36 | return outputValues; 37 | } 38 | } 39 | --------------------------------------------------------------------------------