Browse Source

Smartcontract add nullifier, update contract to last circuit

pull/2/head
arnaucube 3 years ago
parent
commit
2179505f3b
5 changed files with 131 additions and 69 deletions
  1. +1
    -0
      README.md
  2. +35
    -29
      contracts/Miksi.sol
  3. +12
    -11
      contracts/verifier.sol
  4. BIN
      miksi-logo00-small.png
  5. +83
    -29
      test/contracts/miksi.test.ts

+ 1
- 0
README.md

@ -2,6 +2,7 @@
*From Esperanto, **miksi** (miks·i): to mingle, to blend, to mix, to shuffle*
![](miksi-logo00-small.png)
**Warning:** This repository is in a very early stage.

+ 35
- 29
contracts/Miksi.sol

@ -8,52 +8,58 @@ contract Miksi {
constructor( address _verifierContractAddr) public {
verifier = Verifier(_verifierContractAddr);
}
mapping(uint256 => Deposit) deposits;
struct Deposit {
uint256 coinCode;
uint256 amount;
bool used;
}
uint256 amount = uint256(1000000000000000000);
uint256 root;
uint256[] commitments;
mapping(uint256 => bool) nullifiers;
function deposit(
uint256 coinCode,
// uint256 amount,
uint256 commitment
uint256 _commitment,
uint256 _root
) public payable {
deposits[commitment] = Deposit(coinCode, msg.value, false);
// TODO check root state transition update with zkp
require(msg.value==amount, "value should be 1 ETH"); // this can be flexible with a wrapper with preset fixed amounts
commitments.push(_commitment);
root = _root;
}
function getDeposit(
uint256 commitment
) public view returns (uint256, uint256) {
return (
deposits[commitment].coinCode,
deposits[commitment].amount
);
function getCommitments() public view returns (uint256[] memory, uint256) {
return (commitments, root);
}
function withdraw(
uint256 commitment,
address payable _address,
uint256 nullifier,
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c
) public {
uint256[4] memory input = [
deposits[commitment].coinCode,
deposits[commitment].amount,
commitment,
uint256[5] memory input = [
0,
amount,
nullifier,
root,
uint256(_address)
];
require(verifier.verifyProof(a, b, c, input), "zkProof withdraw could not be verified");
// zk verification passed, proceed with the withdraw
require(!deposits[commitment].used, "deposit already withdrawed");
deposits[commitment].used = true;
_address.send(deposits[commitment].amount);
// _address.call.value(deposits[commitment].amount).gas(20317)();
// zk verification passed
require(useNullifier(nullifier), "nullifier already used");
// nullifier check passed
// proceed with the withdraw
_address.send(amount);
// _address.call.value(amount).gas(20317)();
}
function useNullifier(
uint256 nullifier
) internal returns (bool) {
if (nullifiers[nullifier]) {
return false;
}
nullifiers[nullifier] = true;
return true;
}
}

+ 12
- 11
contracts/verifier.sol

@ -174,16 +174,17 @@ contract Verifier {
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(5185992386807636752062921178966578257539151042202977558020078229066726353735,19116203848700332781926088278955228321025476213248649030230870938462300903297);
vk.beta2 = Pairing.G2Point([19030978247664556689560141272090896525855193446598104251860042964819095406467,19780960404766112404848074878225825567855499848739061882556607695385088991192], [20402461031267926255530100927046942331147898484488092600018001239630447470368,17604138488196164031519027655960050500952176071938159024932979559575610655320]);
vk.gamma2 = Pairing.G2Point([20383216224167453593158000496263161259331265217720672612685782338425828607013,12285091765781487940062490824680322009247635572831566743154406937445855014141], [2724044019507218108155409647671037824297854833037683233209628555415178462971,4800441398018446385507678136399891972502082696226688244556543968658013445721]);
vk.delta2 = Pairing.G2Point([17491660837811889973732218736865963003206037853732483884984507582280081743890,18194603073278150162885840058572458854817989194824624275670491325570842555401], [14259520890547064112120136619441621870946603290427647212804054747278532372550,14237711745170441984980821720645206187129227373288931343140891164943633394176]);
vk.IC = new Pairing.G1Point[](5);
vk.IC[0] = Pairing.G1Point(5800430773422603830326865012746294458553705291090306505082228589953960548435,525261600745641876890318660619064985918284670178585976847597013044968033805);
vk.IC[1] = Pairing.G1Point(13962429114312903407632291539456657530314035590343134693033142826413285622014,9649969769508662299834286176418790329944910509961747776147748835638371066986);
vk.IC[2] = Pairing.G1Point(15449758800146945182032375987369505286936830519651791301171981724664755129658,20429634243561140221481565627307606983692378272988345590135001254474940660921);
vk.IC[3] = Pairing.G1Point(6593016958529739953865018414912427672210776178723941512691424600884151791016,2330860586625886543272754640931712670362458263865839970599736410727810605340);
vk.IC[4] = Pairing.G1Point(17799933908098896756054489526152304776886935915002907298028930270007186443766,16808221405053081411172376396342768077671335058872875437488630198515891174714);
vk.alfa1 = Pairing.G1Point(5573537740265625168090285795198286579893954229360118260810063400917126796332,10789719125793022298590118880800334307038768742566971868445296062248489927279);
vk.beta2 = Pairing.G2Point([6072967376916873741947961849223716153342228155851589974405468410733665523375,12287252052014343808789818956357497671178626709800000377423333232855717394550], [19887659544225763042094432491416573265605432229063422403349406354087673898183,6817773539509067572550546188063114549568433731855389350832916458849047861996]);
vk.gamma2 = Pairing.G2Point([21777617475348225837446670587801717739887263496343742680443739578840171280954,21819935038190839400227353700653328252014694909407919035917157743271161479537], [5874472499575263305242127811274992746938244885646767207659488542844992545745,7393451529219086695827003727698071721027731112496364438559915896033740034847]);
vk.delta2 = Pairing.G2Point([21378569762973853737012405126625349874813324397465926818859899411148132219071,14217351759616267906987060014706527451694626178118484166622168408217896429543], [2959651248849223420449727632536103426152162159064239731749254690426832558857,11771456040212156734923327364079425983074170498604769948335767339414964838231]);
vk.IC = new Pairing.G1Point[](6);
vk.IC[0] = Pairing.G1Point(9635854757134834421832904337276664164370386962134477374755137344348461690576,19010444634264471397134767941238249705060700374213396849476569996691707235781);
vk.IC[1] = Pairing.G1Point(11035507535270489026720166819336384993970390089893993872476567909015281065120,8704656742215780532233763414730518226534751719578362784054696564772602495731);
vk.IC[2] = Pairing.G1Point(8237061378219722919406315531805429710952281972371670670227024276177812450561,21788473647720763764729284824775460737201605786977595028535407989906364038374);
vk.IC[3] = Pairing.G1Point(12032524012671168792329029167990930904368673059152483847563381918262080217708,18306948356499743813922422173776199871369081286779005885299858224571300447429);
vk.IC[4] = Pairing.G1Point(10502650737346333699651566811075824924610501915730189047190185075707722044703,7451314863250353539987518216950772741664034028935285984537382148127632592866);
vk.IC[5] = Pairing.G1Point(2254627509888069544735850912948033354698601777682208496156519712223878774652,10613197317835768162576244259798938001105060978002557905703689968987855363120);
}
function verify(uint[] memory input, Proof memory proof) internal view returns (uint) {
@ -209,7 +210,7 @@ contract Verifier {
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[4] memory input
uint[5] memory input
) public view returns (bool r) {
Proof memory proof;
proof.A = Pairing.G1Point(a[0], a[1]);

BIN
miksi-logo00-small.png

Before After
Width: 250  |  Height: 250  |  Size: 27 KiB

+ 83
- 29
test/contracts/miksi.test.ts

@ -10,6 +10,7 @@ const { groth } = require('snarkjs');
const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils;
const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder;
const circomlib = require("circomlib");
const smt = require("circomlib").smt;
contract("miksi", (accounts) => {
@ -24,51 +25,80 @@ contract("miksi", (accounts) => {
let insVerifier;
let insMiksi;
const nLevels = 5;
const secret = "1234567890";
const coinCode = "0"; // refearing to ETH
const ethAmount = '1';
const amount = web3.utils.toWei(ethAmount, 'ether');
const nullifier = "567891234";
let tree;
let commitment;
let proof;
let publicSignals;
before(async () => {
insVerifier = await Verifier.new();
insMiksi = await Miksi.new(insVerifier.address);
});
it("miksi flow", async () => {
const secret = "123456789";
const coinCode = "0"; // refearing to ETH
const ethAmount = '0.5';
const amount = web3.utils.toWei(ethAmount, 'ether');
before(async() => {
let balance_wei = await web3.eth.getBalance(addr1);
console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
// console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
expect(balance_wei).to.be.equal('100000000000000000000');
const poseidon = circomlib.poseidon.createHash(6, 8, 57);
const commitment = poseidon([coinCode, amount, secret]).toString();
commitment = poseidon([coinCode, amount, secret, nullifier]).toString();
// deposit
console.log("Deposit of " + ethAmount + " ETH from " + addr1);
await insMiksi.deposit(coinCode, commitment, {from: addr1, value: amount});
// add commitment into SMT
tree = await smt.newMemEmptyTrie();
await tree.insert(commitment, 0);
await tree.insert(1, 0);
await tree.insert(2, 0);
expect(tree.root.toString()).to.be.equal('9712258649847843172766744803572924784812438285433990419902675958769413333474');
});
it("Make the deposit", async () => {
// console.log("root", tree.root);
// console.log("Deposit of " + ethAmount + " ETH from " + addr1 + ".\nCommitment "+commitment+", root: "+ tree.root);
await insMiksi.deposit(commitment, tree.root.toString(), {from: addr1, value: amount});
balance_wei = await web3.eth.getBalance(addr1);
console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
expect(balance_wei).to.be.equal('99499107180000000000');
// console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
expect(balance_wei).to.be.equal('98998318640000000000');
});
// getDeposit data
const res = await insMiksi.getDeposit(commitment);
expect(res[0].toString()).to.be.equal(coinCode);
expect(res[1].toString()).to.be.equal(amount);
it("Get the commitments data", async () => {
// getCommitments data
let res = await insMiksi.getCommitments();
expect(res[0][0].toString()).to.be.equal('189025084074544266465422070282645213792582195466360448472858620722286781863');
expect(res[1].toString()).to.be.equal('9712258649847843172766744803572924784812438285433990419902675958769413333474');
});
it("Calculate witness and generate the zkProof", async () => {
const resC = await tree.find(commitment);
assert(resC.found);
let siblings = resC.siblings;
while (siblings.length < nLevels) {
siblings.push("0");
};
// console.log("siblings", siblings);
// calculate witness
const wasm = await fs.promises.readFile("./build/withdraw.wasm");
const input = unstringifyBigInts({
"coinCode": coinCode,
"amount": amount,
"commitment": commitment,
"secret": secret,
"nullifier": nullifier,
"siblings": siblings,
"root": tree.root,
"address": addr2
});
const options = {};
console.log("Calculate witness");
// console.log("Calculate witness");
const wc = await WitnessCalculatorBuilder(wasm, options);
const w = await wc.calculateWitness(input);
const witness = unstringifyBigInts(stringifyBigInts(w));
@ -76,14 +106,36 @@ contract("miksi", (accounts) => {
// generate zkproof of commitment using snarkjs (as is a test)
const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/proving_key.json", "utf8")));
console.log("Generate zkSNARK proof");
const {proof, publicSignals} = groth.genProof(provingKey, witness);
// console.log("Generate zkSNARK proof");
const res = groth.genProof(provingKey, witness);
proof = res.proof;
publicSignals = res.publicSignals;
});
it("Try to use the zkProof with another address and get revert", async () => {
// console.log("Try to reuse the zkproof and expect revert");
await truffleAssert.fails(
insMiksi.withdraw(
addr1,
nullifier,
[proof.pi_a[0].toString(), proof.pi_a[1].toString()],
[
[proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
[proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
],
[proof.pi_c[0].toString(), proof.pi_c[1].toString()]
),
truffleAssert.ErrorType.REVERT,
"zkProof withdraw could not be verified"
);
});
it("Withdraw 1 ETH with the zkProof", async () => {
// withdraw
console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
// console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
let resW = await insMiksi.withdraw(
commitment,
addr2,
nullifier,
[proof.pi_a[0].toString(), proof.pi_a[1].toString()],
[
[proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
@ -94,14 +146,16 @@ contract("miksi", (accounts) => {
// console.log("resW", resW);
balance_wei = await web3.eth.getBalance(addr2);
console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
expect(balance_wei).to.be.equal('100500000000000000000');
// console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
expect(balance_wei).to.be.equal('101000000000000000000');
});
console.log("Try to reuse the zkproof and expect revert");
it("Try to reuse the zkProof and get revert", async () => {
// console.log("Try to reuse the zkproof and expect revert");
await truffleAssert.fails(
insMiksi.withdraw(
commitment,
addr2,
nullifier,
[proof.pi_a[0].toString(), proof.pi_a[1].toString()],
[
[proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
@ -110,9 +164,9 @@ contract("miksi", (accounts) => {
[proof.pi_c[0].toString(), proof.pi_c[1].toString()]
),
truffleAssert.ErrorType.REVERT,
"deposit already withdrawed"
"nullifier already used"
);
balance_wei = await web3.eth.getBalance(addr2);
expect(balance_wei).to.be.equal('100500000000000000000');
expect(balance_wei).to.be.equal('101000000000000000000');
});
});

Loading…
Cancel
Save