o1js
o1js copied to clipboard
ZkProgram.compile() gives wrong wrap domain size error in o1js 0.18.0
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
Thank you! Passing overrideWrapDomain: 2
to ZkProgram config resolves the issue.
Clearing the cache or passing option forceRecompile: true
does not resolve it.