@aztec/world-state 0.0.0-test.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/README.md +40 -0
- package/dest/index.d.ts +5 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +4 -0
- package/dest/instrumentation/instrumentation.d.ts +22 -0
- package/dest/instrumentation/instrumentation.d.ts.map +1 -0
- package/dest/instrumentation/instrumentation.js +117 -0
- package/dest/native/fork_checkpoint.d.ts +10 -0
- package/dest/native/fork_checkpoint.d.ts.map +1 -0
- package/dest/native/fork_checkpoint.js +26 -0
- package/dest/native/index.d.ts +3 -0
- package/dest/native/index.d.ts.map +1 -0
- package/dest/native/index.js +2 -0
- package/dest/native/merkle_trees_facade.d.ts +42 -0
- package/dest/native/merkle_trees_facade.d.ts.map +1 -0
- package/dest/native/merkle_trees_facade.js +240 -0
- package/dest/native/message.d.ts +331 -0
- package/dest/native/message.d.ts.map +1 -0
- package/dest/native/message.js +192 -0
- package/dest/native/native_world_state.d.ts +57 -0
- package/dest/native/native_world_state.d.ts.map +1 -0
- package/dest/native/native_world_state.js +229 -0
- package/dest/native/native_world_state_instance.d.ts +33 -0
- package/dest/native/native_world_state_instance.d.ts.map +1 -0
- package/dest/native/native_world_state_instance.js +164 -0
- package/dest/native/world_state_ops_queue.d.ts +19 -0
- package/dest/native/world_state_ops_queue.d.ts.map +1 -0
- package/dest/native/world_state_ops_queue.js +146 -0
- package/dest/synchronizer/config.d.ts +23 -0
- package/dest/synchronizer/config.d.ts.map +1 -0
- package/dest/synchronizer/config.js +39 -0
- package/dest/synchronizer/factory.d.ts +12 -0
- package/dest/synchronizer/factory.d.ts.map +1 -0
- package/dest/synchronizer/factory.js +24 -0
- package/dest/synchronizer/index.d.ts +3 -0
- package/dest/synchronizer/index.d.ts.map +1 -0
- package/dest/synchronizer/index.js +2 -0
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +79 -0
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -0
- package/dest/synchronizer/server_world_state_synchronizer.js +277 -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/utils.d.ts +19 -0
- package/dest/test/utils.d.ts.map +1 -0
- package/dest/test/utils.js +99 -0
- package/dest/testing.d.ts +10 -0
- package/dest/testing.d.ts.map +1 -0
- package/dest/testing.js +37 -0
- package/dest/world-state-db/index.d.ts +3 -0
- package/dest/world-state-db/index.d.ts.map +1 -0
- package/dest/world-state-db/index.js +1 -0
- package/dest/world-state-db/merkle_tree_db.d.ts +68 -0
- package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -0
- package/dest/world-state-db/merkle_tree_db.js +17 -0
- package/package.json +98 -0
- package/src/index.ts +4 -0
- package/src/instrumentation/instrumentation.ts +174 -0
- package/src/native/fork_checkpoint.ts +30 -0
- package/src/native/index.ts +2 -0
- package/src/native/merkle_trees_facade.ts +331 -0
- package/src/native/message.ts +541 -0
- package/src/native/native_world_state.ts +317 -0
- package/src/native/native_world_state_instance.ts +238 -0
- package/src/native/world_state_ops_queue.ts +190 -0
- package/src/synchronizer/config.ts +68 -0
- package/src/synchronizer/factory.ts +53 -0
- package/src/synchronizer/index.ts +2 -0
- package/src/synchronizer/server_world_state_synchronizer.ts +344 -0
- package/src/test/index.ts +1 -0
- package/src/test/utils.ts +153 -0
- package/src/testing.ts +60 -0
- package/src/world-state-db/index.ts +3 -0
- package/src/world-state-db/merkle_tree_db.ts +79 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
3
|
+
MAX_NULLIFIERS_PER_TX,
|
|
4
|
+
NULLIFIER_SUBTREE_HEIGHT,
|
|
5
|
+
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
6
|
+
} from '@aztec/constants';
|
|
7
|
+
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
8
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
9
|
+
import { L2Block } from '@aztec/stdlib/block';
|
|
10
|
+
import type { MerkleTreeReadOperations, MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
|
|
11
|
+
import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees';
|
|
12
|
+
|
|
13
|
+
import type { NativeWorldStateService } from '../native/native_world_state.js';
|
|
14
|
+
|
|
15
|
+
export async function mockBlock(blockNum: number, size: number, fork: MerkleTreeWriteOperations) {
|
|
16
|
+
const l2Block = await L2Block.random(blockNum, size);
|
|
17
|
+
const l1ToL2Messages = Array(16).fill(0).map(Fr.random);
|
|
18
|
+
|
|
19
|
+
// Sync the append only trees
|
|
20
|
+
{
|
|
21
|
+
const noteHashesPadded = l2Block.body.txEffects.flatMap(txEffect =>
|
|
22
|
+
padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
|
|
23
|
+
);
|
|
24
|
+
await fork.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, noteHashesPadded);
|
|
25
|
+
|
|
26
|
+
const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
27
|
+
await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Sync the indexed trees
|
|
31
|
+
{
|
|
32
|
+
// We insert the public data tree leaves with one batch per tx to avoid updating the same key twice
|
|
33
|
+
for (const txEffect of l2Block.body.txEffects) {
|
|
34
|
+
await fork.batchInsert(
|
|
35
|
+
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
36
|
+
txEffect.publicDataWrites.map(write => write.toBuffer()),
|
|
37
|
+
0,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const nullifiersPadded = padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX);
|
|
41
|
+
|
|
42
|
+
await fork.batchInsert(
|
|
43
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
44
|
+
nullifiersPadded.map(nullifier => nullifier.toBuffer()),
|
|
45
|
+
NULLIFIER_SUBTREE_HEIGHT,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const state = await fork.getStateReference();
|
|
51
|
+
l2Block.header.state = state;
|
|
52
|
+
await fork.updateArchive(l2Block.header);
|
|
53
|
+
|
|
54
|
+
const archiveState = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
55
|
+
|
|
56
|
+
l2Block.archive = new AppendOnlyTreeSnapshot(Fr.fromBuffer(archiveState.root), Number(archiveState.size));
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
block: l2Block,
|
|
60
|
+
messages: l1ToL2Messages,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function mockEmptyBlock(blockNum: number, fork: MerkleTreeWriteOperations) {
|
|
65
|
+
const l2Block = L2Block.empty();
|
|
66
|
+
const l1ToL2Messages = Array(16).fill(0).map(Fr.zero);
|
|
67
|
+
|
|
68
|
+
l2Block.header.globalVariables.blockNumber = new Fr(blockNum);
|
|
69
|
+
|
|
70
|
+
// Sync the append only trees
|
|
71
|
+
{
|
|
72
|
+
const noteHashesPadded = l2Block.body.txEffects.flatMap(txEffect =>
|
|
73
|
+
padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
|
|
74
|
+
);
|
|
75
|
+
await fork.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, noteHashesPadded);
|
|
76
|
+
|
|
77
|
+
const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
|
|
78
|
+
await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Sync the indexed trees
|
|
82
|
+
{
|
|
83
|
+
// We insert the public data tree leaves with one batch per tx to avoid updating the same key twice
|
|
84
|
+
for (const txEffect of l2Block.body.txEffects) {
|
|
85
|
+
await fork.batchInsert(
|
|
86
|
+
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
87
|
+
txEffect.publicDataWrites.map(write => write.toBuffer()),
|
|
88
|
+
0,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const nullifiersPadded = padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX);
|
|
92
|
+
|
|
93
|
+
await fork.batchInsert(
|
|
94
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
95
|
+
nullifiersPadded.map(nullifier => nullifier.toBuffer()),
|
|
96
|
+
NULLIFIER_SUBTREE_HEIGHT,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const state = await fork.getStateReference();
|
|
102
|
+
l2Block.header.state = state;
|
|
103
|
+
await fork.updateArchive(l2Block.header);
|
|
104
|
+
|
|
105
|
+
const archiveState = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
106
|
+
|
|
107
|
+
l2Block.archive = new AppendOnlyTreeSnapshot(Fr.fromBuffer(archiveState.root), Number(archiveState.size));
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
block: l2Block,
|
|
111
|
+
messages: l1ToL2Messages,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function mockBlocks(from: number, count: number, numTxs: number, worldState: NativeWorldStateService) {
|
|
116
|
+
const tempFork = await worldState.fork(from - 1);
|
|
117
|
+
|
|
118
|
+
const blocks = [];
|
|
119
|
+
const messagesArray = [];
|
|
120
|
+
for (let blockNumber = from; blockNumber < from + count; blockNumber++) {
|
|
121
|
+
const { block, messages } = await mockBlock(blockNumber, numTxs, tempFork);
|
|
122
|
+
blocks.push(block);
|
|
123
|
+
messagesArray.push(messages);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await tempFork.close();
|
|
127
|
+
|
|
128
|
+
return { blocks, messages: messagesArray };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function assertSameState(forkA: MerkleTreeReadOperations, forkB: MerkleTreeReadOperations) {
|
|
132
|
+
const nativeStateRef = await forkA.getStateReference();
|
|
133
|
+
const nativeArchive = await forkA.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
134
|
+
const legacyStateRef = await forkB.getStateReference();
|
|
135
|
+
const legacyArchive = await forkB.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
136
|
+
|
|
137
|
+
expect(nativeStateRef).toEqual(legacyStateRef);
|
|
138
|
+
expect(nativeArchive).toEqual(legacyArchive);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function compareChains(left: MerkleTreeReadOperations, right: MerkleTreeReadOperations) {
|
|
142
|
+
for (const treeId of [
|
|
143
|
+
MerkleTreeId.ARCHIVE,
|
|
144
|
+
MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
|
|
145
|
+
MerkleTreeId.NOTE_HASH_TREE,
|
|
146
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
147
|
+
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
148
|
+
]) {
|
|
149
|
+
expect(await left.getTreeInfo(treeId)).toEqual(await right.getTreeInfo(treeId));
|
|
150
|
+
|
|
151
|
+
expect(await left.getSiblingPath(treeId, 0n)).toEqual(await right.getSiblingPath(treeId, 0n));
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/testing.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { GENESIS_ARCHIVE_ROOT, GENESIS_BLOCK_HASH } from '@aztec/constants';
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-juice';
|
|
4
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
+
import { MerkleTreeId, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
|
|
6
|
+
|
|
7
|
+
import { NativeWorldStateService } from './native/index.js';
|
|
8
|
+
|
|
9
|
+
async function generateGenesisValues(prefilledPublicData: PublicDataTreeLeaf[]) {
|
|
10
|
+
if (!prefilledPublicData.length) {
|
|
11
|
+
return {
|
|
12
|
+
genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT),
|
|
13
|
+
genesisBlockHash: new Fr(GENESIS_BLOCK_HASH),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Create a temporary world state to compute the genesis values.
|
|
18
|
+
const ws = await NativeWorldStateService.tmp(
|
|
19
|
+
undefined /* rollupAddress */,
|
|
20
|
+
true /* cleanupTmpDir */,
|
|
21
|
+
prefilledPublicData,
|
|
22
|
+
);
|
|
23
|
+
const initialHeader = ws.getInitialHeader();
|
|
24
|
+
const genesisBlockHash = await initialHeader.hash();
|
|
25
|
+
const genesisArchiveRoot = new Fr((await ws.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
26
|
+
await ws.close();
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
genesisArchiveRoot,
|
|
30
|
+
genesisBlockHash,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const defaultInitialAccountFeeJuice = new Fr(10n ** 22n);
|
|
35
|
+
|
|
36
|
+
export async function getGenesisValues(
|
|
37
|
+
initialAccounts: AztecAddress[],
|
|
38
|
+
initialAccountFeeJuice = defaultInitialAccountFeeJuice,
|
|
39
|
+
genesisPublicData: PublicDataTreeLeaf[] = [],
|
|
40
|
+
) {
|
|
41
|
+
// Top up the accounts with fee juice.
|
|
42
|
+
let prefilledPublicData = await Promise.all(
|
|
43
|
+
initialAccounts.map(
|
|
44
|
+
async address => new PublicDataTreeLeaf(await computeFeePayerBalanceLeafSlot(address), initialAccountFeeJuice),
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Add user-defined public data
|
|
49
|
+
prefilledPublicData = prefilledPublicData.concat(genesisPublicData);
|
|
50
|
+
|
|
51
|
+
prefilledPublicData.sort((a, b) => (b.slot.lt(a.slot) ? 1 : -1));
|
|
52
|
+
|
|
53
|
+
const { genesisBlockHash, genesisArchiveRoot } = await generateGenesisValues(prefilledPublicData);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
genesisArchiveRoot,
|
|
57
|
+
genesisBlockHash,
|
|
58
|
+
prefilledPublicData,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX } from '@aztec/constants';
|
|
2
|
+
import type { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
import type { IndexedTreeSnapshot, TreeSnapshot } from '@aztec/merkle-tree';
|
|
4
|
+
import type { L2Block } from '@aztec/stdlib/block';
|
|
5
|
+
import type { ForkMerkleTreeOperations, MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
|
|
6
|
+
import type { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
7
|
+
|
|
8
|
+
import type { WorldStateStatusFull, WorldStateStatusSummary } from '../native/message.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @remarks Short explanation:
|
|
13
|
+
* The nullifier tree must be initially padded as the pre-populated 0 index prevents efficient subtree insertion.
|
|
14
|
+
* Padding with some values solves this issue.
|
|
15
|
+
*
|
|
16
|
+
* @remarks Thorough explanation:
|
|
17
|
+
* There needs to be an initial (0,0,0) leaf in the tree, so that when we insert the first 'proper' leaf, we can
|
|
18
|
+
* prove that any value greater than 0 doesn't exist in the tree yet. We prefill/pad the tree with "the number of
|
|
19
|
+
* leaves that are added by one block" so that the first 'proper' block can insert a full subtree.
|
|
20
|
+
*
|
|
21
|
+
* Without this padding, there would be a leaf (0,0,0) at leaf index 0, making it really difficult to insert e.g.
|
|
22
|
+
* 1024 leaves for the first block, because there's only neat space for 1023 leaves after 0. By padding with 1023
|
|
23
|
+
* more leaves, we can then insert the first block of 1024 leaves into indices 1024:2047.
|
|
24
|
+
*/
|
|
25
|
+
export const INITIAL_NULLIFIER_TREE_SIZE = 2 * MAX_NULLIFIERS_PER_TX;
|
|
26
|
+
|
|
27
|
+
export const INITIAL_PUBLIC_DATA_TREE_SIZE = 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX;
|
|
28
|
+
|
|
29
|
+
export type TreeSnapshots = {
|
|
30
|
+
[MerkleTreeId.NULLIFIER_TREE]: IndexedTreeSnapshot;
|
|
31
|
+
[MerkleTreeId.NOTE_HASH_TREE]: TreeSnapshot<Fr>;
|
|
32
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: IndexedTreeSnapshot;
|
|
33
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: TreeSnapshot<Fr>;
|
|
34
|
+
[MerkleTreeId.ARCHIVE]: TreeSnapshot<Fr>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface MerkleTreeAdminDatabase extends ForkMerkleTreeOperations {
|
|
38
|
+
/**
|
|
39
|
+
* Handles a single L2 block (i.e. Inserts the new note hashes into the merkle tree).
|
|
40
|
+
* @param block - The L2 block to handle.
|
|
41
|
+
* @param l1ToL2Messages - The L1 to L2 messages for the block.
|
|
42
|
+
*/
|
|
43
|
+
handleL2BlockAndMessages(block: L2Block, l1ToL2Messages: Fr[]): Promise<WorldStateStatusFull>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Gets a handle that allows reading the latest committed state
|
|
47
|
+
*/
|
|
48
|
+
getCommitted(): MerkleTreeReadOperations;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Removes all historical snapshots up to but not including the given block number
|
|
52
|
+
* @param toBlockNumber The block number of the new oldest historical block
|
|
53
|
+
* @returns The new WorldStateStatus
|
|
54
|
+
*/
|
|
55
|
+
removeHistoricalBlocks(toBlockNumber: bigint): Promise<WorldStateStatusFull>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Removes all pending blocks down to but not including the given block number
|
|
59
|
+
* @param toBlockNumber The block number of the new tip of the pending chain,
|
|
60
|
+
* @returns The new WorldStateStatus
|
|
61
|
+
*/
|
|
62
|
+
unwindBlocks(toBlockNumber: bigint): Promise<WorldStateStatusFull>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Advances the finalised block number to be the number provided
|
|
66
|
+
* @param toBlockNumber The block number that is now the tip of the finalised chain
|
|
67
|
+
* @returns The new WorldStateStatus
|
|
68
|
+
*/
|
|
69
|
+
setFinalised(toBlockNumber: bigint): Promise<WorldStateStatusSummary>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets the current status summary of the database.
|
|
73
|
+
* @returns The current WorldStateStatus.
|
|
74
|
+
*/
|
|
75
|
+
getStatusSummary(): Promise<WorldStateStatusSummary>;
|
|
76
|
+
|
|
77
|
+
/** Stops the database */
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
}
|