@aztec/world-state 0.0.1-commit.b6e433891 → 0.0.1-commit.b8a057fa
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/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/instrumentation/instrumentation.d.ts +4 -3
- package/dest/instrumentation/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation/instrumentation.js +4 -9
- package/dest/native/index.d.ts +2 -1
- package/dest/native/index.d.ts.map +1 -1
- package/dest/native/index.js +1 -0
- package/dest/native/ipc_world_state_instance.d.ts +89 -0
- package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
- package/dest/native/ipc_world_state_instance.js +661 -0
- package/dest/native/merkle_trees_facade.d.ts +8 -4
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +60 -115
- package/dest/native/message.d.ts +14 -230
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/message.js +0 -42
- package/dest/native/native_world_state.d.ts +19 -7
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +120 -58
- package/dest/native/native_world_state_instance.d.ts +56 -42
- package/dest/native/native_world_state_instance.d.ts.map +1 -1
- package/dest/native/native_world_state_instance.js +1 -203
- package/dest/native/world_state_operation.d.ts +7 -0
- package/dest/native/world_state_operation.d.ts.map +1 -0
- package/dest/native/world_state_operation.js +5 -0
- package/dest/synchronizer/config.d.ts +1 -1
- package/dest/synchronizer/config.d.ts.map +1 -1
- package/dest/synchronizer/config.js +9 -10
- package/dest/synchronizer/errors.d.ts +8 -1
- package/dest/synchronizer/errors.d.ts.map +1 -1
- package/dest/synchronizer/errors.js +8 -1
- package/dest/synchronizer/factory.d.ts +4 -4
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +7 -6
- package/dest/synchronizer/index.d.ts +2 -1
- package/dest/synchronizer/index.d.ts.map +1 -1
- package/dest/synchronizer/index.js +1 -0
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +11 -6
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +67 -40
- package/dest/test/utils.d.ts +1 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +6 -1
- package/dest/testing.d.ts +4 -3
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +14 -7
- package/package.json +13 -10
- package/src/index.ts +1 -0
- package/src/instrumentation/instrumentation.ts +6 -18
- package/src/native/index.ts +1 -0
- package/src/native/ipc_world_state_instance.ts +834 -0
- package/src/native/merkle_trees_facade.ts +85 -112
- package/src/native/message.ts +13 -297
- package/src/native/native_world_state.ts +144 -97
- package/src/native/native_world_state_instance.ts +94 -281
- package/src/native/world_state_operation.ts +33 -0
- package/src/synchronizer/config.ts +14 -10
- package/src/synchronizer/errors.ts +8 -0
- package/src/synchronizer/factory.ts +11 -10
- package/src/synchronizer/index.ts +1 -0
- package/src/synchronizer/server_world_state_synchronizer.ts +73 -36
- package/src/test/utils.ts +10 -1
- package/src/testing.ts +12 -10
- package/dest/native/world_state_ops_queue.d.ts +0 -19
- package/dest/native/world_state_ops_queue.d.ts.map +0 -1
- package/dest/native/world_state_ops_queue.js +0 -146
- package/src/native/world_state_ops_queue.ts +0 -190
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { GENESIS_BLOCK_HEADER_HASH, INITIAL_CHECKPOINT_NUMBER, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
1
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
2
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
@@ -6,15 +5,13 @@ import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
|
6
5
|
import { elapsed } from '@aztec/foundation/timer';
|
|
7
6
|
import {
|
|
8
7
|
type BlockHash,
|
|
9
|
-
|
|
8
|
+
EventDrivenL2BlockStream,
|
|
10
9
|
type L2Block,
|
|
11
|
-
type L2BlockId,
|
|
12
10
|
type L2BlockSource,
|
|
13
|
-
L2BlockStream,
|
|
14
11
|
type L2BlockStreamEvent,
|
|
15
12
|
type L2BlockStreamEventHandler,
|
|
16
13
|
type L2BlockStreamLocalDataProvider,
|
|
17
|
-
type
|
|
14
|
+
type LocalChainTips,
|
|
18
15
|
} from '@aztec/stdlib/block';
|
|
19
16
|
import {
|
|
20
17
|
WorldStateRunningState,
|
|
@@ -51,7 +48,7 @@ export class ServerWorldStateSynchronizer
|
|
|
51
48
|
private currentState: WorldStateRunningState = WorldStateRunningState.IDLE;
|
|
52
49
|
|
|
53
50
|
private syncPromise = promiseWithResolvers<void>();
|
|
54
|
-
protected blockStream:
|
|
51
|
+
protected blockStream: EventDrivenL2BlockStream | undefined;
|
|
55
52
|
|
|
56
53
|
// WorldState doesn't track the proven block number, it only tracks the latest tips of the pending chain and the finalized chain
|
|
57
54
|
// store the proven block number here, in the synchronizer, so that we don't end up spamming the logs with 'chain-proved' events
|
|
@@ -81,6 +78,46 @@ export class ServerWorldStateSynchronizer
|
|
|
81
78
|
return this.merkleTreeDb.getSnapshot(blockNumber);
|
|
82
79
|
}
|
|
83
80
|
|
|
81
|
+
public async getVerifiedSnapshot(blockNumber: BlockNumber, blockHash: BlockHash): Promise<MerkleTreeReadOperations> {
|
|
82
|
+
const snapshot = this.merkleTreeDb.getSnapshot(blockNumber);
|
|
83
|
+
// Block 0's snapshot is the pre-genesis archive view (size 0), so archive leaf 0 is not visible from it;
|
|
84
|
+
// verify against the initial header hash instead. For later blocks, read archive leaf `blockNumber` from the
|
|
85
|
+
// snapshot's own view so the exact handle we return is validated against the requested fork.
|
|
86
|
+
const actualHash =
|
|
87
|
+
blockNumber === BlockNumber.ZERO
|
|
88
|
+
? (await this.merkleTreeCommitted.getInitialHeader().hash()).toString()
|
|
89
|
+
: (await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber)))?.toString();
|
|
90
|
+
|
|
91
|
+
if (actualHash === undefined) {
|
|
92
|
+
// A missing archive leaf means either the block's history has been pruned away (permanent: the block predates
|
|
93
|
+
// the oldest historical block kept by world state) or a reorg flipped the fork between the sync and this read
|
|
94
|
+
// (transient). Only the latter is worth retrying, so it alone surfaces as WorldStateSynchronizerError.
|
|
95
|
+
const { oldestHistoricalBlock } = await this.merkleTreeDb.getStatusSummary();
|
|
96
|
+
if (blockNumber < oldestHistoricalBlock) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Unable to find leaf for block ${blockNumber} in the archive tree: world state history has been pruned to block ${oldestHistoricalBlock}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
throw new WorldStateSynchronizerError(`Unable to read block hash at block ${blockNumber} to verify snapshot`, {
|
|
102
|
+
cause: { reason: 'block_not_available', targetBlockNumber: blockNumber },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (actualHash !== blockHash.toString()) {
|
|
106
|
+
throw new WorldStateSynchronizerError(
|
|
107
|
+
`Block hash mismatch at block ${blockNumber} (expected ${blockHash} but got ${actualHash})`,
|
|
108
|
+
{
|
|
109
|
+
cause: {
|
|
110
|
+
reason: 'block_hash_mismatch',
|
|
111
|
+
targetBlockNumber: blockNumber,
|
|
112
|
+
expectedHash: blockHash.toString(),
|
|
113
|
+
actualHash,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return snapshot;
|
|
119
|
+
}
|
|
120
|
+
|
|
84
121
|
public fork(blockNumber?: BlockNumber, opts?: { closeDelayMs?: number }): Promise<MerkleTreeWriteOperations> {
|
|
85
122
|
return this.merkleTreeDb.fork(blockNumber, opts);
|
|
86
123
|
}
|
|
@@ -123,9 +160,9 @@ export class ServerWorldStateSynchronizer
|
|
|
123
160
|
return this.syncPromise.promise;
|
|
124
161
|
}
|
|
125
162
|
|
|
126
|
-
protected createBlockStream():
|
|
163
|
+
protected createBlockStream(): EventDrivenL2BlockStream {
|
|
127
164
|
const logger = createLogger('world-state:block_stream');
|
|
128
|
-
return new
|
|
165
|
+
return new EventDrivenL2BlockStream(this.l2BlockSource, this, this, logger, {
|
|
129
166
|
pollIntervalMS: this.config.worldStateBlockCheckIntervalMS,
|
|
130
167
|
batchSize: this.config.worldStateBlockRequestBatchSize,
|
|
131
168
|
ignoreCheckpoints: true,
|
|
@@ -266,8 +303,12 @@ export class ServerWorldStateSynchronizer
|
|
|
266
303
|
return this.merkleTreeCommitted.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(number)).then(leaf => leaf?.toString());
|
|
267
304
|
}
|
|
268
305
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
306
|
+
/**
|
|
307
|
+
* Returns the proposed, proven, and finalized block tips of the chain. World state drives its block stream with
|
|
308
|
+
* `ignoreCheckpoints`, so it does not track checkpointed blocks or checkpoints and omits `checkpointed` from the tips
|
|
309
|
+
* it reports.
|
|
310
|
+
*/
|
|
311
|
+
public async getL2Tips(): Promise<LocalChainTips> {
|
|
271
312
|
const status = await this.merkleTreeDb.getStatusSummary();
|
|
272
313
|
const unfinalizedBlockHashPromise = this.getL2BlockHash(status.unfinalizedBlockNumber);
|
|
273
314
|
const finalizedBlockHashPromise = this.getL2BlockHash(status.finalizedBlockNumber);
|
|
@@ -281,25 +322,13 @@ export class ServerWorldStateSynchronizer
|
|
|
281
322
|
finalizedBlockHashPromise,
|
|
282
323
|
provenBlockHashPromise,
|
|
283
324
|
]);
|
|
284
|
-
const latestBlockId: L2BlockId = { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash! };
|
|
285
|
-
|
|
286
|
-
// World state doesn't track checkpointed blocks or checkpoints themselves.
|
|
287
|
-
// but we use a block stream so we need to provide 'local' L2Tips.
|
|
288
|
-
// We configure the block stream to ignore checkpoints and set checkpoint values to genesis here.
|
|
289
|
-
const genesisCheckpointHeaderHash = GENESIS_CHECKPOINT_HEADER_HASH.toString();
|
|
290
325
|
return {
|
|
291
|
-
proposed:
|
|
292
|
-
checkpointed: {
|
|
293
|
-
block: { number: INITIAL_L2_BLOCK_NUM, hash: GENESIS_BLOCK_HEADER_HASH.toString() },
|
|
294
|
-
checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
|
|
295
|
-
},
|
|
326
|
+
proposed: { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash },
|
|
296
327
|
finalized: {
|
|
297
|
-
block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash
|
|
298
|
-
checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
|
|
328
|
+
block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash },
|
|
299
329
|
},
|
|
300
330
|
proven: {
|
|
301
|
-
block: { number: provenBlockNumber, hash: provenBlockHash
|
|
302
|
-
checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
|
|
331
|
+
block: { number: provenBlockNumber, hash: provenBlockHash },
|
|
303
332
|
},
|
|
304
333
|
};
|
|
305
334
|
}
|
|
@@ -319,6 +348,15 @@ export class ServerWorldStateSynchronizer
|
|
|
319
348
|
case 'chain-finalized':
|
|
320
349
|
await this.handleChainFinalized(event.block.number);
|
|
321
350
|
break;
|
|
351
|
+
// World state runs in block mode with ignoreCheckpoints: it tracks tips via blocks-added/pruned/proven/finalized
|
|
352
|
+
// and ignores the thin tip events (it never anchors on them).
|
|
353
|
+
case 'chain-proposed':
|
|
354
|
+
case 'chain-checkpointed':
|
|
355
|
+
break;
|
|
356
|
+
default: {
|
|
357
|
+
const _: never = event;
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
322
360
|
}
|
|
323
361
|
}
|
|
324
362
|
|
|
@@ -404,29 +442,26 @@ export class ServerWorldStateSynchronizer
|
|
|
404
442
|
if (this.historyToKeep === undefined) {
|
|
405
443
|
return;
|
|
406
444
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (finalisedCheckpoint === undefined) {
|
|
445
|
+
const finalisedBlockData = await this.l2BlockSource.getBlockData({ number: summary.finalizedBlockNumber });
|
|
446
|
+
if (finalisedBlockData === undefined) {
|
|
410
447
|
this.log.warn(
|
|
411
448
|
`Failed to retrieve checkpointed block for finalized block number: ${summary.finalizedBlockNumber}`,
|
|
412
449
|
);
|
|
413
450
|
return;
|
|
414
451
|
}
|
|
415
452
|
// Compute the required historic checkpoint number
|
|
416
|
-
const newHistoricCheckpointNumber =
|
|
453
|
+
const newHistoricCheckpointNumber = finalisedBlockData.checkpointNumber - this.historyToKeep + 1;
|
|
417
454
|
if (newHistoricCheckpointNumber <= 1) {
|
|
418
455
|
return;
|
|
419
456
|
}
|
|
420
457
|
// Retrieve the historic checkpoint
|
|
421
|
-
const
|
|
422
|
-
CheckpointNumber(newHistoricCheckpointNumber),
|
|
423
|
-
|
|
424
|
-
)
|
|
425
|
-
if (historicCheckpoints.length === 0 || historicCheckpoints[0] === undefined) {
|
|
458
|
+
const historicCheckpoint = await this.l2BlockSource.getCheckpoint({
|
|
459
|
+
number: CheckpointNumber(newHistoricCheckpointNumber),
|
|
460
|
+
});
|
|
461
|
+
if (!historicCheckpoint) {
|
|
426
462
|
this.log.warn(`Failed to retrieve checkpoint number ${newHistoricCheckpointNumber} from Archiver`);
|
|
427
463
|
return;
|
|
428
464
|
}
|
|
429
|
-
const historicCheckpoint = historicCheckpoints[0];
|
|
430
465
|
if (historicCheckpoint.checkpoint.blocks.length === 0 || historicCheckpoint.checkpoint.blocks[0] === undefined) {
|
|
431
466
|
this.log.warn(`Retrieved checkpoint number ${newHistoricCheckpointNumber} has no blocks!`);
|
|
432
467
|
return;
|
|
@@ -453,7 +488,9 @@ export class ServerWorldStateSynchronizer
|
|
|
453
488
|
private async handleChainPruned(blockNumber: BlockNumber) {
|
|
454
489
|
this.log.info(`Chain pruned to block ${blockNumber}`);
|
|
455
490
|
const status = await this.merkleTreeDb.unwindBlocks(blockNumber);
|
|
456
|
-
this.provenBlockNumber
|
|
491
|
+
if (this.provenBlockNumber !== undefined && this.provenBlockNumber > blockNumber) {
|
|
492
|
+
this.provenBlockNumber = undefined;
|
|
493
|
+
}
|
|
457
494
|
this.instrumentation.updateWorldStateMetrics(status);
|
|
458
495
|
}
|
|
459
496
|
|
package/src/test/utils.ts
CHANGED
|
@@ -60,7 +60,16 @@ export async function updateBlockState(block: L2Block, l1ToL2Messages: Fr[], for
|
|
|
60
60
|
await Promise.all([publicDataInsert, nullifierInsert, noteHashInsert, messageInsert]);
|
|
61
61
|
|
|
62
62
|
const state = await fork.getStateReference();
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
// Capture the archive root *before* appending this block so the header's lastArchive chains off the previous block.
|
|
65
|
+
// sync_block now verifies lastArchive against the committed archive root, so a block carrying an unchained value
|
|
66
|
+
// (e.g. the random lastArchive from L2Block.random) would be rejected as a divergence from the canonical chain.
|
|
67
|
+
const previousArchive = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
68
|
+
block.header = BlockHeader.from({
|
|
69
|
+
...block.header,
|
|
70
|
+
state,
|
|
71
|
+
lastArchive: new AppendOnlyTreeSnapshot(Fr.fromBuffer(previousArchive.root), Number(previousArchive.size)),
|
|
72
|
+
});
|
|
64
73
|
await fork.updateArchive(block.header);
|
|
65
74
|
|
|
66
75
|
const archiveState = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
package/src/testing.ts
CHANGED
|
@@ -3,22 +3,22 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
3
3
|
import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
4
4
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
5
|
import { MerkleTreeId, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
|
|
6
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
6
7
|
|
|
7
8
|
import { NativeWorldStateService } from './native/index.js';
|
|
8
9
|
|
|
9
|
-
async function generateGenesisValues(
|
|
10
|
-
if (!prefilledPublicData.length) {
|
|
10
|
+
async function generateGenesisValues(genesis: GenesisData) {
|
|
11
|
+
if (!genesis.prefilledPublicData.length && genesis.genesisTimestamp === 0n) {
|
|
11
12
|
return {
|
|
12
13
|
genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT),
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
);
|
|
17
|
+
// Compute the genesis values on a throwaway world state. The archive root derives only from the
|
|
18
|
+
// prefilled public data and the genesis timestamp, so the fsync-off ephemeral store (no version
|
|
19
|
+
// manager, no crash-recoverability) produces an identical root while skipping the fsync overhead
|
|
20
|
+
// that `tmp` pays. close() removes the tmpdir.
|
|
21
|
+
const ws = await NativeWorldStateService.ephemeral(genesis);
|
|
22
22
|
const genesisArchiveRoot = new Fr((await ws.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
23
23
|
await ws.close();
|
|
24
24
|
|
|
@@ -33,6 +33,7 @@ export async function getGenesisValues(
|
|
|
33
33
|
initialAccounts: AztecAddress[],
|
|
34
34
|
initialAccountFeeJuice = defaultInitialAccountFeeJuice,
|
|
35
35
|
genesisPublicData: PublicDataTreeLeaf[] = [],
|
|
36
|
+
genesisTimestamp: bigint = 0n,
|
|
36
37
|
) {
|
|
37
38
|
// Top up the accounts with fee juice.
|
|
38
39
|
let prefilledPublicData = await Promise.all(
|
|
@@ -46,11 +47,12 @@ export async function getGenesisValues(
|
|
|
46
47
|
|
|
47
48
|
prefilledPublicData.sort((a, b) => (b.slot.lt(a.slot) ? 1 : -1));
|
|
48
49
|
|
|
49
|
-
const
|
|
50
|
+
const genesis: GenesisData = { prefilledPublicData, genesisTimestamp };
|
|
51
|
+
const { genesisArchiveRoot } = await generateGenesisValues(genesis);
|
|
50
52
|
|
|
51
53
|
return {
|
|
52
54
|
genesisArchiveRoot,
|
|
53
|
-
|
|
55
|
+
genesis,
|
|
54
56
|
fundingNeeded: BigInt(initialAccounts.length) * initialAccountFeeJuice.toBigInt(),
|
|
55
57
|
};
|
|
56
58
|
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { WorldStateMessageType } from './message.js';
|
|
2
|
-
export declare const MUTATING_MSG_TYPES: Set<WorldStateMessageType>;
|
|
3
|
-
export declare class WorldStateOpsQueue {
|
|
4
|
-
private requests;
|
|
5
|
-
private inFlightMutatingCount;
|
|
6
|
-
private inFlightCount;
|
|
7
|
-
private stopPromise?;
|
|
8
|
-
private stopResolve?;
|
|
9
|
-
private requestId;
|
|
10
|
-
private ops;
|
|
11
|
-
execute(request: () => Promise<any>, messageType: WorldStateMessageType, committedOnly: boolean): Promise<any>;
|
|
12
|
-
private executeMutating;
|
|
13
|
-
private executeNonMutatingUncommitted;
|
|
14
|
-
private executeNonMutatingCommitted;
|
|
15
|
-
private checkAndEnqueue;
|
|
16
|
-
private sendEnqueuedRequest;
|
|
17
|
-
stop(): Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ybGRfc3RhdGVfb3BzX3F1ZXVlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbmF0aXZlL3dvcmxkX3N0YXRlX29wc19xdWV1ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUF5QnJELGVBQU8sTUFBTSxrQkFBa0IsNEJBZ0I3QixDQUFDO0FBR0gscUJBQWEsa0JBQWtCO0lBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQXNCO0lBQ3RDLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBSztJQUNsQyxPQUFPLENBQUMsYUFBYSxDQUFLO0lBQzFCLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBZ0I7SUFDcEMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFhO0lBQ2pDLE9BQU8sQ0FBQyxTQUFTLENBQUs7SUFDdEIsT0FBTyxDQUFDLEdBQUcsQ0FBd0M7SUFJNUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxXQUFXLEVBQUUscUJBQXFCLEVBQUUsYUFBYSxFQUFFLE9BQU8sZ0JBc0JyRztJQUdELE9BQU8sQ0FBQyxlQUFlO0lBV3ZCLE9BQU8sQ0FBQyw2QkFBNkI7SUFZckMsT0FBTyxDQUFDLDJCQUEyQjtJQVVuQyxPQUFPLENBQUMsZUFBZTtJQXlDdkIsT0FBTyxDQUFDLG1CQUFtQjtJQWdCcEIsSUFBSSxrQkFnQlY7Q0FDRiJ9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"world_state_ops_queue.d.ts","sourceRoot":"","sources":["../../src/native/world_state_ops_queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAyBrD,eAAO,MAAM,kBAAkB,4BAgB7B,CAAC;AAGH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,GAAG,CAAwC;IAI5C,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,aAAa,EAAE,OAAO,gBAsBrG;IAGD,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,6BAA6B;IAYrC,OAAO,CAAC,2BAA2B;IAUnC,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,mBAAmB;IAgBpB,IAAI,kBAgBV;CACF"}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
2
|
-
import { WorldStateMessageType } from './message.js';
|
|
3
|
-
// These are the set of message types that implement mutating operations
|
|
4
|
-
// Messages of these types require exclusive access to their given forks
|
|
5
|
-
export const MUTATING_MSG_TYPES = new Set([
|
|
6
|
-
WorldStateMessageType.APPEND_LEAVES,
|
|
7
|
-
WorldStateMessageType.BATCH_INSERT,
|
|
8
|
-
WorldStateMessageType.SEQUENTIAL_INSERT,
|
|
9
|
-
WorldStateMessageType.UPDATE_ARCHIVE,
|
|
10
|
-
WorldStateMessageType.COMMIT,
|
|
11
|
-
WorldStateMessageType.ROLLBACK,
|
|
12
|
-
WorldStateMessageType.SYNC_BLOCK,
|
|
13
|
-
WorldStateMessageType.CREATE_FORK,
|
|
14
|
-
WorldStateMessageType.DELETE_FORK,
|
|
15
|
-
WorldStateMessageType.FINALIZE_BLOCKS,
|
|
16
|
-
WorldStateMessageType.UNWIND_BLOCKS,
|
|
17
|
-
WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
|
|
18
|
-
WorldStateMessageType.CREATE_CHECKPOINT,
|
|
19
|
-
WorldStateMessageType.COMMIT_CHECKPOINT,
|
|
20
|
-
WorldStateMessageType.REVERT_CHECKPOINT
|
|
21
|
-
]);
|
|
22
|
-
// This class implements the per-fork operation queue
|
|
23
|
-
export class WorldStateOpsQueue {
|
|
24
|
-
requests = [];
|
|
25
|
-
inFlightMutatingCount = 0;
|
|
26
|
-
inFlightCount = 0;
|
|
27
|
-
stopPromise;
|
|
28
|
-
stopResolve;
|
|
29
|
-
requestId = 0;
|
|
30
|
-
ops = new Map();
|
|
31
|
-
// The primary public api, this is where an operation is queued
|
|
32
|
-
// We return a promise that will ultimately be resolved/rejected with the response/error generated by the 'request' argument
|
|
33
|
-
execute(request, messageType, committedOnly) {
|
|
34
|
-
if (this.stopResolve !== undefined) {
|
|
35
|
-
throw new Error('Unable to send request to world state, queue already stopped');
|
|
36
|
-
}
|
|
37
|
-
const op = {
|
|
38
|
-
requestId: this.requestId++,
|
|
39
|
-
mutating: MUTATING_MSG_TYPES.has(messageType),
|
|
40
|
-
request,
|
|
41
|
-
promise: promiseWithResolvers()
|
|
42
|
-
};
|
|
43
|
-
this.ops.set(op.requestId, op);
|
|
44
|
-
// Perform the appropriate action based upon the queueing rules
|
|
45
|
-
if (op.mutating) {
|
|
46
|
-
this.executeMutating(op);
|
|
47
|
-
} else if (committedOnly === false) {
|
|
48
|
-
this.executeNonMutatingUncommitted(op);
|
|
49
|
-
} else {
|
|
50
|
-
this.executeNonMutatingCommitted(op);
|
|
51
|
-
}
|
|
52
|
-
return op.promise.promise;
|
|
53
|
-
}
|
|
54
|
-
// Mutating requests need exclusive access
|
|
55
|
-
executeMutating(op) {
|
|
56
|
-
// If nothing is in flight then we send the request immediately
|
|
57
|
-
// Otherwise add to the queue
|
|
58
|
-
if (this.inFlightCount === 0) {
|
|
59
|
-
this.sendEnqueuedRequest(op);
|
|
60
|
-
} else {
|
|
61
|
-
this.requests.push(op);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// Non mutating requests including uncommitted state
|
|
65
|
-
executeNonMutatingUncommitted(op) {
|
|
66
|
-
// If there are no mutating requests in flight and there is nothing queued
|
|
67
|
-
// then send the request immediately
|
|
68
|
-
// If a mutating request is in flight then we must wait
|
|
69
|
-
// If a mutating request is not in flight but something is queued then it must be a mutating request
|
|
70
|
-
if (this.inFlightMutatingCount == 0 && this.requests.length == 0) {
|
|
71
|
-
this.sendEnqueuedRequest(op);
|
|
72
|
-
} else {
|
|
73
|
-
this.requests.push(op);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
executeNonMutatingCommitted(op) {
|
|
77
|
-
// This is a non-mutating request for committed data
|
|
78
|
-
// It can always be sent
|
|
79
|
-
op.request().then(op.promise.resolve, op.promise.reject).finally(()=>{
|
|
80
|
-
this.ops.delete(op.requestId);
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
checkAndEnqueue(completedOp) {
|
|
84
|
-
// As request has completed
|
|
85
|
-
// First we decrements the relevant in flight counters
|
|
86
|
-
if (completedOp.mutating) {
|
|
87
|
-
--this.inFlightMutatingCount;
|
|
88
|
-
}
|
|
89
|
-
--this.inFlightCount;
|
|
90
|
-
// If there are still requests in flight then do nothing further
|
|
91
|
-
if (this.inFlightCount != 0) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
// No requests in flight, send next queued requests
|
|
95
|
-
// We loop and send:
|
|
96
|
-
// 1 mutating request if it is next in the queue
|
|
97
|
-
// As many non-mutating requests as we encounter until
|
|
98
|
-
// we exhaust the queue or we reach a mutating request
|
|
99
|
-
while(this.requests.length > 0){
|
|
100
|
-
const next = this.requests[0];
|
|
101
|
-
if (next.mutating) {
|
|
102
|
-
if (this.inFlightCount == 0) {
|
|
103
|
-
// send the mutating request
|
|
104
|
-
this.requests.shift();
|
|
105
|
-
this.sendEnqueuedRequest(next);
|
|
106
|
-
}
|
|
107
|
-
break;
|
|
108
|
-
} else {
|
|
109
|
-
// not mutating, send and go round again
|
|
110
|
-
this.requests.shift();
|
|
111
|
-
this.sendEnqueuedRequest(next);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// If the queue is empty, there is nothing in flight and we have been told to stop, then resolve the stop promise
|
|
115
|
-
if (this.inFlightCount == 0 && this.stopResolve !== undefined) {
|
|
116
|
-
this.stopResolve();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
sendEnqueuedRequest(op) {
|
|
120
|
-
// Here we increment the in flight counts before sending
|
|
121
|
-
++this.inFlightCount;
|
|
122
|
-
if (op.mutating) {
|
|
123
|
-
++this.inFlightMutatingCount;
|
|
124
|
-
}
|
|
125
|
-
// Make the request and pass the response/error through to the stored promise
|
|
126
|
-
op.request().then(op.promise.resolve, op.promise.reject).finally(()=>{
|
|
127
|
-
this.checkAndEnqueue(op);
|
|
128
|
-
this.ops.delete(op.requestId);
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
stop() {
|
|
132
|
-
// If there is already a stop promise then return it
|
|
133
|
-
if (this.stopPromise) {
|
|
134
|
-
return this.stopPromise;
|
|
135
|
-
}
|
|
136
|
-
// Otherwise create a new one and capture the resolve method
|
|
137
|
-
this.stopPromise = new Promise((resolve)=>{
|
|
138
|
-
this.stopResolve = resolve;
|
|
139
|
-
});
|
|
140
|
-
// If no outstanding requests then immediately resolve the promise
|
|
141
|
-
if (this.requests.length == 0 && this.inFlightCount == 0 && this.stopResolve !== undefined) {
|
|
142
|
-
this.stopResolve();
|
|
143
|
-
}
|
|
144
|
-
return this.stopPromise;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
2
|
-
|
|
3
|
-
import { WorldStateMessageType } from './message.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* This is the implementation for queueing requests to the world state.
|
|
7
|
-
* Requests need to be queued for the world state to ensure that writes are correctly ordered
|
|
8
|
-
* and reads return the correct data.
|
|
9
|
-
* Due to the nature of the NAPI we can't really do this there.
|
|
10
|
-
*
|
|
11
|
-
* The rules for queueing are as follows:
|
|
12
|
-
*
|
|
13
|
-
* 1. Reads of committed state never need to be queued. LMDB uses MVCC to ensure readers see a consistent view of the DB.
|
|
14
|
-
* 2. Reads of uncommitted state can happen concurrently with other reads of uncommitted state on the same fork (or reads of committed state)
|
|
15
|
-
* 3. All writes require exclusive access to their respective fork
|
|
16
|
-
*
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
type WorldStateOp = {
|
|
20
|
-
requestId: number;
|
|
21
|
-
mutating: boolean;
|
|
22
|
-
request: () => Promise<any>;
|
|
23
|
-
promise: PromiseWithResolvers<any>;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// These are the set of message types that implement mutating operations
|
|
27
|
-
// Messages of these types require exclusive access to their given forks
|
|
28
|
-
export const MUTATING_MSG_TYPES = new Set([
|
|
29
|
-
WorldStateMessageType.APPEND_LEAVES,
|
|
30
|
-
WorldStateMessageType.BATCH_INSERT,
|
|
31
|
-
WorldStateMessageType.SEQUENTIAL_INSERT,
|
|
32
|
-
WorldStateMessageType.UPDATE_ARCHIVE,
|
|
33
|
-
WorldStateMessageType.COMMIT,
|
|
34
|
-
WorldStateMessageType.ROLLBACK,
|
|
35
|
-
WorldStateMessageType.SYNC_BLOCK,
|
|
36
|
-
WorldStateMessageType.CREATE_FORK,
|
|
37
|
-
WorldStateMessageType.DELETE_FORK,
|
|
38
|
-
WorldStateMessageType.FINALIZE_BLOCKS,
|
|
39
|
-
WorldStateMessageType.UNWIND_BLOCKS,
|
|
40
|
-
WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
|
|
41
|
-
WorldStateMessageType.CREATE_CHECKPOINT,
|
|
42
|
-
WorldStateMessageType.COMMIT_CHECKPOINT,
|
|
43
|
-
WorldStateMessageType.REVERT_CHECKPOINT,
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
|
-
// This class implements the per-fork operation queue
|
|
47
|
-
export class WorldStateOpsQueue {
|
|
48
|
-
private requests: WorldStateOp[] = [];
|
|
49
|
-
private inFlightMutatingCount = 0;
|
|
50
|
-
private inFlightCount = 0;
|
|
51
|
-
private stopPromise?: Promise<void>;
|
|
52
|
-
private stopResolve?: () => void;
|
|
53
|
-
private requestId = 0;
|
|
54
|
-
private ops: Map<number, WorldStateOp> = new Map();
|
|
55
|
-
|
|
56
|
-
// The primary public api, this is where an operation is queued
|
|
57
|
-
// We return a promise that will ultimately be resolved/rejected with the response/error generated by the 'request' argument
|
|
58
|
-
public execute(request: () => Promise<any>, messageType: WorldStateMessageType, committedOnly: boolean) {
|
|
59
|
-
if (this.stopResolve !== undefined) {
|
|
60
|
-
throw new Error('Unable to send request to world state, queue already stopped');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const op: WorldStateOp = {
|
|
64
|
-
requestId: this.requestId++,
|
|
65
|
-
mutating: MUTATING_MSG_TYPES.has(messageType),
|
|
66
|
-
request,
|
|
67
|
-
promise: promiseWithResolvers(),
|
|
68
|
-
};
|
|
69
|
-
this.ops.set(op.requestId, op);
|
|
70
|
-
|
|
71
|
-
// Perform the appropriate action based upon the queueing rules
|
|
72
|
-
if (op.mutating) {
|
|
73
|
-
this.executeMutating(op);
|
|
74
|
-
} else if (committedOnly === false) {
|
|
75
|
-
this.executeNonMutatingUncommitted(op);
|
|
76
|
-
} else {
|
|
77
|
-
this.executeNonMutatingCommitted(op);
|
|
78
|
-
}
|
|
79
|
-
return op.promise.promise;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Mutating requests need exclusive access
|
|
83
|
-
private executeMutating(op: WorldStateOp) {
|
|
84
|
-
// If nothing is in flight then we send the request immediately
|
|
85
|
-
// Otherwise add to the queue
|
|
86
|
-
if (this.inFlightCount === 0) {
|
|
87
|
-
this.sendEnqueuedRequest(op);
|
|
88
|
-
} else {
|
|
89
|
-
this.requests.push(op);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Non mutating requests including uncommitted state
|
|
94
|
-
private executeNonMutatingUncommitted(op: WorldStateOp) {
|
|
95
|
-
// If there are no mutating requests in flight and there is nothing queued
|
|
96
|
-
// then send the request immediately
|
|
97
|
-
// If a mutating request is in flight then we must wait
|
|
98
|
-
// If a mutating request is not in flight but something is queued then it must be a mutating request
|
|
99
|
-
if (this.inFlightMutatingCount == 0 && this.requests.length == 0) {
|
|
100
|
-
this.sendEnqueuedRequest(op);
|
|
101
|
-
} else {
|
|
102
|
-
this.requests.push(op);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private executeNonMutatingCommitted(op: WorldStateOp) {
|
|
107
|
-
// This is a non-mutating request for committed data
|
|
108
|
-
// It can always be sent
|
|
109
|
-
op.request()
|
|
110
|
-
.then(op.promise.resolve, op.promise.reject)
|
|
111
|
-
.finally(() => {
|
|
112
|
-
this.ops.delete(op.requestId);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private checkAndEnqueue(completedOp: WorldStateOp) {
|
|
117
|
-
// As request has completed
|
|
118
|
-
// First we decrements the relevant in flight counters
|
|
119
|
-
if (completedOp.mutating) {
|
|
120
|
-
--this.inFlightMutatingCount;
|
|
121
|
-
}
|
|
122
|
-
--this.inFlightCount;
|
|
123
|
-
|
|
124
|
-
// If there are still requests in flight then do nothing further
|
|
125
|
-
if (this.inFlightCount != 0) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// No requests in flight, send next queued requests
|
|
130
|
-
// We loop and send:
|
|
131
|
-
// 1 mutating request if it is next in the queue
|
|
132
|
-
// As many non-mutating requests as we encounter until
|
|
133
|
-
// we exhaust the queue or we reach a mutating request
|
|
134
|
-
while (this.requests.length > 0) {
|
|
135
|
-
const next = this.requests[0];
|
|
136
|
-
if (next.mutating) {
|
|
137
|
-
if (this.inFlightCount == 0) {
|
|
138
|
-
// send the mutating request
|
|
139
|
-
this.requests.shift();
|
|
140
|
-
this.sendEnqueuedRequest(next);
|
|
141
|
-
}
|
|
142
|
-
// this request is mutating, we need to stop here
|
|
143
|
-
break;
|
|
144
|
-
} else {
|
|
145
|
-
// not mutating, send and go round again
|
|
146
|
-
this.requests.shift();
|
|
147
|
-
this.sendEnqueuedRequest(next);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// If the queue is empty, there is nothing in flight and we have been told to stop, then resolve the stop promise
|
|
152
|
-
if (this.inFlightCount == 0 && this.stopResolve !== undefined) {
|
|
153
|
-
this.stopResolve();
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private sendEnqueuedRequest(op: WorldStateOp) {
|
|
158
|
-
// Here we increment the in flight counts before sending
|
|
159
|
-
++this.inFlightCount;
|
|
160
|
-
if (op.mutating) {
|
|
161
|
-
++this.inFlightMutatingCount;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Make the request and pass the response/error through to the stored promise
|
|
165
|
-
op.request()
|
|
166
|
-
.then(op.promise.resolve, op.promise.reject)
|
|
167
|
-
.finally(() => {
|
|
168
|
-
this.checkAndEnqueue(op);
|
|
169
|
-
this.ops.delete(op.requestId);
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
public stop() {
|
|
174
|
-
// If there is already a stop promise then return it
|
|
175
|
-
if (this.stopPromise) {
|
|
176
|
-
return this.stopPromise;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Otherwise create a new one and capture the resolve method
|
|
180
|
-
this.stopPromise = new Promise(resolve => {
|
|
181
|
-
this.stopResolve = resolve;
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// If no outstanding requests then immediately resolve the promise
|
|
185
|
-
if (this.requests.length == 0 && this.inFlightCount == 0 && this.stopResolve !== undefined) {
|
|
186
|
-
this.stopResolve();
|
|
187
|
-
}
|
|
188
|
-
return this.stopPromise;
|
|
189
|
-
}
|
|
190
|
-
}
|