mirror of
https://github.com/arnaucube/miksi-core.git
synced 2026-02-06 19:16:40 +01:00
Smartcontract add nullifier, update contract to last circuit
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
*From Esperanto, **miksi** (miks·i): to mingle, to blend, to mix, to shuffle*
|
||||
|
||||

|
||||
|
||||
|
||||
**Warning:** This repository is in a very early stage.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Normal file
BIN
miksi-logo00-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user