Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions packages/common/src/compiling/CompileRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { inject, injectable, singleton } from "tsyringe";

import { AreProofsEnabled } from "../zkProgrammable/ZkProgrammable";
import {
AreProofsEnabled,
CompileArtifact,
} from "../zkProgrammable/ZkProgrammable";

import {
ArtifactRecord,
Expand All @@ -27,18 +30,32 @@ export class CompileRegistry {

private artifacts: ArtifactRecord = {};

// TODO Add possibility to force recompilation for non-sideloaded dependencies
private inForceProverBlock = false;

/**
* This function forces compilation even if the artifact itself is in the registry.
* Basically the statement is: The artifact along is not enough, we need to
* actually have the prover compiled.
* This is true for non-sideloaded circuit dependencies.
*/
public async forceProverExists(
f: (registry: CompileRegistry) => Promise<void>
) {
this.inForceProverBlock = true;
await f(this);
this.inForceProverBlock = false;
}

public async compile(target: CompileTarget) {
if (this.artifacts[target.name] === undefined) {
if (this.artifacts[target.name] === undefined || this.inForceProverBlock) {
const artifact = await this.compiler.compileContract(target);
this.artifacts[target.name] = artifact;
return artifact;
}
return this.artifacts[target.name];
}

public getArtifact(name: string) {
public getArtifact(name: string): CompileArtifact | undefined {
if (this.artifacts[name] === undefined) {
throw new Error(
`Artifact for ${name} not available, did you compile it via the CompileRegistry?`
Expand Down
4 changes: 4 additions & 0 deletions packages/common/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ export function isSubtypeOfName(
clas: TypedClass<unknown>,
name: string
): boolean {
if (clas === undefined || clas === null) {
return false;
}

if (clas.name === name) {
return true;
}
Expand Down
6 changes: 4 additions & 2 deletions packages/protocol/src/prover/block/BlockProver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -940,8 +940,10 @@ export class BlockProver
public async compile(
registry: CompileRegistry
): Promise<Record<string, CompileArtifact> | undefined> {
await this.stateTransitionProver.compile(registry);
await this.runtime.compile(registry);
await registry.forceProverExists(async () => {
await this.stateTransitionProver.compile(registry);
await this.runtime.compile(registry);
});

return await this.zkProgrammable.compile(registry);
}
Expand Down
23 changes: 19 additions & 4 deletions packages/sequencer/src/protocol/baselayer/MinaBaseLayer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { DependencyFactory } from "@proto-kit/common";
import { AreProofsEnabled, DependencyFactory } from "@proto-kit/common";
import { Mina } from "o1js";
import { match } from "ts-pattern";
import { inject } from "tsyringe";

import { MinaIncomingMessageAdapter } from "../../settlement/messages/MinaIncomingMessageAdapter";
import { SequencerModule } from "../../sequencer/builder/SequencerModule";
import {
sequencerModule,
SequencerModule,
} from "../../sequencer/builder/SequencerModule";
import { MinaTransactionSender } from "../../settlement/transactions/MinaTransactionSender";
import { WithdrawalQueue } from "../../settlement/messages/WithdrawalQueue";

Expand All @@ -27,6 +31,7 @@ export interface MinaBaseLayerConfig {
};
}

@sequencerModule()
export class MinaBaseLayer
extends SequencerModule<MinaBaseLayerConfig>
implements BaseLayer, DependencyFactory
Expand All @@ -35,6 +40,13 @@ export class MinaBaseLayer

public originalNetwork?: Parameters<typeof Mina.setActiveInstance>[0];

public constructor(
@inject("AreProofsEnabled")
private readonly areProofsEnabled: AreProofsEnabled
) {
super();
}

public dependencies() {
return {
IncomingMessageAdapter: {
Expand Down Expand Up @@ -63,15 +75,18 @@ export class MinaBaseLayer
const Network = await match(network)
.with(
{ type: "local" },
async () => await Mina.LocalBlockchain({ proofsEnabled: false })
async () =>
await Mina.LocalBlockchain({
proofsEnabled: this.areProofsEnabled.areProofsEnabled,
})
)
.with({ type: "lightnet" }, async (lightnet) => {
const net = Mina.Network({
mina: lightnet.graphql,
archive: lightnet.archive,
lightnetAccountManager: lightnet.accountManager,
});
net.proofsEnabled = false;
net.proofsEnabled = this.areProofsEnabled.areProofsEnabled;
return net;
})
.with({ type: "remote" }, async (remote) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CompileRegistry,
CompilableModule,
safeParseJson,
reduceSequential,
} from "@proto-kit/common";
import {
MandatorySettlementModulesRecord,
Expand Down Expand Up @@ -132,11 +133,29 @@ export class CircuitCompilerTask extends UnpreparingTask<
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
settlementModule.moduleNames as StringKeyOf<MandatorySettlementModulesRecord>[];

const modules = moduleNames.map((name) => [
const modules = moduleNames.map<[string, CompilableModule]>((name) => [
`Settlement.${name}`,
settlementModule.resolve(name),
]);

const sumModule = {
compile: async (registry: CompileRegistry) => {
await reduceSequential<CompilableModule, ArtifactRecord>(
modules.map(([, module]) => module),
async (record, module) => {
const artifacts = await module.compile(registry);
return {
...record,
...artifacts,
};
},
{}
);
},
};

modules.push(["Settlement", sumModule]);

return Object.fromEntries(modules);
}
return {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,6 @@ export class StateTransitionTask
): Promise<StateTransitionProof> {
const stBatch = input.stateTransitions.slice();
const merkleWitnesses = input.merkleWitnesses.slice();
// Array.from({
// length: ProtocolConstants.stateTransitionProverBatchSize - stBatch.length,
// }).forEach(() => {
// stBatch.push({
// ProvableStateTransition.dummy()
// });
// });

const output = await this.stateTransitionProver.runBatch(
input.publicInput,
Expand Down
10 changes: 0 additions & 10 deletions packages/sequencer/src/sequencer/SequencerStartupModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,6 @@ export class SequencerStartupModule extends SequencerModule {
bridgeVk.verificationKey;
}

const record = await this.pushCompileTask(flow, {
existingArtifacts: this.compileRegistry.getAllArtifacts(),
targets: ["Settlement.SettlementContract"],
});

this.compileRegistry.addArtifactsRaw(record);

// TODO Compile all contracts and retrieve artifacts to enable crafting of
// the deployments - edit: can also be done on-demand with the CompileTask

await this.registrationFlow.start({
runtimeVerificationKeyRoot: root,
bridgeContractVerificationKey: bridgeVk?.verificationKey,
Expand Down
77 changes: 77 additions & 0 deletions packages/sequencer/src/sequencer/SettlementStartupModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { injectable } from "tsyringe";
import {
ArtifactRecord,
CompileArtifact,
CompileRegistry,
log,
} from "@proto-kit/common";

import { FlowCreator } from "../worker/flow/Flow";
import { CircuitCompilerTask } from "../protocol/production/tasks/CircuitCompilerTask";

@injectable()
export class SettlementStartupModule {
public constructor(
private readonly compileRegistry: CompileRegistry,
private readonly flowCreator: FlowCreator,
private readonly compileTask: CircuitCompilerTask
) {}

// TODO Compile only individual contracts - this however runs into the
// unlinkability issue from module name to artifact name
// although - the settlement proving task currently also only works if
// all contracts that a tx touches are compiled on that worker instance
private async compile() {
const flow = this.flowCreator.createFlow("compile-deploy", {});
const artifacts = await flow.withFlow<ArtifactRecord>(async (res) => {
await flow.pushTask(
this.compileTask,
{
existingArtifacts: this.compileRegistry.getAllArtifacts(),
targets: ["Settlement"],
runtimeVKRoot: undefined,
},
async (result) => res(result)
);
});
this.compileRegistry.addArtifactsRaw(artifacts);
return artifacts;
}

private async getArtifacts(retry: boolean): Promise<{
SettlementSmartContract: CompileArtifact;
DispatchSmartContract: CompileArtifact;
}> {
const settlementVerificationKey = this.compileRegistry.getArtifact(
"SettlementSmartContract"
);
const dispatchVerificationKey = this.compileRegistry.getArtifact(
"DispatchSmartContract"
);

if (
settlementVerificationKey === undefined ||
dispatchVerificationKey === undefined
) {
if (!retry) {
log.info(
"Settlement Contracts not yet compiled, initializing compilation"
);
await this.compile();
return await this.getArtifacts(true);
}
throw new Error(
"Settlement contract verification keys not available for deployment"
);
}

return {
SettlementSmartContract: settlementVerificationKey,
DispatchSmartContract: dispatchVerificationKey,
};
}

public async retrieveVerificationKeys() {
return await this.getArtifacts(false);
}
}
16 changes: 9 additions & 7 deletions packages/sequencer/src/settlement/SettlementModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
log,
AreProofsEnabled,
DependencyFactory,
CompileRegistry,
} from "@proto-kit/common";
import truncate from "lodash/truncate";

Expand All @@ -41,6 +40,7 @@ import { Batch, SettleableBatch } from "../storage/model/Batch";
import { BlockProofSerializer } from "../protocol/production/helpers/BlockProofSerializer";
import { Settlement } from "../storage/model/Settlement";
import { FeeStrategy } from "../protocol/baselayer/fees/FeeStrategy";
import { SettlementStartupModule } from "../sequencer/SettlementStartupModule";

import { IncomingMessageAdapter } from "./messages/IncomingMessageAdapter";
import { MinaTransactionSender } from "./transactions/MinaTransactionSender";
Expand Down Expand Up @@ -97,7 +97,7 @@ export class SettlementModule
@inject("AreProofsEnabled") areProofsEnabled: AreProofsEnabled,
@inject("FeeStrategy")
private readonly feeStrategy: FeeStrategy,
private readonly compileRegistry: CompileRegistry
private readonly settlementStartupModule: SettlementStartupModule
) {
super();
this.utils = new SettlementUtils(areProofsEnabled, baseLayer);
Expand Down Expand Up @@ -253,8 +253,6 @@ export class SettlementModule

const nonce = options?.nonce ?? 0;

// const verificationKey:

const sm = this.protocol.dependencyContainer.resolve<
SettlementContractModule<MandatorySettlementModulesRecord>
>("SettlementContractModule");
Expand All @@ -263,6 +261,9 @@ export class SettlementModule
dispatch: dispatchKey.toPublicKey(),
});

const verificationsKeys =
await this.settlementStartupModule.retrieveVerificationKeys();

const permissions = this.utils.isSignedSettlement()
? new SignedSettlementPermissions()
: new ProvenSettlementPermissions();
Expand All @@ -277,13 +278,14 @@ export class SettlementModule
async () => {
AccountUpdate.fundNewAccount(feepayer, 2);
await settlement.deploy({
// TODO Create compilation task that generates those artifacts if proofs enabled
verificationKey: undefined,
verificationKey:
verificationsKeys.SettlementSmartContract.verificationKey,
});
settlement.account.permissions.set(permissions.settlementContract());

await dispatch.deploy({
verificationKey: undefined,
verificationKey:
verificationsKeys.DispatchSmartContract.verificationKey,
});
dispatch.account.permissions.set(permissions.dispatchContract());
}
Expand Down
Loading
Loading