diff --git a/package-lock.json b/package-lock.json index f5626d39d..9f35a71f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,8 @@ }, "devDependencies": { "@nrwl/nx-cloud": "latest", + "@swc/core": "^1.15.11", + "@swc/wasm": "^1.15.11", "@types/jest": "^29.4.0", "@types/node": "^20.2.5", "@typescript-eslint/eslint-plugin": "^7.3.1", @@ -6866,6 +6868,215 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@swc/core": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz", + "integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.11", + "@swc/core-darwin-x64": "1.15.11", + "@swc/core-linux-arm-gnueabihf": "1.15.11", + "@swc/core-linux-arm64-gnu": "1.15.11", + "@swc/core-linux-arm64-musl": "1.15.11", + "@swc/core-linux-x64-gnu": "1.15.11", + "@swc/core-linux-x64-musl": "1.15.11", + "@swc/core-win32-arm64-msvc": "1.15.11", + "@swc/core-win32-ia32-msvc": "1.15.11", + "@swc/core-win32-x64-msvc": "1.15.11" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz", + "integrity": "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz", + "integrity": "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz", + "integrity": "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz", + "integrity": "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz", + "integrity": "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz", + "integrity": "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz", + "integrity": "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz", + "integrity": "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz", + "integrity": "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz", + "integrity": "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -6873,15 +7084,34 @@ "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" + "@swc/counter": "^0.1.3" } }, + "node_modules/@swc/wasm": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.15.11.tgz", + "integrity": "sha512-230rdYZf8ux3nIwISOQNCFrxzxpL/UFY4Khv/3UsvpEdo709j/+Tg80yXWW3DXETeZNPBV72QpvEBhXsl7Lc9g==", + "devOptional": true, + "license": "Apache-2.0" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "dev": true, @@ -21035,6 +21265,16 @@ } } }, + "node_modules/next/node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -26850,7 +27090,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tsscmp": { diff --git a/package.json b/package.json index a27f72dc1..41ced0d57 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "private": true, "devDependencies": { "@nrwl/nx-cloud": "latest", + "@swc/core": "^1.15.11", + "@swc/wasm": "^1.15.11", "@types/jest": "^29.4.0", "@types/node": "^20.2.5", "@typescript-eslint/eslint-plugin": "^7.3.1", diff --git a/packages/deployment/src/queue/BullQueue.ts b/packages/deployment/src/queue/BullQueue.ts index 179003e47..69d10a734 100644 --- a/packages/deployment/src/queue/BullQueue.ts +++ b/packages/deployment/src/queue/BullQueue.ts @@ -1,5 +1,5 @@ import { MetricsTime, Queue, QueueEvents, Worker } from "bullmq"; -import { log } from "@proto-kit/common"; +import { log, ModuleContainerLike } from "@proto-kit/common"; import { TaskPayload, Closeable, @@ -7,9 +7,11 @@ import { TaskQueue, AbstractTaskQueue, closeable, + sequencerModule, } from "@proto-kit/sequencer"; import { InstantiatedBullQueue } from "./InstantiatedBullQueue"; +import { inject } from "tsyringe"; export interface BullQueueConfig { redis: { @@ -26,12 +28,23 @@ export interface BullQueueConfig { * TaskQueue implementation for BullMQ */ @closeable() +@sequencerModule() export class BullQueue extends AbstractTaskQueue implements TaskQueue, Closeable { + public constructor( + @inject("ParentContainer") private parent: ModuleContainerLike + ) { + super(); + } + private activePromise?: Promise; + private workers: Worker[] = []; + + private jobsInProgress = 0; + public createWorker( name: string, executor: (data: TaskPayload) => Promise, @@ -45,11 +58,11 @@ export class BullQueue // computing them, so that leads to bad performance over multiple workers. // For that we need to restructure tasks to be flowing through a single queue however - // TODO Use worker.pause() while (this.activePromise !== undefined) { // eslint-disable-next-line no-await-in-loop await this.activePromise; } + let resOutside: () => void = () => {}; // TODO Use Promise.withResolvers() for that const promise = new Promise((res) => { @@ -57,22 +70,32 @@ export class BullQueue }); this.activePromise = promise; + this.jobsInProgress += 1; + await Promise.all(this.workers.map((w) => w.pause())); + const result = await executor(job.data); this.activePromise = undefined; void resOutside(); + this.jobsInProgress -= 1; + if (this.jobsInProgress === 0) { + this.workers.map((w) => w.resume()); + } + return result; }, { concurrency: options?.concurrency ?? 1, connection: this.config.redis, stalledInterval: 60000, // 1 minute - lockDuration: 60000, // 1 minute + lockDuration: 60000 * 5, // 5 minutes metrics: { maxDataPoints: MetricsTime.ONE_HOUR * 24 }, } ); + this.workers.push(worker); + // We have to do this, because we want to prevent the worker from crashing worker.on("error", (error) => { log.error("Worker threw error:"); @@ -101,9 +124,16 @@ export class BullQueue }); } + private isMaster() { + return this.parent.dependencyContainer.isRegistered("BatchProducerModule"); + } + public async start() { - // Drain all queues to clear stale tasks from previous sequencer instances - await this.drainAllQueues(); + if (this.isMaster()) { + log.debug("Instance is master, draining queue"); + // Drain all queues to clear stale tasks from previous sequencer instances + await this.drainAllQueues(); + } } public async close() { diff --git a/packages/protocol/src/state/WitnessBlockContext.ts b/packages/protocol/src/state/WitnessBlockContext.ts index aafbfd242..ee9ce3881 100644 --- a/packages/protocol/src/state/WitnessBlockContext.ts +++ b/packages/protocol/src/state/WitnessBlockContext.ts @@ -19,9 +19,11 @@ const asyncProxyWitnessFunction = < return async (...args: Parameters) => { const context = container.resolve(WitnessBlockContext); context.witnessBlockDepth += 1; - const ret = await originalFuncDef(...args); - context.witnessBlockDepth -= 1; - return ret; + try { + return await originalFuncDef(...args); + } finally { + context.witnessBlockDepth -= 1; + } }; }; @@ -35,9 +37,11 @@ const proxySyncWitnessFunction = < return (...args: Params): Ret => { const context = container.resolve(WitnessBlockContext); context.witnessBlockDepth += 1; - const ret = originalFuncDef(...args); - context.witnessBlockDepth -= 1; - return ret; + try { + return originalFuncDef(...args); + } finally { + context.witnessBlockDepth -= 1; + } }; }; diff --git a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts index dfa151eaf..d4da54714 100644 --- a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts @@ -169,7 +169,6 @@ export class NewBlockTask } public async prepare(): Promise { - // Compile - await this.transactionProver.compile(this.compileRegistry); + await this.blockProver.compile(this.compileRegistry); } } diff --git a/packages/sequencer/src/settlement/tasks/SettlementProvingTask.ts b/packages/sequencer/src/settlement/tasks/SettlementProvingTask.ts index 48dce2994..3a8cb2318 100644 --- a/packages/sequencer/src/settlement/tasks/SettlementProvingTask.ts +++ b/packages/sequencer/src/settlement/tasks/SettlementProvingTask.ts @@ -47,6 +47,7 @@ type Account = ReturnType; export type ChainStateTaskArgs = { accounts: Account[]; graphql: string | undefined; + archive: string | undefined; }; export type TransactionTaskArgs = { @@ -106,7 +107,7 @@ export class SettlementProvingTask state: ChainStateTaskArgs, f: () => Promise ): Promise { - const { graphql, accounts } = state; + const { accounts, graphql, archive } = state; // For this, we assume that remote networks will only be used with separate // worker instances, since they only work with proofs enabled. For @@ -116,7 +117,10 @@ export class SettlementProvingTask if (graphql !== undefined) { const oldInstance = Mina.activeInstance; - const newInstance = Mina.Network(graphql); + const newInstance = Mina.Network({ + mina: graphql, + archive, + }); newInstance.proofsEnabled = this.areProofsEnabled.areProofsEnabled; Mina.setActiveInstance(newInstance); @@ -223,6 +227,7 @@ export class SettlementProvingTask lazyProofs: (LazyProofJson | null)[]; chainState: { graphql: string | undefined | null; + archive: string | undefined | null; accounts: AccountJson[]; }; }; @@ -323,6 +328,7 @@ export class SettlementProvingTask transaction, chainState: { graphql: jsonObject.chainState.graphql ?? undefined, + archive: jsonObject.chainState.archive ?? undefined, accounts: jsonObject.chainState.accounts.map((account) => Types.Account.fromJSON(account) ), @@ -414,6 +420,7 @@ export class SettlementProvingTask lazyProofs, chainState: { graphql: input.chainState.graphql, + archive: input.chainState.archive, accounts: input.chainState.accounts.map((account) => Types.Account.toJSON(account) ), diff --git a/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts b/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts index a594d85c0..32c59ea8a 100644 --- a/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts +++ b/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts @@ -157,6 +157,7 @@ export class MinaTransactionSender { const { network } = this.baseLayer.config; const graphql = network.type === "local" ? undefined : network.graphql; + const archive = network.type === "local" ? undefined : network.archive; const resultPromise = flow.withFlow( async (resolve, reject) => { @@ -166,6 +167,7 @@ export class MinaTransactionSender { transaction, chainState: { graphql, + archive, accounts: accounts .map((r) => r.account) .filter(filterNonUndefined), diff --git a/packages/sequencer/src/worker/flow/Flow.ts b/packages/sequencer/src/worker/flow/Flow.ts index 6dd7d5e02..3c4f43105 100644 --- a/packages/sequencer/src/worker/flow/Flow.ts +++ b/packages/sequencer/src/worker/flow/Flow.ts @@ -112,6 +112,7 @@ export class Flow implements Closeable { completed?: CompletedCallback, overrides?: { taskName?: string; + maxRetries?: number; } ): Promise { // We wrap this in a try-catch here, because the flow architecture diff --git a/packages/sequencer/src/worker/queue/LocalTaskQueue.ts b/packages/sequencer/src/worker/queue/LocalTaskQueue.ts index e325508ca..5ce6826f3 100644 --- a/packages/sequencer/src/worker/queue/LocalTaskQueue.ts +++ b/packages/sequencer/src/worker/queue/LocalTaskQueue.ts @@ -15,12 +15,14 @@ interface QueueListener { export interface LocalTaskQueueConfig { simulatedDuration?: number; + retryAttempts?: number; } class InMemoryInstantiatedQueue implements InstantiatedQueue { public constructor( public readonly name: string, - public taskQueue: LocalTaskQueue + public taskQueue: LocalTaskQueue, + private readonly retries: number ) {} private id = 0; @@ -35,7 +37,11 @@ class InMemoryInstantiatedQueue implements InstantiatedQueue { ): Promise<{ taskId: string }> { this.id += 1; const nextId = taskId ?? String(this.id).toString(); - this.taskQueue.queuedTasks[this.name].push({ payload, taskId: nextId }); + this.taskQueue.queuedTasks[this.name].push({ + payload, + taskId: nextId, + retries: this.retries, + }); void this.taskQueue.workNextTasks(); @@ -74,7 +80,7 @@ export class LocalTaskQueue implements TaskQueue { public queuedTasks: { - [key: string]: { payload: TaskPayload; taskId: string }[]; + [key: string]: { payload: TaskPayload; taskId: string; retries: number }[]; } = {}; private workers: { @@ -117,6 +123,22 @@ export class LocalTaskQueue } log.trace("LocalTaskQueue got", JSON.stringify(payload)); + if (payload.status === "error" && task.retries >= 1) { + log.info( + `Task ${task.taskId} ${task.payload.name} failed, retrying` + ); + + // TODO Not sounds yet, iterator iterates over old entries without + // this new task + this.queuedTasks[queueName].push({ + payload: task.payload, + taskId: task.taskId, + retries: task.retries - 1, + }); + + return; + } + // Notify listeners about result const listenerPromises = this.listeners[queueName]?.map( async (listener) => { @@ -187,7 +209,11 @@ export class LocalTaskQueue public async getQueue(queueName: string): Promise { return this.createOrGetQueue(queueName, (name) => { this.queuedTasks[name] = []; - return new InMemoryInstantiatedQueue(name, this); + return new InMemoryInstantiatedQueue( + name, + this, + this.config.retryAttempts ?? 2 + ); }); } diff --git a/packages/sequencer/src/worker/worker/FlowTaskWorker.ts b/packages/sequencer/src/worker/worker/FlowTaskWorker.ts index d07142898..0a131e315 100644 --- a/packages/sequencer/src/worker/worker/FlowTaskWorker.ts +++ b/packages/sequencer/src/worker/worker/FlowTaskWorker.ts @@ -142,6 +142,13 @@ export class FlowTaskWorker[]> this.preparePromise = preparePromise; if (startupTasks.length > 0) { + for (const task of startupTasks) { + log.info(`Preparing task ${task.constructor.name}`); + // eslint-disable-next-line no-await-in-loop + await task.prepare(); + log.debug(`${task.constructor.name} prepared`); + } + this.workers = Object.fromEntries( unpreparingTasks .concat(startupTasks) @@ -161,6 +168,7 @@ export class FlowTaskWorker[]> } }); }); + log.debug(`Waiting on ${startupTasks.length} startup tasks to be called`); } else { await this.prepareTasks(normalTasks.concat(unpreparingTasks)); } diff --git a/packages/sequencer/src/worker/worker/startup/WorkerRegistrationTask.ts b/packages/sequencer/src/worker/worker/startup/WorkerRegistrationTask.ts index 30339d0fc..c1cc8a3f5 100644 --- a/packages/sequencer/src/worker/worker/startup/WorkerRegistrationTask.ts +++ b/packages/sequencer/src/worker/worker/startup/WorkerRegistrationTask.ts @@ -58,7 +58,7 @@ export class WorkerRegistrationTask public name = "worker-registration"; public async prepare() { - noop(); + log.info("Waiting on sequencer to send registration info..."); } public async compute(input: WorkerStartupPayload) { @@ -115,6 +115,8 @@ export class WorkerRegistrationTask this.events.emit("startup-task-finished"); + log.info("Worker registration completed"); + this.done = true; return true; } diff --git a/packages/sequencer/test-integration/Settlement-devnet.test.ts b/packages/sequencer/test-integration/Settlement-devnet.test.ts new file mode 100644 index 000000000..f02d7563b --- /dev/null +++ b/packages/sequencer/test-integration/Settlement-devnet.test.ts @@ -0,0 +1,50 @@ +import { describe } from "@jest/globals"; +import { settlementTestFn } from "../test/settlement/Settlement"; +import { MinaBaseLayerConfig } from "../src"; +import { FungibleToken } from "mina-fungible-token"; +import { PrivateKey } from "o1js"; + +describe.skip("Settlement - Devnet", () => { + const network: MinaBaseLayerConfig = { + network: { + type: "remote", + graphql: "https://api.minascan.io/node/devnet/v1/graphql", + archive: "https://api.minascan.io/archive/devnet/v1/graphql", + // type: "local" + }, + }; + + const sequencerKey = PrivateKey.fromBase58( + "EKFBrGinEnesgbsNJMHikKVSJxxcRQBaSUEi55jD5YfQeRxVLBKN" + ); + + const userKey = PrivateKey.fromBase58( + "EKE6jLyNZePXufcRDrJ8HbtWvTqoJKXFwKsd2ZG3iUeHxJ2xegHp" + ); + + it("Random pk", () => { + const pk = PrivateKey.random(); + console.log(pk.toBase58()); + console.log(pk.toPublicKey().toBase58()); + }); + + describe("Default token", () => { + settlementTestFn("proven", network, undefined, 500_000, undefined, [ + sequencerKey, + userKey, + ]); + }); + + describe("Custom token", () => { + settlementTestFn( + "proven", + network, + { + tokenOwner: FungibleToken, + }, + 500_000, + undefined, + [sequencerKey, userKey] + ); + }); +}); diff --git a/packages/sequencer/test-integration/Settlement-lightnet-worker.test.ts b/packages/sequencer/test-integration/Settlement-lightnet-worker.test.ts new file mode 100644 index 000000000..4d0c71f99 --- /dev/null +++ b/packages/sequencer/test-integration/Settlement-lightnet-worker.test.ts @@ -0,0 +1,67 @@ +import { PrivateKey } from "o1js"; +import { log } from "@proto-kit/common"; +import { FungibleToken } from "mina-fungible-token"; +import { BullQueueConfig } from "@proto-kit/deployment"; +import { afterAll, beforeAll } from "@jest/globals"; + +import { settlementTestFn } from "../test/settlement/Settlement"; + +import { ChildProcessWorker } from "./workers/ChildProcessWorker"; +import { BullConfig } from "./workers/modules"; + +console.log(PrivateKey.random().toPublicKey().toBase58()); + +log.setLevel("DEBUG"); + +describe.skip.each(["signed"] as const)( + "settlement contracts: workers + lightnet - %s", + (type) => { + const network = { + network: { + type: "lightnet", + graphql: "http://127.0.0.1:8080/graphql", + archive: "http://127.0.0.1:8282", + accountManager: "http://127.0.0.1:8181", + }, + } as const; + + const queue: BullQueueConfig = { + redis: BullConfig.redis, + retryAttempts: 2, + }; + + const worker = new ChildProcessWorker(); + + beforeAll(() => { + console.error("Starting"); + worker.start( + "worker-0", + "./test-integration/settlement-worker.ts", + true, + { + PROOFS_ENABLED: `${(type as string) === "proven"}`, + } + ); + }); + + afterAll(() => { + worker.kill(); + }); + + describe("Default token", () => { + settlementTestFn(type, network, undefined, 360_000, queue); + }); + + describe.skip("Custom token", () => { + settlementTestFn( + type, + network, + { + tokenOwner: FungibleToken, + }, + 360_000, + queue + ); + }); + } +); diff --git a/packages/sequencer/test-integration/settlement-worker.ts b/packages/sequencer/test-integration/settlement-worker.ts new file mode 100644 index 000000000..b630507a0 --- /dev/null +++ b/packages/sequencer/test-integration/settlement-worker.ts @@ -0,0 +1,73 @@ +#!/usr/bin/env ts-node + +import "reflect-metadata"; + +// eslint-disable-next-line import/no-extraneous-dependencies +import { BullQueue } from "@proto-kit/deployment"; +import { log } from "@proto-kit/common"; +import { container } from "tsyringe"; +import { Runtime } from "@proto-kit/module"; +import { Protocol } from "@proto-kit/protocol"; + +import { + protocolModules, + protocolModulesConfig, + runtimeModules, + runtimeModulesConfig, +} from "../test/settlement/Settlement"; +import { + AppChain, + LocalTaskWorkerModule, + Sequencer, + VanillaTaskWorkerModules, +} from "../src"; + +import { MinimumWorkerModules } from "./workers/WorkerModules"; +import { BullConfig } from "./workers/modules"; + +/* eslint-disable no-console */ +async function main() { + const proofsEnabled = process.env.PROOFS_ENABLED === "true"; + // const settlementEnabled = process.env.SETTLEMENT_ENABLED === "true"; + + console.log("Starting worker..."); + console.log(`Worker proofs enabled: ${proofsEnabled}`); + // console.log(`Worker settlement enabled: ${settlementEnabled}`); + + log.setLevel("DEBUG"); + + const sequencerClass = Sequencer.from({ + TaskQueue: BullQueue, + LocalTaskWorkerModule: LocalTaskWorkerModule.from( + // settlementEnabled + VanillaTaskWorkerModules.allTasks() + // : VanillaTaskWorkerModules.withoutSettlement() + ), + } satisfies MinimumWorkerModules); + + const app = AppChain.from({ + Runtime: Runtime.from(runtimeModules), + Sequencer: sequencerClass, + Protocol: Protocol.from(protocolModules), + }); + + app.configure({ + Runtime: runtimeModulesConfig, + Protocol: protocolModulesConfig, + Sequencer: { + TaskQueue: BullConfig, + LocalTaskWorkerModule: VanillaTaskWorkerModules.defaultConfig(), + }, + }); + + log.setLevel("DEBUG"); + + await app.start(proofsEnabled, container.createChildContainer()); +} + +const isSpawned = process.env.IS_SPAWNED_PROCESS === "true"; + +if (isSpawned) { + await main(); +} +/* eslint-enable no-console */ diff --git a/packages/sequencer/test-integration/tsconfig.json b/packages/sequencer/test-integration/tsconfig.json index c53620588..0a123c904 100644 --- a/packages/sequencer/test-integration/tsconfig.json +++ b/packages/sequencer/test-integration/tsconfig.json @@ -3,5 +3,15 @@ "compilerOptions": { "experimentalDecorators": true }, + "ts-node": { + "experimentalSpecifierResolution": "node", + "esm": true, + "experimentalResolver": true, + "transpileOnly": true, + "swc": true, + "compilerOptions": { + "skipLibCheck": true, + } + }, "include": ["./**/*.ts", "./**/*.test.ts", "./*.ts", "./*.ts"] } diff --git a/packages/sequencer/test-integration/workers/ChildProcessWorker.ts b/packages/sequencer/test-integration/workers/ChildProcessWorker.ts index d27ba9560..32eb74c2a 100644 --- a/packages/sequencer/test-integration/workers/ChildProcessWorker.ts +++ b/packages/sequencer/test-integration/workers/ChildProcessWorker.ts @@ -3,14 +3,24 @@ import { spawn, ChildProcess } from "node:child_process"; export class ChildProcessWorker { process?: ChildProcess; - start(forwardLogs: boolean = true, env_args: Record = {}) { + start( + name: string, + file: string, + forwardLogs: boolean = true, + env_args: Record = {} + ) { + console.log("Spawning process"); + const s = spawn( "node", [ + "--loader", + "ts-node/esm", "--experimental-vm-modules", "--experimental-wasm-modules", - "../../node_modules/jest/bin/jest.js", - "./test-integration/workers/worker.test.ts", + "--es-module-specifier-resolution=node", + "--no-warnings", + file, ], { env: { @@ -20,17 +30,31 @@ export class ChildProcessWorker { }, } ); + + [ + "exit", + "SIGINT", + "SIGUSR1", + "SIGUSR2", + "uncaughtException", + "SIGTERM", + ].forEach((eventType) => { + process.on(eventType, () => this.kill()); + }); + s.on("error", (err) => { // eslint-disable-next-line no-console console.error(err); }); if (forwardLogs) { s.stdout.on("data", (data) => { + process.stdout.write(`${name}: `); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument process.stdout.write(data); }); } s.stderr.on("data", (data) => { + process.stderr.write(`${name}: `); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument process.stderr.write(data); }); diff --git a/packages/sequencer/test-integration/workers/modules.ts b/packages/sequencer/test-integration/workers/modules.ts index d65204c04..64fbf290f 100644 --- a/packages/sequencer/test-integration/workers/modules.ts +++ b/packages/sequencer/test-integration/workers/modules.ts @@ -27,11 +27,7 @@ export const runtimeProtocolConfig: ModulesConfig<{ Balance: {}, }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), ProtocolStateTestHook: {}, }, }; diff --git a/packages/sequencer/test-integration/workers/worker.test.ts b/packages/sequencer/test-integration/workers/worker.test.ts deleted file mode 100644 index 7d599915b..000000000 --- a/packages/sequencer/test-integration/workers/worker.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import "reflect-metadata"; -import { BullQueue } from "@proto-kit/deployment"; -import { container } from "tsyringe"; -import { log, sleep } from "@proto-kit/common"; - -import { - AppChain, - LocalTaskWorkerModule, - Sequencer, - VanillaTaskWorkerModules, -} from "../../src"; - -import { - BullConfig, - protocolClass, - runtimeClass, - runtimeProtocolConfig, -} from "./modules"; -import { MinimumWorkerModules } from "./WorkerModules"; - -describe("worker", () => { - const isSpawned = process.env.IS_SPAWNED_PROCESS === "true"; - - it("spin up and wait", async () => { - if (!isSpawned) { - return; - } - - const proofsEnabled = process.env.PROOFS_ENABLED === "true"; - - const sequencerClass = Sequencer.from({ - TaskQueue: BullQueue, - LocalTaskWorkerModule: LocalTaskWorkerModule.from( - VanillaTaskWorkerModules.withoutSettlement() - ), - } satisfies MinimumWorkerModules); - - const app = AppChain.from({ - Runtime: runtimeClass, - Sequencer: sequencerClass, - Protocol: protocolClass, - }); - - app.configure({ - ...runtimeProtocolConfig, - Sequencer: { - TaskQueue: BullConfig, - LocalTaskWorkerModule: VanillaTaskWorkerModules.defaultConfig(), - }, - }); - - console.log("Starting worker..."); - console.log(`Worker proofs enabled: ${proofsEnabled}`); - - log.setLevel("DEBUG"); - - await app.start(proofsEnabled, container.createChildContainer()); - - console.log("Worker started..."); - - const ready = await new Promise((res) => { - app - .resolve("Sequencer") - .resolve("LocalTaskWorkerModule") - .containerEvents.on("ready", res); - }); - - expect(ready).toBe(true); - - console.log("Ready received!"); - - await sleep(10000000); - }, 10000000); -}); diff --git a/packages/sequencer/test-integration/workers/worker.ts b/packages/sequencer/test-integration/workers/worker.ts new file mode 100644 index 000000000..d75cf9f56 --- /dev/null +++ b/packages/sequencer/test-integration/workers/worker.ts @@ -0,0 +1,73 @@ +import "reflect-metadata"; +// eslint-disable-next-line import/no-extraneous-dependencies +import { BullQueue } from "@proto-kit/deployment"; +import { container } from "tsyringe"; +import { log, sleep } from "@proto-kit/common"; + +import { + AppChain, + LocalTaskWorkerModule, + Sequencer, + VanillaTaskWorkerModules, +} from "../../src"; + +import { + BullConfig, + protocolClass, + runtimeClass, + runtimeProtocolConfig, +} from "./modules"; +import { MinimumWorkerModules } from "./WorkerModules"; + +async function main() { + const proofsEnabled = process.env.PROOFS_ENABLED === "true"; + + const sequencerClass = Sequencer.from({ + TaskQueue: BullQueue, + LocalTaskWorkerModule: LocalTaskWorkerModule.from( + VanillaTaskWorkerModules.withoutSettlement() + ), + } satisfies MinimumWorkerModules); + + const app = AppChain.from({ + Runtime: runtimeClass, + Sequencer: sequencerClass, + Protocol: protocolClass, + }); + + app.configure({ + ...runtimeProtocolConfig, + Sequencer: { + TaskQueue: BullConfig, + LocalTaskWorkerModule: VanillaTaskWorkerModules.defaultConfig(), + }, + }); + + console.log("Starting worker..."); + console.log(`Worker proofs enabled: ${proofsEnabled}`); + + log.setLevel("DEBUG"); + + await app.start(proofsEnabled, container.createChildContainer()); + + console.log("Worker started..."); + + const ready = await new Promise((res) => { + app + .resolve("Sequencer") + .resolve("LocalTaskWorkerModule") + .containerEvents.on("ready", res); + }); + + expect(ready).toBe(true); + + console.log("Ready received!"); + + await sleep(10000000); +} + +const isSpawned = process.env.IS_SPAWNED_PROCESS === "true"; + +if (isSpawned) { + await main(); +} diff --git a/packages/sequencer/test-integration/workers/workers-proven.test.ts b/packages/sequencer/test-integration/workers/workers-proven.test.ts index f914473e4..9bc774e2a 100644 --- a/packages/sequencer/test-integration/workers/workers-proven.test.ts +++ b/packages/sequencer/test-integration/workers/workers-proven.test.ts @@ -29,21 +29,35 @@ const timeout = 300000; const proofsEnabled = false; +const numWorkers = 1; + describe("worker-proven", () => { describe("sequencer", () => { let test: BlockTestService; - let worker: ChildProcessWorker; + const workers: ChildProcessWorker[] = []; let appChain: AppChain; beforeAll(async () => { - worker = new ChildProcessWorker(); - worker.start(true, { PROOFS_ENABLED: `${proofsEnabled}` }); + for (let i = 0; i < numWorkers; i++) { + const worker = new ChildProcessWorker(); + worker.start( + `worker-${i}`, + "./test-integration/workers/worker.ts", + true, + { + PROOFS_ENABLED: `${proofsEnabled}`, + } + ); + workers.push(worker); + } }); afterAll(async () => { - worker.kill(); + workers.forEach((worker) => { + worker.kill(); + }); await appChain.close(); }); @@ -77,7 +91,9 @@ describe("worker-proven", () => { BlockTrigger: {}, Mempool: {}, BatchProducerModule: {}, - BlockProducerModule: {}, + BlockProducerModule: { + maximumBlockSize: 5, + }, // BaseLayer: {}, TaskQueue: BullConfig, FeeStrategy: {}, @@ -109,17 +125,21 @@ describe("worker-proven", () => { const privateKey = PrivateKey.random(); - await test.addTransaction({ - method: ["Balance", "addBalance"], - privateKey, - args: [PrivateKey.random().toPublicKey(), UInt64.from(100)], - }); + const txs = 4; + + for (let i = 0; i < txs; i++) { + await test.addTransaction({ + method: ["Balance", "addBalance"], + privateKey, + args: [PrivateKey.random().toPublicKey(), UInt64.from(100)], + }); + } const [block, batch] = await test.produceBlockAndBatch(); expectDefined(block); - expect(block.transactions).toHaveLength(1); + expect(block.transactions).toHaveLength(txs); expect(block.transactions[0].status.toBoolean()).toBe(true); expectDefined(batch); @@ -134,9 +154,22 @@ describe("worker-proven", () => { timeout ); - it.each([5, 14, 20])( + it.each([ + [5, 4], + [14, 10], + ])( "should produce a batch of a %s of blocks", - async (numBlocks) => { + async (numBlocks, txs) => { + const privateKey = PrivateKey.random(); + + for (let i = 0; i < txs; i++) { + await test.addTransaction({ + method: ["Balance", "addBalance"], + privateKey, + args: [PrivateKey.random().toPublicKey(), UInt64.from(100)], + }); + } + for (let i = 0; i < numBlocks; i++) { await test.produceBlock(); } diff --git a/packages/sequencer/test/integration/Proven.test.ts b/packages/sequencer/test/integration/Proven.test.ts index bca7f53af..9fd465f5b 100644 --- a/packages/sequencer/test/integration/Proven.test.ts +++ b/packages/sequencer/test/integration/Proven.test.ts @@ -5,6 +5,7 @@ import { MOCK_VERIFICATION_KEY, ChildVerificationKeyService, CompileRegistry, + range, } from "@proto-kit/common"; import { Runtime } from "@proto-kit/module"; import { @@ -14,7 +15,6 @@ import { ContractArgsRegistry, DispatchSmartContract, Protocol, - SettlementContractModule, } from "@proto-kit/protocol"; import { VanillaProtocolModules } from "@proto-kit/library"; import { container } from "tsyringe"; @@ -22,24 +22,20 @@ import { PrivateKey, UInt64 } from "o1js"; import { testingSequencerModules } from "../TestingSequencer"; import { - MinaBaseLayer, ProvenSettlementPermissions, Sequencer, - SettlementModule, - SettlementProvingTask, VanillaTaskWorkerModules, AppChain, InMemoryAreProofsEnabled, } from "../../src"; import { SettlementStartupModule } from "../../src/sequencer/SettlementStartupModule"; -import { ProtocolStateTestHook } from "./mocks/ProtocolStateTestHook"; import { BlockTestService } from "./services/BlockTestService"; import { ProvenBalance } from "./mocks/ProvenBalance"; const timeout = 300000; -describe.skip("Proven", () => { +describe("Proven", () => { let test: BlockTestService; let appChain: ReturnType; @@ -52,11 +48,11 @@ describe.skip("Proven", () => { const sequencerClass = Sequencer.from( testingSequencerModules( { - BaseLayer: MinaBaseLayer, - SettlementModule, + // BaseLayer: MinaBaseLayer, + // SettlementModule, }, { - SettlementProvingTask, + // SettlementProvingTask, } ) ); @@ -64,14 +60,14 @@ describe.skip("Proven", () => { // TODO Analyze how we can get rid of the library import for mandatory modules const protocolClass = Protocol.from({ ...VanillaProtocolModules.mandatoryModules({ - ProtocolStateTestHook, + // ProtocolStateTestHook, // ProtocolStateTestHook2, }), - SettlementContractModule: SettlementContractModule.from({ - ...SettlementContractModule.settlementAndBridging(), - // FungibleToken: FungibleTokenContractModule, - // FungibleTokenAdmin: FungibleTokenAdminContractModule, - }), + // SettlementContractModule: SettlementContractModule.from({ + // ...SettlementContractModule.settlementAndBridging(), + // FungibleToken: FungibleTokenContractModule, + // FungibleTokenAdmin: FungibleTokenAdminContractModule, + // }), // modules: VanillaProtocolModules.with({}), }); @@ -82,6 +78,10 @@ describe.skip("Proven", () => { }); } + afterAll(async () => { + await appChain.close(); + }); + it( "should start up and compile", async () => { @@ -95,33 +95,35 @@ describe.skip("Proven", () => { BlockTrigger: {}, Mempool: {}, BatchProducerModule: {}, - BlockProducerModule: {}, + BlockProducerModule: { + maximumBlockSize: 5, + }, LocalTaskWorkerModule: VanillaTaskWorkerModules.defaultConfig(), TaskQueue: {}, FeeStrategy: {}, SequencerStartupModule: {}, BaseLayer: { - network: { - type: "local", - }, + // network: { + // type: "local", + // }, }, - SettlementModule: {}, + // SettlementModule: {}, }, Runtime: { Balances: {}, }, Protocol: { ...Protocol.defaultConfig(), - ProtocolStateTestHook: {}, - SettlementContractModule: { - SettlementContract: {}, - BridgeContract: {}, - DispatchContract: { - incomingMessagesMethods: { - deposit: "Balances.deposit", - }, - }, - }, + // ProtocolStateTestHook: {}, + // SettlementContractModule: { + // SettlementContract: {}, + // BridgeContract: {}, + // DispatchContract: { + // incomingMessagesMethods: { + // deposit: "Balances.deposit", + // }, + // }, + // }, // ProtocolStateTestHook2: {}, }, }); @@ -129,7 +131,7 @@ describe.skip("Proven", () => { try { // Start AppChain const childContainer = container.createChildContainer(); - await app.start(true, childContainer); + await app.start(false, childContainer); test = app.sequencer.dependencyContainer.resolve(BlockTestService); @@ -142,7 +144,7 @@ describe.skip("Proven", () => { timeout ); - it("should compile settlement contracts", async () => { + it.skip("should compile settlement contracts", async () => { const module = appChain.sequencer.dependencyContainer.resolve( SettlementStartupModule ); @@ -191,7 +193,7 @@ describe.skip("Proven", () => { } }, 500000); - it( + it.skip( "should produce simple block", async () => { expect.assertions(6); @@ -217,9 +219,48 @@ describe.skip("Proven", () => { console.log(batch.proof); - expect(batch.proof.proof.length).toBeGreaterThan(50); expect(batch.blockHashes).toHaveLength(1); + expect(batch.proof.proof.length).toBeGreaterThan(50); }, timeout ); + + it( + "should produce large block", + async () => { + log.setLevel("INFO"); + + const privateKey = PrivateKey.random(); + + for (const i of range(0, 30)) { + await test.addTransaction({ + method: ["Balances", "addBalance"], + privateKey, + args: [PrivateKey.random().toPublicKey(), UInt64.from(100)], + }); + } + + // Produce 6 blocks, 5 txs each into 1 batch + const block = await test.produceBlock(); + + expectDefined(block); + expect(block.transactions).toHaveLength(5); + expect(block.transactions[0].status.toBoolean()).toBe(true); + + await test.produceBlock(); + await test.produceBlock(); + await test.produceBlock(); + await test.produceBlock(); + await test.produceBlock(); + const batch = await test.produceBatch(); + + expectDefined(batch); + + console.log(batch.proof); + + expect(batch.blockHashes).toHaveLength(6); + expect(batch.proof.proof.length).toBeGreaterThan(50); + }, + timeout * 10 + ); }); diff --git a/packages/sequencer/test/settlement/Settlement.ts b/packages/sequencer/test/settlement/Settlement.ts index 0b89e8871..532d1b8ee 100644 --- a/packages/sequencer/test/settlement/Settlement.ts +++ b/packages/sequencer/test/settlement/Settlement.ts @@ -3,6 +3,7 @@ import { mapSequential, TypedClass, LinkedMerkleTree, + ModulesConfig, } from "@proto-kit/common"; import { VanillaProtocolModules } from "@proto-kit/library"; import { Runtime } from "@proto-kit/module"; @@ -40,6 +41,7 @@ import { import "reflect-metadata"; import { container } from "tsyringe"; import { FungibleToken, FungibleTokenAdmin } from "mina-fungible-token"; +import { BullQueue, BullQueueConfig } from "@proto-kit/deployment"; import { ManualBlockTrigger, @@ -48,7 +50,6 @@ import { BlockQueue, SettlementModule, MinaBaseLayer, - SettlementProvingTask, MinaTransactionSender, MinaBaseLayerConfig, SignedSettlementPermissions, @@ -57,9 +58,15 @@ import { Sequencer, InMemoryMinaSigner, CircuitAnalysisModule, + LocalTaskWorkerModule, + InMemoryDatabase, + BatchProducerModule, + BlockProducerModule, + LocalTaskQueue, + ConstantFeeStrategy, + SequencerStartupModule, } from "../../src"; import { BlockProofSerializer } from "../../src/protocol/production/tasks/serializers/BlockProofSerializer"; -import { testingSequencerModules } from "../TestingSequencer"; import { createTransaction } from "../integration/utils"; import { FeeStrategy } from "../../src/protocol/baselayer/fees/FeeStrategy"; import { BridgingModule } from "../../src/settlement/BridgingModule"; @@ -70,17 +77,57 @@ import { MinaNetworkUtils } from "../../src/protocol/baselayer/network-utils/Min import { Balances, BalancesKey } from "./mocks/Balances"; import { WithdrawalMessageProcessor, Withdrawals } from "./mocks/Withdrawals"; +export const runtimeModules = { + Balances, + Withdrawals, +}; + +export const runtimeModulesConfig = { + Balances: { + totalSupply: UInt64.from(1000), + }, + Withdrawals: {}, +} satisfies ModulesConfig; + +export const protocolModules = { + ...VanillaProtocolModules.mandatoryModules({}), + SettlementContractModule: SettlementContractModule.from({ + ...SettlementContractModule.settlementAndBridging(), + FungibleToken: FungibleTokenContractModule, + FungibleTokenAdmin: FungibleTokenAdminContractModule, + }), + WithdrawalMessageProcessor, +}; + +export const protocolModulesConfig = { + ...Protocol.defaultConfig(), + SettlementContractModule: { + SettlementContract: {}, + BridgeContract: {}, + DispatchContract: { + incomingMessagesMethods: { + deposit: "Balances.deposit", + }, + }, + FungibleToken: {}, + FungibleTokenAdmin: {}, + }, + WithdrawalMessageProcessor: {}, +} satisfies ModulesConfig; + export const settlementTestFn = ( settlementType: "signed" | "mock-proofs" | "proven", baseLayerConfig: MinaBaseLayerConfig, tokenConfig?: { tokenOwner: TypedClass & typeof SmartContract; }, - timeout: number = 120_000 + timeout: number = 120_000, + bullQueueConfig?: BullQueueConfig, + fundedKeysInput: PrivateKey[] = [] ) => { - let testAccounts: PrivateKey[] = []; + let testAccounts: PrivateKey[] = fundedKeysInput.slice(1); - const sequencerKey = PrivateKey.random(); + const sequencerKey = fundedKeysInput[0] ?? PrivateKey.random(); const settlementKey = PrivateKey.random(); const dispatchKey = PrivateKey.random(); const minaBridgeKey = PrivateKey.random(); @@ -117,42 +164,41 @@ export const settlementTestFn = ( tokenConfig === undefined ? TokenId.default : tokenOwner!.deriveTokenId(); function setupAppChain() { - const runtime = Runtime.from({ - Balances, - Withdrawals, - }); + const runtime = Runtime.from(runtimeModules); // eslint-disable-next-line @typescript-eslint/dot-notation MinaBaseLayer.prototype["isSignedSettlement"] = () => settlementType === "signed"; - const sequencer = Sequencer.from( - testingSequencerModules( - { - BaseLayer: MinaBaseLayer, - SettlementModule: SettlementModule, - BridgingModule: BridgingModule, - SettlementSigner: InMemoryMinaSigner, - }, - { - SettlementProvingTask, - } - ) + const taskWorkerModule = LocalTaskWorkerModule.from( + VanillaTaskWorkerModules.allTasks() ); + const sequencer = Sequencer.from({ + Database: InMemoryDatabase, + Mempool: PrivateMempool, + BatchProducerModule, + BlockProducerModule, + BlockTrigger: ManualBlockTrigger, + TaskQueue: bullQueueConfig !== undefined ? BullQueue : LocalTaskQueue, + FeeStrategy: ConstantFeeStrategy, + BaseLayer: MinaBaseLayer, + SettlementModule: SettlementModule, + BridgingModule: BridgingModule, + SettlementSigner: InMemoryMinaSigner, + ...(bullQueueConfig !== undefined + ? {} + : { + LocalTaskWorkerModule: taskWorkerModule, + }), + SequencerStartupModule, + }); + const appchain = ClientAppChain.from({ Runtime: runtime, Sequencer: sequencer, - Protocol: Protocol.from({ - ...VanillaProtocolModules.mandatoryModules({}), - SettlementContractModule: SettlementContractModule.from({ - ...SettlementContractModule.settlementAndBridging(), - FungibleToken: FungibleTokenContractModule, - FungibleTokenAdmin: FungibleTokenAdminContractModule, - }), - WithdrawalMessageProcessor, - }), + Protocol: Protocol.from(protocolModules), Signer: InMemorySigner, TransactionSender: InMemoryTransactionSender, @@ -162,12 +208,7 @@ export const settlementTestFn = ( }); appchain.configure({ - Runtime: { - Balances: { - totalSupply: UInt64.from(1000), - }, - Withdrawals: {}, - }, + Runtime: runtimeModulesConfig, Sequencer: { Database: {}, @@ -193,25 +234,11 @@ export const settlementTestFn = ( BridgingModule: {}, SequencerStartupModule: {}, - TaskQueue: { + TaskQueue: bullQueueConfig ?? { simulatedDuration: 0, }, }, - Protocol: { - ...Protocol.defaultConfig(), - SettlementContractModule: { - SettlementContract: {}, - BridgeContract: {}, - DispatchContract: { - incomingMessagesMethods: { - deposit: "Balances.deposit", - }, - }, - FungibleToken: {}, - FungibleTokenAdmin: {}, - }, - WithdrawalMessageProcessor: {}, - }, + Protocol: protocolModulesConfig, TransactionSender: {}, QueryTransportModule: {}, Signer: { @@ -264,6 +291,10 @@ export const settlementTestFn = ( return result; } + let nonceCounter = 0; + let user0Nonce = 0; + let acc0L2Nonce = 0; + beforeAll(async () => { appChain = setupAppChain(); @@ -292,16 +323,31 @@ export const settlementTestFn = ( appChain.sequencer.dependencyContainer.resolve( "NetworkUtils" ); - const accs = await networkUtils.getFundedAccounts(3); - testAccounts = accs.slice(1); - await networkUtils.waitForNetwork(); - console.log( - `Funding ${sequencerKey.toPublicKey().toBase58()} from ${accs[0].toPublicKey().toBase58()}` - ); + if (fundedKeysInput.length === 0) { + const accs = await networkUtils.getFundedAccounts(2); + testAccounts = accs.slice(1); + + console.log( + `Funding ${sequencerKey.toPublicKey().toBase58()} from ${accs[0].toPublicKey().toBase58()}` + ); - await networkUtils.faucet(sequencerKey.toPublicKey(), 20 * 1e9); + await networkUtils.faucet(sequencerKey.toPublicKey(), 20 * 1e9); + } else { + const sequencerAccount = await fetchAccount({ + publicKey: fundedKeysInput[0].toPublicKey(), + }); + nonceCounter = parseInt( + sequencerAccount.account?.nonce.toString() ?? "0", + 10 + ); + + const account = await fetchAccount({ + publicKey: testAccounts[0].toPublicKey(), + }); + user0Nonce = parseInt(account.account?.nonce.toString() ?? "0", 10); + } }, timeout * 3); afterAll(async () => { @@ -310,10 +356,6 @@ export const settlementTestFn = ( await appChain.close(); }); - let nonceCounter = 0; - let user0Nonce = 0; - let acc0L2Nonce = 0; - it.skip("Print constraint summary", async () => { await appChain.protocol.dependencyContainer .resolve(CircuitAnalysisModule) @@ -608,6 +650,7 @@ export const settlementTestFn = ( signingWithSignatureCheck: [ tokenOwnerPubKeys.tokenOwner, settlementModule.getSettlementContractAddress(), + bridgingModule.getDispatchContractAddress(), ], signingPublicKeys: [userPublicKey], preventNoncePreconditionFor: [dispatch.address],