Skip to main content

Submit a Snarkjs FFlonk Proof

In this tutorial we will submit a fflonk proof generated by snarkjs.

Requirements

Limitations

For now ZkVerify accepts only fflonk proofs generated with Bn128 Elliptic Curve and exactly one public input.

Submit proof

Assume you have the following files:

We should convert the proof in a ZkVerify valid format: a 780 bytes array where the first 768 ones are the proof itself and the following 32 bytes are the public input value. To get the proof bytes from the snarkjs json file we use fflonk_verifier project that provide a simple executable to convert the json proof in the needed array.

Convert the proof

fflonk_verifier project provide some useful utilities to convert proofs and verify them:

git clone https://github.com/HorizenLabs/fflonk_verifier
cd fflonk_verifier/
cargo install --features bins --path .

Now you can come back in the proof folder and convert it in a format that we can use to send it to ZkVerify:

proof-converter proof.json

This command will dump the proof bytes in the hexadecimal format that we can use to submit it.

Send the proof

Here we'll adapt a bit the polkadot.js simple transfer events example to send the proof and check if it's valid. First we initialize the project and add the needed dependencies:

mkdir send_proof
cd send_proof
yarn init
yarn add @polkadot/api
yarn add @polkadot/keyring
yarn add dotenv
yarn add bignumber.js

Now define a .env file to write destination chain and our private key:

echo "WEBSOCKET=wss://testnet-rpc.zkverify.io
PRIVATE_KEY=your_private_key" > .env

and add the script section to project.json

  "scripts": {
"start": "node index.js"
},

The index.js file is not so different from the polkadot.js example:

const { ApiPromise, WsProvider } = require('@polkadot/api');
const { Keyring } = require('@polkadot/keyring');
require('dotenv').config();
const fs = require('node:fs');
const { BigNumber } = require('bignumber.js');

// Verification key json file path
const vkFile = process.argv[2];
// Hex proof bytes
const proof = process.argv[3];
// Public input value
const pub = process.argv[4];

async function main() {
// Use address from provided environment
const provider = new WsProvider(process.env.WEBSOCKET);
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Here we load the snarkjs verification key and rename two field
// that have a different name in ZkVerify
const vk = {
...JSON.parse(fs.readFileSync(vkFile, 'utf8')),
get x2() { return this.X_2 },
get c0() { return this.C0 },
};
// Use the given private key
const account = keyring.addFromUri(process.env.PRIVATE_KEY);
// Concatenate the proof bytes to the public inputs ones
const submit = api.tx.settlementFFlonkPallet.submitProof(
{ 'Vk': vk },
proof,
"0x" + BigNumber(pub).toString(16).padStart(64, '0')
);
await submit.signAndSend(account, ({ txHash, status, dispatchError }) => {
if (status.isReady) {
console.log('Proof submitted with hash : ', txHash.toHex());
} else if (status.isInBlock) {
console.log(`Proof included in block`);
let exitCode = 0;
if (!dispatchError) {
console.log("Proof verified!!!");
} else {
console.log("Proof verification failed");
exitCode = 1;
}
process.exit(exitCode);
}
});
}

main().catch(console.error);

Now run it

yarn start \
snarkjs/proof/path/verification_key.json \
0x13650a0d5de7a7fdc871c494493929b40a7926afc0912292abaf2f2d5c6cc481\
094dcd8c9579795b9062039f67aa7084505b2a91bd5733b8e0ab356da13c4921\
0f0036bac3346e4701737cbab3d3bc98a3655e22cf15ea397bb0d1d0987f17a6\
17693abb6a68dc5aaaffefdf91bffaadedc5237e67d8c3d879f2e0974890e1be\
0cb3dc14a9786928657042d91de94da19d5a35cac333ddf78b2d2034601334a4\
24dcd0904f3dbc4334e8ba39fc611b282dea13a33b0e51495ddc29a4e1ca0418\
20192d266e98c3a9a6922136539a4ccf92f78f08bb5dbb4e3b8387dcdeacd38a\
1ffb0c26d7474cc4677e85ecc9b7384d8865c8d72363d5ba2c26ab42d0dc5a5a\
29d0695cde42cc4e1e47fe296502c6460e4abd26d27944ed6453d749a1dbc86b\
0969d93f17de7c46fa8d16d48305531eb65229d57c305510d921913987571088\
2a86fe0abd7f2b10ef733e2bdfbe4d8681289e318e617294cd556d500d363747\
23d175851438772e36a73fd89a8a00ce78b20b6a5a110e58bde7985a08349b13\
0000000000000000000000000000000000000000000000000000000000000000\
15351d6ddffe12be3d14373fda2b73ec3a4a42bf24e8127a5a1acddf09adb8b6\
23472d76e75fd536b78927b50977e4436afee3ad52112abb47f6ec802a9a231d\
23fa8d4b61305859389e7bdc5daa16d4e58e54303d6e15f16bc3188dd32eecfe\
1ea9e431a07f57ccc8468d85b66fac6471d582e5a10963ba5164772c06052c76\
0e43f716b264c0a128d12d7c0530b20b7c199db68ff8dbdd5b13da046017b2b2\
12e9e8ccd5408f2a4b815a057469633cf9d724550b17aa531cf9e294d3363786\
1445b0aa0c9b69fd28d28de56dc5c8d513fc0e4e0e8b51060208525bc969183c\
14ea898cc07f20b9ae5d8f86d1cc2f5cfcf9b6105c302763818144783f3a21b1\
27232541d9ada744f797f3d27423d6923d191592da4c77f3362f3d45dac1c1a1\
085d01ef359cb02cbb12c7e24f80e90a4a4ed4f8802b5930f80fc5ff50333989\
125f70f842b24812398693692de0eea8368ca1345564b2b5e8438e60a9328192 \
7713112592372404476342535432037683616424591277138491596200192981572885523208

Will give the follow output

[...]
Proof submitted with hash : 0x2180e6fcd07252f92f8d47a7f4f2aa502aabdb64945fd3e36e0da6945e853025
Proof included in block
Proof verified!!!
Done in 4.82s.