Skip to content

Commit 42e316c

Browse files
committed
Added ChildVerificationKeyService to allow for sideloading in protocol circuits
1 parent a45933f commit 42e316c

File tree

6 files changed

+151
-22
lines changed

6 files changed

+151
-22
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { injectable, Lifecycle, scoped } from "tsyringe";
2+
3+
import { CompileRegistry } from "../CompileRegistry";
4+
5+
@injectable()
6+
@scoped(Lifecycle.ContainerScoped)
7+
export class ChildVerificationKeyService {
8+
private compileRegistry?: CompileRegistry;
9+
10+
public setCompileRegistry(registry: CompileRegistry) {
11+
this.compileRegistry = registry;
12+
}
13+
14+
public getVerificationKey(name: string) {
15+
if (this.compileRegistry === undefined) {
16+
throw new Error("CompileRegistry hasn't been set yet");
17+
}
18+
const artifact = this.compileRegistry.getArtifact(name);
19+
if (artifact === undefined) {
20+
throw new Error(
21+
`Verification Key for child program ${name} not found in registry`
22+
);
23+
}
24+
return artifact.verificationKey;
25+
}
26+
}

packages/protocol/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from "./compiling/AtomicCompileHelper";
22
export * from "./compiling/CompileRegistry";
33
export * from "./compiling/CompilableModule";
4+
export * from "./compiling/services/ChildVerificationKeyService";
45
export * from "./hooks/AccountStateHook";
56
export * from "./hooks/BlockHeightHook";
67
export * from "./hooks/LastStateRootBlockHook";

packages/sequencer/src/protocol/production/tasks/CircuitCompilerTask.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
CompileRegistry,
1010
CompilableModule,
1111
BridgeContractProtocolModule,
12+
RuntimeVerificationKeyRootService,
1213
} from "@proto-kit/protocol";
1314

1415
import { TaskSerializer } from "../../../worker/flow/Task";
@@ -18,6 +19,7 @@ import { VerificationKeySerializer } from "../helpers/VerificationKeySerializer"
1819
export type CompilerTaskParams = {
1920
existingArtifacts: ArtifactRecord;
2021
targets: string[];
22+
runtimeVKRoot?: string;
2123
};
2224

2325
type SerializedArtifactRecord = Record<
@@ -117,6 +119,14 @@ export class CircuitCompilerTask extends UnpreparingTask<
117119
public async compute(input: CompilerTaskParams): Promise<ArtifactRecord> {
118120
this.compileRegistry.addArtifactsRaw(input.existingArtifacts);
119121

122+
// We need to initialize the VK tree root if we have it, so that
123+
// the BlockProver can bake in that root
124+
if (input.runtimeVKRoot !== undefined) {
125+
this.protocol.dependencyContainer
126+
.resolve(RuntimeVerificationKeyRootService)
127+
.setRoot(BigInt(input.runtimeVKRoot));
128+
}
129+
120130
log.info("Computing VKs");
121131

122132
// TODO make adaptive
@@ -132,7 +142,10 @@ export class CircuitCompilerTask extends UnpreparingTask<
132142
if (target in targets) {
133143
await targets[target].compile(this.compileRegistry);
134144
} else {
135-
throw new Error(`Compile target ${target} not found`);
145+
log.info(
146+
// TODO Is that right? Or should we check that the bridge exists on the sequencer side?
147+
`Compile target ${target} not found, skipping`
148+
);
136149
}
137150
});
138151
log.timeEnd.info(msg);

packages/sequencer/src/protocol/runtime/RuntimeVerificationKeyService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class VerificationKeyService extends ConfigurableModule<{}> {
9393
);
9494
}
9595

96-
public async initializeVKTreeFromMethodMappings(verificationKeys: VKRecord) {
96+
private async initializeVKTreeFromMethodMappings(verificationKeys: VKRecord) {
9797
const tree = new VKTree(new InMemoryMerkleTreeStorage());
9898
const valuesVK: Record<string, { data: string; hash: Field }> = {};
9999
const indexes: VKIndexes = {};

packages/sequencer/src/sequencer/SequencerStartupModule.ts

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { inject } from "tsyringe";
22
import {
33
ArtifactRecord,
4+
CompileRegistry,
45
MandatoryProtocolModulesRecord,
56
Protocol,
67
RuntimeVerificationKeyRootService,
78
SettlementSmartContractBase,
89
} from "@proto-kit/protocol";
9-
import { log } from "@proto-kit/common";
10+
import { CompileArtifact, log } from "@proto-kit/common";
1011

11-
import { FlowCreator } from "../worker/flow/Flow";
12+
import { Flow, FlowCreator } from "../worker/flow/Flow";
1213
import { WorkerRegistrationFlow } from "../worker/worker/startup/WorkerRegistrationFlow";
13-
import { CircuitCompilerTask } from "../protocol/production/tasks/CircuitCompilerTask";
14+
import {
15+
CircuitCompilerTask,
16+
CompilerTaskParams,
17+
} from "../protocol/production/tasks/CircuitCompilerTask";
1418
import { VerificationKeyService } from "../protocol/runtime/RuntimeVerificationKeyService";
1519

1620
import { SequencerModule, sequencerModule } from "./builder/SequencerModule";
@@ -23,47 +27,114 @@ export class SequencerStartupModule extends SequencerModule {
2327
private readonly protocol: Protocol<MandatoryProtocolModulesRecord>,
2428
private readonly compileTask: CircuitCompilerTask,
2529
private readonly verificationKeyService: VerificationKeyService,
26-
private readonly registrationFlow: WorkerRegistrationFlow
30+
private readonly registrationFlow: WorkerRegistrationFlow,
31+
private readonly compileRegistry: CompileRegistry
2732
) {
2833
super();
2934
}
3035

31-
public async start() {
32-
const flow = this.flowCreator.createFlow("compile-circuits", {});
36+
private async pushCompileTask(
37+
flow: Flow<{}>,
38+
payload: CompilerTaskParams
39+
): Promise<ArtifactRecord> {
40+
return await flow.withFlow<ArtifactRecord>(async (res, rej) => {
41+
await flow.pushTask(this.compileTask, payload, async (result) => {
42+
res(result);
43+
});
44+
});
45+
}
3346

34-
log.info("Compiling Protocol circuits, this can take a few minutes");
47+
public async compileRuntime(flow: Flow<{}>) {
48+
const artifacts = await this.pushCompileTask(flow, {
49+
existingArtifacts: {},
50+
targets: ["runtime"],
51+
runtimeVKRoot: undefined,
52+
});
53+
54+
// Init runtime VK tree
55+
await this.verificationKeyService.initializeVKTree(artifacts);
56+
57+
const root = this.verificationKeyService.getRoot();
58+
59+
this.protocol.dependencyContainer
60+
.resolve(RuntimeVerificationKeyRootService)
61+
.setRoot(root);
62+
63+
this.compileRegistry.addArtifactsRaw(artifacts);
64+
65+
return root;
66+
}
67+
68+
private async compileProtocolAndBridge(flow: Flow<{}>) {
69+
// Can happen in parallel
70+
type ParallelResult = {
71+
protocol?: ArtifactRecord;
72+
bridge?: ArtifactRecord;
73+
};
74+
const result = await flow.withFlow<ArtifactRecord>(async (res, rej) => {
75+
const results: ParallelResult = {};
76+
77+
const resolveIfPossible = () => {
78+
const { bridge, protocol } = results;
79+
if (bridge !== undefined && protocol !== undefined) {
80+
res({ ...protocol, ...bridge });
81+
}
82+
};
3583

36-
const artifacts = await flow.withFlow<ArtifactRecord>(async (res, rej) => {
3784
await flow.pushTask(
3885
this.compileTask,
39-
{ existingArtifacts: {}, targets: ["runtime"] },
86+
{
87+
existingArtifacts: {},
88+
targets: ["protocol"],
89+
runtimeVKRoot: undefined,
90+
},
4091
async (result) => {
41-
res(result);
92+
results.protocol = result;
93+
resolveIfPossible();
94+
}
95+
);
96+
97+
await flow.pushTask(
98+
this.compileTask,
99+
{
100+
existingArtifacts: {},
101+
targets: ["bridge"],
102+
runtimeVKRoot: undefined,
103+
},
104+
async (result) => {
105+
results.bridge = result;
106+
resolveIfPossible();
42107
}
43108
);
44109
});
110+
this.compileRegistry.addArtifactsRaw(result);
111+
return result;
112+
}
45113

46-
log.info("Protocol circuits compiled");
114+
public async start() {
115+
const flow = this.flowCreator.createFlow("compile-circuits", {});
47116

48-
// Init runtime VK tree
49-
await this.verificationKeyService.initializeVKTree(artifacts);
117+
log.info("Compiling Protocol circuits, this can take a few minutes");
50118

51-
const root = this.verificationKeyService.getRoot();
119+
const root = await this.compileRuntime(flow);
52120

53-
this.protocol.dependencyContainer
54-
.resolve(RuntimeVerificationKeyRootService)
55-
.setRoot(root);
121+
const protocolBridgeArtifacts = await this.compileProtocolAndBridge(flow);
122+
123+
log.info("Protocol circuits compiled");
56124

57125
// Init BridgeContract vk for settlement contract
58-
const bridgeVk = artifacts.BridgeContract;
126+
const bridgeVk = protocolBridgeArtifacts.BridgeContract;
59127
if (bridgeVk !== undefined) {
60128
SettlementSmartContractBase.args.BridgeContractVerificationKey =
61129
bridgeVk.verificationKey;
62130
}
63131

132+
// TODO Add vk record to start params
133+
64134
await this.registrationFlow.start({
65135
runtimeVerificationKeyRoot: root,
66-
bridgeContractVerificationKey: bridgeVk.verificationKey,
136+
bridgeContractVerificationKey: bridgeVk?.verificationKey,
137+
compiledArtifacts: this.compileRegistry.getAllArtifacts(),
67138
});
68139

69140
log.info("Protocol circuits compiled successfully, commencing startup");

packages/sequencer/src/worker/worker/startup/WorkerRegistrationTask.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { log, noop } from "@proto-kit/common";
22
import { inject, injectable } from "tsyringe";
33
import {
4+
ArtifactRecord,
5+
ChildVerificationKeyService,
6+
CompileRegistry,
47
Protocol,
58
RuntimeVerificationKeyRootService,
69
SettlementSmartContractBase,
@@ -12,11 +15,13 @@ import { AbstractStartupTask } from "../../flow/AbstractStartupTask";
1215
import { VerificationKeySerializer } from "../../../protocol/production/helpers/VerificationKeySerializer";
1316

1417
import { CloseWorkerError } from "./CloseWorkerError";
18+
import { ArtifactRecordSerializer } from "../../../protocol/production/tasks/CircuitCompilerTask";
1519

1620
export type WorkerStartupPayload = {
1721
runtimeVerificationKeyRoot: bigint;
1822
// This has to be nullable, since
1923
bridgeContractVerificationKey?: VerificationKey;
24+
compiledArtifacts: ArtifactRecord;
2025
};
2126

2227
@injectable()
@@ -28,7 +33,8 @@ export class WorkerRegistrationTask
2833
private done = false;
2934

3035
public constructor(
31-
@inject("Protocol") private readonly protocol: Protocol<any>
36+
@inject("Protocol") private readonly protocol: Protocol<any>,
37+
private readonly compileRegistry: CompileRegistry
3238
) {
3339
super();
3440
}
@@ -55,13 +61,19 @@ export class WorkerRegistrationTask
5561
input.bridgeContractVerificationKey;
5662
}
5763

64+
this.compileRegistry.addArtifactsRaw(input.compiledArtifacts);
65+
this.protocol.dependencyContainer
66+
.resolve(ChildVerificationKeyService)
67+
.setCompileRegistry(this.compileRegistry);
68+
5869
this.events.emit("startup-task-finished");
5970

6071
this.done = true;
6172
return true;
6273
}
6374

6475
public inputSerializer() {
76+
const artifactSerializer = new ArtifactRecordSerializer();
6577
return {
6678
toJSON: (payload: WorkerStartupPayload) => {
6779
return JSON.stringify({
@@ -73,6 +85,9 @@ export class WorkerRegistrationTask
7385
payload.bridgeContractVerificationKey
7486
)
7587
: undefined,
88+
compiledArtifacts: artifactSerializer.toJSON(
89+
payload.compiledArtifacts
90+
),
7691
});
7792
},
7893
fromJSON: (payload: string) => {
@@ -91,6 +106,9 @@ export class WorkerRegistrationTask
91106
jsonObject.bridgeContractVerificationKey
92107
)
93108
: undefined,
109+
compiledArtifacts: artifactSerializer.fromJSON(
110+
jsonObject.compiledArtifacts
111+
),
94112
};
95113
},
96114
};

0 commit comments

Comments
 (0)