blazorjavascript-importzk-snark

Blazor calling SnarkJS


For SnarkJS (https://github.com/iden3/snarkjs), there's an HTML example showing how to consume their ES6 javascript library:

<!doctype html>
<html>
<body>

  <script src="snarkjs.min.js">   </script>
  <script>

async function calculateProof() {

    const { proof, publicSignals } =
      await snarkjs.groth16.fullProve( { a: 3, b: 11}, "circuit.wasm", "circuit_final.zkey");
      
  </script>

</body>
</html>

This call to snarkjs.groth16.fullProve works perfectly in the browser.

In Blazor, I load snarkjs.min.js into an IJSObjectReference (myJSmodule), but I don't know how to emulate the snarkjs.groth16.fullProve call from Blazor to the module. Something like:

await myJSmodule.InvokeVoidAsync("snarkjs.groth16.fullProve", args...)

but that produces

Could not find 'snarkjs.groth16.fullProve'...'snarkjs' was undefined

EDIT: In the end, I had two problems:

  1. Chrome was caching my embedded javascript (not the dynamically loaded modules) so running the app would result in it using outdated cached javascript. Solution was to long-click on the Chrome refresh button, and choosing "Empty Cache and Hard Reload". Though I'm not sure if this bodes well for deployed consumer-facing websites.

  2. Dimitris' answer below took care of the second problem.


Solution

  • You can create a helper js module that exports the calculateProof function:

    snarkJsInteropHelper.js

    export async function calculateProof() {
        const { proof, publicSignals } =
            await snarkjs.groth16.fullProve({ a: 3, b: 11 }, "circuit.wasm", "circuit_final.zkey");
    }
    

    Then in blazor component:

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var module = await JS.InvokeAsync<IJSObjectReference>(
              "import", "./snarkJsInteropHelper.js");
    
            await module.InvokeVoidAsync("calculateProof");
        }
    }