Zokrates / ZoKrates

A toolbox for zkSNARKs on Ethereum
https://zokrates.github.io
GNU Lesser General Public License v3.0
1.81k stars 362 forks source link

Serialisation Compatibility #909

Closed rayyan808 closed 3 years ago

rayyan808 commented 3 years ago

Description

Passing the bincode-encoded program to zokrates-js via (React) Javascript. It is due to this nature of being client-side that I cannot use a function such as 'fs.readFileSync' from node-js. I have tried passing the data as raw binary (readAsBinaryString: throws a 'panic error') as well as the below implementation of an ArrayBuffer (throws an 'exceeded usize' error). Looking to the wrapper.js file, the interface shows an input requirement of uint8Array for a program, but parsing it as an uint8 does not work either.There is very bare documentation on how the ZIR works and looking into bincode has shown that the owner does not recommend this for anything cross-compatible. So.., I'm not sure why this is the current method of encoding. Any solutions on how to get this working are greatly appreciated!

Environment

Steps to Reproduce

 getHashBinary = (event) => { 
      console.log('getHashBinary called \n');
      const file = event.target.files[0];
      console.log('files: ' + file);
 //   this.blobToBuffer(event.target.files[0]);
      const reader = new FileReader();
      reader.readAsArrayBuffer(file);
      reader.addEventListener('load', (e) => {
        const buffer = reader.result;
        const bufferLength = buffer.length;
        console.log("Data loaded successfully");
   //     const data = reader.result.slice(reader.result.)
        console.log("Reader Loaded.\n")
        console.log("Buffer Length: " + buffer.byteLength);
        var binArray = new Uint8Array(buffer);
        console.log("binArray length : " + binArray.length)
        //var binaryString = String.fromCharCode.apply(null,binArray) ;
        //console.log("binaryString: " + binaryString);

        this.setState({hashBinary: buffer/*CHANGED*/});
      });

//Somewhere else in the code to communicate with zokrates-js
console.log("Computing Witness fo")
        let { witness, output } = zkProvider.computeWitness({program: this.state.hashBinary, abi: hashABI}, ["1337", "0"]);

The Error:

Uncaught (in promise) Could not deserialize program: Invalid size 12970648401803758415: sizes must fit in a usize (0 to 4294967295)
 

`

rayyan808 commented 3 years ago

I've reproduced the same error within node.js using fs.readFileSync, which further proves the issue: initialize().then((zk) => { zkProvider = zk; voteKeyGenerator = fs.readFileSync("./snarkBinaries/hashTestBinary"); let hashABI = { "inputs": [ { "name": "alpha", "public": true, "type": "array", "components": { "size": 2, "type": "field" } } ], "outputs": [ { "type": "field" } ] }; //@TODO: Convert hashABI into direct JSON read console.log("Computing Witness for dummy inputs"); let { witness, output } = zkProvider.computeWitness({program: voteKeyGenerator, abi: hashABI}, ["1337", "0"]); console.log("Witness:" + witness); console.log("Output: " + output); }).catch((e) => { console.log("Error during iniZokrates: " + e); });

OUTPUT:

Error during iniZokrates: Could not deserialize program: Invalid size 72057594042863450: sizes must fit in a usize (0 to 4294967295)

dark64 commented 3 years ago

Hey @rayyan808, thanks for the report! I just confirmed this is an issue when using CLI compiled binaries in the JS environment. As a quick workaround until we solve the issue, you can compile your program in JS using the compile API function, keep in mind it might take more time if you have a large circuit.

Linked to https://github.com/Zokrates/ZoKrates/issues/887

rayyan808 commented 3 years ago

Hello @dark64, thanks for the prompt response! I actually also thought it may be due to a different compilation environment, so I tried to compile within the JS environment but got the error: Uncaught RuntimeError: unreachable

I tried implementing the resolveCallback method of compile options in order to compile all imports but this is not intuitive and I couldn't get it to work. Instead, I combined all into a single .zok file (renaming the functions ofcourse) to pass into the compile API: compileZok = (e) => { e.preventDefault(); console.log(this.state.zokFile); const artifacts = zkProvider.compile(this.zokFile); console.log("artifact compiled: " + artifacts); }

Where this.state.zokFile is a simple string inferred by readAsText called on the file buffer.

If you could show me how to compile these, using the resolveCallBack method. I would be super greatful!

Here you can see the 3 seperate Zokrates Files:


import "./mimc-encryption.zok" as hash
/* All operations using field elements are mod p where p is the prime field,
So we don't need to perform a modulo each iteration*/
def main(field[2] alpha)->(field):
  field r = 0
  for u32 i in 0..2 do
    r = r + alpha[i] + hash(alpha[i], r)
  endfor
  return r

import "./mimc-constants.zok" as constants

def main(field x, field k)->(field):
  field[91] c = constants()
  for u32 i in 0..91 do
    field t = x + c[i] + k //mulmod(t,t,prime)
    x = t**7 // t^7 because 7th power is bijective in this field
  endfor
  return x + k

def main()->(field[91]):
    return [ in here theres 91 large primes but i wont print```
dark64 commented 3 years ago

You don't have to implement the resolveCallback if you inline everything so you don't have any import statements in your code.

def constants() -> field[91]:
    return [0; 91] // put your actual constants here

def hash(field x, field k)->(field):
  field[91] c = constants()
  for u32 i in 0..91 do
    field t = x + c[i] + k //mulmod(t,t,prime)
    x = t**7 // t^7 because 7th power is bijective in this field
  endfor
  return x + k

def main(field[2] alpha) -> field:
  field r = 0
  for u32 i in 0..2 do
    r = r + alpha[i] + hash(alpha[i], r)
  endfor
  return r

then something like this would work:

const { initialize } = require("zokrates-js/node"); // do not use `/node` if you are in the browser
const fs = require("fs");

initialize()
  .then((zk) => {
    let artifacts = zk.compile(fs.readFileSync("./test.zok").toString());
    let { witness, output } = zk.computeWitness(artifacts, [["1337", "0"]]);
    console.log("Witness: " + witness);
    console.log("Output: " + output);
  })
  .catch((e) => console.error(e));

Also, I've opened a PR regarding this issue https://github.com/Zokrates/ZoKrates/pull/912

rayyan808 commented 3 years ago

Yes, I have already tried putting everything inline into a single string. This does not work either. Taken from my post on Gittr "After merging all the zok files into one large zok file, it is still not compiling. "Uncaught RuntimeError: unreachable" thrown from wasm".

On Mon, 7 Jun 2021 at 18:50, Darko Macesic @.***> wrote:

You don't have to implement the resolveCallback if you inline everything so you don't have any import statements in your code.

def constants() -> field[91]: return [0; 91] // put your actual constants here

def hash(field x, field k)->(field): field[91] c = constants() for u32 i in 0..91 do field t = x + c[i] + k //mulmod(t,t,prime) x = t**7 // t^7 because 7th power is bijective in this field endfor return x + k

def main(field[2] alpha) -> field: field r = 0 for u32 i in 0..2 do r = r + alpha[i] + hash(alpha[i], r) endfor return r

then something like this would work:

const { initialize } = require("zokrates-js/node");const fs = require("fs"); initialize() .then((zk) => { let artifacts = zk.compile(fs.readFileSync("./test.zok").toString()); let { witness, output } = zk.computeWitness(artifacts, [["1337", "0"]]); console.log("Witness: " + witness); console.log("Output: " + output); }) .catch((e) => console.error(e));

Also, I've opened a PR regarding this issue #912 https://github.com/Zokrates/ZoKrates/pull/912

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Zokrates/ZoKrates/issues/909#issuecomment-856099128, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL3CZDZXVDNX3JIWGCRR4A3TRT2DRANCNFSM46GG2QUQ .

dark64 commented 3 years ago

Can you provide the whole code you are trying to inline and compile? The example I gave you above compiles just fine with the latest zokrates-js version, I cannot help you if I don't see what you're actually doing.