Skip to content

Commit

Permalink
Merge pull request #224 from proto-kit/feature/proven-settlemnet
Browse files Browse the repository at this point in the history
Enabled proven settlement
  • Loading branch information
maht0rz authored Jan 28, 2025
2 parents d9cc60a + c157add commit c0b98ce
Show file tree
Hide file tree
Showing 16 changed files with 294 additions and 95 deletions.
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

0 comments on commit c0b98ce

Please sign in to comment.