Skip to content

Commit 76b4fa8

Browse files
committed
Smart Contracts compiling
1 parent 42e316c commit 76b4fa8

37 files changed

+650
-296
lines changed

packages/protocol/src/compiling/AtomicCompileHelper.ts renamed to packages/common/src/compiling/AtomicCompileHelper.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import {
22
AreProofsEnabled,
33
CompileArtifact,
4-
log,
54
MOCK_VERIFICATION_KEY,
6-
} from "@proto-kit/common";
7-
import { SmartContract, VerificationKey } from "o1js";
5+
} from "../zkProgrammable/ZkProgrammable";
6+
import { isSubtypeOfName } from "../utils";
7+
import { TypedClass } from "../types";
8+
import { log } from "../log";
89

910
export type ArtifactRecord = Record<string, CompileArtifact>;
1011

@@ -29,12 +30,24 @@ export class AtomicCompileHelper {
2930
if (this.compilationPromises[name] === undefined) {
3031
const proofsEnabled =
3132
overrideProofsEnabled ?? this.areProofsEnabled.areProofsEnabled;
32-
// This wierd any is necessary otherwise the compiler optimized that check away
33-
if (proofsEnabled || !((contract as any) instanceof SmartContract)) {
33+
34+
// We only care about proofs enabled here if it's a contract, because
35+
// in all other cases, ZkProgrammable already handles this switch, and we
36+
// want to preserve the artifact layout (which might be more than one
37+
// entry for ZkProgrammables)
38+
if (
39+
proofsEnabled ||
40+
!isSubtypeOfName(
41+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
42+
contract as unknown as TypedClass<any>,
43+
"SmartContract"
44+
)
45+
) {
3446
log.time(`Compiling ${name}`);
3547
this.compilationPromises[name] = contract.compile();
3648
newPromise = true;
3749
} else {
50+
log.trace(`Compiling ${name} - mock`);
3851
this.compilationPromises[name] = Promise.resolve({
3952
verificationKey: MOCK_VERIFICATION_KEY,
4053
});

packages/protocol/src/compiling/CompilableModule.ts renamed to packages/common/src/compiling/CompilableModule.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { CompileRegistry } from "./CompileRegistry";
2-
import { ArtifactRecord } from "./AtomicCompileHelper";
2+
import type { ArtifactRecord } from "./AtomicCompileHelper";
33

44
export interface CompilableModule {
55
compile(registry: CompileRegistry): Promise<ArtifactRecord | void>;
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { inject, injectable, singleton } from "tsyringe";
2+
3+
import { AreProofsEnabled } from "../zkProgrammable/ZkProgrammable";
4+
5+
import {
6+
ArtifactRecord,
7+
AtomicCompileHelper,
8+
CompileTarget,
9+
} from "./AtomicCompileHelper";
10+
import { CompilableModule } from "./CompilableModule";
11+
12+
interface GenericCompilableModule<Artifact> {
13+
compile(registry: CompileRegistry): Promise<Artifact>;
14+
}
15+
16+
export type InferDependencyArtifacts<
17+
Dependencies extends Record<string, CompilableModule>,
18+
> = {
19+
[Key in keyof Dependencies]: Dependencies[Key] extends GenericCompilableModule<
20+
infer Artifact
21+
>
22+
? Artifact
23+
: void;
24+
};
25+
/**
26+
* The CompileRegistry compiles "compilable modules"
27+
* (i.e. zkprograms, contracts or contractmodules)
28+
* while making sure they don't get compiled twice in the same process in parallel.
29+
*/
30+
@injectable()
31+
@singleton()
32+
export class CompileRegistry {
33+
public constructor(
34+
@inject("AreProofsEnabled")
35+
private readonly areProofsEnabled: AreProofsEnabled
36+
) {
37+
this.compiler = new AtomicCompileHelper(this.areProofsEnabled);
38+
}
39+
40+
private compiler: AtomicCompileHelper;
41+
42+
private artifacts: ArtifactRecord = {};
43+
44+
// private cachedModuleOutputs: Record<string, ArtifactRecord | void> = {};
45+
46+
// TODO Add possibility to force recompilation for non-sideloaded dependencies
47+
48+
public async compile(target: CompileTarget) {
49+
if (this.artifacts[target.name] === undefined) {
50+
const artifact = await this.compiler.compileContract(target);
51+
this.artifacts[target.name] = artifact;
52+
return artifact;
53+
}
54+
return this.artifacts[target.name];
55+
}
56+
57+
// public async compileModule<
58+
// ReturnType extends ArtifactRecord,
59+
// Dependencies extends Record<string, CompilableModule>,
60+
// >(
61+
// compile: (
62+
// compiler: AtomicCompileHelper,
63+
// args: InferDependencyArtifacts<Dependencies>
64+
// ) => Promise<ReturnType>,
65+
// dependencies?: Dependencies
66+
// ): Promise<ReturnType | undefined> {
67+
// const collectedArtifacts = await mapSequential(
68+
// Object.entries(dependencies ?? {}),
69+
// async ([depName, dep]) => {
70+
// if (this.cachedModuleOutputs[depName] !== undefined) {
71+
// return [depName, this.cachedModuleOutputs[depName]];
72+
// }
73+
// const artifact = await dep.compile(this);
74+
// if (artifact !== undefined) {
75+
// this.artifacts = {
76+
// ...this.artifacts,
77+
// ...artifact,
78+
// };
79+
// }
80+
// this.cachedModuleOutputs[depName] = artifact;
81+
// return [depName, artifact];
82+
// }
83+
// );
84+
//
85+
// const artifacts = await compile(
86+
// this.compile,
87+
// Object.fromEntries(collectedArtifacts)
88+
// );
89+
//
90+
// this.artifacts = {
91+
// ...this.artifacts,
92+
// ...artifacts,
93+
// };
94+
//
95+
// return artifacts;
96+
// }
97+
98+
public getArtifact(name: string) {
99+
if (this.artifacts[name] === undefined) {
100+
throw new Error(
101+
`Artifact for ${name} not available, did you compile it via the CompileRegistry?`
102+
);
103+
}
104+
105+
return this.artifacts[name];
106+
}
107+
108+
public addArtifactsRaw(artifacts: ArtifactRecord) {
109+
this.artifacts = {
110+
...this.artifacts,
111+
...artifacts,
112+
};
113+
}
114+
115+
public getAllArtifacts() {
116+
return this.artifacts;
117+
}
118+
}
File renamed without changes.

packages/common/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ export * from "./trees/RollupMerkleTree";
1818
export * from "./events/EventEmitterProxy";
1919
export * from "./events/ReplayingSingleUseEventEmitter";
2020
export * from "./trees/MockAsyncMerkleStore";
21+
export * from "./compiling/AtomicCompileHelper";
22+
export * from "./compiling/CompileRegistry";
23+
export * from "./compiling/CompilableModule";
24+
export * from "./compiling/services/ChildVerificationKeyService";

packages/common/src/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// allows to reference interfaces as 'classes' rather than instances
2-
import { Bool, Field, PublicKey } from "o1js";
2+
import { Bool, DynamicProof, Field, Proof, ProofBase, PublicKey } from "o1js";
33

44
export type TypedClass<Class> = new (...args: any[]) => Class;
55

@@ -47,3 +47,12 @@ export const EMPTY_PUBLICKEY = PublicKey.fromObject({
4747
export type OverwriteObjectType<Base, New> = {
4848
[Key in keyof Base]: Key extends keyof New ? New[Key] : Base[Key];
4949
} & New;
50+
51+
export type InferProofBase<
52+
ProofType extends Proof<any, any> | DynamicProof<any, any>,
53+
> =
54+
ProofType extends Proof<infer PI, infer PO>
55+
? ProofBase<PI, PO>
56+
: ProofType extends DynamicProof<infer PI, infer PO>
57+
? ProofBase<PI, PO>
58+
: undefined;

packages/common/src/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
Proof,
77
} from "o1js";
88

9+
import { TypedClass } from "./types";
10+
911
export function requireTrue(
1012
condition: boolean,
1113
errorOrFunction: Error | (() => Error)
@@ -164,3 +166,19 @@ type NonMethodKeys<Type> = {
164166
[Key in keyof Type]: Type[Key] extends Function ? never : Key;
165167
}[keyof Type];
166168
export type NonMethods<Type> = Pick<Type, NonMethodKeys<Type>>;
169+
170+
/**
171+
* Returns a boolean indicating whether a given class is a subclass of another class,
172+
* indicated by the name parameter.
173+
*/
174+
// TODO Change to class reference based comparisons
175+
export function isSubtypeOfName(
176+
clas: TypedClass<unknown>,
177+
name: string
178+
): boolean {
179+
if (clas.name === name) {
180+
return true;
181+
}
182+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
183+
return isSubtypeOfName(Object.getPrototypeOf(clas), name);
184+
}

packages/common/src/zkProgrammable/ZkProgrammable.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Memoize } from "typescript-memoize";
44
import { log } from "../log";
55
import { dummyVerificationKey } from "../dummyVerificationKey";
66
import { reduceSequential } from "../utils";
7+
import type { CompileRegistry } from "../compiling/CompileRegistry";
78

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

@@ -74,8 +75,6 @@ export function verifyToMockable<PublicInput, PublicOutput>(
7475
return verified;
7576
}
7677

77-
console.log("VerifyMocked");
78-
7978
return proof.proof === MOCK_PROOF;
8079
};
8180
}
@@ -128,11 +127,11 @@ export abstract class ZkProgrammable<
128127
});
129128
}
130129

131-
public async compile() {
130+
public async compile(registry: CompileRegistry) {
132131
return await reduceSequential(
133132
this.zkProgram,
134133
async (acc, program) => {
135-
const result = await program.compile();
134+
const result = await registry.compile(program);
136135
return {
137136
...acc,
138137
[program.name]: result,

packages/module/src/runtime/Runtime.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import {
1111
PlainZkProgram,
1212
AreProofsEnabled,
1313
ChildContainerProvider,
14+
CompilableModule,
15+
CompileRegistry,
1416
} from "@proto-kit/common";
1517
import {
1618
MethodPublicOutput,
1719
StateServiceProvider,
1820
SimpleAsyncStateService,
19-
CompilableModule,
20-
CompileRegistry,
2121
RuntimeMethodExecutionContext,
2222
RuntimeTransaction,
2323
NetworkState,
@@ -384,14 +384,12 @@ export class Runtime<Modules extends RuntimeModulesRecord>
384384
}
385385

386386
public async compile(registry: CompileRegistry) {
387-
return await registry.compileModule(async () => {
388-
const context = container.resolve(RuntimeMethodExecutionContext);
389-
context.setup({
390-
transaction: RuntimeTransaction.dummyTransaction(),
391-
networkState: NetworkState.empty(),
392-
});
393-
return await this.zkProgrammable.compile();
387+
const context = container.resolve(RuntimeMethodExecutionContext);
388+
context.setup({
389+
transaction: RuntimeTransaction.dummyTransaction(),
390+
networkState: NetworkState.empty(),
394391
});
392+
return await this.zkProgrammable.compile(registry);
395393
}
396394
}
397395
/* eslint-enable @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-argument */

packages/protocol/src/compiling/CompileRegistry.ts

Lines changed: 0 additions & 84 deletions
This file was deleted.

0 commit comments

Comments
 (0)