@aztec/prover-client 0.65.2 → 0.66.0
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/block_builder/index.d.ts +6 -0
- package/dest/block_builder/index.d.ts.map +1 -0
- package/dest/block_builder/index.js +2 -0
- package/dest/block_builder/light.d.ts +32 -0
- package/dest/block_builder/light.d.ts.map +1 -0
- package/dest/block_builder/light.js +71 -0
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -3
- package/dest/mocks/fixtures.d.ts +1 -2
- package/dest/mocks/fixtures.d.ts.map +1 -1
- package/dest/mocks/fixtures.js +3 -7
- package/dest/mocks/test_context.d.ts +28 -10
- package/dest/mocks/test_context.d.ts.map +1 -1
- package/dest/mocks/test_context.js +58 -21
- package/dest/orchestrator/epoch-proving-state.d.ts +5 -6
- package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/epoch-proving-state.js +10 -12
- package/dest/orchestrator/orchestrator.d.ts +8 -6
- package/dest/orchestrator/orchestrator.d.ts.map +1 -1
- package/dest/orchestrator/orchestrator.js +83 -72
- package/dest/orchestrator/tx-proving-state.d.ts +0 -1
- package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/tx-proving-state.js +2 -34
- package/dest/prover-client/factory.d.ts +6 -0
- package/dest/prover-client/factory.d.ts.map +1 -0
- package/dest/prover-client/factory.js +6 -0
- package/dest/prover-client/index.d.ts +3 -0
- package/dest/prover-client/index.d.ts.map +1 -0
- package/dest/prover-client/index.js +3 -0
- package/dest/{tx-prover/tx-prover.d.ts → prover-client/prover-client.d.ts} +8 -11
- package/dest/prover-client/prover-client.d.ts.map +1 -0
- package/dest/prover-client/prover-client.js +107 -0
- package/dest/proving_broker/factory.d.ts +2 -1
- package/dest/proving_broker/factory.d.ts.map +1 -1
- package/dest/proving_broker/factory.js +4 -4
- package/dest/proving_broker/proving_agent.d.ts +5 -0
- package/dest/proving_broker/proving_agent.d.ts.map +1 -1
- package/dest/proving_broker/proving_agent.js +13 -2
- package/dest/proving_broker/proving_agent_instrumentation.d.ts +8 -0
- package/dest/proving_broker/proving_agent_instrumentation.d.ts.map +1 -0
- package/dest/proving_broker/proving_agent_instrumentation.js +16 -0
- package/dest/proving_broker/proving_broker.d.ts +6 -1
- package/dest/proving_broker/proving_broker.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker.js +37 -4
- package/dest/proving_broker/proving_broker_database/persisted.d.ts +3 -1
- package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker_database/persisted.js +10 -2
- package/dest/proving_broker/proving_broker_instrumentation.d.ts +25 -0
- package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker_instrumentation.js +91 -0
- package/dest/test/mock_prover.d.ts +1 -1
- package/dest/test/mock_prover.d.ts.map +1 -1
- package/dest/test/mock_prover.js +4 -3
- package/package.json +14 -13
- package/src/block_builder/index.ts +6 -0
- package/src/block_builder/light.ts +117 -0
- package/src/index.ts +1 -2
- package/src/mocks/fixtures.ts +2 -14
- package/src/mocks/test_context.ts +80 -24
- package/src/orchestrator/epoch-proving-state.ts +10 -13
- package/src/orchestrator/orchestrator.ts +97 -77
- package/src/orchestrator/tx-proving-state.ts +1 -56
- package/src/{tx-prover → prover-client}/factory.ts +4 -3
- package/src/prover-client/index.ts +2 -0
- package/src/{tx-prover/tx-prover.ts → prover-client/prover-client.ts} +23 -13
- package/src/proving_broker/factory.ts +7 -3
- package/src/proving_broker/proving_agent.ts +16 -1
- package/src/proving_broker/proving_agent_instrumentation.ts +21 -0
- package/src/proving_broker/proving_broker.ts +46 -3
- package/src/proving_broker/proving_broker_database/persisted.ts +17 -2
- package/src/proving_broker/proving_broker_instrumentation.ts +130 -0
- package/src/test/mock_prover.ts +3 -2
- package/dest/tx-prover/factory.d.ts +0 -6
- package/dest/tx-prover/factory.d.ts.map +0 -1
- package/dest/tx-prover/factory.js +0 -6
- package/dest/tx-prover/tx-prover.d.ts.map +0 -1
- package/dest/tx-prover/tx-prover.js +0 -110
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type BBProverConfig } from '@aztec/bb-prover';
|
|
2
2
|
import {
|
|
3
|
-
type
|
|
3
|
+
type L2Block,
|
|
4
4
|
type ProcessedTx,
|
|
5
5
|
type ProcessedTxHandler,
|
|
6
6
|
type PublicExecutionRequest,
|
|
@@ -8,10 +8,13 @@ import {
|
|
|
8
8
|
type Tx,
|
|
9
9
|
type TxValidator,
|
|
10
10
|
} from '@aztec/circuit-types';
|
|
11
|
-
import {
|
|
11
|
+
import { makeBloatedProcessedTx } from '@aztec/circuit-types/test';
|
|
12
|
+
import { type AppendOnlyTreeSnapshot, type Gas, type GlobalVariables, Header } from '@aztec/circuits.js';
|
|
13
|
+
import { times } from '@aztec/foundation/collection';
|
|
12
14
|
import { Fr } from '@aztec/foundation/fields';
|
|
13
15
|
import { type DebugLogger } from '@aztec/foundation/log';
|
|
14
|
-
import {
|
|
16
|
+
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types';
|
|
17
|
+
import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
|
|
15
18
|
import {
|
|
16
19
|
PublicProcessor,
|
|
17
20
|
PublicTxSimulator,
|
|
@@ -20,32 +23,34 @@ import {
|
|
|
20
23
|
type WorldStateDB,
|
|
21
24
|
} from '@aztec/simulator';
|
|
22
25
|
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
|
|
23
|
-
import {
|
|
26
|
+
import { type MerkleTreeAdminDatabase } from '@aztec/world-state';
|
|
24
27
|
import { NativeWorldStateService } from '@aztec/world-state/native';
|
|
25
28
|
|
|
26
29
|
import { jest } from '@jest/globals';
|
|
27
30
|
import * as fs from 'fs/promises';
|
|
28
|
-
import {
|
|
31
|
+
import { mock } from 'jest-mock-extended';
|
|
29
32
|
|
|
30
33
|
import { TestCircuitProver } from '../../../bb-prover/src/test/test_circuit_prover.js';
|
|
31
34
|
import { AvmFinalizedCallResult } from '../../../simulator/src/avm/avm_contract_call_result.js';
|
|
32
35
|
import { type AvmPersistableStateManager } from '../../../simulator/src/avm/journal/journal.js';
|
|
36
|
+
import { buildBlock } from '../block_builder/light.js';
|
|
33
37
|
import { ProvingOrchestrator } from '../orchestrator/index.js';
|
|
34
38
|
import { MemoryProvingQueue } from '../prover-agent/memory-proving-queue.js';
|
|
35
39
|
import { ProverAgent } from '../prover-agent/prover-agent.js';
|
|
36
40
|
import { getEnvironmentConfig, getSimulationProvider, makeGlobals } from './fixtures.js';
|
|
37
41
|
|
|
38
42
|
export class TestContext {
|
|
43
|
+
private headers: Map<number, Header> = new Map();
|
|
44
|
+
|
|
39
45
|
constructor(
|
|
40
46
|
public publicTxSimulator: PublicTxSimulator,
|
|
41
|
-
public
|
|
47
|
+
public worldState: MerkleTreeAdminDatabase,
|
|
42
48
|
public publicProcessor: PublicProcessor,
|
|
43
49
|
public simulationProvider: SimulationProvider,
|
|
44
50
|
public globalVariables: GlobalVariables,
|
|
45
|
-
public actualDb: MerkleTreeWriteOperations,
|
|
46
51
|
public prover: ServerCircuitProver,
|
|
47
52
|
public proverAgent: ProverAgent,
|
|
48
|
-
public orchestrator:
|
|
53
|
+
public orchestrator: TestProvingOrchestrator,
|
|
49
54
|
public blockNumber: number,
|
|
50
55
|
public directoriesToCleanup: string[],
|
|
51
56
|
public logger: DebugLogger,
|
|
@@ -57,11 +62,10 @@ export class TestContext {
|
|
|
57
62
|
|
|
58
63
|
static async new(
|
|
59
64
|
logger: DebugLogger,
|
|
60
|
-
worldState: 'native' | 'legacy' = 'native',
|
|
61
65
|
proverCount = 4,
|
|
62
66
|
createProver: (bbConfig: BBProverConfig) => Promise<ServerCircuitProver> = _ =>
|
|
63
67
|
Promise.resolve(new TestCircuitProver(new NoopTelemetryClient(), new WASMSimulator())),
|
|
64
|
-
blockNumber =
|
|
68
|
+
blockNumber = 1,
|
|
65
69
|
) {
|
|
66
70
|
const directoriesToCleanup: string[] = [];
|
|
67
71
|
const globalVariables = makeGlobals(blockNumber);
|
|
@@ -70,18 +74,9 @@ export class TestContext {
|
|
|
70
74
|
const telemetry = new NoopTelemetryClient();
|
|
71
75
|
|
|
72
76
|
// Separated dbs for public processor and prover - see public_processor for context
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
const ws = await NativeWorldStateService.tmp();
|
|
78
|
+
const publicDb = await ws.fork();
|
|
75
79
|
|
|
76
|
-
if (worldState === 'native') {
|
|
77
|
-
const ws = await NativeWorldStateService.tmp();
|
|
78
|
-
publicDb = await ws.fork();
|
|
79
|
-
proverDb = await ws.fork();
|
|
80
|
-
} else {
|
|
81
|
-
const ws = await MerkleTrees.new(openTmpStore(), telemetry);
|
|
82
|
-
publicDb = await ws.getLatest();
|
|
83
|
-
proverDb = await ws.getLatest();
|
|
84
|
-
}
|
|
85
80
|
worldStateDB.getMerkleInterface.mockReturnValue(publicDb);
|
|
86
81
|
|
|
87
82
|
const publicTxSimulator = new PublicTxSimulator(publicDb, worldStateDB, telemetry, globalVariables);
|
|
@@ -118,7 +113,7 @@ export class TestContext {
|
|
|
118
113
|
}
|
|
119
114
|
|
|
120
115
|
const queue = new MemoryProvingQueue(telemetry);
|
|
121
|
-
const orchestrator = new
|
|
116
|
+
const orchestrator = new TestProvingOrchestrator(ws, queue, telemetry, Fr.ZERO);
|
|
122
117
|
const agent = new ProverAgent(localProver, proverCount);
|
|
123
118
|
|
|
124
119
|
queue.start();
|
|
@@ -126,11 +121,10 @@ export class TestContext {
|
|
|
126
121
|
|
|
127
122
|
return new this(
|
|
128
123
|
publicTxSimulator,
|
|
129
|
-
|
|
124
|
+
ws,
|
|
130
125
|
processor,
|
|
131
126
|
simulationProvider,
|
|
132
127
|
globalVariables,
|
|
133
|
-
proverDb,
|
|
134
128
|
localProver,
|
|
135
129
|
agent,
|
|
136
130
|
orchestrator,
|
|
@@ -140,6 +134,16 @@ export class TestContext {
|
|
|
140
134
|
);
|
|
141
135
|
}
|
|
142
136
|
|
|
137
|
+
public getFork() {
|
|
138
|
+
return this.worldState.fork();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public getHeader(blockNumber: 0): Header;
|
|
142
|
+
public getHeader(blockNumber: number): Header | undefined;
|
|
143
|
+
public getHeader(blockNumber = 0) {
|
|
144
|
+
return blockNumber === 0 ? this.worldState.getCommitted().getInitialHeader() : this.headers.get(blockNumber);
|
|
145
|
+
}
|
|
146
|
+
|
|
143
147
|
async cleanup() {
|
|
144
148
|
await this.proverAgent.stop();
|
|
145
149
|
for (const dir of this.directoriesToCleanup.filter(x => x !== '')) {
|
|
@@ -147,6 +151,42 @@ export class TestContext {
|
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
|
|
154
|
+
public makeProcessedTx(opts?: Parameters<typeof makeBloatedProcessedTx>[0]): ProcessedTx;
|
|
155
|
+
public makeProcessedTx(seed?: number): ProcessedTx;
|
|
156
|
+
public makeProcessedTx(seedOrOpts?: Parameters<typeof makeBloatedProcessedTx>[0] | number): ProcessedTx {
|
|
157
|
+
const opts = typeof seedOrOpts === 'number' ? { seed: seedOrOpts } : seedOrOpts;
|
|
158
|
+
const blockNum = (opts?.globalVariables ?? this.globalVariables).blockNumber.toNumber();
|
|
159
|
+
const header = this.getHeader(blockNum - 1);
|
|
160
|
+
return makeBloatedProcessedTx({
|
|
161
|
+
header,
|
|
162
|
+
vkTreeRoot: getVKTreeRoot(),
|
|
163
|
+
protocolContractTreeRoot,
|
|
164
|
+
globalVariables: this.globalVariables,
|
|
165
|
+
...opts,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Creates a block with the given number of txs and adds it to world-state */
|
|
170
|
+
public async makePendingBlock(
|
|
171
|
+
numTxs: number,
|
|
172
|
+
numMsgs: number = 0,
|
|
173
|
+
blockNumOrGlobals: GlobalVariables | number = this.globalVariables,
|
|
174
|
+
makeProcessedTxOpts: (index: number) => Partial<Parameters<typeof makeBloatedProcessedTx>[0]> = () => ({}),
|
|
175
|
+
) {
|
|
176
|
+
const globalVariables = typeof blockNumOrGlobals === 'number' ? makeGlobals(blockNumOrGlobals) : blockNumOrGlobals;
|
|
177
|
+
const blockNum = globalVariables.blockNumber.toNumber();
|
|
178
|
+
const db = await this.worldState.fork();
|
|
179
|
+
const msgs = times(numMsgs, i => new Fr(blockNum * 100 + i));
|
|
180
|
+
const txs = times(numTxs, i =>
|
|
181
|
+
this.makeProcessedTx({ seed: i + blockNum * 1000, globalVariables, ...makeProcessedTxOpts(i) }),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const block = await buildBlock(txs, globalVariables, msgs, db);
|
|
185
|
+
this.headers.set(blockNum, block.header);
|
|
186
|
+
await this.worldState.handleL2BlockAndMessages(block, msgs);
|
|
187
|
+
return { block, txs, msgs };
|
|
188
|
+
}
|
|
189
|
+
|
|
150
190
|
public async processPublicFunctions(
|
|
151
191
|
txs: Tx[],
|
|
152
192
|
maxTransactions: number,
|
|
@@ -217,3 +257,19 @@ export class TestContext {
|
|
|
217
257
|
return await this.publicProcessor.process(txs, maxTransactions, txHandler, txValidator);
|
|
218
258
|
}
|
|
219
259
|
}
|
|
260
|
+
|
|
261
|
+
class TestProvingOrchestrator extends ProvingOrchestrator {
|
|
262
|
+
public isVerifyBuiltBlockAgainstSyncedStateEnabled = false;
|
|
263
|
+
|
|
264
|
+
// Disable this check by default, since it requires seeding world state with the block being built
|
|
265
|
+
// This is only enabled in some tests with multiple blocks that populate the pending chain via makePendingBlock
|
|
266
|
+
protected override verifyBuiltBlockAgainstSyncedState(
|
|
267
|
+
l2Block: L2Block,
|
|
268
|
+
newArchive: AppendOnlyTreeSnapshot,
|
|
269
|
+
): Promise<void> {
|
|
270
|
+
if (this.isVerifyBuiltBlockAgainstSyncedStateEnabled) {
|
|
271
|
+
return super.verifyBuiltBlockAgainstSyncedState(l2Block, newArchive);
|
|
272
|
+
}
|
|
273
|
+
return Promise.resolve();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -50,20 +50,16 @@ export class EpochProvingState {
|
|
|
50
50
|
private mergeRollupInputs: BlockMergeRollupInputData[] = [];
|
|
51
51
|
public rootRollupPublicInputs: RootRollupPublicInputs | undefined;
|
|
52
52
|
public finalProof: Proof | undefined;
|
|
53
|
-
public blocks: BlockProvingState[] = [];
|
|
53
|
+
public blocks: (BlockProvingState | undefined)[] = [];
|
|
54
54
|
|
|
55
55
|
constructor(
|
|
56
56
|
public readonly epochNumber: number,
|
|
57
|
+
public readonly firstBlockNumber: number,
|
|
57
58
|
public readonly totalNumBlocks: number,
|
|
58
59
|
private completionCallback: (result: ProvingResult) => void,
|
|
59
60
|
private rejectionCallback: (reason: string) => void,
|
|
60
61
|
) {}
|
|
61
62
|
|
|
62
|
-
/** Returns the current block proving state */
|
|
63
|
-
public get currentBlock(): BlockProvingState | undefined {
|
|
64
|
-
return this.blocks.at(-1);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
63
|
// Returns the number of levels of merge rollups
|
|
68
64
|
public get numMergeLevels() {
|
|
69
65
|
const totalLeaves = Math.max(2, this.totalNumBlocks);
|
|
@@ -110,9 +106,10 @@ export class EpochProvingState {
|
|
|
110
106
|
archiveTreeSnapshot: AppendOnlyTreeSnapshot,
|
|
111
107
|
archiveTreeRootSiblingPath: Tuple<Fr, typeof ARCHIVE_HEIGHT>,
|
|
112
108
|
previousBlockHash: Fr,
|
|
113
|
-
) {
|
|
109
|
+
): BlockProvingState {
|
|
110
|
+
const index = globalVariables.blockNumber.toNumber() - this.firstBlockNumber;
|
|
114
111
|
const block = new BlockProvingState(
|
|
115
|
-
|
|
112
|
+
index,
|
|
116
113
|
numTxs,
|
|
117
114
|
globalVariables,
|
|
118
115
|
padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP),
|
|
@@ -124,11 +121,11 @@ export class EpochProvingState {
|
|
|
124
121
|
previousBlockHash,
|
|
125
122
|
this,
|
|
126
123
|
);
|
|
127
|
-
this.blocks
|
|
128
|
-
if (this.blocks.length === this.totalNumBlocks) {
|
|
124
|
+
this.blocks[index] = block;
|
|
125
|
+
if (this.blocks.filter(b => !!b).length === this.totalNumBlocks) {
|
|
129
126
|
this.provingStateLifecycle = PROVING_STATE_LIFECYCLE.PROVING_STATE_FULL;
|
|
130
127
|
}
|
|
131
|
-
return
|
|
128
|
+
return block;
|
|
132
129
|
}
|
|
133
130
|
|
|
134
131
|
// Returns true if this proving state is still valid, false otherwise
|
|
@@ -180,8 +177,8 @@ export class EpochProvingState {
|
|
|
180
177
|
}
|
|
181
178
|
|
|
182
179
|
// Returns a specific transaction proving state
|
|
183
|
-
public
|
|
184
|
-
return this.blocks
|
|
180
|
+
public getBlockProvingStateByBlockNumber(blockNumber: number) {
|
|
181
|
+
return this.blocks.find(block => block?.blockNumber === blockNumber);
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
// Returns a set of merge rollup inputs
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@aztec/circuit-types';
|
|
8
8
|
import {
|
|
9
9
|
type EpochProver,
|
|
10
|
+
type ForkMerkleTreeOperations,
|
|
10
11
|
type MerkleTreeWriteOperations,
|
|
11
12
|
type ProofAndVerificationKey,
|
|
12
13
|
} from '@aztec/circuit-types/interfaces';
|
|
@@ -14,6 +15,7 @@ import { type CircuitName } from '@aztec/circuit-types/stats';
|
|
|
14
15
|
import {
|
|
15
16
|
AVM_PROOF_LENGTH_IN_FIELDS,
|
|
16
17
|
AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS,
|
|
18
|
+
type AppendOnlyTreeSnapshot,
|
|
17
19
|
type BaseOrMergeRollupPublicInputs,
|
|
18
20
|
BaseParityInputs,
|
|
19
21
|
type BaseRollupHints,
|
|
@@ -38,7 +40,7 @@ import {
|
|
|
38
40
|
makeEmptyRecursiveProof,
|
|
39
41
|
} from '@aztec/circuits.js';
|
|
40
42
|
import { makeTuple } from '@aztec/foundation/array';
|
|
41
|
-
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
43
|
+
import { maxBy, padArrayEnd } from '@aztec/foundation/collection';
|
|
42
44
|
import { AbortError } from '@aztec/foundation/error';
|
|
43
45
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
44
46
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
@@ -98,9 +100,10 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
98
100
|
|
|
99
101
|
private provingPromise: Promise<ProvingResult> | undefined = undefined;
|
|
100
102
|
private metrics: ProvingOrchestratorMetrics;
|
|
103
|
+
private dbs: Map<number, MerkleTreeWriteOperations> = new Map();
|
|
101
104
|
|
|
102
105
|
constructor(
|
|
103
|
-
private
|
|
106
|
+
private dbProvider: ForkMerkleTreeOperations,
|
|
104
107
|
private prover: ServerCircuitProver,
|
|
105
108
|
telemetryClient: TelemetryClient,
|
|
106
109
|
private readonly proverId: Fr = Fr.ZERO,
|
|
@@ -123,14 +126,14 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
123
126
|
this.paddingTxProof = undefined;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
public startNewEpoch(epochNumber: number, totalNumBlocks: number) {
|
|
129
|
+
public startNewEpoch(epochNumber: number, firstBlockNumber: number, totalNumBlocks: number) {
|
|
127
130
|
const { promise: _promise, resolve, reject } = promiseWithResolvers<ProvingResult>();
|
|
128
131
|
const promise = _promise.catch((reason): ProvingResult => ({ status: 'failure', reason }));
|
|
129
132
|
if (totalNumBlocks <= 0 || !Number.isInteger(totalNumBlocks)) {
|
|
130
133
|
throw new Error(`Invalid number of blocks for epoch (got ${totalNumBlocks})`);
|
|
131
134
|
}
|
|
132
135
|
logger.info(`Starting epoch ${epochNumber} with ${totalNumBlocks} blocks`);
|
|
133
|
-
this.provingState = new EpochProvingState(epochNumber, totalNumBlocks, resolve, reject);
|
|
136
|
+
this.provingState = new EpochProvingState(epochNumber, firstBlockNumber, totalNumBlocks, resolve, reject);
|
|
134
137
|
this.provingPromise = promise;
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -159,24 +162,14 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
159
162
|
throw new Error(`Invalid number of txs for block (got ${numTxs})`);
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
if (this.provingState.currentBlock && !this.provingState.currentBlock.block) {
|
|
163
|
-
throw new Error(`Must end previous block before starting a new one`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// TODO(palla/prover): Store block number in the db itself to make this check more reliable,
|
|
167
|
-
// and turn this warning into an exception that we throw.
|
|
168
|
-
const { blockNumber } = globalVariables;
|
|
169
|
-
const dbBlockNumber = (await this.db.getTreeInfo(MerkleTreeId.ARCHIVE)).size - 1n;
|
|
170
|
-
if (dbBlockNumber !== blockNumber.toBigInt() - 1n) {
|
|
171
|
-
logger.warn(
|
|
172
|
-
`Database is at wrong block number (starting block ${blockNumber.toBigInt()} with db at ${dbBlockNumber})`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
165
|
logger.info(
|
|
177
|
-
`Starting block ${globalVariables.blockNumber} for slot ${globalVariables.slotNumber} with ${numTxs} transactions`,
|
|
166
|
+
`Starting block ${globalVariables.blockNumber.toNumber()} for slot ${globalVariables.slotNumber.toNumber()} with ${numTxs} transactions`,
|
|
178
167
|
);
|
|
179
168
|
|
|
169
|
+
// Fork world state at the end of the immediately previous block
|
|
170
|
+
const db = await this.dbProvider.fork(globalVariables.blockNumber.toNumber() - 1);
|
|
171
|
+
this.dbs.set(globalVariables.blockNumber.toNumber(), db);
|
|
172
|
+
|
|
180
173
|
// we start the block by enqueueing all of the base parity circuits
|
|
181
174
|
let baseParityInputs: BaseParityInputs[] = [];
|
|
182
175
|
let l1ToL2MessagesPadded: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>;
|
|
@@ -189,12 +182,12 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
189
182
|
BaseParityInputs.fromSlice(l1ToL2MessagesPadded, i, getVKTreeRoot()),
|
|
190
183
|
);
|
|
191
184
|
|
|
192
|
-
const messageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
|
|
185
|
+
const messageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
|
|
193
186
|
|
|
194
187
|
const newL1ToL2MessageTreeRootSiblingPathArray = await getSubtreeSiblingPath(
|
|
195
188
|
MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
|
|
196
189
|
L1_TO_L2_MSG_SUBTREE_HEIGHT,
|
|
197
|
-
|
|
190
|
+
db,
|
|
198
191
|
);
|
|
199
192
|
|
|
200
193
|
const newL1ToL2MessageTreeRootSiblingPath = makeTuple(
|
|
@@ -205,18 +198,18 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
205
198
|
);
|
|
206
199
|
|
|
207
200
|
// Update the local trees to include the new l1 to l2 messages
|
|
208
|
-
await
|
|
209
|
-
const messageTreeSnapshotAfterInsertion = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
|
|
201
|
+
await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
|
|
202
|
+
const messageTreeSnapshotAfterInsertion = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
|
|
210
203
|
|
|
211
204
|
// Get archive snapshot before this block lands
|
|
212
|
-
const startArchiveSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE,
|
|
213
|
-
const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE,
|
|
214
|
-
const previousBlockHash = await
|
|
205
|
+
const startArchiveSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
|
|
206
|
+
const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db);
|
|
207
|
+
const previousBlockHash = await db.getLeafValue(
|
|
215
208
|
MerkleTreeId.ARCHIVE,
|
|
216
209
|
BigInt(startArchiveSnapshot.nextAvailableLeafIndex - 1),
|
|
217
210
|
);
|
|
218
211
|
|
|
219
|
-
this.provingState!.startNewBlock(
|
|
212
|
+
const blockProvingState = this.provingState!.startNewBlock(
|
|
220
213
|
numTxs,
|
|
221
214
|
globalVariables,
|
|
222
215
|
l1ToL2MessagesPadded,
|
|
@@ -230,7 +223,7 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
230
223
|
|
|
231
224
|
// Enqueue base parity circuits for the block
|
|
232
225
|
for (let i = 0; i < baseParityInputs.length; i++) {
|
|
233
|
-
this.enqueueBaseParityCircuit(
|
|
226
|
+
this.enqueueBaseParityCircuit(blockProvingState, baseParityInputs[i], i);
|
|
234
227
|
}
|
|
235
228
|
}
|
|
236
229
|
|
|
@@ -242,33 +235,40 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
242
235
|
[Attributes.TX_HASH]: tx.hash.toString(),
|
|
243
236
|
}))
|
|
244
237
|
public async addNewTx(tx: ProcessedTx): Promise<void> {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
238
|
+
const blockNumber = tx.constants.globalVariables.blockNumber.toNumber();
|
|
239
|
+
try {
|
|
240
|
+
const provingState = this.provingState?.getBlockProvingStateByBlockNumber(blockNumber);
|
|
241
|
+
if (!provingState) {
|
|
242
|
+
throw new Error(`Block proving state for ${blockNumber} not found`);
|
|
243
|
+
}
|
|
249
244
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
245
|
+
if (!provingState.isAcceptingTransactions()) {
|
|
246
|
+
throw new Error(`Rollup not accepting further transactions`);
|
|
247
|
+
}
|
|
253
248
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
249
|
+
if (!provingState.verifyState()) {
|
|
250
|
+
throw new Error(`Invalid proving state when adding a tx`);
|
|
251
|
+
}
|
|
257
252
|
|
|
258
|
-
|
|
253
|
+
validateTx(tx);
|
|
259
254
|
|
|
260
|
-
|
|
255
|
+
logger.info(`Received transaction: ${tx.hash}`);
|
|
261
256
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
257
|
+
if (tx.isEmpty) {
|
|
258
|
+
logger.warn(`Ignoring empty transaction ${tx.hash} - it will not be added to this block`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
266
261
|
|
|
267
|
-
|
|
268
|
-
|
|
262
|
+
const [hints, treeSnapshots] = await this.prepareTransaction(tx, provingState);
|
|
263
|
+
this.enqueueFirstProofs(hints, treeSnapshots, tx, provingState);
|
|
269
264
|
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
if (provingState.transactionsReceived === provingState.totalNumTxs) {
|
|
266
|
+
logger.verbose(`All transactions received for block ${provingState.globalVariables.blockNumber}.`);
|
|
267
|
+
}
|
|
268
|
+
} catch (err: any) {
|
|
269
|
+
throw new Error(`Error adding transaction ${tx.hash.toString()} to block ${blockNumber}: ${err.message}`, {
|
|
270
|
+
cause: err,
|
|
271
|
+
});
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -276,21 +276,13 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
276
276
|
* Marks the block as full and pads it if required, no more transactions will be accepted.
|
|
277
277
|
* Computes the block header and updates the archive tree.
|
|
278
278
|
*/
|
|
279
|
-
@trackSpan('ProvingOrchestrator.setBlockCompleted',
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return {
|
|
285
|
-
[Attributes.BLOCK_NUMBER]: block.globalVariables.blockNumber.toNumber(),
|
|
286
|
-
[Attributes.BLOCK_SIZE]: block.totalNumTxs,
|
|
287
|
-
[Attributes.BLOCK_TXS_COUNT]: block.transactionsReceived,
|
|
288
|
-
};
|
|
289
|
-
})
|
|
290
|
-
public async setBlockCompleted(expectedHeader?: Header): Promise<L2Block> {
|
|
291
|
-
const provingState = this.provingState?.currentBlock;
|
|
279
|
+
@trackSpan('ProvingOrchestrator.setBlockCompleted', (blockNumber: number) => ({
|
|
280
|
+
[Attributes.BLOCK_NUMBER]: blockNumber,
|
|
281
|
+
}))
|
|
282
|
+
public async setBlockCompleted(blockNumber: number, expectedHeader?: Header): Promise<L2Block> {
|
|
283
|
+
const provingState = this.provingState?.getBlockProvingStateByBlockNumber(blockNumber);
|
|
292
284
|
if (!provingState) {
|
|
293
|
-
throw new Error(`
|
|
285
|
+
throw new Error(`Block proving state for ${blockNumber} not found`);
|
|
294
286
|
}
|
|
295
287
|
|
|
296
288
|
if (!provingState.verifyState()) {
|
|
@@ -313,7 +305,7 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
313
305
|
// base rollup inputs
|
|
314
306
|
// Then enqueue the proving of all the transactions
|
|
315
307
|
const unprovenPaddingTx = makeEmptyProcessedTx(
|
|
316
|
-
this.
|
|
308
|
+
this.dbs.get(blockNumber)!.getInitialHeader(),
|
|
317
309
|
provingState.globalVariables.chainId,
|
|
318
310
|
provingState.globalVariables.version,
|
|
319
311
|
getVKTreeRoot(),
|
|
@@ -344,7 +336,7 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
344
336
|
|
|
345
337
|
/** Returns the block as built for a given index. */
|
|
346
338
|
public getBlock(index: number): L2Block {
|
|
347
|
-
const block = this.provingState?.blocks[index]
|
|
339
|
+
const block = this.provingState?.blocks[index]?.block;
|
|
348
340
|
if (!block) {
|
|
349
341
|
throw new Error(`Block at index ${index} not available`);
|
|
350
342
|
}
|
|
@@ -362,7 +354,10 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
362
354
|
})
|
|
363
355
|
private padEpoch(): Promise<void> {
|
|
364
356
|
const provingState = this.provingState!;
|
|
365
|
-
const lastBlock =
|
|
357
|
+
const lastBlock = maxBy(
|
|
358
|
+
provingState.blocks.filter(b => !!b),
|
|
359
|
+
b => b!.blockNumber,
|
|
360
|
+
)?.block;
|
|
366
361
|
if (!lastBlock) {
|
|
367
362
|
return Promise.reject(new Error(`Epoch needs at least one completed block in order to be padded`));
|
|
368
363
|
}
|
|
@@ -416,13 +411,16 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
416
411
|
// Collect all new nullifiers, commitments, and contracts from all txs in this block to build body
|
|
417
412
|
const txs = provingState!.allTxs.map(a => a.processedTx);
|
|
418
413
|
|
|
414
|
+
// Get db for this block
|
|
415
|
+
const db = this.dbs.get(provingState.blockNumber)!;
|
|
416
|
+
|
|
419
417
|
// Given we've applied every change from this block, now assemble the block header
|
|
420
418
|
// and update the archive tree, so we're ready to start processing the next block
|
|
421
419
|
const { header, body } = await buildHeaderAndBodyFromTxs(
|
|
422
420
|
txs,
|
|
423
421
|
provingState.globalVariables,
|
|
424
422
|
provingState.newL1ToL2Messages,
|
|
425
|
-
|
|
423
|
+
db,
|
|
426
424
|
);
|
|
427
425
|
|
|
428
426
|
if (expectedHeader && !header.equals(expectedHeader)) {
|
|
@@ -431,10 +429,10 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
431
429
|
}
|
|
432
430
|
|
|
433
431
|
logger.verbose(`Updating archive tree with block ${provingState.blockNumber} header ${header.hash().toString()}`);
|
|
434
|
-
await
|
|
432
|
+
await db.updateArchive(header);
|
|
435
433
|
|
|
436
434
|
// Assemble the L2 block
|
|
437
|
-
const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE,
|
|
435
|
+
const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
|
|
438
436
|
const l2Block = new L2Block(newArchive, header, body);
|
|
439
437
|
|
|
440
438
|
if (!l2Block.body.getTxsEffectsHash().equals(header.contentCommitment.txsEffectsHash)) {
|
|
@@ -445,10 +443,24 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
445
443
|
);
|
|
446
444
|
}
|
|
447
445
|
|
|
446
|
+
await this.verifyBuiltBlockAgainstSyncedState(l2Block, newArchive);
|
|
447
|
+
|
|
448
448
|
logger.verbose(`Orchestrator finalised block ${l2Block.number}`);
|
|
449
449
|
provingState.block = l2Block;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
// Flagged as protected to disable in certain unit tests
|
|
453
|
+
protected async verifyBuiltBlockAgainstSyncedState(l2Block: L2Block, newArchive: AppendOnlyTreeSnapshot) {
|
|
454
|
+
const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(l2Block.number));
|
|
455
|
+
if (!syncedArchive.equals(newArchive)) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
`Archive tree mismatch for block ${l2Block.number}: world state synced to ${inspect(
|
|
458
|
+
syncedArchive,
|
|
459
|
+
)} but built ${inspect(newArchive)}`,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
452
464
|
// Enqueues the proving of the required padding transactions
|
|
453
465
|
// If the fully proven padding transaction is not available, this will first be proven
|
|
454
466
|
private enqueuePaddingTxs(
|
|
@@ -602,13 +614,6 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
602
614
|
provingState: BlockProvingState,
|
|
603
615
|
) {
|
|
604
616
|
const txProvingState = new TxProvingState(tx, hints, treeSnapshots);
|
|
605
|
-
|
|
606
|
-
const rejectReason = txProvingState.verifyStateOrReject();
|
|
607
|
-
if (rejectReason) {
|
|
608
|
-
provingState.reject(rejectReason);
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
617
|
const txIndex = provingState.addNewTx(txProvingState);
|
|
613
618
|
this.enqueueTube(provingState, txIndex);
|
|
614
619
|
if (txProvingState.requireAvmProof) {
|
|
@@ -692,9 +697,11 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
692
697
|
return;
|
|
693
698
|
}
|
|
694
699
|
|
|
700
|
+
const db = this.dbs.get(provingState.blockNumber)!;
|
|
701
|
+
|
|
695
702
|
// We build the base rollup inputs using a mock proof and verification key.
|
|
696
703
|
// These will be overwritten later once we have proven the tube circuit and any public kernels
|
|
697
|
-
const [ms, hints] = await elapsed(buildBaseRollupHints(tx, provingState.globalVariables,
|
|
704
|
+
const [ms, hints] = await elapsed(buildBaseRollupHints(tx, provingState.globalVariables, db));
|
|
698
705
|
|
|
699
706
|
if (!tx.isEmpty) {
|
|
700
707
|
this.metrics.recordBaseRollupInputs(ms);
|
|
@@ -702,7 +709,7 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
702
709
|
|
|
703
710
|
const promises = [MerkleTreeId.NOTE_HASH_TREE, MerkleTreeId.NULLIFIER_TREE, MerkleTreeId.PUBLIC_DATA_TREE].map(
|
|
704
711
|
async (id: MerkleTreeId) => {
|
|
705
|
-
return { key: id, value: await getTreeSnapshot(id,
|
|
712
|
+
return { key: id, value: await getTreeSnapshot(id, db) };
|
|
706
713
|
},
|
|
707
714
|
);
|
|
708
715
|
const treeSnapshots: TreeSnapshots = new Map((await Promise.all(promises)).map(obj => [obj.key, obj.value]));
|
|
@@ -1055,6 +1062,19 @@ export class ProvingOrchestrator implements EpochProver {
|
|
|
1055
1062
|
logger.debug('Block root rollup already started');
|
|
1056
1063
|
return;
|
|
1057
1064
|
}
|
|
1065
|
+
const blockNumber = provingState.blockNumber;
|
|
1066
|
+
|
|
1067
|
+
// TODO(palla/prover): This closes the fork only on the happy path. If this epoch orchestrator
|
|
1068
|
+
// is aborted and never reaches this point, it will leak the fork. We need to add a global cleanup,
|
|
1069
|
+
// but have to make sure it only runs once all operations are completed, otherwise some function here
|
|
1070
|
+
// will attempt to access the fork after it was closed.
|
|
1071
|
+
logger.debug(`Cleaning up world state fork for ${blockNumber}`);
|
|
1072
|
+
void this.dbs
|
|
1073
|
+
.get(blockNumber)
|
|
1074
|
+
?.close()
|
|
1075
|
+
.then(() => this.dbs.delete(blockNumber))
|
|
1076
|
+
.catch(err => logger.error(`Error closing db for block ${blockNumber}`, err));
|
|
1077
|
+
|
|
1058
1078
|
this.enqueueBlockRootRollup(provingState);
|
|
1059
1079
|
}
|
|
1060
1080
|
|