@aztec/prover-client 5.0.0-private.20260319 → 5.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +16 -2
- package/dest/light/lightweight_checkpoint_builder.d.ts +1 -1
- package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -1
- package/dest/light/lightweight_checkpoint_builder.js +15 -5
- package/dest/mocks/test_context.d.ts +11 -11
- package/dest/mocks/test_context.d.ts.map +1 -1
- package/dest/mocks/test_context.js +25 -28
- package/dest/orchestrator/block-building-helpers.d.ts +1 -1
- package/dest/orchestrator/checkpoint-proving-state.d.ts +20 -36
- package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/checkpoint-proving-state.js +22 -120
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts +161 -0
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts.map +1 -0
- package/dest/orchestrator/{orchestrator.js → checkpoint-sub-tree-orchestrator.js} +267 -451
- package/dest/orchestrator/chonk-cache.d.ts +39 -0
- package/dest/orchestrator/chonk-cache.d.ts.map +1 -0
- package/dest/orchestrator/chonk-cache.js +80 -0
- package/dest/orchestrator/index.d.ts +4 -2
- package/dest/orchestrator/index.d.ts.map +1 -1
- package/dest/orchestrator/index.js +3 -1
- package/dest/orchestrator/proving-scheduler.d.ts +81 -0
- package/dest/orchestrator/proving-scheduler.d.ts.map +1 -0
- package/dest/orchestrator/proving-scheduler.js +120 -0
- package/dest/orchestrator/top-tree-orchestrator.d.ts +88 -0
- package/dest/orchestrator/top-tree-orchestrator.d.ts.map +1 -0
- package/dest/orchestrator/top-tree-orchestrator.js +232 -0
- package/dest/orchestrator/top-tree-proving-state.d.ts +61 -0
- package/dest/orchestrator/top-tree-proving-state.d.ts.map +1 -0
- package/dest/orchestrator/top-tree-proving-state.js +185 -0
- package/dest/prover-client/prover-client.d.ts +61 -4
- package/dest/prover-client/prover-client.d.ts.map +1 -1
- package/dest/prover-client/prover-client.js +54 -7
- package/dest/proving_broker/broker_prover_facade.d.ts +1 -1
- package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
- package/dest/proving_broker/broker_prover_facade.js +10 -16
- package/dest/proving_broker/config.d.ts +8 -72
- package/dest/proving_broker/config.d.ts.map +1 -1
- package/dest/proving_broker/config.js +2 -2
- package/dest/proving_broker/index.d.ts +2 -1
- package/dest/proving_broker/index.d.ts.map +1 -1
- package/dest/proving_broker/index.js +1 -0
- package/dest/proving_broker/proving_broker.d.ts +2 -2
- package/dest/proving_broker/proving_broker.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker.js +32 -7
- package/dest/proving_broker/proving_broker_database/persisted.js +2 -2
- package/dest/proving_broker/rpc.d.ts +3 -1
- package/dest/proving_broker/rpc.d.ts.map +1 -1
- package/dest/proving_broker/rpc.js +80 -24
- package/dest/test/epoch_settlement.d.ts +36 -0
- package/dest/test/epoch_settlement.d.ts.map +1 -0
- package/dest/test/epoch_settlement.js +52 -0
- package/dest/test/index.d.ts +2 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +1 -0
- package/dest/test/mock_prover.d.ts +3 -3
- package/package.json +19 -18
- package/src/config.ts +18 -2
- package/src/light/lightweight_checkpoint_builder.ts +16 -6
- package/src/mocks/test_context.ts +24 -41
- package/src/orchestrator/checkpoint-proving-state.ts +21 -162
- package/src/orchestrator/{orchestrator.ts → checkpoint-sub-tree-orchestrator.ts} +415 -661
- package/src/orchestrator/chonk-cache.ts +99 -0
- package/src/orchestrator/index.ts +8 -1
- package/src/orchestrator/proving-scheduler.ts +160 -0
- package/src/orchestrator/top-tree-orchestrator.ts +384 -0
- package/src/orchestrator/top-tree-proving-state.ts +219 -0
- package/src/prover-client/prover-client.ts +128 -18
- package/src/proving_broker/broker_prover_facade.ts +8 -16
- package/src/proving_broker/config.ts +2 -1
- package/src/proving_broker/index.ts +1 -0
- package/src/proving_broker/proving_broker.ts +27 -5
- package/src/proving_broker/proving_broker_database/persisted.ts +2 -2
- package/src/proving_broker/rpc.ts +36 -24
- package/src/test/epoch_settlement.ts +82 -0
- package/src/test/index.ts +1 -0
- package/dest/orchestrator/epoch-proving-state.d.ts +0 -75
- package/dest/orchestrator/epoch-proving-state.d.ts.map +0 -1
- package/dest/orchestrator/epoch-proving-state.js +0 -269
- package/dest/orchestrator/orchestrator.d.ts +0 -130
- package/dest/orchestrator/orchestrator.d.ts.map +0 -1
- package/dest/prover-client/server-epoch-prover.d.ts +0 -32
- package/dest/prover-client/server-epoch-prover.d.ts.map +0 -1
- package/dest/prover-client/server-epoch-prover.js +0 -40
- package/src/orchestrator/epoch-proving-state.ts +0 -380
- package/src/prover-client/server-epoch-prover.ts +0 -69
|
@@ -370,53 +370,49 @@ function applyDecs2203RFactory() {
|
|
|
370
370
|
function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
371
371
|
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
372
372
|
}
|
|
373
|
-
var _dec, _dec1, _dec2, _dec3,
|
|
374
|
-
import { L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH,
|
|
373
|
+
var _dec, _dec1, _dec2, _dec3, _initProto;
|
|
374
|
+
import { L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH, NUM_BASE_PARITY_PER_ROOT_PARITY } from '@aztec/constants';
|
|
375
375
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
376
|
-
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
377
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
378
376
|
import { AbortError } from '@aztec/foundation/error';
|
|
379
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
380
377
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
381
|
-
import { SerialQueue } from '@aztec/foundation/queue';
|
|
382
378
|
import { assertLength } from '@aztec/foundation/serialize';
|
|
383
|
-
import {
|
|
384
|
-
import {
|
|
385
|
-
import { elapsed } from '@aztec/foundation/timer';
|
|
386
|
-
import { BlockRootEmptyTxFirstRollupPrivateInputs, BlockRootFirstRollupPrivateInputs, BlockRootSingleTxFirstRollupPrivateInputs, BlockRootSingleTxRollupPrivateInputs, CheckpointRootSingleBlockRollupPrivateInputs, PrivateTxBaseRollupPrivateInputs } from '@aztec/stdlib/rollup';
|
|
379
|
+
import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging';
|
|
380
|
+
import { BlockRootEmptyTxFirstRollupPrivateInputs, BlockRootFirstRollupPrivateInputs, BlockRootSingleTxFirstRollupPrivateInputs, BlockRootSingleTxRollupPrivateInputs, PrivateTxBaseRollupPrivateInputs } from '@aztec/stdlib/rollup';
|
|
387
381
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
388
382
|
import { Attributes, getTelemetryClient, trackSpan, wrapCallbackInSpan } from '@aztec/telemetry-client';
|
|
389
383
|
import { inspect } from 'util';
|
|
390
384
|
import { buildHeaderFromCircuitOutputs, getLastSiblingPath, getPublicChonkVerifierPrivateInputsFromTx, getRootTreeSiblingPath, getSubtreeSiblingPath, getTreeSnapshot, insertSideEffectsAndBuildBaseRollupHints, validatePartialState, validateTx } from './block-building-helpers.js';
|
|
391
|
-
import {
|
|
385
|
+
import { CheckpointProvingState } from './checkpoint-proving-state.js';
|
|
392
386
|
import { ProvingOrchestratorMetrics } from './orchestrator_metrics.js';
|
|
387
|
+
import { ProvingScheduler } from './proving-scheduler.js';
|
|
393
388
|
import { TxProvingState } from './tx-proving-state.js';
|
|
394
|
-
_dec = trackSpan('
|
|
389
|
+
_dec = trackSpan('CheckpointSubTreeOrchestrator.startNewBlock', (blockNumber)=>({
|
|
395
390
|
[Attributes.BLOCK_NUMBER]: blockNumber
|
|
396
|
-
})), _dec1 = trackSpan('
|
|
391
|
+
})), _dec1 = trackSpan('CheckpointSubTreeOrchestrator.addTxs', (txs)=>({
|
|
397
392
|
[Attributes.BLOCK_TXS_COUNT]: txs.length
|
|
398
|
-
})), _dec2 = trackSpan('
|
|
393
|
+
})), _dec2 = trackSpan('CheckpointSubTreeOrchestrator.setBlockCompleted', (blockNumber)=>({
|
|
399
394
|
[Attributes.BLOCK_NUMBER]: blockNumber
|
|
400
|
-
})),
|
|
395
|
+
})), _dec3 = trackSpan('CheckpointSubTreeOrchestrator.prepareBaseRollupInputs', (tx)=>({
|
|
401
396
|
[Attributes.TX_HASH]: tx.hash.toString()
|
|
402
397
|
}));
|
|
403
398
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
* 4. Once a transaction is proven, it will be incorporated into a merge proof
|
|
409
|
-
* 5. Merge proofs are produced at each level of the tree until the root proof is produced
|
|
399
|
+
* Orchestrates block-level proving for a single checkpoint, stopping at the boundary
|
|
400
|
+
* where checkpoint root rollup would otherwise begin. Used by the per-checkpoint
|
|
401
|
+
* `CheckpointProver` in production; the top-tree orchestrator then composes the
|
|
402
|
+
* sub-tree's block proofs into the epoch proof.
|
|
410
403
|
*
|
|
411
|
-
*
|
|
412
|
-
|
|
413
|
-
*
|
|
414
|
-
|
|
404
|
+
* Wiring: a single-checkpoint mini-proving session is owned by the constructor. The
|
|
405
|
+
* canonical way to obtain a fully-started sub-tree is the `start` static factory,
|
|
406
|
+
* which also drives the single internal `startCheckpoint` call. The sub-tree never
|
|
407
|
+
* escalates past the checkpoint root boundary; `getSubTreeResult()` resolves once
|
|
408
|
+
* every block-level proof in the checkpoint's tree is ready.
|
|
409
|
+
*/ export class CheckpointSubTreeOrchestrator extends ProvingScheduler {
|
|
415
410
|
dbProvider;
|
|
416
411
|
prover;
|
|
417
412
|
proverId;
|
|
413
|
+
chonkCache;
|
|
414
|
+
epochNumber;
|
|
418
415
|
cancelJobsOnStop;
|
|
419
|
-
enqueueConcurrency;
|
|
420
416
|
static{
|
|
421
417
|
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
422
418
|
[
|
|
@@ -432,43 +428,33 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
432
428
|
[
|
|
433
429
|
_dec2,
|
|
434
430
|
2,
|
|
435
|
-
"startChonkVerifierCircuits"
|
|
436
|
-
],
|
|
437
|
-
[
|
|
438
|
-
_dec3,
|
|
439
|
-
2,
|
|
440
431
|
"setBlockCompleted"
|
|
441
432
|
],
|
|
442
433
|
[
|
|
443
|
-
|
|
434
|
+
_dec3,
|
|
444
435
|
2,
|
|
445
436
|
"prepareBaseRollupInputs"
|
|
446
437
|
]
|
|
447
438
|
], []));
|
|
448
439
|
}
|
|
449
|
-
provingState;
|
|
450
|
-
|
|
451
|
-
provingPromise;
|
|
440
|
+
/** The single checkpoint proving state this sub-tree owns. Allocated in the `start` factory. */ provingState;
|
|
441
|
+
subTreeResult;
|
|
452
442
|
metrics;
|
|
453
|
-
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
454
443
|
dbs;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
this.cancelJobsOnStop = cancelJobsOnStop;
|
|
462
|
-
this.
|
|
463
|
-
this.
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
this.
|
|
467
|
-
this.deferredJobQueue = new SerialQueue();
|
|
468
|
-
this.logger = createLogger('prover-client:orchestrator', bindings);
|
|
469
|
-
this.metrics = new ProvingOrchestratorMetrics(telemetryClient, 'ProvingOrchestrator');
|
|
470
|
-
this.deferredJobQueue.start(this.enqueueConcurrency);
|
|
444
|
+
constructor(dbProvider, prover, proverId, /**
|
|
445
|
+
* Shared chonk-verifier proof cache. Every chonk-verifier proof started by this
|
|
446
|
+
* sub-tree lives on the cache and survives the sub-tree's cancellation, so a tx
|
|
447
|
+
* whose original checkpoint is reorged out and re-appears in a replacement
|
|
448
|
+
* checkpoint reuses the cached proof.
|
|
449
|
+
*/ chonkCache, /** The epoch this sub-tree proves into. */ epochNumber, cancelJobsOnStop = false, deferredJobQueue, telemetryClient = getTelemetryClient(), bindings){
|
|
450
|
+
super(deferredJobQueue, 'prover-client:checkpoint-sub-tree-orchestrator', bindings), this.dbProvider = dbProvider, this.prover = prover, this.proverId = proverId, this.chonkCache = chonkCache, this.epochNumber = epochNumber, this.cancelJobsOnStop = cancelJobsOnStop, this.provingState = (_initProto(this), undefined), this.dbs = new Map(), this.cancelled = false;
|
|
451
|
+
this.metrics = new ProvingOrchestratorMetrics(telemetryClient, 'CheckpointSubTreeOrchestrator');
|
|
452
|
+
this.subTreeResult = promiseWithResolvers();
|
|
453
|
+
// Mark the rejection branch as observed so a `cancel()` or proving failure does not
|
|
454
|
+
// surface an unhandled rejection when no consumer awaits getSubTreeResult().
|
|
455
|
+
this.subTreeResult.promise.catch(()=>{});
|
|
471
456
|
}
|
|
457
|
+
/** Tracks whether `cancel()` has been called; flows into the checkpoint state's isAlive hook. */ cancelled;
|
|
472
458
|
get tracer() {
|
|
473
459
|
return this.metrics.tracer;
|
|
474
460
|
}
|
|
@@ -478,72 +464,74 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
478
464
|
getNumActiveForks() {
|
|
479
465
|
return this.dbs.size;
|
|
480
466
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const oldQueue = this.deferredJobQueue;
|
|
484
|
-
this.cancel();
|
|
485
|
-
await oldQueue.cancel();
|
|
486
|
-
}
|
|
487
|
-
startNewEpoch(epochNumber, totalNumCheckpoints, finalBlobBatchingChallenges) {
|
|
488
|
-
if (this.provingState?.verifyState()) {
|
|
489
|
-
throw new Error(`Cannot start epoch ${epochNumber} when epoch ${this.provingState.epochNumber} is still being processed.`);
|
|
490
|
-
}
|
|
491
|
-
const { promise: _promise, resolve, reject } = promiseWithResolvers();
|
|
492
|
-
const promise = _promise.catch((reason)=>({
|
|
493
|
-
status: 'failure',
|
|
494
|
-
reason
|
|
495
|
-
}));
|
|
496
|
-
this.logger.info(`Starting epoch ${epochNumber} with ${totalNumCheckpoints} checkpoints.`);
|
|
497
|
-
this.provingState = new EpochProvingState(epochNumber, totalNumCheckpoints, finalBlobBatchingChallenges, (provingState)=>this.checkAndEnqueueCheckpointRootRollup(provingState), resolve, reject);
|
|
498
|
-
this.provingPromise = promise;
|
|
467
|
+
/** Returns a promise that resolves when block-level proving completes for the checkpoint. */ getSubTreeResult() {
|
|
468
|
+
return this.subTreeResult.promise;
|
|
499
469
|
}
|
|
500
470
|
/**
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
|
|
506
|
-
* @param headerOfLastBlockInPreviousCheckpoint - The header of the last block in the previous checkpoint.
|
|
507
|
-
*/ async startNewCheckpoint(checkpointIndex, constants, l1ToL2Messages, totalNumBlocks, headerOfLastBlockInPreviousCheckpoint) {
|
|
471
|
+
* Returns the archive sibling path captured at the internal checkpoint start.
|
|
472
|
+
* Available synchronously once `start` has resolved, before block-level proving
|
|
473
|
+
* completes. The top-tree consumer uses this to assemble checkpoint root rollup hints
|
|
474
|
+
* up-front so checkpoint root proofs can pipeline against in-flight sub-tree proving.
|
|
475
|
+
*/ getPreviousArchiveSiblingPath() {
|
|
508
476
|
if (!this.provingState) {
|
|
509
|
-
throw new Error('
|
|
477
|
+
throw new Error('Checkpoint not started; call CheckpointSubTreeOrchestrator.start first.');
|
|
510
478
|
}
|
|
511
|
-
|
|
512
|
-
|
|
479
|
+
return this.provingState.getLastArchiveSiblingPath();
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Constructs and starts a sub-tree for a single checkpoint. The returned sub-tree
|
|
483
|
+
* has had its single internal checkpoint state allocated; callers proceed directly
|
|
484
|
+
* to per-block `startNewBlock` / `addTxs` / `setBlockCompleted`.
|
|
485
|
+
*
|
|
486
|
+
* If the internal start rejects, the partially-constructed sub-tree is stopped
|
|
487
|
+
* before the error propagates, so no broker resources leak.
|
|
488
|
+
*/ static async start(dbProvider, prover, proverId, chonkCache, epochNumber, cancelJobsOnStop, deferredJobQueue, checkpointConstants, l1ToL2Messages, totalNumBlocks, headerOfLastBlockInPreviousCheckpoint, telemetryClient = getTelemetryClient(), bindings) {
|
|
489
|
+
const subTree = new CheckpointSubTreeOrchestrator(dbProvider, prover, proverId, chonkCache, epochNumber, cancelJobsOnStop, deferredJobQueue, telemetryClient, bindings);
|
|
490
|
+
try {
|
|
491
|
+
await subTree.startCheckpoint(checkpointConstants, l1ToL2Messages, totalNumBlocks, headerOfLastBlockInPreviousCheckpoint);
|
|
492
|
+
return subTree;
|
|
493
|
+
} catch (err) {
|
|
494
|
+
await subTree.stop().catch(()=>{});
|
|
495
|
+
throw err;
|
|
513
496
|
}
|
|
514
|
-
// Fork world state at the end of the immediately previous block.
|
|
515
|
-
const lastBlockNumber = headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber;
|
|
516
|
-
const db = await this.dbProvider.fork(lastBlockNumber);
|
|
517
|
-
const firstBlockNumber = BlockNumber(lastBlockNumber + 1);
|
|
518
|
-
this.dbs.set(firstBlockNumber, db);
|
|
519
|
-
// Get archive sibling path before any block in this checkpoint lands.
|
|
520
|
-
const lastArchiveSiblingPath = await getLastSiblingPath(MerkleTreeId.ARCHIVE, db);
|
|
521
|
-
// Insert all the l1 to l2 messages into the db. And get the states before and after the insertion.
|
|
522
|
-
const { lastL1ToL2MessageTreeSnapshot, lastL1ToL2MessageSubtreeRootSiblingPath, newL1ToL2MessageTreeSnapshot, newL1ToL2MessageSubtreeRootSiblingPath } = await this.updateL1ToL2MessageTree(l1ToL2Messages, db);
|
|
523
|
-
this.provingState.startNewCheckpoint(checkpointIndex, constants, totalNumBlocks, headerOfLastBlockInPreviousCheckpoint, lastArchiveSiblingPath, l1ToL2Messages, lastL1ToL2MessageTreeSnapshot, lastL1ToL2MessageSubtreeRootSiblingPath, newL1ToL2MessageTreeSnapshot, newL1ToL2MessageSubtreeRootSiblingPath);
|
|
524
497
|
}
|
|
525
498
|
/**
|
|
526
|
-
*
|
|
499
|
+
* Kickstart chonk-verifier circuits via the shared `ChonkCache`. The cache owns the
|
|
500
|
+
* broker job lifecycle, so the proof survives this sub-tree's `cancel()` — a tx that
|
|
501
|
+
* ends up in a replacement checkpoint after a reorg can pick the cached promise up
|
|
502
|
+
* and skip re-proving.
|
|
503
|
+
*/ startChonkVerifierCircuits(txs) {
|
|
504
|
+
if (!this.provingState?.verifyState()) {
|
|
505
|
+
return Promise.reject(new Error('Sub-tree proving state is not active.'));
|
|
506
|
+
}
|
|
507
|
+
const publicTxs = txs.filter((tx)=>tx.data.forPublic);
|
|
508
|
+
for (const tx of publicTxs){
|
|
509
|
+
const txHash = tx.getTxHash().toString();
|
|
510
|
+
const inputs = getPublicChonkVerifierPrivateInputsFromTx(tx, this.getProverId().toField());
|
|
511
|
+
// Fire and forget — getOrEnqueueChonkVerifier later picks up the cached promise
|
|
512
|
+
// when the tx is processed inside its block.
|
|
513
|
+
void this.chonkCache.getOrCompute(txHash, (signal)=>this.prover.getPublicChonkVerifierProof(inputs, signal, this.epochNumber));
|
|
514
|
+
}
|
|
515
|
+
return Promise.resolve();
|
|
516
|
+
}
|
|
517
|
+
// ---------------- per-block driving (called by the per-checkpoint CheckpointProver) ----------------
|
|
518
|
+
/**
|
|
519
|
+
* Starts off a new block.
|
|
527
520
|
* @param blockNumber - The block number
|
|
528
|
-
* @param timestamp - The timestamp of the block.
|
|
529
|
-
*
|
|
530
|
-
* @param totalNumTxs - The total number of txs in the block
|
|
521
|
+
* @param timestamp - The timestamp of the block. Required for empty blocks to construct private inputs.
|
|
522
|
+
* @param totalNumTxs - The total number of txs in the block.
|
|
531
523
|
*/ async startNewBlock(blockNumber, timestamp, totalNumTxs) {
|
|
532
524
|
if (!this.provingState) {
|
|
533
|
-
throw new Error('Empty
|
|
534
|
-
}
|
|
535
|
-
const checkpointProvingState = this.provingState.getCheckpointProvingStateByBlockNumber(blockNumber);
|
|
536
|
-
if (!checkpointProvingState) {
|
|
537
|
-
throw new Error(`Checkpoint not started. Call startNewCheckpoint first.`);
|
|
525
|
+
throw new Error('Empty proving state. The checkpoint sub-tree has not been started.');
|
|
538
526
|
}
|
|
539
|
-
if (!
|
|
527
|
+
if (!this.provingState.isAcceptingBlocks()) {
|
|
540
528
|
throw new Error(`Checkpoint not accepting further blocks.`);
|
|
541
529
|
}
|
|
542
|
-
const constants =
|
|
530
|
+
const constants = this.provingState.constants;
|
|
543
531
|
this.logger.info(`Starting block ${blockNumber} for slot ${constants.slotNumber}.`);
|
|
544
|
-
// Fork the db only when it's not already set. The db for the first block is set in
|
|
532
|
+
// Fork the db only when it's not already set. The db for the first block is set in startCheckpoint.
|
|
545
533
|
if (!this.dbs.has(blockNumber)) {
|
|
546
|
-
// Fork world state at the end of the immediately previous block
|
|
534
|
+
// Fork world state at the end of the immediately previous block.
|
|
547
535
|
const db = await this.dbProvider.fork(BlockNumber(blockNumber - 1));
|
|
548
536
|
this.dbs.set(blockNumber, db);
|
|
549
537
|
}
|
|
@@ -551,15 +539,15 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
551
539
|
// Get archive snapshot and sibling path before any txs in this block lands.
|
|
552
540
|
const lastArchiveTreeSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
|
|
553
541
|
const lastArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db);
|
|
554
|
-
const blockProvingState =
|
|
542
|
+
const blockProvingState = this.provingState.startNewBlock(blockNumber, timestamp, totalNumTxs, lastArchiveTreeSnapshot, lastArchiveSiblingPath);
|
|
555
543
|
// Enqueue base parity circuits for the first block in the checkpoint.
|
|
556
544
|
if (blockProvingState.index === 0) {
|
|
557
545
|
for(let i = 0; i < NUM_BASE_PARITY_PER_ROOT_PARITY; i++){
|
|
558
|
-
this.enqueueBaseParityCircuit(
|
|
546
|
+
this.enqueueBaseParityCircuit(this.provingState, blockProvingState, i);
|
|
559
547
|
}
|
|
560
548
|
}
|
|
561
|
-
// Because `addTxs` won't be called for a block without txs, and that's where the sponge blob state is computed
|
|
562
|
-
//
|
|
549
|
+
// Because `addTxs` won't be called for a block without txs, and that's where the sponge blob state is computed,
|
|
550
|
+
// set its end sponge blob here. This becomes the start sponge blob for the next block.
|
|
563
551
|
if (totalNumTxs === 0) {
|
|
564
552
|
const endState = await db.getStateReference();
|
|
565
553
|
blockProvingState.setEndState(endState);
|
|
@@ -567,22 +555,19 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
567
555
|
const blockEndBlobFields = blockProvingState.getBlockEndBlobFields();
|
|
568
556
|
await endSpongeBlob.absorb(blockEndBlobFields);
|
|
569
557
|
blockProvingState.setEndSpongeBlob(endSpongeBlob);
|
|
570
|
-
// Try to accumulate the out hashes and blobs as far as we can:
|
|
571
|
-
await this.provingState.accumulateCheckpointOutHashes();
|
|
572
|
-
await this.provingState.setBlobAccumulators();
|
|
573
558
|
}
|
|
574
559
|
}
|
|
575
560
|
/**
|
|
576
|
-
* The interface to add simulated transactions to the scheduler.
|
|
561
|
+
* The interface to add simulated transactions to the scheduler. Called at most once per block.
|
|
577
562
|
* @param txs - The transactions to be proven
|
|
578
563
|
*/ async addTxs(txs) {
|
|
579
564
|
if (!this.provingState) {
|
|
580
|
-
throw new Error(`Empty
|
|
565
|
+
throw new Error(`Empty proving state. The checkpoint sub-tree has not been started.`);
|
|
581
566
|
}
|
|
582
567
|
if (!txs.length) {
|
|
583
|
-
//
|
|
584
|
-
//
|
|
585
|
-
this.logger.warn(`Provided no txs to
|
|
568
|
+
// Empty block: setBlockCompleted handles this without addTxs being called. Bail to
|
|
569
|
+
// avoid the throw below (we cannot find the blockNumber without any txs).
|
|
570
|
+
this.logger.warn(`Provided no txs to addTxs.`);
|
|
586
571
|
return;
|
|
587
572
|
}
|
|
588
573
|
const blockNumber = BlockNumber(txs[0].globalVariables.blockNumber);
|
|
@@ -635,29 +620,6 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
635
620
|
const blockEndBlobFields = provingState.getBlockEndBlobFields();
|
|
636
621
|
await spongeBlobState.absorb(blockEndBlobFields);
|
|
637
622
|
provingState.setEndSpongeBlob(spongeBlobState);
|
|
638
|
-
// Txs have been added to the block. Now try to accumulate the out hashes and blobs as far as we can:
|
|
639
|
-
await this.provingState.accumulateCheckpointOutHashes();
|
|
640
|
-
await this.provingState.setBlobAccumulators();
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* Kickstarts chonk verifier circuits for the specified txs. These will be used during epoch proving.
|
|
644
|
-
* Note that if the chonk verifier circuits are not started this way, they will be started nontheless after processing.
|
|
645
|
-
*/ startChonkVerifierCircuits(txs) {
|
|
646
|
-
if (!this.provingState?.verifyState()) {
|
|
647
|
-
throw new Error(`Empty epoch proving state. call startNewEpoch before starting chonk verifier circuits.`);
|
|
648
|
-
}
|
|
649
|
-
const publicTxs = txs.filter((tx)=>tx.data.forPublic);
|
|
650
|
-
for (const tx of publicTxs){
|
|
651
|
-
const txHash = tx.getTxHash().toString();
|
|
652
|
-
const privateInputs = getPublicChonkVerifierPrivateInputsFromTx(tx, this.proverId.toField());
|
|
653
|
-
const tubeProof = promiseWithResolvers();
|
|
654
|
-
this.logger.debug(`Starting chonk verifier circuit for tx ${txHash}`);
|
|
655
|
-
this.doEnqueueChonkVerifier(txHash, privateInputs, (proof)=>{
|
|
656
|
-
tubeProof.resolve(proof);
|
|
657
|
-
});
|
|
658
|
-
this.provingState.cachedChonkVerifierProofs.set(txHash, tubeProof.promise);
|
|
659
|
-
}
|
|
660
|
-
return Promise.resolve();
|
|
661
623
|
}
|
|
662
624
|
/**
|
|
663
625
|
* Marks the block as completed.
|
|
@@ -700,146 +662,50 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
700
662
|
await this.verifyBuiltBlockAgainstSyncedState(provingState);
|
|
701
663
|
return header;
|
|
702
664
|
}
|
|
703
|
-
//
|
|
704
|
-
async verifyBuiltBlockAgainstSyncedState(provingState) {
|
|
705
|
-
const builtBlockHeader = provingState.getBuiltBlockHeader();
|
|
706
|
-
if (!builtBlockHeader) {
|
|
707
|
-
this.logger.debug('Block header not built yet, skipping header check.');
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
const output = provingState.getBlockRootRollupOutput();
|
|
711
|
-
if (!output) {
|
|
712
|
-
this.logger.debug('Block root rollup proof not built yet, skipping header check.');
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
const newArchive = provingState.getBuiltArchive();
|
|
716
|
-
if (!newArchive) {
|
|
717
|
-
this.logger.debug('Archive snapshot not yet captured, skipping header check.');
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
const header = await buildHeaderFromCircuitOutputs(output);
|
|
721
|
-
if (!(await header.hash()).equals(await builtBlockHeader.hash())) {
|
|
722
|
-
this.logger.error(`Block header mismatch.\nCircuit: ${inspect(header)}\nComputed: ${inspect(builtBlockHeader)}`);
|
|
723
|
-
provingState.reject(`Block header hash mismatch.`);
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
const blockNumber = provingState.blockNumber;
|
|
727
|
-
const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(blockNumber));
|
|
728
|
-
if (!syncedArchive.equals(newArchive)) {
|
|
729
|
-
this.logger.error(`Archive tree mismatch for block ${blockNumber}: world state synced to ${inspect(syncedArchive)} but built ${inspect(newArchive)}`);
|
|
730
|
-
provingState.reject(`Archive tree mismatch.`);
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
const circuitArchive = output.newArchive;
|
|
734
|
-
if (!newArchive.equals(circuitArchive)) {
|
|
735
|
-
this.logger.error(`New archive mismatch.\nCircuit: ${output.newArchive}\nComputed: ${newArchive}`);
|
|
736
|
-
provingState.reject(`New archive mismatch.`);
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
665
|
+
// ---------------- lifecycle ----------------
|
|
740
666
|
/**
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
* If cancelJobsOnStop is false (default), jobs remain in the broker queue and can be reused on restart/reorg.
|
|
667
|
+
* Cancels any further proving. If `cancelJobsOnStop` was set, aborts all pending broker jobs
|
|
668
|
+
* (used on reorg). Otherwise jobs remain in the broker queue and can be reused on restart.
|
|
744
669
|
*/ cancel() {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
if (this.cancelJobsOnStop) {
|
|
750
|
-
for (const controller of this.pendingProvingJobs){
|
|
751
|
-
controller.abort();
|
|
752
|
-
}
|
|
753
|
-
}
|
|
670
|
+
this.cancelled = true;
|
|
671
|
+
this.resetSchedulerState(this.cancelJobsOnStop);
|
|
672
|
+
// Reject the proving state (and hence subTreeResult) so anyone awaiting the sub-tree result
|
|
673
|
+
// is released rather than hanging — matching TopTreeOrchestrator.cancel().
|
|
754
674
|
this.provingState?.cancel();
|
|
755
675
|
for (const [blockNumber, db] of this.dbs.entries()){
|
|
756
676
|
void db.close().catch((err)=>this.logger.error(`Error closing db for block ${blockNumber}`, err));
|
|
757
677
|
}
|
|
758
678
|
this.dbs.clear();
|
|
759
679
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
if (!db) {
|
|
763
|
-
throw new Error(`World state fork for block ${blockNumber} not found.`);
|
|
764
|
-
}
|
|
765
|
-
return db;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Returns the proof for the current epoch.
|
|
769
|
-
*/ async finalizeEpoch() {
|
|
770
|
-
if (!this.provingState || !this.provingPromise) {
|
|
771
|
-
throw new Error(`Invalid proving state, an epoch must be proven before it can be finalized`);
|
|
772
|
-
}
|
|
773
|
-
const result = await this.provingPromise;
|
|
774
|
-
if (result.status === 'failure') {
|
|
775
|
-
throw new Error(`Epoch proving failed: ${result.reason}`);
|
|
776
|
-
}
|
|
777
|
-
await this.provingState.finalizeBatchedBlob();
|
|
778
|
-
const epochProofResult = this.provingState.getEpochProofResult();
|
|
779
|
-
pushTestData('epochProofResult', {
|
|
780
|
-
proof: epochProofResult.proof.toString(),
|
|
781
|
-
publicInputs: epochProofResult.publicInputs.toString()
|
|
782
|
-
});
|
|
783
|
-
return epochProofResult;
|
|
680
|
+
cancelInternal() {
|
|
681
|
+
this.cancel();
|
|
784
682
|
}
|
|
683
|
+
// ---------------- private: per-checkpoint init ----------------
|
|
785
684
|
/**
|
|
786
|
-
*
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
this.logger.debug(`Not enqueuing job, state no longer valid`);
|
|
793
|
-
return;
|
|
685
|
+
* Internal driver for the single-checkpoint init. Allocates the world-state fork,
|
|
686
|
+
* inserts L1-to-L2 messages, and creates the per-checkpoint proving state with this
|
|
687
|
+
* sub-tree as its parent. Only called once, from the `start` factory.
|
|
688
|
+
*/ async startCheckpoint(constants, l1ToL2Messages, totalNumBlocks, headerOfLastBlockInPreviousCheckpoint) {
|
|
689
|
+
if (this.provingState) {
|
|
690
|
+
throw new Error('Checkpoint sub-tree already started.');
|
|
794
691
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const result = await request(controller.signal);
|
|
806
|
-
if (!provingState.verifyState()) {
|
|
807
|
-
this.logger.debug(`State no longer valid, discarding result`);
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
// we could have been cancelled whilst waiting for the result
|
|
811
|
-
// and the prover ignored the signal. Drop the result in that case
|
|
812
|
-
if (controller.signal.aborted) {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
await callback(result);
|
|
816
|
-
} catch (err) {
|
|
817
|
-
if (err instanceof AbortError) {
|
|
818
|
-
// operation was cancelled, probably because the block was cancelled
|
|
819
|
-
// drop this result
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
|
-
this.logger.error(`Error thrown when proving job`, err);
|
|
823
|
-
provingState.reject(`${err}`);
|
|
824
|
-
} finally{
|
|
825
|
-
const index = this.pendingProvingJobs.indexOf(controller);
|
|
826
|
-
if (index > -1) {
|
|
827
|
-
this.pendingProvingJobs.splice(index, 1);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
};
|
|
831
|
-
void this.deferredJobQueue.put(async ()=>{
|
|
832
|
-
void safeJob();
|
|
833
|
-
// we yield here to the macro task queue such to give Nodejs a chance to run other operatoins in between enqueues
|
|
834
|
-
await sleep(0);
|
|
835
|
-
});
|
|
692
|
+
// Fork world state at the end of the immediately previous block.
|
|
693
|
+
const lastBlockNumber = headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber;
|
|
694
|
+
const db = await this.dbProvider.fork(lastBlockNumber);
|
|
695
|
+
const firstBlockNumber = BlockNumber(lastBlockNumber + 1);
|
|
696
|
+
this.dbs.set(firstBlockNumber, db);
|
|
697
|
+
// Get archive sibling path before any block in this checkpoint lands.
|
|
698
|
+
const lastArchiveSiblingPath = await getLastSiblingPath(MerkleTreeId.ARCHIVE, db);
|
|
699
|
+
// Insert all the l1 to l2 messages into the db. Get the states before and after the insertion.
|
|
700
|
+
const { lastL1ToL2MessageTreeSnapshot, lastL1ToL2MessageSubtreeRootSiblingPath, newL1ToL2MessageTreeSnapshot, newL1ToL2MessageSubtreeRootSiblingPath } = await this.updateL1ToL2MessageTree(l1ToL2Messages, db);
|
|
701
|
+
this.provingState = new CheckpointProvingState(/* index */ 0, constants, totalNumBlocks, headerOfLastBlockInPreviousCheckpoint, lastArchiveSiblingPath, l1ToL2Messages, lastL1ToL2MessageTreeSnapshot, lastL1ToL2MessageSubtreeRootSiblingPath, newL1ToL2MessageTreeSnapshot, newL1ToL2MessageSubtreeRootSiblingPath, Number(this.epochNumber), /* isAlive */ ()=>!this.cancelled, /* onReject */ (reason)=>this.subTreeResult.reject(new Error(reason)));
|
|
836
702
|
}
|
|
703
|
+
// ---------------- private: per-block proof orchestration ----------------
|
|
837
704
|
async updateL1ToL2MessageTree(l1ToL2Messages, db) {
|
|
838
|
-
const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 'Too many L1 to L2 messages');
|
|
839
705
|
const lastL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
|
|
840
706
|
const lastL1ToL2MessageSubtreeRootSiblingPath = assertLength(await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH);
|
|
841
|
-
// Update the local trees to include the new l1 to l2 messages
|
|
842
|
-
await db
|
|
707
|
+
// Update the local trees to include the new l1 to l2 messages.
|
|
708
|
+
await appendL1ToL2MessagesToTree(db, l1ToL2Messages);
|
|
843
709
|
const newL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
|
|
844
710
|
const newL1ToL2MessageSubtreeRootSiblingPath = assertLength(await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH);
|
|
845
711
|
return {
|
|
@@ -849,12 +715,15 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
849
715
|
newL1ToL2MessageSubtreeRootSiblingPath
|
|
850
716
|
};
|
|
851
717
|
}
|
|
852
|
-
// Updates the merkle trees for a transaction. The first enqueued job for a transaction
|
|
718
|
+
// Updates the merkle trees for a transaction. The first enqueued job for a transaction.
|
|
853
719
|
async prepareBaseRollupInputs(tx, lastArchive, newL1ToL2MessageTreeSnapshot, startSpongeBlob, db) {
|
|
854
|
-
//
|
|
855
|
-
//
|
|
856
|
-
|
|
857
|
-
|
|
720
|
+
// These hints deliberately carry no recursive proof or verification key — see
|
|
721
|
+
// BaseRollupHintsWithoutProofAndVK. The tx's proof + VK are attached later in
|
|
722
|
+
// TxProvingState.getBaseRollupTypeAndInputs from the proven chonk-verifier / kernel / AVM
|
|
723
|
+
// proofs, which are required there and so cannot be silently omitted.
|
|
724
|
+
const start = performance.now();
|
|
725
|
+
const hints = await insertSideEffectsAndBuildBaseRollupHints(tx, lastArchive, newL1ToL2MessageTreeSnapshot, startSpongeBlob, this.proverId.toField(), db);
|
|
726
|
+
this.metrics.recordBaseRollupInputs(performance.now() - start);
|
|
858
727
|
const promises = [
|
|
859
728
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
860
729
|
MerkleTreeId.NULLIFIER_TREE,
|
|
@@ -874,8 +743,8 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
874
743
|
treeSnapshots
|
|
875
744
|
];
|
|
876
745
|
}
|
|
877
|
-
// Executes the base rollup circuit and
|
|
878
|
-
// Executes the next level of merge if all inputs are available
|
|
746
|
+
// Executes the base rollup circuit and stores the output as intermediate state for the parent merge/root circuit.
|
|
747
|
+
// Executes the next level of merge if all inputs are available.
|
|
879
748
|
enqueueBaseRollup(provingState, txIndex) {
|
|
880
749
|
if (!provingState.verifyState()) {
|
|
881
750
|
this.logger.debug('Not running base rollup, state invalid');
|
|
@@ -889,15 +758,15 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
889
758
|
const { processedTx } = txProvingState;
|
|
890
759
|
const { rollupType, inputs } = txProvingState.getBaseRollupTypeAndInputs();
|
|
891
760
|
this.logger.debug(`Enqueuing deferred proving base rollup for ${processedTx.hash.toString()}`);
|
|
892
|
-
this.deferredProving(provingState,
|
|
893
|
-
[Attributes.TX_HASH]: processedTx.hash.toString(),
|
|
894
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
|
|
895
|
-
}, (signal)=>{
|
|
761
|
+
this.deferredProving(provingState, this.wrapCircuitCall(inputs instanceof PrivateTxBaseRollupPrivateInputs ? 'getPrivateTxBaseRollupProof' : 'getPublicTxBaseRollupProof', (signal)=>{
|
|
896
762
|
if (inputs instanceof PrivateTxBaseRollupPrivateInputs) {
|
|
897
763
|
return this.prover.getPrivateTxBaseRollupProof(inputs, signal, provingState.epochNumber);
|
|
898
764
|
} else {
|
|
899
765
|
return this.prover.getPublicTxBaseRollupProof(inputs, signal, provingState.epochNumber);
|
|
900
766
|
}
|
|
767
|
+
}, {
|
|
768
|
+
[Attributes.TX_HASH]: processedTx.hash.toString(),
|
|
769
|
+
[Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
|
|
901
770
|
}), (result)=>{
|
|
902
771
|
this.logger.debug(`Completed proof for ${rollupType} for tx ${processedTx.hash.toString()}`);
|
|
903
772
|
validatePartialState(result.inputs.endTreeSnapshots, txProvingState.treeSnapshots);
|
|
@@ -909,46 +778,37 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
909
778
|
}
|
|
910
779
|
});
|
|
911
780
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
781
|
+
/**
|
|
782
|
+
* Route the tx's chonk-verifier dependency through the per-epoch context: read the
|
|
783
|
+
* cached promise (or enqueue if missing), then `.then(handleResult)` to progress to
|
|
784
|
+
* the base rollup once the proof lands.
|
|
785
|
+
*/ getOrEnqueueChonkVerifier(provingState, txIndex) {
|
|
915
786
|
if (!provingState.verifyState()) {
|
|
916
|
-
this.logger.debug('Not running chonk verifier circuit, state invalid');
|
|
917
787
|
return;
|
|
918
788
|
}
|
|
919
789
|
const txProvingState = provingState.getTxProvingState(txIndex);
|
|
920
790
|
const txHash = txProvingState.processedTx.hash.toString();
|
|
921
|
-
NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH;
|
|
922
791
|
const handleResult = (result)=>{
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
792
|
+
if (!provingState.verifyState()) {
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
926
795
|
txProvingState.setPublicChonkVerifierProof(result);
|
|
927
|
-
this.provingState?.cachedChonkVerifierProofs.delete(txHash);
|
|
928
796
|
this.checkAndEnqueueBaseRollup(provingState, txIndex);
|
|
929
797
|
};
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
this.logger.debug('Not running chonk verifier circuit, state invalid');
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getPublicChonkVerifierProof', {
|
|
946
|
-
[Attributes.TX_HASH]: txHash,
|
|
947
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'chonk-verifier-public'
|
|
948
|
-
}, (signal)=>this.prover.getPublicChonkVerifierProof(inputs, signal, provingState.epochNumber)), handler);
|
|
798
|
+
const promise = this.chonkCache.getOrCompute(txHash, (signal)=>this.prover.getPublicChonkVerifierProof(txProvingState.getPublicChonkVerifierPrivateInputs(), signal, this.epochNumber));
|
|
799
|
+
void promise.then(handleResult).catch((err)=>{
|
|
800
|
+
// The cache self-cleans on rejection, so a replacement sub-tree for this tx will see the
|
|
801
|
+
// miss and re-enqueue. But if this proving state is still active, the failure must abort
|
|
802
|
+
// it: otherwise the base rollup for this tx is never enqueued and the checkpoint (and
|
|
803
|
+
// epoch) orchestrators hang forever waiting for a proof that will never arrive.
|
|
804
|
+
if (err instanceof AbortError || !provingState.verifyState()) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
this.logger.error(`Chonk verifier proof failed for tx ${txHash}`, err);
|
|
808
|
+
provingState.reject(`Chonk verifier proof failed for tx ${txHash}: ${err}`);
|
|
809
|
+
});
|
|
949
810
|
}
|
|
950
|
-
// Executes the merge rollup circuit
|
|
951
|
-
// Enqueues the next level of merge if all inputs are available
|
|
811
|
+
// Executes the merge rollup circuit. Enqueues the next level of merge if all inputs are available.
|
|
952
812
|
enqueueMergeRollup(provingState, location) {
|
|
953
813
|
if (!provingState.verifyState()) {
|
|
954
814
|
this.logger.debug('Not running merge rollup. State no longer valid.');
|
|
@@ -959,14 +819,14 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
959
819
|
return;
|
|
960
820
|
}
|
|
961
821
|
const inputs = provingState.getMergeRollupInputs(location);
|
|
962
|
-
this.deferredProving(provingState,
|
|
822
|
+
this.deferredProving(provingState, this.wrapCircuitCall('getTxMergeRollupProof', (signal)=>this.prover.getTxMergeRollupProof(inputs, signal, provingState.epochNumber), {
|
|
963
823
|
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-tx-merge'
|
|
964
|
-
}
|
|
824
|
+
}), (result)=>{
|
|
965
825
|
provingState.setMergeRollupProof(location, result);
|
|
966
826
|
this.checkAndEnqueueNextMergeRollup(provingState, location);
|
|
967
827
|
});
|
|
968
828
|
}
|
|
969
|
-
// Executes the block root rollup circuit
|
|
829
|
+
// Executes the block root rollup circuit.
|
|
970
830
|
enqueueBlockRootRollup(provingState) {
|
|
971
831
|
if (!provingState.verifyState()) {
|
|
972
832
|
this.logger.debug('Not running block root rollup, state no longer valid');
|
|
@@ -978,9 +838,7 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
978
838
|
}
|
|
979
839
|
const { rollupType, inputs } = provingState.getBlockRootRollupTypeAndInputs();
|
|
980
840
|
this.logger.debug(`Enqueuing ${rollupType} for block ${provingState.blockNumber}.`);
|
|
981
|
-
this.deferredProving(provingState,
|
|
982
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
|
|
983
|
-
}, (signal)=>{
|
|
841
|
+
this.deferredProving(provingState, this.wrapCircuitCall('getBlockRootRollupProof', (signal)=>{
|
|
984
842
|
if (inputs instanceof BlockRootFirstRollupPrivateInputs) {
|
|
985
843
|
return this.prover.getBlockRootFirstRollupProof(inputs, signal, provingState.epochNumber);
|
|
986
844
|
} else if (inputs instanceof BlockRootSingleTxFirstRollupPrivateInputs) {
|
|
@@ -992,22 +850,27 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
992
850
|
} else {
|
|
993
851
|
return this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber);
|
|
994
852
|
}
|
|
853
|
+
}, {
|
|
854
|
+
[Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
|
|
995
855
|
}), async (result)=>{
|
|
996
|
-
this.logger.debug(`Completed ${rollupType} proof for block ${provingState.blockNumber}
|
|
856
|
+
this.logger.debug(`Completed ${rollupType} proof for block ${provingState.blockNumber}`, {
|
|
857
|
+
blockNumber: provingState.blockNumber,
|
|
858
|
+
checkpointIndex: provingState.parentCheckpoint.index,
|
|
859
|
+
...result.inputs.toInspect()
|
|
860
|
+
});
|
|
997
861
|
const leafLocation = provingState.setBlockRootRollupProof(result);
|
|
998
862
|
const checkpointProvingState = provingState.parentCheckpoint;
|
|
999
863
|
// Verification is called from both here and setBlockCompleted. Whichever runs last
|
|
1000
864
|
// will be the first to see all three pieces (header, proof output, archive) and run the checks.
|
|
1001
865
|
await this.verifyBuiltBlockAgainstSyncedState(provingState);
|
|
1002
866
|
if (checkpointProvingState.totalNumBlocks === 1) {
|
|
1003
|
-
|
|
867
|
+
this.checkAndEnqueueSubTreeResolution(checkpointProvingState);
|
|
1004
868
|
} else {
|
|
1005
|
-
|
|
869
|
+
this.checkAndEnqueueNextBlockMergeRollup(checkpointProvingState, leafLocation);
|
|
1006
870
|
}
|
|
1007
871
|
});
|
|
1008
872
|
}
|
|
1009
|
-
// Executes the base parity circuit
|
|
1010
|
-
// Enqueues the root parity circuit if all inputs are available
|
|
873
|
+
// Executes the base parity circuit. Enqueues the root parity circuit if all inputs are available.
|
|
1011
874
|
enqueueBaseParityCircuit(checkpointProvingState, provingState, baseParityIndex) {
|
|
1012
875
|
if (!provingState.verifyState()) {
|
|
1013
876
|
this.logger.debug('Not running base parity. State no longer valid.');
|
|
@@ -1018,9 +881,9 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
1018
881
|
return;
|
|
1019
882
|
}
|
|
1020
883
|
const inputs = checkpointProvingState.getBaseParityInputs(baseParityIndex);
|
|
1021
|
-
this.deferredProving(provingState,
|
|
884
|
+
this.deferredProving(provingState, this.wrapCircuitCall('getBaseParityProof', (signal)=>this.prover.getBaseParityProof(inputs, signal, provingState.epochNumber), {
|
|
1022
885
|
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'parity-base'
|
|
1023
|
-
}
|
|
886
|
+
}), (provingOutput)=>{
|
|
1024
887
|
provingState.setBaseParityProof(baseParityIndex, provingOutput);
|
|
1025
888
|
this.checkAndEnqueueRootParityCircuit(provingState);
|
|
1026
889
|
});
|
|
@@ -1031,8 +894,8 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
1031
894
|
}
|
|
1032
895
|
this.enqueueRootParityCircuit(provingState);
|
|
1033
896
|
}
|
|
1034
|
-
// Runs the root parity circuit
|
|
1035
|
-
// Enqueues the root rollup
|
|
897
|
+
// Runs the root parity circuit and stores the outputs.
|
|
898
|
+
// Enqueues the block root rollup if all inputs are available.
|
|
1036
899
|
enqueueRootParityCircuit(provingState) {
|
|
1037
900
|
if (!provingState.verifyState()) {
|
|
1038
901
|
this.logger.debug('Not running root parity. State no longer valid.');
|
|
@@ -1043,15 +906,14 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
1043
906
|
return;
|
|
1044
907
|
}
|
|
1045
908
|
const inputs = provingState.getParityRootInputs();
|
|
1046
|
-
this.deferredProving(provingState,
|
|
909
|
+
this.deferredProving(provingState, this.wrapCircuitCall('getRootParityProof', (signal)=>this.prover.getRootParityProof(inputs, signal, provingState.epochNumber), {
|
|
1047
910
|
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'parity-root'
|
|
1048
|
-
}
|
|
911
|
+
}), (result)=>{
|
|
1049
912
|
provingState.setRootParityProof(result);
|
|
1050
913
|
this.checkAndEnqueueBlockRootRollup(provingState);
|
|
1051
914
|
});
|
|
1052
915
|
}
|
|
1053
|
-
// Executes the block merge rollup circuit
|
|
1054
|
-
// Enqueues the next level of merge if all inputs are available
|
|
916
|
+
// Executes the block merge rollup circuit.
|
|
1055
917
|
enqueueBlockMergeRollup(provingState, location) {
|
|
1056
918
|
if (!provingState.verifyState()) {
|
|
1057
919
|
this.logger.debug('Not running block merge rollup. State no longer valid.');
|
|
@@ -1062,104 +924,16 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
1062
924
|
return;
|
|
1063
925
|
}
|
|
1064
926
|
const inputs = provingState.getBlockMergeRollupInputs(location);
|
|
1065
|
-
this.deferredProving(provingState,
|
|
927
|
+
this.deferredProving(provingState, this.wrapCircuitCall('getBlockMergeRollupProof', (signal)=>this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber), {
|
|
1066
928
|
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-block-merge'
|
|
1067
|
-
}, (signal)=>this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber)), async (result)=>{
|
|
1068
|
-
provingState.setBlockMergeRollupProof(location, result);
|
|
1069
|
-
await this.checkAndEnqueueNextBlockMergeRollup(provingState, location);
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
async enqueueCheckpointRootRollup(provingState) {
|
|
1073
|
-
if (!provingState.verifyState()) {
|
|
1074
|
-
this.logger.debug('Not running checkpoint root rollup. State no longer valid.');
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
if (!provingState.tryStartProvingCheckpointRoot()) {
|
|
1078
|
-
this.logger.debug('Checkpoint root rollup already started.');
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
const rollupType = provingState.getCheckpointRootRollupType();
|
|
1082
|
-
this.logger.debug(`Enqueuing ${rollupType} for checkpoint ${provingState.index}.`);
|
|
1083
|
-
const inputs = await provingState.getCheckpointRootRollupInputs();
|
|
1084
|
-
this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getCheckpointRootRollupProof', {
|
|
1085
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
|
|
1086
|
-
}, (signal)=>{
|
|
1087
|
-
if (inputs instanceof CheckpointRootSingleBlockRollupPrivateInputs) {
|
|
1088
|
-
return this.prover.getCheckpointRootSingleBlockRollupProof(inputs, signal, provingState.epochNumber);
|
|
1089
|
-
} else {
|
|
1090
|
-
return this.prover.getCheckpointRootRollupProof(inputs, signal, provingState.epochNumber);
|
|
1091
|
-
}
|
|
1092
929
|
}), (result)=>{
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
provingState.reject(`Blob accumulator state mismatch.`);
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
this.logger.debug(`Completed ${rollupType} proof for checkpoint ${provingState.index}.`);
|
|
1101
|
-
const leafLocation = provingState.setCheckpointRootRollupProof(result);
|
|
1102
|
-
const epochProvingState = provingState.parentEpoch;
|
|
1103
|
-
if (epochProvingState.totalNumCheckpoints === 1) {
|
|
1104
|
-
this.enqueueEpochPadding(epochProvingState);
|
|
1105
|
-
} else {
|
|
1106
|
-
this.checkAndEnqueueNextCheckpointMergeRollup(epochProvingState, leafLocation);
|
|
1107
|
-
}
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
enqueueCheckpointMergeRollup(provingState, location) {
|
|
1111
|
-
if (!provingState.verifyState()) {
|
|
1112
|
-
this.logger.debug('Not running checkpoint merge rollup. State no longer valid.');
|
|
1113
|
-
return;
|
|
1114
|
-
}
|
|
1115
|
-
if (!provingState.tryStartProvingCheckpointMerge(location)) {
|
|
1116
|
-
this.logger.debug('Checkpoint merge rollup already started.');
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
const inputs = provingState.getCheckpointMergeRollupInputs(location);
|
|
1120
|
-
this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getCheckpointMergeRollupProof', {
|
|
1121
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-merge'
|
|
1122
|
-
}, (signal)=>this.prover.getCheckpointMergeRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
|
|
1123
|
-
this.logger.debug('Completed proof for checkpoint merge rollup.');
|
|
1124
|
-
provingState.setCheckpointMergeRollupProof(location, result);
|
|
1125
|
-
this.checkAndEnqueueNextCheckpointMergeRollup(provingState, location);
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
enqueueEpochPadding(provingState) {
|
|
1129
|
-
if (!provingState.verifyState()) {
|
|
1130
|
-
this.logger.debug('Not running epoch padding. State no longer valid.');
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
if (!provingState.tryStartProvingPaddingCheckpoint()) {
|
|
1134
|
-
this.logger.debug('Padding checkpoint already started.');
|
|
1135
|
-
return;
|
|
1136
|
-
}
|
|
1137
|
-
this.logger.debug('Padding epoch proof with a padding block root proof.');
|
|
1138
|
-
const inputs = provingState.getPaddingCheckpointInputs();
|
|
1139
|
-
this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getCheckpointPaddingRollupProof', {
|
|
1140
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-padding'
|
|
1141
|
-
}, (signal)=>this.prover.getCheckpointPaddingRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
|
|
1142
|
-
this.logger.debug('Completed proof for padding checkpoint.');
|
|
1143
|
-
provingState.setCheckpointPaddingProof(result);
|
|
1144
|
-
this.checkAndEnqueueRootRollup(provingState);
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
// Executes the root rollup circuit
|
|
1148
|
-
enqueueRootRollup(provingState) {
|
|
1149
|
-
if (!provingState.verifyState()) {
|
|
1150
|
-
this.logger.debug('Not running root rollup, state no longer valid');
|
|
1151
|
-
return;
|
|
1152
|
-
}
|
|
1153
|
-
this.logger.debug(`Preparing root rollup`);
|
|
1154
|
-
const inputs = provingState.getRootRollupInputs();
|
|
1155
|
-
this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getRootRollupProof', {
|
|
1156
|
-
[Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-root'
|
|
1157
|
-
}, (signal)=>this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
|
|
1158
|
-
this.logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`);
|
|
1159
|
-
provingState.setRootRollupProof(result);
|
|
1160
|
-
provingState.resolve({
|
|
1161
|
-
status: 'success'
|
|
930
|
+
this.logger.debug(`Completed block merge rollup proof for checkpoint ${provingState.index}`, {
|
|
931
|
+
checkpointIndex: provingState.index,
|
|
932
|
+
mergeLocation: location,
|
|
933
|
+
...result.inputs.toInspect()
|
|
1162
934
|
});
|
|
935
|
+
provingState.setBlockMergeRollupProof(location, result);
|
|
936
|
+
this.checkAndEnqueueNextBlockMergeRollup(provingState, location);
|
|
1163
937
|
});
|
|
1164
938
|
}
|
|
1165
939
|
checkAndEnqueueNextMergeRollup(provingState, currentLocation) {
|
|
@@ -1180,59 +954,48 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
1180
954
|
}
|
|
1181
955
|
this.enqueueBlockRootRollup(provingState);
|
|
1182
956
|
}
|
|
1183
|
-
|
|
957
|
+
checkAndEnqueueNextBlockMergeRollup(provingState, currentLocation) {
|
|
1184
958
|
if (!provingState.isReadyForBlockMerge(currentLocation)) {
|
|
1185
959
|
return;
|
|
1186
960
|
}
|
|
1187
961
|
const parentLocation = provingState.getParentLocation(currentLocation);
|
|
1188
962
|
if (parentLocation.level === 0) {
|
|
1189
|
-
|
|
963
|
+
this.checkAndEnqueueSubTreeResolution(provingState);
|
|
1190
964
|
} else {
|
|
1191
965
|
this.enqueueBlockMergeRollup(provingState, parentLocation);
|
|
1192
966
|
}
|
|
1193
967
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if (
|
|
1202
|
-
|
|
1203
|
-
}
|
|
1204
|
-
const parentLocation = provingState.getParentLocation(currentLocation);
|
|
1205
|
-
if (parentLocation.level === 0) {
|
|
1206
|
-
this.checkAndEnqueueRootRollup(provingState);
|
|
1207
|
-
} else {
|
|
1208
|
-
this.enqueueCheckpointMergeRollup(provingState, parentLocation);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
checkAndEnqueueRootRollup(provingState) {
|
|
1212
|
-
if (!provingState.isReadyForRootRollup()) {
|
|
1213
|
-
this.logger.debug('Not ready for root rollup');
|
|
968
|
+
/**
|
|
969
|
+
* Sub-tree analogue of the orchestrator's `checkAndEnqueueCheckpointRootRollup`:
|
|
970
|
+
* resolves the sub-tree promise with the block-level proof outputs once they're all ready,
|
|
971
|
+
* instead of escalating to the checkpoint root rollup.
|
|
972
|
+
*/ checkAndEnqueueSubTreeResolution(provingState) {
|
|
973
|
+
const proofs = provingState.getSubTreeOutputProofs();
|
|
974
|
+
const nonEmpty = proofs.filter((p)=>!!p);
|
|
975
|
+
if (proofs.length !== nonEmpty.length) {
|
|
976
|
+
// Block merge tree not fully resolved yet — retried as more block proofs land.
|
|
1214
977
|
return;
|
|
1215
978
|
}
|
|
1216
|
-
this.
|
|
979
|
+
this.subTreeResult.resolve({
|
|
980
|
+
blockProofOutputs: nonEmpty,
|
|
981
|
+
previousArchiveSiblingPath: provingState.getLastArchiveSiblingPath()
|
|
982
|
+
});
|
|
1217
983
|
}
|
|
1218
984
|
/**
|
|
1219
|
-
* Executes the VM circuit for a public function
|
|
1220
|
-
*
|
|
1221
|
-
* @param provingState - The proving state being operated on
|
|
1222
|
-
* @param txIndex - The index of the transaction being proven
|
|
985
|
+
* Executes the VM circuit for a public function. Enqueues the base rollup once the
|
|
986
|
+
* tx's chonk-verifier + VM proofs are both ready.
|
|
1223
987
|
*/ enqueueVM(provingState, txIndex) {
|
|
1224
988
|
if (!provingState.verifyState()) {
|
|
1225
989
|
this.logger.debug(`Not running VM circuit as state is no longer valid`);
|
|
1226
990
|
return;
|
|
1227
991
|
}
|
|
1228
992
|
const txProvingState = provingState.getTxProvingState(txIndex);
|
|
1229
|
-
|
|
1230
|
-
[Attributes.TX_HASH]: txProvingState.processedTx.hash.toString()
|
|
1231
|
-
}, async (signal)=>{
|
|
993
|
+
this.deferredProving(provingState, this.wrapCircuitCall('getAvmProof', async (signal)=>{
|
|
1232
994
|
const inputs = txProvingState.getAvmInputs();
|
|
1233
995
|
return await this.prover.getAvmProof(inputs, signal, provingState.epochNumber);
|
|
1234
|
-
}
|
|
1235
|
-
|
|
996
|
+
}, {
|
|
997
|
+
[Attributes.TX_HASH]: txProvingState.processedTx.hash.toString()
|
|
998
|
+
}), (proof)=>{
|
|
1236
999
|
this.logger.debug(`Proven VM for tx index: ${txIndex}`);
|
|
1237
1000
|
txProvingState.setAvmProof(proof);
|
|
1238
1001
|
this.checkAndEnqueueBaseRollup(provingState, txIndex);
|
|
@@ -1243,8 +1006,61 @@ _dec = trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
|
|
|
1243
1006
|
if (!txProvingState.ready()) {
|
|
1244
1007
|
return;
|
|
1245
1008
|
}
|
|
1246
|
-
//
|
|
1009
|
+
// All upstream proofs (chonk verifier and, if required, vm) are ready — proceed to the base rollup.
|
|
1247
1010
|
this.logger.debug(`Public functions completed for tx ${txIndex} enqueueing base rollup`);
|
|
1248
1011
|
this.enqueueBaseRollup(provingState, txIndex);
|
|
1249
1012
|
}
|
|
1013
|
+
// Flagged as protected so unit tests can override.
|
|
1014
|
+
async verifyBuiltBlockAgainstSyncedState(provingState) {
|
|
1015
|
+
const builtBlockHeader = provingState.getBuiltBlockHeader();
|
|
1016
|
+
if (!builtBlockHeader) {
|
|
1017
|
+
this.logger.debug('Block header not built yet, skipping header check.');
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const output = provingState.getBlockRootRollupOutput();
|
|
1021
|
+
if (!output) {
|
|
1022
|
+
this.logger.debug('Block root rollup proof not built yet, skipping header check.');
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
const newArchive = provingState.getBuiltArchive();
|
|
1026
|
+
if (!newArchive) {
|
|
1027
|
+
this.logger.debug('Archive snapshot not yet captured, skipping header check.');
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const header = await buildHeaderFromCircuitOutputs(output);
|
|
1031
|
+
if (!(await header.hash()).equals(await builtBlockHeader.hash())) {
|
|
1032
|
+
this.logger.error(`Block header mismatch.\nCircuit: ${inspect(header)}\nComputed: ${inspect(builtBlockHeader)}`);
|
|
1033
|
+
provingState.reject(`Block header hash mismatch.`);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
const blockNumber = provingState.blockNumber;
|
|
1037
|
+
const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(blockNumber));
|
|
1038
|
+
if (!syncedArchive.equals(newArchive)) {
|
|
1039
|
+
this.logger.error(`Archive tree mismatch for block ${blockNumber}: world state synced to ${inspect(syncedArchive)} but built ${inspect(newArchive)}`);
|
|
1040
|
+
provingState.reject(`Archive tree mismatch.`);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
const circuitArchive = output.newArchive;
|
|
1044
|
+
if (!newArchive.equals(circuitArchive)) {
|
|
1045
|
+
this.logger.error(`New archive mismatch.\nCircuit: ${output.newArchive}\nComputed: ${newArchive}`);
|
|
1046
|
+
provingState.reject(`New archive mismatch.`);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
getDbForBlock(blockNumber) {
|
|
1051
|
+
const db = this.dbs.get(blockNumber);
|
|
1052
|
+
if (!db) {
|
|
1053
|
+
throw new Error(`World state fork for block ${blockNumber} not found.`);
|
|
1054
|
+
}
|
|
1055
|
+
return db;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Wraps a circuit call with a tracer span and circuit attributes. Replaces the
|
|
1059
|
+
* `ProvingScheduler.wrapCircuitCall` indirection that used to live on the abstract base.
|
|
1060
|
+
*/ wrapCircuitCall(circuitName, fn, attributes = {}) {
|
|
1061
|
+
return wrapCallbackInSpan(this.tracer, `CheckpointSubTreeOrchestrator.prover.${circuitName}`, {
|
|
1062
|
+
[Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName,
|
|
1063
|
+
...attributes
|
|
1064
|
+
}, fn);
|
|
1065
|
+
}
|
|
1250
1066
|
}
|