@aztec/pxe 0.82.3 → 0.83.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/config/package_info.js +1 -1
- package/dest/pxe_oracle_interface/pxe_oracle_interface.d.ts +15 -20
- package/dest/pxe_oracle_interface/pxe_oracle_interface.d.ts.map +1 -1
- package/dest/pxe_oracle_interface/pxe_oracle_interface.js +56 -103
- package/dest/pxe_service/pxe_service.d.ts +3 -2
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +26 -56
- package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts +9 -0
- package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts.map +1 -1
- package/dest/storage/capsule_data_provider/capsule_data_provider.js +24 -0
- package/dest/storage/index.d.ts +1 -0
- package/dest/storage/index.d.ts.map +1 -1
- package/dest/storage/index.js +1 -0
- package/dest/storage/private_event_data_provider/private_event_data_provider.d.ts +37 -0
- package/dest/storage/private_event_data_provider/private_event_data_provider.d.ts.map +1 -0
- package/dest/storage/private_event_data_provider/private_event_data_provider.js +106 -0
- package/dest/storage/sync_data_provider/sync_data_provider.d.ts +1 -1
- package/dest/storage/sync_data_provider/sync_data_provider.d.ts.map +1 -1
- package/dest/storage/sync_data_provider/sync_data_provider.js +1 -1
- package/dest/storage/tagging_data_provider/tagging_data_provider.d.ts +4 -4
- package/dest/storage/tagging_data_provider/tagging_data_provider.d.ts.map +1 -1
- package/dest/storage/tagging_data_provider/tagging_data_provider.js +29 -12
- package/dest/synchronizer/synchronizer.d.ts +4 -6
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +11 -15
- package/dest/test/pxe_test_suite.js +1 -1
- package/package.json +15 -15
- package/src/config/package_info.ts +1 -1
- package/src/pxe_oracle_interface/pxe_oracle_interface.ts +113 -143
- package/src/pxe_service/pxe_service.ts +35 -76
- package/src/storage/capsule_data_provider/capsule_data_provider.ts +26 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/private_event_data_provider/private_event_data_provider.ts +137 -0
- package/src/storage/sync_data_provider/sync_data_provider.ts +2 -2
- package/src/storage/tagging_data_provider/tagging_data_provider.ts +44 -13
- package/src/synchronizer/synchronizer.ts +11 -14
- package/src/test/pxe_test_suite.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/constants';
|
|
2
|
-
import { Fr
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { SerialQueue } from '@aztec/foundation/queue';
|
|
5
5
|
import { Timer } from '@aztec/foundation/timer';
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
FunctionCall,
|
|
21
21
|
FunctionSelector,
|
|
22
22
|
FunctionType,
|
|
23
|
+
decodeFromAbi,
|
|
23
24
|
decodeFunctionSignature,
|
|
24
25
|
encodeArguments,
|
|
25
26
|
} from '@aztec/stdlib/abi';
|
|
@@ -36,7 +37,6 @@ import {
|
|
|
36
37
|
getContractClassFromArtifact,
|
|
37
38
|
} from '@aztec/stdlib/contract';
|
|
38
39
|
import { SimulationError } from '@aztec/stdlib/errors';
|
|
39
|
-
import { EventMetadata, L1EventPayload } from '@aztec/stdlib/event';
|
|
40
40
|
import type { GasFees } from '@aztec/stdlib/gas';
|
|
41
41
|
import { siloNullifier } from '@aztec/stdlib/hash';
|
|
42
42
|
import type {
|
|
@@ -49,7 +49,6 @@ import type {
|
|
|
49
49
|
PrivateKernelProver,
|
|
50
50
|
} from '@aztec/stdlib/interfaces/client';
|
|
51
51
|
import type { PrivateKernelExecutionProofOutput, PrivateKernelTailCircuitPublicInputs } from '@aztec/stdlib/kernel';
|
|
52
|
-
import { computeAddressSecret } from '@aztec/stdlib/keys';
|
|
53
52
|
import type { LogFilter } from '@aztec/stdlib/logs';
|
|
54
53
|
import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
|
|
55
54
|
import { type NotesFilter, UniqueNote } from '@aztec/stdlib/note';
|
|
@@ -82,6 +81,7 @@ import { AddressDataProvider } from '../storage/address_data_provider/address_da
|
|
|
82
81
|
import { CapsuleDataProvider } from '../storage/capsule_data_provider/capsule_data_provider.js';
|
|
83
82
|
import { ContractDataProvider } from '../storage/contract_data_provider/contract_data_provider.js';
|
|
84
83
|
import { NoteDataProvider } from '../storage/note_data_provider/note_data_provider.js';
|
|
84
|
+
import { PrivateEventDataProvider } from '../storage/private_event_data_provider/private_event_data_provider.js';
|
|
85
85
|
import { SyncDataProvider } from '../storage/sync_data_provider/sync_data_provider.js';
|
|
86
86
|
import { TaggingDataProvider } from '../storage/tagging_data_provider/tagging_data_provider.js';
|
|
87
87
|
import { Synchronizer } from '../synchronizer/index.js';
|
|
@@ -101,6 +101,7 @@ export class PXEService implements PXE {
|
|
|
101
101
|
private syncDataProvider: SyncDataProvider,
|
|
102
102
|
private taggingDataProvider: TaggingDataProvider,
|
|
103
103
|
private addressDataProvider: AddressDataProvider,
|
|
104
|
+
private privateEventDataProvider: PrivateEventDataProvider,
|
|
104
105
|
private simulator: AcirSimulator,
|
|
105
106
|
private packageVersion: string,
|
|
106
107
|
private proverEnabled: boolean,
|
|
@@ -134,6 +135,7 @@ export class PXEService implements PXE {
|
|
|
134
135
|
const packageVersion = getPackageInfo().version;
|
|
135
136
|
const proverEnabled = !!config.proverEnabled;
|
|
136
137
|
const addressDataProvider = new AddressDataProvider(store);
|
|
138
|
+
const privateEventDataProvider = new PrivateEventDataProvider(store);
|
|
137
139
|
const contractDataProvider = new ContractDataProvider(store);
|
|
138
140
|
const noteDataProvider = await NoteDataProvider.create(store);
|
|
139
141
|
const syncDataProvider = new SyncDataProvider(store);
|
|
@@ -153,13 +155,13 @@ export class PXEService implements PXE {
|
|
|
153
155
|
const pxeOracleInterface = new PXEOracleInterface(
|
|
154
156
|
node,
|
|
155
157
|
keyStore,
|
|
156
|
-
simulationProvider,
|
|
157
158
|
contractDataProvider,
|
|
158
159
|
noteDataProvider,
|
|
159
160
|
capsuleDataProvider,
|
|
160
161
|
syncDataProvider,
|
|
161
162
|
taggingDataProvider,
|
|
162
163
|
addressDataProvider,
|
|
164
|
+
privateEventDataProvider,
|
|
163
165
|
log,
|
|
164
166
|
);
|
|
165
167
|
const simulator = new AcirSimulator(pxeOracleInterface, simulationProvider);
|
|
@@ -175,6 +177,7 @@ export class PXEService implements PXE {
|
|
|
175
177
|
syncDataProvider,
|
|
176
178
|
taggingDataProvider,
|
|
177
179
|
addressDataProvider,
|
|
180
|
+
privateEventDataProvider,
|
|
178
181
|
simulator,
|
|
179
182
|
packageVersion,
|
|
180
183
|
proverEnabled,
|
|
@@ -188,7 +191,7 @@ export class PXEService implements PXE {
|
|
|
188
191
|
|
|
189
192
|
await pxeService.#registerProtocolContracts();
|
|
190
193
|
const info = await pxeService.getNodeInfo();
|
|
191
|
-
log.info(`Started PXE connected to chain ${info.l1ChainId} version ${info.
|
|
194
|
+
log.info(`Started PXE connected to chain ${info.l1ChainId} version ${info.rollupVersion}`);
|
|
192
195
|
return pxeService;
|
|
193
196
|
}
|
|
194
197
|
|
|
@@ -846,20 +849,19 @@ export class PXEService implements PXE {
|
|
|
846
849
|
}
|
|
847
850
|
|
|
848
851
|
public async getNodeInfo(): Promise<NodeInfo> {
|
|
849
|
-
const [nodeVersion,
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
]);
|
|
852
|
+
const [nodeVersion, rollupVersion, chainId, enr, contractAddresses, protocolContractAddresses] = await Promise.all([
|
|
853
|
+
this.node.getNodeVersion(),
|
|
854
|
+
this.node.getVersion(),
|
|
855
|
+
this.node.getChainId(),
|
|
856
|
+
this.node.getEncodedEnr(),
|
|
857
|
+
this.node.getL1ContractAddresses(),
|
|
858
|
+
this.node.getProtocolContractAddresses(),
|
|
859
|
+
]);
|
|
858
860
|
|
|
859
861
|
const nodeInfo: NodeInfo = {
|
|
860
862
|
nodeVersion,
|
|
861
863
|
l1ChainId: chainId,
|
|
862
|
-
|
|
864
|
+
rollupVersion,
|
|
863
865
|
enr,
|
|
864
866
|
l1ContractAddresses: contractAddresses,
|
|
865
867
|
protocolContractAddresses: protocolContractAddresses,
|
|
@@ -881,78 +883,35 @@ export class PXEService implements PXE {
|
|
|
881
883
|
}
|
|
882
884
|
|
|
883
885
|
public async getPrivateEvents<T>(
|
|
886
|
+
contractAddress: AztecAddress,
|
|
884
887
|
eventMetadataDef: EventMetadataDefinition,
|
|
885
888
|
from: number,
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
vpks: Point[],
|
|
889
|
+
numBlocks: number,
|
|
890
|
+
recipients: AztecAddress[],
|
|
889
891
|
): Promise<T[]> {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
throw new Error('Tried to get encrypted events without supplying any viewing public keys');
|
|
892
|
+
if (recipients.length === 0) {
|
|
893
|
+
throw new Error('Recipients are required to get private events');
|
|
893
894
|
}
|
|
894
895
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
const txEffects = blocks.flatMap(block => block.body.txEffects);
|
|
898
|
-
const privateLogs = txEffects.flatMap(txEffect => txEffect.privateLogs);
|
|
896
|
+
this.log.verbose(`Getting private events for ${contractAddress.toString()} from ${from} to ${from + numBlocks}`);
|
|
899
897
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const [keyPrefix, account] = await this.keyStore.getKeyPrefixAndAccount(vpk);
|
|
903
|
-
let secretKey = await this.keyStore.getMasterSecretKey(vpk);
|
|
904
|
-
if (keyPrefix === 'iv') {
|
|
905
|
-
const registeredAccount = (await this.getRegisteredAccounts()).find(completeAddress =>
|
|
906
|
-
completeAddress.address.equals(account),
|
|
907
|
-
);
|
|
908
|
-
if (!registeredAccount) {
|
|
909
|
-
throw new Error('No registered account');
|
|
910
|
-
}
|
|
898
|
+
// TODO(#13113): This is a temporary hack to ensure that the notes are synced before getting the events.
|
|
899
|
+
await this.simulateUnconstrained('sync_notes', [], contractAddress);
|
|
911
900
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
}),
|
|
901
|
+
const events = await this.privateEventDataProvider.getPrivateEvents(
|
|
902
|
+
contractAddress,
|
|
903
|
+
from,
|
|
904
|
+
numBlocks,
|
|
905
|
+
recipients,
|
|
906
|
+
eventMetadataDef.eventSelector,
|
|
919
907
|
);
|
|
920
908
|
|
|
921
|
-
const
|
|
922
|
-
await Promise.all(
|
|
923
|
-
privateLogs.map(async log => {
|
|
924
|
-
for (const sk of vsks) {
|
|
925
|
-
// TODO: Verify that the first field of the log is the tag siloed with contract address.
|
|
926
|
-
// Or use tags to query logs, like we do with notes.
|
|
927
|
-
const decryptedEvent = await L1EventPayload.decryptAsIncoming(log, sk);
|
|
928
|
-
if (decryptedEvent !== undefined) {
|
|
929
|
-
return [decryptedEvent];
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
return [];
|
|
934
|
-
}),
|
|
935
|
-
)
|
|
936
|
-
).flat();
|
|
937
|
-
|
|
938
|
-
const decodedEvents = visibleEvents
|
|
939
|
-
.map(visibleEvent => {
|
|
940
|
-
if (visibleEvent === undefined) {
|
|
941
|
-
return undefined;
|
|
942
|
-
}
|
|
943
|
-
if (!visibleEvent.eventTypeId.equals(eventMetadata.eventSelector)) {
|
|
944
|
-
return undefined;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
return eventMetadata.decode(visibleEvent);
|
|
948
|
-
})
|
|
949
|
-
.filter(visibleEvent => visibleEvent !== undefined) as T[];
|
|
909
|
+
const decodedEvents = events.map((event: Fr[]): T => decodeFromAbi([eventMetadataDef.abiType], event) as T);
|
|
950
910
|
|
|
951
911
|
return decodedEvents;
|
|
952
912
|
}
|
|
953
913
|
|
|
954
914
|
async getPublicEvents<T>(eventMetadataDef: EventMetadataDefinition, from: number, limit: number): Promise<T[]> {
|
|
955
|
-
const eventMetadata = new EventMetadata<T>(eventMetadataDef);
|
|
956
915
|
const { logs } = await this.node.getPublicLogs({
|
|
957
916
|
fromBlock: from,
|
|
958
917
|
toBlock: from + limit,
|
|
@@ -961,10 +920,10 @@ export class PXEService implements PXE {
|
|
|
961
920
|
const decodedEvents = logs
|
|
962
921
|
.map(log => {
|
|
963
922
|
// +1 for the event selector
|
|
964
|
-
const expectedLength =
|
|
923
|
+
const expectedLength = eventMetadataDef.fieldNames.length + 1;
|
|
965
924
|
const logFields = log.log.log.slice(0, expectedLength);
|
|
966
925
|
// We are assuming here that event logs are the last 4 bytes of the event. This is not enshrined but is a function of aztec.nr raw log emission.
|
|
967
|
-
if (!EventSelector.fromField(logFields[logFields.length - 1]).equals(
|
|
926
|
+
if (!EventSelector.fromField(logFields[logFields.length - 1]).equals(eventMetadataDef.eventSelector)) {
|
|
968
927
|
return undefined;
|
|
969
928
|
}
|
|
970
929
|
// If any of the remaining fields, are non-zero, the payload does match expected:
|
|
@@ -974,7 +933,7 @@ export class PXEService implements PXE {
|
|
|
974
933
|
);
|
|
975
934
|
}
|
|
976
935
|
|
|
977
|
-
return
|
|
936
|
+
return decodeFromAbi([eventMetadataDef.abiType], log.log.log) as T;
|
|
978
937
|
})
|
|
979
938
|
.filter(log => log !== undefined) as T[];
|
|
980
939
|
|
|
@@ -67,6 +67,32 @@ export class CapsuleDataProvider implements DataProvider {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Appends multiple capsules to a capsule array stored at the base slot.
|
|
72
|
+
* The array length is stored at the base slot, and elements are stored in consecutive slots after it.
|
|
73
|
+
* All operations are performed in a single transaction.
|
|
74
|
+
* @param contractAddress - The contract address that owns the capsule array
|
|
75
|
+
* @param baseSlot - The slot where the array length is stored
|
|
76
|
+
* @param capsules - Array of capsule data to append
|
|
77
|
+
*/
|
|
78
|
+
appendToCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr, capsules: Fr[][]): Promise<void> {
|
|
79
|
+
return this.#store.transactionAsync(async () => {
|
|
80
|
+
// Load current length, defaulting to 0 if not found
|
|
81
|
+
const lengthData = await this.loadCapsule(contractAddress, baseSlot);
|
|
82
|
+
const currentLength = lengthData ? lengthData[0].toBigInt() : 0n;
|
|
83
|
+
|
|
84
|
+
// Store each capsule at consecutive slots after baseSlot + 1 + currentLength
|
|
85
|
+
for (let i = 0; i < capsules.length; i++) {
|
|
86
|
+
const nextSlot = baseSlot.add(new Fr(1)).add(new Fr(currentLength + BigInt(i)));
|
|
87
|
+
await this.storeCapsule(contractAddress, nextSlot, capsules[i]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Update length to include all new capsules
|
|
91
|
+
const newLength = currentLength + BigInt(capsules.length);
|
|
92
|
+
await this.storeCapsule(contractAddress, baseSlot, [new Fr(newLength)]);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
70
96
|
public async getSize() {
|
|
71
97
|
return (await toArray(this.#capsules.valuesAsync())).reduce(
|
|
72
98
|
(sum, value) => sum + value.length * Fr.SIZE_IN_BYTES,
|
package/src/storage/index.ts
CHANGED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
2
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
4
|
+
import type { AztecAsyncArray, AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
5
|
+
import type { EventSelector } from '@aztec/stdlib/abi';
|
|
6
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
|
+
import type { TxHash } from '@aztec/stdlib/tx';
|
|
8
|
+
|
|
9
|
+
import type { DataProvider } from '../data_provider.js';
|
|
10
|
+
|
|
11
|
+
interface PrivateEventEntry {
|
|
12
|
+
logContent: Buffer;
|
|
13
|
+
blockNumber: number;
|
|
14
|
+
logIndexInTx: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Stores decrypted private event logs.
|
|
19
|
+
*/
|
|
20
|
+
export class PrivateEventDataProvider implements DataProvider {
|
|
21
|
+
#store: AztecAsyncKVStore;
|
|
22
|
+
/** Array storing the actual private event log entries containing the log content and block number */
|
|
23
|
+
#eventLogs: AztecAsyncArray<PrivateEventEntry>;
|
|
24
|
+
/** Map from contract_address_recipient_eventSelector to array of indices into #eventLogs for efficient lookup */
|
|
25
|
+
#eventLogIndex: AztecAsyncMap<string, number[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Map from txHash_logIndexInTx to boolean indicating if log has been seen.
|
|
28
|
+
* @dev A single transaction can have multiple logs.
|
|
29
|
+
*/
|
|
30
|
+
#seenLogs: AztecAsyncMap<string, boolean>;
|
|
31
|
+
|
|
32
|
+
logger = createLogger('private_event_data_provider');
|
|
33
|
+
|
|
34
|
+
constructor(store: AztecAsyncKVStore) {
|
|
35
|
+
this.#store = store;
|
|
36
|
+
this.#eventLogs = this.#store.openArray('private_event_logs');
|
|
37
|
+
this.#eventLogIndex = this.#store.openMap('private_event_log_index');
|
|
38
|
+
this.#seenLogs = this.#store.openMap('seen_logs');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Store a private event log.
|
|
43
|
+
* @param contractAddress - The address of the contract that emitted the event.
|
|
44
|
+
* @param recipient - The recipient of the event.
|
|
45
|
+
* @param eventSelector - The event selector of the event.
|
|
46
|
+
* @param logContent - The content of the event.
|
|
47
|
+
* @param txHash - The transaction hash of the event log.
|
|
48
|
+
* @param logIndexInTx - The index of the log within the transaction.
|
|
49
|
+
* @param blockNumber - The block number in which the event was emitted.
|
|
50
|
+
*/
|
|
51
|
+
storePrivateEventLog(
|
|
52
|
+
contractAddress: AztecAddress,
|
|
53
|
+
recipient: AztecAddress,
|
|
54
|
+
eventSelector: EventSelector,
|
|
55
|
+
logContent: Fr[],
|
|
56
|
+
txHash: TxHash,
|
|
57
|
+
logIndexInTx: number,
|
|
58
|
+
blockNumber: number,
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
return this.#store.transactionAsync(async () => {
|
|
61
|
+
const key = `${contractAddress.toString()}_${recipient.toString()}_${eventSelector.toString()}`;
|
|
62
|
+
|
|
63
|
+
// We identify a unique log by its transaction hash and index within that transaction
|
|
64
|
+
const txKey = `${txHash.toString()}_${logIndexInTx}`;
|
|
65
|
+
|
|
66
|
+
// Check if this exact log has already been stored
|
|
67
|
+
const hasBeenSeen = await this.#seenLogs.getAsync(txKey);
|
|
68
|
+
if (hasBeenSeen) {
|
|
69
|
+
this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), logIndexInTx });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.logger.verbose('storing private event log', { contractAddress, recipient, logContent, blockNumber });
|
|
74
|
+
|
|
75
|
+
const index = await this.#eventLogs.lengthAsync();
|
|
76
|
+
await this.#eventLogs.push({ logContent: serializeToBuffer(logContent), blockNumber, logIndexInTx });
|
|
77
|
+
|
|
78
|
+
const existingIndices = (await this.#eventLogIndex.getAsync(key)) || [];
|
|
79
|
+
await this.#eventLogIndex.set(key, [...existingIndices, index]);
|
|
80
|
+
|
|
81
|
+
// Mark this log as seen
|
|
82
|
+
await this.#seenLogs.set(txKey, true);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns the private events given search parameters.
|
|
88
|
+
* @param contractAddress - The address of the contract to get events from.
|
|
89
|
+
* @param from - The block number to search from.
|
|
90
|
+
* @param numBlocks - The amount of blocks to search.
|
|
91
|
+
* @param recipients - The addresses that decrypted the logs.
|
|
92
|
+
* @param eventSelector - The event selector to filter by.
|
|
93
|
+
* @returns - The event log contents.
|
|
94
|
+
*/
|
|
95
|
+
public async getPrivateEvents(
|
|
96
|
+
contractAddress: AztecAddress,
|
|
97
|
+
from: number,
|
|
98
|
+
numBlocks: number,
|
|
99
|
+
recipients: AztecAddress[],
|
|
100
|
+
eventSelector: EventSelector,
|
|
101
|
+
): Promise<Fr[][]> {
|
|
102
|
+
const events: Array<{ logContent: Fr[]; blockNumber: number; logIndexInTx: number }> = [];
|
|
103
|
+
|
|
104
|
+
for (const recipient of recipients) {
|
|
105
|
+
const key = `${contractAddress.toString()}_${recipient.toString()}_${eventSelector.toString()}`;
|
|
106
|
+
const indices = (await this.#eventLogIndex.getAsync(key)) || [];
|
|
107
|
+
|
|
108
|
+
for (const index of indices) {
|
|
109
|
+
const entry = await this.#eventLogs.atAsync(index);
|
|
110
|
+
if (!entry || entry.blockNumber < from || entry.blockNumber >= from + numBlocks) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Convert buffer back to Fr array
|
|
115
|
+
const reader = BufferReader.asReader(entry.logContent);
|
|
116
|
+
const numFields = entry.logContent.length / Fr.SIZE_IN_BYTES;
|
|
117
|
+
const logContent = reader.readArray(numFields, Fr);
|
|
118
|
+
|
|
119
|
+
events.push({ logContent, blockNumber: entry.blockNumber, logIndexInTx: entry.logIndexInTx });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Sort by block number first, then by logIndexInTx (note that we currently don't order by txs within a block)
|
|
124
|
+
events.sort((a, b) => {
|
|
125
|
+
if (a.blockNumber !== b.blockNumber) {
|
|
126
|
+
return a.blockNumber - b.blockNumber;
|
|
127
|
+
}
|
|
128
|
+
return a.logIndexInTx - b.logIndexInTx;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return events.map(e => e.logContent);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getSize(): Promise<number> {
|
|
135
|
+
return this.#eventLogs.lengthAsync();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -16,10 +16,10 @@ export class SyncDataProvider implements DataProvider {
|
|
|
16
16
|
await this.#synchronizedHeader.set(header.toBuffer());
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async getBlockNumber(): Promise<number
|
|
19
|
+
async getBlockNumber(): Promise<number> {
|
|
20
20
|
const headerBuffer = await this.#synchronizedHeader.getAsync();
|
|
21
21
|
if (!headerBuffer) {
|
|
22
|
-
|
|
22
|
+
throw new Error(`Trying to get block number with a not-yet-synchronized PXE - this should never happen`);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
return Number(BlockHeader.fromBuffer(headerBuffer).globalVariables.blockNumber.toBigInt());
|
|
@@ -23,32 +23,63 @@ export class TaggingDataProvider {
|
|
|
23
23
|
this.#taggingSecretIndexesForRecipients = this.#store.openMap('tagging_secret_indexes_for_recipients');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[], sender: AztecAddress) {
|
|
27
|
+
return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders, sender);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[], recipient: AztecAddress) {
|
|
31
|
+
return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients, recipient);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Sets the indexes of the tagging secrets for the given app tagging secrets in the direction of the given address.
|
|
36
|
+
* @dev We need to specify the direction because app tagging secrets are direction-less due to the way they are generated
|
|
37
|
+
* but we need to guarantee that the index is stored under a uni-directional key because the tags are themselves
|
|
38
|
+
* uni-directional.
|
|
39
|
+
* @param indexedSecrets - The app tagging secrets and indexes to set.
|
|
40
|
+
* @param storageMap - The storage map to set the indexes in.
|
|
41
|
+
* @param inDirectionOf - The address that the secrets are in the direction of.
|
|
42
|
+
*/
|
|
43
|
+
#setTaggingSecretsIndexes(
|
|
44
|
+
indexedSecrets: IndexedTaggingSecret[],
|
|
45
|
+
storageMap: AztecAsyncMap<string, number>,
|
|
46
|
+
inDirectionOf: AztecAddress,
|
|
47
|
+
) {
|
|
48
|
+
return Promise.all(
|
|
36
49
|
indexedSecrets.map(indexedSecret =>
|
|
37
|
-
storageMap.set(indexedSecret.appTaggingSecret.toString()
|
|
50
|
+
storageMap.set(`${indexedSecret.appTaggingSecret.toString()}_${inDirectionOf.toString()}`, indexedSecret.index),
|
|
38
51
|
),
|
|
39
52
|
);
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
return
|
|
55
|
+
getTaggingSecretsIndexesAsRecipient(appTaggingSecrets: Fr[], recipient: AztecAddress) {
|
|
56
|
+
return this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForRecipients, recipient);
|
|
44
57
|
}
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
return
|
|
59
|
+
getTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[], sender: AztecAddress) {
|
|
60
|
+
return this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForSenders, sender);
|
|
48
61
|
}
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Returns the indexes of the tagging secrets for the given app tagging secrets in the direction of the given address.
|
|
65
|
+
* @dev We need to specify the direction because app tagging secrets are direction-less due to the way they are generated
|
|
66
|
+
* but we need to guarantee that the index is stored under a uni-directional key because the tags are themselves
|
|
67
|
+
* uni-directional.
|
|
68
|
+
* @param appTaggingSecrets - The app tagging secrets to get the indexes for.
|
|
69
|
+
* @param storageMap - The storage map to get the indexes from.
|
|
70
|
+
* @param inDirectionOf - The address that the secrets are in the direction of.
|
|
71
|
+
* @returns The indexes of the tagging secrets.
|
|
72
|
+
*/
|
|
73
|
+
#getTaggingSecretsIndexes(
|
|
74
|
+
appTaggingSecrets: Fr[],
|
|
75
|
+
storageMap: AztecAsyncMap<string, number>,
|
|
76
|
+
inDirectionOf: AztecAddress,
|
|
77
|
+
): Promise<number[]> {
|
|
78
|
+
return Promise.all(
|
|
79
|
+
appTaggingSecrets.map(
|
|
80
|
+
async secret => (await storageMap.getAsync(`${secret.toString()}_${inDirectionOf.toString()}`)) ?? 0,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
52
83
|
}
|
|
53
84
|
|
|
54
85
|
resetNoteSyncData(): Promise<void> {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
1
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
2
|
import type { L2TipsKVStore } from '@aztec/kv-store/stores';
|
|
4
3
|
import { L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler } from '@aztec/stdlib/block';
|
|
@@ -10,13 +9,11 @@ import type { SyncDataProvider } from '../storage/sync_data_provider/sync_data_p
|
|
|
10
9
|
import type { TaggingDataProvider } from '../storage/tagging_data_provider/tagging_data_provider.js';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
* The Synchronizer class
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* details, and fetch transactions by hash.
|
|
12
|
+
* The Synchronizer class orchestrates synchronization between the PXE and Aztec node, maintaining an up-to-date
|
|
13
|
+
* view of the L2 chain state. It handles block header retrieval, chain reorganizations, and provides an interface
|
|
14
|
+
* for querying sync status.
|
|
17
15
|
*/
|
|
18
16
|
export class Synchronizer implements L2BlockStreamEventHandler {
|
|
19
|
-
private initialSyncBlockNumber = INITIAL_L2_BLOCK_NUM - 1;
|
|
20
17
|
private log: Logger;
|
|
21
18
|
private isSyncing: Promise<void> | undefined;
|
|
22
19
|
protected readonly blockStream: L2BlockStream;
|
|
@@ -59,18 +56,18 @@ export class Synchronizer implements L2BlockStreamEventHandler {
|
|
|
59
56
|
break;
|
|
60
57
|
}
|
|
61
58
|
case 'chain-pruned': {
|
|
62
|
-
this.log.warn(`Pruning data after block ${event.
|
|
59
|
+
this.log.warn(`Pruning data after block ${event.block.number} due to reorg`);
|
|
63
60
|
// We first unnullify and then remove so that unnullified notes that were created after the block number end up deleted.
|
|
64
61
|
const lastSynchedBlockNumber = await this.syncDataProvider.getBlockNumber();
|
|
65
|
-
await this.noteDataProvider.unnullifyNotesAfter(event.
|
|
66
|
-
await this.noteDataProvider.removeNotesAfter(event.
|
|
62
|
+
await this.noteDataProvider.unnullifyNotesAfter(event.block.number, lastSynchedBlockNumber);
|
|
63
|
+
await this.noteDataProvider.removeNotesAfter(event.block.number);
|
|
67
64
|
// Remove all note tagging indexes to force a full resync. This is suboptimal, but unless we track the
|
|
68
65
|
// block number in which each index is used it's all we can do.
|
|
69
66
|
await this.taggingDataProvider.resetNoteSyncData();
|
|
70
67
|
// Update the header to the last block.
|
|
71
|
-
const newHeader = await this.node.getBlockHeader(event.
|
|
68
|
+
const newHeader = await this.node.getBlockHeader(event.block.number);
|
|
72
69
|
if (!newHeader) {
|
|
73
|
-
this.log.error(`Block header not found for block number ${event.
|
|
70
|
+
this.log.error(`Block header not found for block number ${event.block.number} during chain prune`);
|
|
74
71
|
} else {
|
|
75
72
|
await this.syncDataProvider.setHeader(newHeader);
|
|
76
73
|
}
|
|
@@ -80,7 +77,7 @@ export class Synchronizer implements L2BlockStreamEventHandler {
|
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
/**
|
|
83
|
-
* Syncs PXE and the node by
|
|
80
|
+
* Syncs PXE and the node by downloading the metadata of the latest blocks, allowing simulations to use
|
|
84
81
|
* recent data (e.g. notes), and handling any reorgs that might have occurred.
|
|
85
82
|
*/
|
|
86
83
|
public async sync() {
|
|
@@ -115,7 +112,7 @@ export class Synchronizer implements L2BlockStreamEventHandler {
|
|
|
115
112
|
await this.blockStream.sync();
|
|
116
113
|
}
|
|
117
114
|
|
|
118
|
-
public
|
|
119
|
-
return
|
|
115
|
+
public getSynchedBlockNumber() {
|
|
116
|
+
return this.syncDataProvider.getBlockNumber();
|
|
120
117
|
}
|
|
121
118
|
}
|
|
@@ -105,7 +105,7 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
|
|
|
105
105
|
|
|
106
106
|
it('successfully gets node info', async () => {
|
|
107
107
|
const nodeInfo = await pxe.getNodeInfo();
|
|
108
|
-
expect(typeof nodeInfo.
|
|
108
|
+
expect(typeof nodeInfo.rollupVersion).toEqual('number');
|
|
109
109
|
expect(typeof nodeInfo.l1ChainId).toEqual('number');
|
|
110
110
|
expect(nodeInfo.l1ContractAddresses.rollupAddress.toString()).toMatch(/0x[a-fA-F0-9]+/);
|
|
111
111
|
});
|