o1-labs / o1js

TypeScript framework for zk-SNARKs and zkApps

Home Page:https://docs.minaprotocol.com/en/zkapps/how-to-write-a-zkapp

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ZkProgram.compile() gives wrong wrap domain size error in o1js 0.18.0

dfstio opened this issue · comments

commented

While compiling the ZkProgram using o1js 0.18.0, the following error occur:

This circuit was compiled for proofs using the wrap domain of size 14, but the actual wrap domain size for the circuit has size 15. You should pass the ~override_wrap_domain argument to set the correct domain size.

This circuit was compiled for proofs using the wrap domain of size 14, but the actual wrap domain size for the circuit has size 15. You should pass the ~override_wrap_domain argument to set the correct domain size.

      at s (ocaml/ocaml/stdlib.ml:29:14)
      at ../../../../../../home/gregor/.opam/4.14.0/lib/base/printf.ml:6:43
      at _iAh_ (src/mina/src/lib/pickles/compile.ml:712:18)
      at ../../../../../../workspace_root/src/mina/src/lib/promise/js/promise.js:25:37
      at node_modules/o1js/dist/node/index.cjs:9317:29
      at withThreadPool (o1js/dist/node/index.cjs:3674:14)
      at prettifyStacktracePromise (o1js/dist/node/index.cjs:1931:12)
      at compileProgram (o1js/dist/node/index.cjs:9304:60)
      at Object.compile (o1js/dist/node/index.cjs:9144:67)
      at Object.<anonymous> (tests/compile.test.ts:294:5)

The test to reproduce the error:

import { describe, expect, it } from "@jest/globals";
import {
  Cache,
  Field,
  SelfProof,
  ZkProgram,
  Struct,
  Poseidon,
  PublicKey,
  UInt64,
  UInt8,
  Signature,
  MerkleMapWitness,
} from "o1js";

export class Data extends Struct({
  name: Field,
  data: Field,
}) {
  static empty(): Data {
    return new Data({
      name: Field(0),
      data: Field(0),
    });
  }
  hash(): Field {
    return Poseidon.hashPacked(Data, this);
  }
}

export type TransactionType = "add" | "extend" | "update" | "remove";

export const TransactionEnum: { [k in TransactionType]: UInt8 } = {
  add: UInt8.from(1),
  extend: UInt8.from(2),
  update: UInt8.from(3),
  remove: UInt8.from(4),
};

export class Transaction extends Struct({
  type: UInt8,
  data: Data,
}) {
  hash(): Field {
    return Poseidon.hashPacked(Transaction, this);
  }
}

class MapUpdateData extends Struct({
  oldRoot: Field,
  newRoot: Field,
  time: UInt64, // unix time when the map was updated
  tx: Transaction,
  witness: MerkleMapWitness,
}) {}

class MapTransition extends Struct({
  oldRoot: Field,
  newRoot: Field,
  time: UInt64,
  hash: Field,
  count: Field,
}) {
  static add(update: MapUpdateData) {
    update.tx.type.assertEquals(TransactionEnum.add);
    const key = update.tx.data.name;
    const value = update.tx.data.hash();

    const [rootBefore, keyBefore] = update.witness.computeRootAndKey(Field(0));
    update.oldRoot.assertEquals(rootBefore);
    key.assertEquals(keyBefore);

    const [rootAfter, keyAfter] = update.witness.computeRootAndKey(value);
    update.newRoot.assertEquals(rootAfter);
    key.assertEquals(keyAfter);

    const hash = update.tx.hash();

    return new MapTransition({
      oldRoot: update.oldRoot,
      newRoot: update.newRoot,
      hash,
      count: Field(1),
      time: update.time,
    });
  }

  static update(
    update: MapUpdateData,
    oldData: Data,
    signature: Signature,
    publicKey: PublicKey
  ) {
    update.tx.type.assertEquals(TransactionEnum.update);
    const key = update.tx.data.name;
    key.assertEquals(oldData.name);
    const value = update.tx.data.hash();
    const oldValue = oldData.hash();

    const [rootBefore, keyBefore] = update.witness.computeRootAndKey(oldValue);
    update.oldRoot.assertEquals(rootBefore);
    key.assertEquals(keyBefore);

    const [rootAfter, keyAfter] = update.witness.computeRootAndKey(value);
    update.newRoot.assertEquals(rootAfter);
    key.assertEquals(keyAfter);

    signature.verify(publicKey, Transaction.toFields(update.tx));

    const hash = update.tx.hash();

    return new MapTransition({
      oldRoot: update.oldRoot,
      newRoot: update.newRoot,
      hash,
      count: Field(1),
      time: update.time,
    });
  }

  static extend(update: MapUpdateData, oldData: Data) {
    update.tx.data.data.assertEquals(oldData.data);

    update.tx.type.assertEquals(TransactionEnum.extend);
    const key = update.tx.data.name;
    key.assertEquals(oldData.name);
    const value = update.tx.data.hash();
    const oldValue = oldData.hash();

    const [rootBefore, keyBefore] = update.witness.computeRootAndKey(oldValue);
    update.oldRoot.assertEquals(rootBefore);
    key.assertEquals(keyBefore);

    const [rootAfter, keyAfter] = update.witness.computeRootAndKey(value);
    update.newRoot.assertEquals(rootAfter);
    key.assertEquals(keyAfter);

    const hash = update.tx.hash();

    return new MapTransition({
      oldRoot: update.oldRoot,
      newRoot: update.newRoot,
      hash,
      count: Field(1),
      time: update.time,
    });
  }

  static remove(update: MapUpdateData) {
    update.tx.type.assertEquals(TransactionEnum.remove);
    const key = update.tx.data.name;
    const value = update.tx.data.hash();

    const [rootBefore, keyBefore] = update.witness.computeRootAndKey(value);
    update.oldRoot.assertEquals(rootBefore);
    key.assertEquals(keyBefore);

    const [rootAfter, keyAfter] = update.witness.computeRootAndKey(Field(0));
    update.newRoot.assertEquals(rootAfter);
    key.assertEquals(keyAfter);

    const hash = update.tx.hash();

    return new MapTransition({
      oldRoot: update.oldRoot,
      newRoot: update.newRoot,
      hash,
      count: Field(1),
      time: update.time,
    });
  }

  static reject(root: Field, time: UInt64, data: Data) {
    const hash = data.hash();
    return new MapTransition({
      oldRoot: root,
      newRoot: root,
      hash,
      count: Field(1),
      time,
    });
  }

  static merge(transition1: MapTransition, transition2: MapTransition) {
    transition1.newRoot.assertEquals(transition2.oldRoot);
    transition1.time.assertEquals(transition2.time);
    return new MapTransition({
      oldRoot: transition1.oldRoot,
      newRoot: transition2.newRoot,
      hash: transition1.hash.add(transition2.hash),
      count: transition1.count.add(transition2.count),
      time: transition1.time,
    });
  }

  static assertEquals(transition1: MapTransition, transition2: MapTransition) {
    transition1.oldRoot.assertEquals(transition2.oldRoot);
    transition1.newRoot.assertEquals(transition2.newRoot);
    transition1.hash.assertEquals(transition2.hash);
    transition1.count.assertEquals(transition2.count);
    transition1.time.assertEquals(transition2.time);
  }
}

const MyZkProgram = ZkProgram({
  name: "MyZkProgram",
  publicInput: MapTransition,

  methods: {
    add: {
      privateInputs: [MapUpdateData],

      async method(state: MapTransition, update: MapUpdateData) {
        const computedState = MapTransition.add(update);
        MapTransition.assertEquals(computedState, state);
      },
    },

    update: {
      privateInputs: [MapUpdateData, Data, Signature, PublicKey],

      async method(
        state: MapTransition,
        update: MapUpdateData,
        oldData: Data,
        signature: Signature,
        publicKey: PublicKey
      ) {
        const computedState = MapTransition.update(
          update,
          oldData,
          signature,
          publicKey
        );
        MapTransition.assertEquals(computedState, state);
      },
    },

    extend: {
      privateInputs: [MapUpdateData, Data],

      async method(state: MapTransition, update: MapUpdateData, oldData: Data) {
        const computedState = MapTransition.extend(update, oldData);
        MapTransition.assertEquals(computedState, state);
      },
    },

    remove: {
      privateInputs: [MapUpdateData],

      async method(state: MapTransition, update: MapUpdateData) {
        const computedState = MapTransition.remove(update);
        MapTransition.assertEquals(computedState, state);
      },
    },

    reject: {
      privateInputs: [Field, UInt64, Data],

      async method(
        state: MapTransition,
        root: Field,
        time: UInt64,
        data: Data
      ) {
        const computedState = MapTransition.reject(root, time, data);
        MapTransition.assertEquals(computedState, state);
      },
    },

    merge: {
      privateInputs: [SelfProof, SelfProof],

      async method(
        newState: MapTransition,
        proof1: SelfProof<MapTransition, void>,
        proof2: SelfProof<MapTransition, void>
      ) {
        proof1.verify();
        proof2.verify();
        const computedState = MapTransition.merge(
          proof1.publicInput,
          proof2.publicInput
        );
        MapTransition.assertEquals(computedState, newState);
      },
    },
  },
});

describe("Compile", () => {
  it(`should compile the ZkProgram`, async () => {
    const cache: Cache = Cache.FileSystem("./cache");
    await MyZkProgram.compile({ cache });
  });
});

Do you have the same issue with forceRecompile: true?

If yes, try passing overrideWrapDomain: 2 to the zkprogram config

commented

Thank you! Passing overrideWrapDomain: 2 to ZkProgram config resolves the issue.
Clearing the cache or passing option forceRecompile: true does not resolve it.