Skip to content

Commit 92f8420

Browse files
committed
Added new CompileRegistry with adaptive compiling and caching
1 parent 8b50c29 commit 92f8420

20 files changed

+286
-146
lines changed

packages/common/src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function reduceSequential<T, U>(
5656
array: T[]
5757
) => Promise<U>,
5858
initialValue: U
59-
) {
59+
): Promise<U> {
6060
return array.reduce<Promise<U>>(
6161
async (previousPromise, current, index, arr) => {
6262
const previous = await previousPromise;

packages/common/src/zkProgrammable/ZkProgrammable.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Memoize } from "typescript-memoize";
33

44
import { log } from "../log";
55
import { dummyVerificationKey } from "../dummyVerificationKey";
6+
import { reduceSequential } from "../utils";
67

78
import { MOCK_PROOF } from "./provableMethod";
89

@@ -126,6 +127,21 @@ export abstract class ZkProgrammable<
126127
};
127128
});
128129
}
130+
131+
public async compile() {
132+
return await reduceSequential(
133+
this.zkProgram,
134+
async (acc, program) => {
135+
const result = await program.compile();
136+
return {
137+
...acc,
138+
[program.name]: result,
139+
};
140+
},
141+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
142+
{} as Record<string, CompileArtifact>
143+
);
144+
}
129145
}
130146

131147
export interface WithZkProgrammable<
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
AreProofsEnabled,
3+
log,
4+
MOCK_VERIFICATION_KEY,
5+
} from "@proto-kit/common";
6+
7+
export type Artifact = string | object | undefined;
8+
9+
export type GenericCompileTarget<T extends Artifact> = {
10+
compile: () => Promise<T>;
11+
};
12+
13+
export class AtomicCompileHelper {
14+
public constructor(private readonly areProofsEnabled: AreProofsEnabled) {}
15+
16+
private compilationPromises: {
17+
[key: string]: Promise<Artifact | undefined>;
18+
} = {};
19+
20+
// Generic params for zkProgrammable should be unknown, but verify makes those types invariant
21+
// public async zkProgrammable(zkProgrammable: ZkProgrammable<any, any>) {
22+
// await reduceSequential(
23+
// zkProgrammable.zkProgram,
24+
// async (acc, program) => {
25+
// const res = await this.program(program);
26+
// return {
27+
// ...acc,
28+
// [program.name]: res,
29+
// };
30+
// },
31+
// {}
32+
// );
33+
// }
34+
35+
public async program<ReturnArtifact extends Artifact>(
36+
name: string,
37+
contract: GenericCompileTarget<ReturnArtifact>,
38+
overrideProofsEnabled?: boolean
39+
): Promise<ReturnArtifact> {
40+
let newPromise = false;
41+
if (this.compilationPromises[name] === undefined) {
42+
const proofsEnabled =
43+
overrideProofsEnabled ?? this.areProofsEnabled.areProofsEnabled;
44+
if (proofsEnabled) {
45+
log.time(`Compiling ${name}`);
46+
this.compilationPromises[name] = contract.compile();
47+
newPromise = true;
48+
} else {
49+
// TODO Mock VK here is not at all generalized and safe
50+
// Better way would be to package the Smart contracts into a mock-compile as well
51+
this.compilationPromises[name] = Promise.resolve(MOCK_VERIFICATION_KEY);
52+
}
53+
}
54+
const result = await this.compilationPromises[name];
55+
if (newPromise) {
56+
log.timeEnd.info(`Compiling ${name}`);
57+
}
58+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
59+
return result as ReturnArtifact;
60+
}
61+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { CompileRegistry } from "./CompileRegistry";
2+
import { Artifact } from "./AtomicCompileHelper";
3+
4+
export interface CompilableModule {
5+
compile(registry: CompileRegistry): Promise<Artifact | void>;
6+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { inject, injectable, singleton } from "tsyringe";
2+
import {
3+
AreProofsEnabled,
4+
mapSequential,
5+
MOCK_VERIFICATION_KEY,
6+
} from "@proto-kit/common";
7+
8+
import {
9+
Artifact,
10+
AtomicCompileHelper,
11+
GenericCompileTarget,
12+
} from "./AtomicCompileHelper";
13+
import { CompilableModule } from "./CompilableModule";
14+
15+
/**
16+
* The CompileRegistry compiles "compilable modules"
17+
* (i.e. zkprograms, contracts or contractmodules)
18+
* while making sure they don't get compiled twice in the same process in parallel.
19+
*/
20+
@injectable()
21+
@singleton()
22+
export class CompileRegistry {
23+
public constructor(
24+
@inject("AreProofsEnabled")
25+
private readonly areProofsEnabled: AreProofsEnabled
26+
) {
27+
this.compile = new AtomicCompileHelper(this.areProofsEnabled);
28+
}
29+
30+
public compile: AtomicCompileHelper;
31+
32+
private artifacts: Record<string, Artifact | "compiled-but-no-artifact"> = {};
33+
34+
public async compileModule<ReturnArtifact extends Artifact>(
35+
// TODO Make name inferred by the module token
36+
name: string,
37+
compile: (
38+
registry: CompileRegistry,
39+
...args: unknown[]
40+
) => GenericCompileTarget<ReturnArtifact>,
41+
dependencies: Record<string, CompilableModule> = {}
42+
): Promise<ReturnArtifact | undefined> {
43+
const collectedArtifacts = await mapSequential(
44+
Object.entries(dependencies),
45+
async ([depName, dep]) => {
46+
if (this.artifacts[depName] !== undefined) {
47+
return this.artifacts[depName];
48+
}
49+
const artifact =
50+
(await dep.compile(this)) ?? "compiled-but-no-artifact";
51+
this.artifacts[depName] = artifact;
52+
return artifact;
53+
}
54+
);
55+
56+
const target = compile(this, ...collectedArtifacts);
57+
const artifact = await this.compile.program<ReturnArtifact>(name, target);
58+
59+
this.artifacts[name] = artifact ?? "compiled-but-no-artifact";
60+
61+
return artifact;
62+
}
63+
64+
public getArtifact<ArtifactType extends Artifact>(name: string) {
65+
if (this.artifacts[name] === undefined) {
66+
throw new Error(
67+
`Artifact for ${name} not available, did you compile it via the CompileRegistry?`
68+
);
69+
}
70+
if (!this.areProofsEnabled.areProofsEnabled) {
71+
return MOCK_VERIFICATION_KEY;
72+
}
73+
74+
const artifact = this.artifacts[name];
75+
if (artifact === "compiled-but-no-artifact") {
76+
throw new Error(
77+
`Module ${name} didn't return the requested artifact even though proofs are enabled`
78+
);
79+
}
80+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
81+
return artifact as ArtifactType;
82+
}
83+
84+
public addArtifactsRaw(artifacts: Record<string, Artifact>) {
85+
Object.entries(artifacts).forEach(([key, value]) => {
86+
this.artifacts[key] = value;
87+
});
88+
}
89+
}

packages/protocol/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
export * from "./compiling/AtomicCompileHelper";
2+
export * from "./compiling/CompileRegistry";
3+
export * from "./compiling/CompilableModule";
14
export * from "./hooks/AccountStateHook";
25
export * from "./hooks/BlockHeightHook";
36
export * from "./hooks/LastStateRootBlockHook";
@@ -17,7 +20,6 @@ export * from "./utils/MinaPrefixedProvableHashList";
1720
export * from "./utils/ProvableReductionHashList";
1821
export * from "./utils/StateTransitionReductionList";
1922
export * from "./utils/utils";
20-
export * from "./utils/CompileRegistry";
2123
export * from "./prover/block/BlockProver";
2224
export * from "./prover/block/BlockProvable";
2325
export * from "./prover/block/accummulators/RuntimeVerificationKeyTree";

packages/protocol/src/prover/block/BlockProvable.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { NetworkState } from "../../model/network/NetworkState";
1616

1717
import { BlockHashMerkleTreeWitness } from "./accummulators/BlockHashMerkleTree";
1818
import { RuntimeVerificationKeyAttestation } from "./accummulators/RuntimeVerificationKeyTree";
19+
import { CompilableModule } from "../../compiling/CompilableModule";
1920

2021
export class BlockProverPublicInput extends Struct({
2122
transactionsHash: Field,
@@ -77,7 +78,8 @@ export class DynamicRuntimeProof extends DynamicProof<
7778
}
7879

7980
export interface BlockProvable
80-
extends WithZkProgrammable<BlockProverPublicInput, BlockProverPublicOutput> {
81+
extends WithZkProgrammable<BlockProverPublicInput, BlockProverPublicOutput>,
82+
CompilableModule {
8183
proveTransaction: (
8284
publicInput: BlockProverPublicInput,
8385
stateProof: StateTransitionProof,

packages/protocol/src/prover/block/BlockProver.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { container, inject, injectable, injectAll } from "tsyringe";
1313
import {
1414
AreProofsEnabled,
15+
CompileArtifact,
1516
PlainZkProgram,
1617
provableMethod,
1718
WithZkProgrammable,
@@ -41,6 +42,7 @@ import {
4142
MinaActionsHashList,
4243
} from "../../utils/MinaPrefixedProvableHashList";
4344
import { StateTransitionReductionList } from "../../utils/StateTransitionReductionList";
45+
import { CompileRegistry } from "../../compiling/CompileRegistry";
4446

4547
import {
4648
BlockProvable,
@@ -60,6 +62,7 @@ import {
6062
RuntimeVerificationKeyAttestation,
6163
} from "./accummulators/RuntimeVerificationKeyTree";
6264
import { RuntimeVerificationKeyRootService } from "./services/RuntimeVerificationKeyRootService";
65+
import { CompilableModule } from "../../compiling/CompilableModule";
6366

6467
const errors = {
6568
stateProofNotStartingAtZero: () =>
@@ -149,6 +152,8 @@ export class BlockProverProgrammable extends ZkProgrammable<
149152
super();
150153
}
151154

155+
name = "BlockProver";
156+
152157
public get areProofsEnabled(): AreProofsEnabled | undefined {
153158
return this.prover.areProofsEnabled;
154159
}
@@ -889,7 +894,10 @@ export class BlockProverProgrammable extends ZkProgrammable<
889894
* then be merged to be committed to the base-layer contract
890895
*/
891896
@injectable()
892-
export class BlockProver extends ProtocolModule implements BlockProvable {
897+
export class BlockProver
898+
extends ProtocolModule
899+
implements BlockProvable, CompilableModule
900+
{
893901
public zkProgrammable: BlockProverProgrammable;
894902

895903
public constructor(
@@ -917,6 +925,15 @@ export class BlockProver extends ProtocolModule implements BlockProvable {
917925
);
918926
}
919927

928+
public async compile(
929+
registry: CompileRegistry
930+
): Promise<Record<string, CompileArtifact> | undefined> {
931+
return await registry.compileModule(
932+
"BlockProver",
933+
() => this.zkProgrammable
934+
);
935+
}
936+
920937
public proveTransaction(
921938
publicInput: BlockProverPublicInput,
922939
stateProof: StateTransitionProof,

packages/protocol/src/prover/statetransition/StateTransitionProvable.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { WithZkProgrammable } from "@proto-kit/common";
44
import { StateTransitionProvableBatch } from "../../model/StateTransitionProvableBatch";
55

66
import { StateTransitionWitnessProviderReference } from "./StateTransitionWitnessProviderReference";
7+
import { CompilableModule } from "../../compiling/CompilableModule";
78

89
export class StateTransitionProverPublicInput extends Struct({
910
stateTransitionsHash: Field,
@@ -26,9 +27,10 @@ export type StateTransitionProof = Proof<
2627

2728
export interface StateTransitionProvable
2829
extends WithZkProgrammable<
29-
StateTransitionProverPublicInput,
30-
StateTransitionProverPublicOutput
31-
> {
30+
StateTransitionProverPublicInput,
31+
StateTransitionProverPublicOutput
32+
>,
33+
CompilableModule {
3234
witnessProviderReference: StateTransitionWitnessProviderReference;
3335

3436
runBatch: (

packages/protocol/src/prover/statetransition/StateTransitionProver.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import {
2929
} from "./StateTransitionProvable";
3030
import { StateTransitionWitnessProvider } from "./StateTransitionWitnessProvider";
3131
import { StateTransitionWitnessProviderReference } from "./StateTransitionWitnessProviderReference";
32+
import { CompilableModule } from "../../compiling/CompilableModule";
33+
import { Artifact } from "../../compiling/AtomicCompileHelper";
34+
import { CompileRegistry } from "../../compiling/CompileRegistry";
3235

3336
const errors = {
3437
propertyNotMatching: (property: string, step: string) =>
@@ -346,7 +349,10 @@ export class StateTransitionProverProgrammable extends ZkProgrammable<
346349
@injectable()
347350
export class StateTransitionProver
348351
extends ProtocolModule
349-
implements StateTransitionProvable, StateTransitionProverType
352+
implements
353+
StateTransitionProvable,
354+
StateTransitionProverType,
355+
CompilableModule
350356
{
351357
public zkProgrammable: StateTransitionProverProgrammable;
352358

@@ -361,6 +367,13 @@ export class StateTransitionProver
361367
);
362368
}
363369

370+
public compile(registry: CompileRegistry): Promise<void | Artifact> {
371+
return registry.compileModule(
372+
"StateTransitionProver",
373+
() => this.zkProgrammable
374+
);
375+
}
376+
364377
public runBatch(
365378
publicInput: StateTransitionProverPublicInput,
366379
batch: StateTransitionProvableBatch

0 commit comments

Comments
 (0)