@aztec/pxe 0.0.1-commit.321f6a9 → 0.0.1-commit.330febf
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/bin/check_oracle_version.js +2 -38
- package/dest/bin/index.d.ts +2 -2
- package/dest/bin/index.d.ts.map +1 -1
- package/dest/bin/index.js +1 -1
- package/dest/bin/oracle_version_helpers.d.ts +1 -15
- package/dest/bin/oracle_version_helpers.d.ts.map +1 -1
- package/dest/bin/oracle_version_helpers.js +0 -21
- package/dest/block_synchronizer/block_synchronizer.d.ts +3 -5
- package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
- package/dest/block_synchronizer/block_synchronizer.js +5 -18
- package/dest/contract/contract_class_service.d.ts +1 -8
- package/dest/contract/contract_class_service.d.ts.map +1 -1
- package/dest/contract/contract_class_service.js +8 -40
- package/dest/contract_function_simulator/contract_function_simulator.d.ts +1 -8
- package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.js +0 -17
- package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts +1 -1
- package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle_type_mappings.js +11 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +6 -2
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +10 -2
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +128 -38
- package/dest/events/event_service.d.ts +13 -6
- package/dest/events/event_service.d.ts.map +1 -1
- package/dest/events/event_service.js +13 -13
- package/dest/logs/log_service.d.ts +13 -5
- package/dest/logs/log_service.d.ts.map +1 -1
- package/dest/logs/log_service.js +20 -36
- package/dest/messages/tx_resolver_service.d.ts +18 -7
- package/dest/messages/tx_resolver_service.d.ts.map +1 -1
- package/dest/messages/tx_resolver_service.js +9 -8
- package/dest/node/benchmarked_node.d.ts +20 -0
- package/dest/node/benchmarked_node.d.ts.map +1 -0
- package/dest/node/benchmarked_node.js +94 -0
- package/dest/node/caching_aztec_node.d.ts +24 -0
- package/dest/node/caching_aztec_node.d.ts.map +1 -0
- package/dest/node/caching_aztec_node.js +267 -0
- package/dest/notes/note_service.d.ts +12 -6
- package/dest/notes/note_service.d.ts.map +1 -1
- package/dest/notes/note_service.js +9 -9
- package/dest/oracle_version.js +2 -2
- package/dest/pxe.d.ts +1 -1
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +31 -14
- package/dest/storage/contract_store/contract_store.d.ts +1 -1
- package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
- package/dest/storage/contract_store/contract_store.js +2 -2
- package/dest/storage/fact_store/origin_state.d.ts +1 -1
- package/dest/storage/fact_store/origin_state.js +1 -1
- package/dest/storage/tagging_store/sender_tagging_store.d.ts +23 -8
- package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
- package/dest/storage/tagging_store/sender_tagging_store.js +33 -18
- package/dest/tagging/constants.d.ts +9 -1
- package/dest/tagging/constants.d.ts.map +1 -1
- package/dest/tagging/constants.js +10 -0
- package/dest/tagging/get_all_logs_by_tags.d.ts +29 -8
- package/dest/tagging/get_all_logs_by_tags.d.ts.map +1 -1
- package/dest/tagging/get_all_logs_by_tags.js +29 -10
- package/dest/tagging/index.d.ts +2 -2
- package/dest/tagging/index.d.ts.map +1 -1
- package/dest/tagging/index.js +1 -1
- package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts +2 -2
- package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts.map +1 -1
- package/dest/tagging/recipient_sync/sync_tagged_private_logs.js +13 -17
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +6 -3
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +23 -38
- package/dest/tagging/sender_sync/utils/classify_pending_txs.d.ts +22 -0
- package/dest/tagging/sender_sync/utils/classify_pending_txs.d.ts.map +1 -0
- package/dest/tagging/sender_sync/utils/classify_pending_txs.js +61 -0
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +15 -5
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +30 -33
- package/dest/tagging/sender_sync/utils/resolve_pending_txs.d.ts +29 -0
- package/dest/tagging/sender_sync/utils/resolve_pending_txs.d.ts.map +1 -0
- package/dest/tagging/sender_sync/utils/resolve_pending_txs.js +41 -0
- package/dest/tagging/sender_sync/utils/test_utils.d.ts +16 -0
- package/dest/tagging/sender_sync/utils/test_utils.d.ts.map +1 -0
- package/dest/tagging/sender_sync/utils/test_utils.js +24 -0
- package/package.json +17 -17
- package/src/bin/check_oracle_version.ts +2 -46
- package/src/bin/index.ts +1 -1
- package/src/bin/oracle_version_helpers.ts +0 -24
- package/src/block_synchronizer/block_synchronizer.ts +6 -23
- package/src/contract/contract_class_service.ts +8 -46
- package/src/contract_function_simulator/contract_function_simulator.ts +0 -17
- package/src/contract_function_simulator/oracle/oracle_type_mappings.ts +16 -1
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +7 -2
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +130 -40
- package/src/events/event_service.ts +24 -14
- package/src/logs/log_service.ts +37 -44
- package/src/messages/tx_resolver_service.ts +22 -11
- package/src/node/benchmarked_node.ts +140 -0
- package/src/node/caching_aztec_node.ts +406 -0
- package/src/notes/note_service.ts +19 -13
- package/src/oracle_version.ts +2 -2
- package/src/pxe.ts +32 -16
- package/src/storage/contract_store/contract_store.ts +2 -1
- package/src/storage/fact_store/origin_state.ts +1 -1
- package/src/storage/tagging_store/sender_tagging_store.ts +51 -26
- package/src/tagging/constants.ts +12 -0
- package/src/tagging/get_all_logs_by_tags.ts +47 -11
- package/src/tagging/index.ts +6 -1
- package/src/tagging/recipient_sync/sync_tagged_private_logs.ts +19 -22
- package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +27 -57
- package/src/tagging/sender_sync/utils/classify_pending_txs.ts +91 -0
- package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +40 -38
- package/src/tagging/sender_sync/utils/resolve_pending_txs.ts +64 -0
- package/src/tagging/sender_sync/utils/test_utils.ts +46 -0
- package/dest/contract_function_simulator/aztec_node_read_cache.d.ts +0 -39
- package/dest/contract_function_simulator/aztec_node_read_cache.d.ts.map +0 -1
- package/dest/contract_function_simulator/aztec_node_read_cache.js +0 -60
- package/dest/contract_function_simulator/benchmarked_node.d.ts +0 -9
- package/dest/contract_function_simulator/benchmarked_node.d.ts.map +0 -1
- package/dest/contract_function_simulator/benchmarked_node.js +0 -77
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +0 -17
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +0 -1
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +0 -60
- package/src/contract_function_simulator/aztec_node_read_cache.ts +0 -89
- package/src/contract_function_simulator/benchmarked_node.ts +0 -103
- package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +0 -66
|
@@ -109,7 +109,6 @@ import type { RecipientTaggingStore } from '../storage/tagging_store/recipient_t
|
|
|
109
109
|
import type { SenderTaggingStore } from '../storage/tagging_store/sender_tagging_store.js';
|
|
110
110
|
import type { TaggingSecretSourcesStore } from '../storage/tagging_store/tagging_secret_sources_store.js';
|
|
111
111
|
import { AnchoredContractData } from './anchored_contract_data.js';
|
|
112
|
-
import type { BenchmarkedNode } from './benchmarked_node.js';
|
|
113
112
|
import { ExecutionNoteCache } from './execution_note_cache.js';
|
|
114
113
|
import { ExecutionTaggingIndexCache } from './execution_tagging_index_cache.js';
|
|
115
114
|
import { HashedValuesCache } from './hashed_values_cache.js';
|
|
@@ -444,22 +443,6 @@ export class ContractFunctionSimulator {
|
|
|
444
443
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
445
444
|
}
|
|
446
445
|
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Returns the execution statistics collected during the simulator run.
|
|
450
|
-
* @returns The execution statistics.
|
|
451
|
-
*/
|
|
452
|
-
getStats() {
|
|
453
|
-
const nodeRPCCalls =
|
|
454
|
-
typeof (this.aztecNode as BenchmarkedNode).getStats === 'function'
|
|
455
|
-
? (this.aztecNode as BenchmarkedNode).getStats()
|
|
456
|
-
: {
|
|
457
|
-
perMethod: {},
|
|
458
|
-
roundTrips: { roundTrips: 0, totalBlockingTime: 0, roundTripDurations: [], roundTripMethods: [] },
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
return { nodeRPCCalls };
|
|
462
|
-
}
|
|
463
446
|
}
|
|
464
447
|
|
|
465
448
|
class OrderedSideEffect<T> {
|
|
@@ -712,8 +712,23 @@ export function BOUNDED_VEC<T>(inner: TypeMapping<T>): BoundedVecMapping<T> {
|
|
|
712
712
|
fn: ([storageReader, lengthReader]) => {
|
|
713
713
|
// slot 0 is the padded storage, slot 1 the actual length. Parse only the first `length` elements out of
|
|
714
714
|
// storage, then drain the trailing zero-padding so the storage reader is fully consumed.
|
|
715
|
-
const
|
|
715
|
+
const elementWidth = fieldWidth(inner.shape);
|
|
716
|
+
const storageFields = storageReader.remainingFields();
|
|
717
|
+
if (storageFields % elementWidth !== 0) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`Malformed BoundedVec: storage array holds ${storageFields} field(s), which is not a whole number of ` +
|
|
720
|
+
`${elementWidth}-field elements`,
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
// The BoundedVec's maximum length is determined by the size of the storage array, so its length is bounded
|
|
724
|
+
// by it.
|
|
725
|
+
const maxLength = storageFields / elementWidth;
|
|
716
726
|
const length = lengthReader.readField().toNumber();
|
|
727
|
+
if (length > maxLength) {
|
|
728
|
+
throw new Error(
|
|
729
|
+
`Malformed BoundedVec: length ${length} exceeds the ${maxLength} element(s) its storage array holds`,
|
|
730
|
+
);
|
|
731
|
+
}
|
|
717
732
|
const elements = unpackElements(inner, storageReader, length);
|
|
718
733
|
storageReader.skip(storageReader.remainingFields());
|
|
719
734
|
return BoundedVec.from<T>({ data: elements, maxLength });
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import { NoteService } from '../../notes/note_service.js';
|
|
39
39
|
import { assertAllowedScope } from '../../storage/allowed_scopes.js';
|
|
40
40
|
import type { SenderTaggingStore } from '../../storage/tagging_store/sender_tagging_store.js';
|
|
41
|
-
import { syncSenderTaggingIndexes } from '../../tagging/index.js';
|
|
41
|
+
import { logQueryAnchorOf, syncSenderTaggingIndexes } from '../../tagging/index.js';
|
|
42
42
|
import type { ExecutionNoteCache } from '../execution_note_cache.js';
|
|
43
43
|
import { ExecutionTaggingIndexCache } from '../execution_tagging_index_cache.js';
|
|
44
44
|
import type { HashedValuesCache } from '../hashed_values_cache.js';
|
|
@@ -375,11 +375,16 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
375
375
|
// This is a tagging secret we've not yet used in this tx, so first sync our store to make sure its indices
|
|
376
376
|
// are up to date. We do this here because this store is not synced as part of the global sync because
|
|
377
377
|
// that'd be wasteful as most tagging secrets are not used in each tx.
|
|
378
|
+
const [{ finalized }, anchor] = await Promise.all([
|
|
379
|
+
this.l2TipsStore.getL2Tips(),
|
|
380
|
+
logQueryAnchorOf(this.anchorBlockHeader),
|
|
381
|
+
]);
|
|
378
382
|
await syncSenderTaggingIndexes(
|
|
379
383
|
secret,
|
|
380
384
|
this.aztecNode,
|
|
381
385
|
this.senderTaggingStore,
|
|
382
|
-
|
|
386
|
+
finalized.block.number,
|
|
387
|
+
anchor,
|
|
383
388
|
this.jobId,
|
|
384
389
|
);
|
|
385
390
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ARCHIVE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
|
|
1
|
+
import { ARCHIVE_HEIGHT, type NOTE_HASH_TREE_HEIGHT, PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants';
|
|
2
2
|
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
3
|
import { uniqueBy } from '@aztec/foundation/collection';
|
|
4
4
|
import { Aes128 } from '@aztec/foundation/crypto/aes128';
|
|
@@ -33,20 +33,20 @@ import {
|
|
|
33
33
|
type BlockHeader,
|
|
34
34
|
CallContext,
|
|
35
35
|
type Capsule,
|
|
36
|
-
type IndexedTxEffect,
|
|
37
36
|
type OffchainEffect,
|
|
38
37
|
type TxEffect,
|
|
39
38
|
type TxHash,
|
|
39
|
+
type TxReceipt,
|
|
40
40
|
} from '@aztec/stdlib/tx';
|
|
41
41
|
|
|
42
42
|
import type { ContractSyncService } from '../../contract/contract_sync_service.js';
|
|
43
43
|
import { createContractLogger, logContractMessage, stripAztecnrLogPrefix } from '../../contract_logging.js';
|
|
44
|
-
import { EventService } from '../../events/event_service.js';
|
|
44
|
+
import { EventService, type EventValidationTxData } from '../../events/event_service.js';
|
|
45
45
|
import type { UtilityCallAuthorizationRequest } from '../../hooks/authorize_utility_call.js';
|
|
46
46
|
import type { ExecutionHooks } from '../../hooks/index.js';
|
|
47
|
-
import { LogService } from '../../logs/log_service.js';
|
|
48
|
-
import { TxResolverService } from '../../messages/tx_resolver_service.js';
|
|
49
|
-
import { NoteService } from '../../notes/note_service.js';
|
|
47
|
+
import { LogService, type RetrievedTaggedLog } from '../../logs/log_service.js';
|
|
48
|
+
import { type TxOnchainContext, TxResolverService } from '../../messages/tx_resolver_service.js';
|
|
49
|
+
import { NoteService, type NoteValidationTxData } from '../../notes/note_service.js';
|
|
50
50
|
import { ORACLE_VERSION_MAJOR } from '../../oracle_version.js';
|
|
51
51
|
import type { AddressStore } from '../../storage/address_store/address_store.js';
|
|
52
52
|
import { assertAllowedScope } from '../../storage/allowed_scopes.js';
|
|
@@ -58,7 +58,6 @@ import type { PrivateEventStore } from '../../storage/private_event_store/privat
|
|
|
58
58
|
import type { RecipientTaggingStore } from '../../storage/tagging_store/recipient_tagging_store.js';
|
|
59
59
|
import type { TaggingSecretSourcesStore } from '../../storage/tagging_store/tagging_secret_sources_store.js';
|
|
60
60
|
import type { AnchoredContractData } from '../anchored_contract_data.js';
|
|
61
|
-
import { AztecNodeReadCache } from '../aztec_node_read_cache.js';
|
|
62
61
|
import { EphemeralArrayService } from '../ephemeral_array_service.js';
|
|
63
62
|
import { BoundedVec } from '../noir-structs/bounded_vec.js';
|
|
64
63
|
import type { EmbeddedCurvePoint } from '../noir-structs/embedded_curve_point.js';
|
|
@@ -122,7 +121,15 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
122
121
|
private offchainEffects: OffchainEffect[] = [];
|
|
123
122
|
private readonly ephemeralArrayService = new EphemeralArrayService();
|
|
124
123
|
protected readonly transientArrayService: TransientArrayService;
|
|
125
|
-
|
|
124
|
+
/** Keyed by tx hash string. */
|
|
125
|
+
private readonly txReceiptsCache = new Map<string, Promise<TxReceipt<{ includeTxEffect: true }>>>();
|
|
126
|
+
/**
|
|
127
|
+
* Information that can be used to validate the existence of a note or an event, keyed by tx hash string. It is
|
|
128
|
+
* populated by the node queries that precede validation (tagged log retrieval, tx resolution), which already return
|
|
129
|
+
* everything validation needs, so validating a note or event created in one of those txs costs no node roundtrip.
|
|
130
|
+
* Notes and events reached through other paths (e.g. offchain inbox messages) still need a receipt.
|
|
131
|
+
*/
|
|
132
|
+
private readonly validationTxDataCache = new Map<string, ValidationTxData>();
|
|
126
133
|
|
|
127
134
|
// We store oracle version to be able to show a nice error message when an oracle handler is missing.
|
|
128
135
|
private contractOracleVersion: { major: number; minor: number } | undefined;
|
|
@@ -176,7 +183,6 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
176
183
|
this.hooks = args.hooks;
|
|
177
184
|
this.utilityExecutor = args.utilityExecutor;
|
|
178
185
|
this.transientArrayService = args.transientArrayService;
|
|
179
|
-
this.aztecNodeReadCache = new AztecNodeReadCache(args.aztecNode);
|
|
180
186
|
}
|
|
181
187
|
|
|
182
188
|
public assertCompatibleOracleVersion(major: number, minor: number): void {
|
|
@@ -270,7 +276,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
270
276
|
// hash at all. If the block hash did not exist by the reference block hash, then the node will not return the
|
|
271
277
|
// membership witness as there is none.
|
|
272
278
|
const witness = await this.#queryWithBlockHashNotAfterAnchor(referenceBlockHash, () =>
|
|
273
|
-
this.
|
|
279
|
+
this.aztecNode.getBlockHashMembershipWitness(referenceBlockHash, blockHash),
|
|
274
280
|
);
|
|
275
281
|
return witness ? Option.some(witness) : Option.none();
|
|
276
282
|
}
|
|
@@ -284,7 +290,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
284
290
|
const memberships = await this.#queryWithBlockHashNotAfterAnchor(referenceBlockHash, () =>
|
|
285
291
|
Promise.all(
|
|
286
292
|
hashes.map(blockHash =>
|
|
287
|
-
this.
|
|
293
|
+
this.aztecNode.getBlockHashMembershipWitness(referenceBlockHash, blockHash).then(Boolean),
|
|
288
294
|
),
|
|
289
295
|
),
|
|
290
296
|
);
|
|
@@ -339,7 +345,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
339
345
|
*/
|
|
340
346
|
public async getPublicDataWitness(blockHash: BlockHash, leafSlot: Fr): Promise<PublicDataWitness> {
|
|
341
347
|
const witness = await this.#queryWithBlockHashNotAfterAnchor(blockHash, () =>
|
|
342
|
-
this.
|
|
348
|
+
this.aztecNode.getPublicDataWitness(blockHash, leafSlot),
|
|
343
349
|
);
|
|
344
350
|
if (!witness) {
|
|
345
351
|
throw new Error(`Public data witness not found for slot ${leafSlot} at block hash ${blockHash.toString()}.`);
|
|
@@ -530,11 +536,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
530
536
|
numberOfElements: number,
|
|
531
537
|
) {
|
|
532
538
|
return this.#queryWithBlockHashNotAfterAnchor(blockHash, async () => {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
539
|
+
const slots = Array(numberOfElements)
|
|
540
|
+
.fill(0)
|
|
541
|
+
.map((_, i) => new Fr(startStorageSlot.toBigInt() + BigInt(i)));
|
|
542
|
+
const values = await Promise.all(
|
|
543
|
+
slots.map(storageSlot => this.aztecNode.getPublicStorageAt(blockHash, contractAddress, storageSlot)),
|
|
538
544
|
);
|
|
539
545
|
|
|
540
546
|
this.logger.debug(
|
|
@@ -601,8 +607,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
601
607
|
.map(ps => new AppTaggingSecret(ps.secret, this.contractAddress, ps.mode));
|
|
602
608
|
|
|
603
609
|
const logService = this.#createLogService();
|
|
604
|
-
const
|
|
605
|
-
|
|
610
|
+
const retrievedLogs = await logService.fetchTaggedLogs(this.contractAddress, scope, secrets);
|
|
611
|
+
|
|
612
|
+
this.#cacheValidationTxData(retrievedLogs);
|
|
613
|
+
|
|
614
|
+
return EphemeralArray.fromValues(this.ephemeralArrayService, retrievedLogs.map(toPendingTaggedLog));
|
|
606
615
|
}
|
|
607
616
|
|
|
608
617
|
#createLogService(): LogService {
|
|
@@ -637,7 +646,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
637
646
|
eventValidationRequests: EventValidationRequest[],
|
|
638
647
|
scope: AztecAddress,
|
|
639
648
|
) {
|
|
640
|
-
const
|
|
649
|
+
const validationTxData = await this.#getValidationTxData([
|
|
641
650
|
...noteValidationRequests.map(r => r.txHash),
|
|
642
651
|
...eventValidationRequests.map(r => r.txHash),
|
|
643
652
|
]);
|
|
@@ -646,8 +655,8 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
646
655
|
const eventService = new EventService(this.anchorBlockHeader, this.aztecNode, this.privateEventStore, this.jobId);
|
|
647
656
|
|
|
648
657
|
await Promise.all([
|
|
649
|
-
noteService.validateAndStoreNotes(noteValidationRequests, scope,
|
|
650
|
-
eventService.validateAndStoreEvents(eventValidationRequests, scope,
|
|
658
|
+
noteService.validateAndStoreNotes(noteValidationRequests, scope, validationTxData),
|
|
659
|
+
eventService.validateAndStoreEvents(eventValidationRequests, scope, validationTxData),
|
|
651
660
|
]);
|
|
652
661
|
}
|
|
653
662
|
|
|
@@ -657,11 +666,13 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
657
666
|
const logRetrievalRequests = requests.readAll(this.ephemeralArrayService);
|
|
658
667
|
const logService = this.#createLogService();
|
|
659
668
|
|
|
660
|
-
const
|
|
669
|
+
const retrievedLogsPerRequest = await logService.fetchLogsByTag(this.contractAddress, logRetrievalRequests);
|
|
670
|
+
|
|
671
|
+
this.#cacheValidationTxData(retrievedLogsPerRequest.flat());
|
|
661
672
|
|
|
662
673
|
// Create an inner ephemeral array for each request's matching logs, then wrap all slots in an outer array.
|
|
663
|
-
const innerArrays =
|
|
664
|
-
EphemeralArray.fromValues(this.ephemeralArrayService,
|
|
674
|
+
const innerArrays = retrievedLogsPerRequest.map(retrievedLogs =>
|
|
675
|
+
EphemeralArray.fromValues(this.ephemeralArrayService, retrievedLogs.map(toLogRetrievalResponse)),
|
|
665
676
|
);
|
|
666
677
|
|
|
667
678
|
return EphemeralArray.fromValues(this.ephemeralArrayService, innerArrays);
|
|
@@ -673,7 +684,9 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
673
684
|
|
|
674
685
|
const resolved = await this.txResolver.resolveTxs(txHashes, this.anchorBlockHeader.getBlockNumber());
|
|
675
686
|
|
|
676
|
-
|
|
687
|
+
this.#cacheValidationTxData(resolved.filter(tx => tx !== null));
|
|
688
|
+
|
|
689
|
+
const options = resolved.map(tx => (tx ? Option.some(toResolvedTx(tx)) : Option.none<ResolvedTx>()));
|
|
677
690
|
return EphemeralArray.fromValues(this.ephemeralArrayService, options);
|
|
678
691
|
}
|
|
679
692
|
|
|
@@ -1107,16 +1120,34 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
1107
1120
|
return this.offchainEffects;
|
|
1108
1121
|
}
|
|
1109
1122
|
|
|
1123
|
+
/** Stores the onchain context of the given txs, so that validating the notes and events they created is free. */
|
|
1124
|
+
#cacheValidationTxData(txs: TxOnchainContext[]) {
|
|
1125
|
+
txs.forEach(tx => this.validationTxDataCache.set(tx.txHash.toString(), toValidationTxData(tx)));
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1110
1128
|
/**
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1129
|
+
* Returns the information needed to validate the notes and events created in the given txs, keyed by
|
|
1130
|
+
* `TxHash.toString()`. Txs already in {@link validationTxDataCache} cost no node request, and the rest are read from
|
|
1131
|
+
* the node. Txs with no tx effect are absent from the returned map.
|
|
1113
1132
|
*/
|
|
1114
|
-
async #
|
|
1115
|
-
const
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1133
|
+
async #getValidationTxData(txHashes: TxHash[]): Promise<Map<string, ValidationTxData>> {
|
|
1134
|
+
const known: [string, ValidationTxData][] = [];
|
|
1135
|
+
const misses: TxHash[] = [];
|
|
1136
|
+
for (const txHash of uniqueBy(txHashes, h => h.toString())) {
|
|
1137
|
+
const key = txHash.toString();
|
|
1138
|
+
const cached = this.validationTxDataCache.get(key);
|
|
1139
|
+
if (cached) {
|
|
1140
|
+
known.push([key, cached]);
|
|
1141
|
+
} else {
|
|
1142
|
+
misses.push(txHash);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const fetched = await Promise.all(misses.map(h => this.#getTxReceiptWithEffect(h)));
|
|
1147
|
+
return new Map([
|
|
1148
|
+
...known,
|
|
1149
|
+
...misses
|
|
1150
|
+
.map((h, i): [string, ValidationTxData | undefined] => {
|
|
1120
1151
|
const receipt = fetched[i];
|
|
1121
1152
|
if (!receipt.isMined() || !receipt.txEffect) {
|
|
1122
1153
|
return [h.toString(), undefined];
|
|
@@ -1124,20 +1155,41 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
1124
1155
|
return [
|
|
1125
1156
|
h.toString(),
|
|
1126
1157
|
{
|
|
1127
|
-
|
|
1158
|
+
noteHashes: receipt.txEffect.noteHashes,
|
|
1159
|
+
nullifiers: receipt.txEffect.nullifiers,
|
|
1128
1160
|
l2BlockNumber: receipt.blockNumber,
|
|
1129
1161
|
l2BlockHash: receipt.blockHash,
|
|
1130
1162
|
txIndexInBlock: receipt.txIndexInBlock,
|
|
1131
|
-
slotNumber: receipt.slotNumber,
|
|
1132
1163
|
},
|
|
1133
1164
|
];
|
|
1134
1165
|
})
|
|
1135
|
-
.filter((entry): entry is [string,
|
|
1136
|
-
);
|
|
1166
|
+
.filter((entry): entry is [string, ValidationTxData] => entry[1] !== undefined),
|
|
1167
|
+
]);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Reads a receipt with its effect, at most once per tx for the lifetime of this execution.
|
|
1172
|
+
*
|
|
1173
|
+
* A receipt is not cacheable in general, since pending, mined and dropped are all correct answers to the same call
|
|
1174
|
+
* over time. Within one execution it is: the execution is anchored at a fixed block, and validation runs in several
|
|
1175
|
+
* batches that name overlapping tx hashes, so re-reading would both cost extra requests and let one execution see a
|
|
1176
|
+
* tx as included in one batch and absent in the next.
|
|
1177
|
+
*/
|
|
1178
|
+
#getTxReceiptWithEffect(txHash: TxHash) {
|
|
1179
|
+
const key = txHash.toString();
|
|
1180
|
+
let receipt = this.txReceiptsCache.get(key);
|
|
1181
|
+
if (!receipt) {
|
|
1182
|
+
receipt = this.aztecNode.getTxReceipt(txHash, { includeTxEffect: true }).catch(err => {
|
|
1183
|
+
this.txReceiptsCache.delete(key);
|
|
1184
|
+
throw err;
|
|
1185
|
+
});
|
|
1186
|
+
this.txReceiptsCache.set(key, receipt);
|
|
1187
|
+
}
|
|
1188
|
+
return receipt;
|
|
1137
1189
|
}
|
|
1138
1190
|
|
|
1139
1191
|
async #getTxEffectOption(txHash: TxHash): Promise<Option<TxEffectData>> {
|
|
1140
|
-
const receipt = await this
|
|
1192
|
+
const receipt = await this.#getTxReceiptWithEffect(txHash);
|
|
1141
1193
|
if (!receipt.isMined() || !receipt.txEffect || receipt.blockNumber > this.anchorBlockHeader.getBlockNumber()) {
|
|
1142
1194
|
return Option.none();
|
|
1143
1195
|
}
|
|
@@ -1168,7 +1220,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
1168
1220
|
const [response] = await Promise.all([
|
|
1169
1221
|
query(),
|
|
1170
1222
|
(async () => {
|
|
1171
|
-
const block = await this.
|
|
1223
|
+
const block = await this.aztecNode.getBlock(blockHash);
|
|
1172
1224
|
const header = block?.header;
|
|
1173
1225
|
if (!header) {
|
|
1174
1226
|
throw new Error(`Could not find block header for block hash ${blockHash}`);
|
|
@@ -1229,3 +1281,41 @@ async function isStandardHandshakeRegistryUtilityRead(
|
|
|
1229
1281
|
);
|
|
1230
1282
|
return matches.some(Boolean);
|
|
1231
1283
|
}
|
|
1284
|
+
|
|
1285
|
+
function toPendingTaggedLog(retrievedLog: RetrievedTaggedLog): PendingTaggedLog {
|
|
1286
|
+
return { log: retrievedLog.logData, context: toResolvedTx(retrievedLog) };
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
function toResolvedTx(tx: TxOnchainContext): ResolvedTx {
|
|
1290
|
+
const { txHash, blockNumber, blockHash, noteHashes, nullifiers } = tx;
|
|
1291
|
+
return {
|
|
1292
|
+
txHash,
|
|
1293
|
+
uniqueNoteHashesInTx: noteHashes,
|
|
1294
|
+
firstNullifierInTx: nullifiers[0],
|
|
1295
|
+
blockNumber,
|
|
1296
|
+
blockHash: blockHash.toFr(),
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
function toLogRetrievalResponse(retrievedLog: RetrievedTaggedLog): LogRetrievalResponse {
|
|
1301
|
+
const { logData, txHash, blockNumber, blockHash, blockTimestamp, noteHashes, nullifiers } = retrievedLog;
|
|
1302
|
+
return {
|
|
1303
|
+
// Skip the tag, and clip to the wire cap: public logs can exceed PRIVATE_LOG_CIPHERTEXT_LEN, which is the fixed
|
|
1304
|
+
// size of the oracle's BoundedVec slot. A no-op for private logs, which are already within the cap.
|
|
1305
|
+
logPayload: logData.slice(1, 1 + PRIVATE_LOG_CIPHERTEXT_LEN),
|
|
1306
|
+
txHash,
|
|
1307
|
+
uniqueNoteHashesInTx: noteHashes,
|
|
1308
|
+
firstNullifierInTx: nullifiers[0],
|
|
1309
|
+
blockNumber,
|
|
1310
|
+
blockTimestamp,
|
|
1311
|
+
blockHash,
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function toValidationTxData(tx: TxOnchainContext): ValidationTxData {
|
|
1316
|
+
const { blockNumber, blockHash, txIndexInBlock, noteHashes, nullifiers } = tx;
|
|
1317
|
+
return { noteHashes, nullifiers, l2BlockNumber: blockNumber, l2BlockHash: blockHash, txIndexInBlock };
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/** The onchain context of a tx served to note and event validation: the union of what the two services read. */
|
|
1321
|
+
type ValidationTxData = NoteValidationTxData & EventValidationTxData;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
1
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
3
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
|
+
import type { InBlock } from '@aztec/stdlib/block';
|
|
3
5
|
import { computePrivateEventCommitment, siloNullifier } from '@aztec/stdlib/hash';
|
|
4
6
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
5
|
-
import type { BlockHeader
|
|
7
|
+
import type { BlockHeader } from '@aztec/stdlib/tx';
|
|
6
8
|
|
|
7
9
|
import type { EventValidationRequest } from '../contract_function_simulator/noir-structs/event_validation_request.js';
|
|
8
10
|
import { PrivateEventStore } from '../storage/private_event_store/private_event_store.js';
|
|
@@ -17,17 +19,17 @@ export class EventService {
|
|
|
17
19
|
) {}
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
|
-
* Validates and stores a batch of private events against pre-fetched
|
|
22
|
+
* Validates and stores a batch of private events against the pre-fetched onchain context of their txs.
|
|
21
23
|
*
|
|
22
24
|
* @param requests - The events to validate and store.
|
|
23
25
|
* @param scope - The scope under which the events are being stored.
|
|
24
|
-
* @param
|
|
25
|
-
*
|
|
26
|
+
* @param validationTxData - The onchain context of each request's tx, keyed by `TxHash.toString()`. Must contain
|
|
27
|
+
* entries for every request's txHash; missing entries are treated as a node bug and cause an error.
|
|
26
28
|
*/
|
|
27
29
|
public async validateAndStoreEvents(
|
|
28
30
|
requests: EventValidationRequest[],
|
|
29
31
|
scope: AztecAddress,
|
|
30
|
-
|
|
32
|
+
validationTxData: ReadonlyMap<string, EventValidationTxData>,
|
|
31
33
|
): Promise<void> {
|
|
32
34
|
if (requests.length === 0) {
|
|
33
35
|
return;
|
|
@@ -35,13 +37,15 @@ export class EventService {
|
|
|
35
37
|
|
|
36
38
|
const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber();
|
|
37
39
|
|
|
38
|
-
await Promise.all(
|
|
40
|
+
await Promise.all(
|
|
41
|
+
requests.map(req => this.#validateAndStoreEvent(req, scope, validationTxData, anchorBlockNumber)),
|
|
42
|
+
);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
async #validateAndStoreEvent(
|
|
42
46
|
request: EventValidationRequest,
|
|
43
47
|
scope: AztecAddress,
|
|
44
|
-
|
|
48
|
+
validationTxData: ReadonlyMap<string, EventValidationTxData>,
|
|
45
49
|
anchorBlockNumber: number,
|
|
46
50
|
): Promise<void> {
|
|
47
51
|
const {
|
|
@@ -71,15 +75,15 @@ export class EventService {
|
|
|
71
75
|
// since `fetchTaggedLogs` only processes logs up to the synced block.
|
|
72
76
|
const siloedEventCommitment = await siloNullifier(contractAddress, eventCommitment);
|
|
73
77
|
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
78
|
+
const txData = validationTxData.get(txHash.toString());
|
|
79
|
+
if (!txData) {
|
|
76
80
|
// We error out instead of just logging a warning and skipping the event because this would indicate a bug. This
|
|
77
81
|
// is because the node has already served info about this tx either when obtaining the log (LogResult carries
|
|
78
82
|
// the tx info) or when getting metadata for the offchain message (before the message got passed to `process_log`).
|
|
79
83
|
throw new Error(`Could not find tx effect for tx hash ${txHash} when processing an event.`);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
if (
|
|
86
|
+
if (txData.l2BlockNumber > anchorBlockNumber) {
|
|
83
87
|
// We should never process a message from a tx past the anchor block. If we got here, a preprocessing step made
|
|
84
88
|
// a mistake.
|
|
85
89
|
throw new Error(
|
|
@@ -88,7 +92,7 @@ export class EventService {
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
// Find the index of the event commitment in the nullifiers array to determine event ordering within the tx
|
|
91
|
-
const eventIndexInTx =
|
|
95
|
+
const eventIndexInTx = txData.nullifiers.findIndex(n => n.equals(siloedEventCommitment));
|
|
92
96
|
if (eventIndexInTx === -1) {
|
|
93
97
|
// Unlike in NoteService, this might not be a bug since the commitment hasn't been verified yet in the message
|
|
94
98
|
// processing pipeline. A malformed or malicious message could trigger this condition. Because of this we don't
|
|
@@ -108,12 +112,18 @@ export class EventService {
|
|
|
108
112
|
contractAddress,
|
|
109
113
|
scope,
|
|
110
114
|
txHash,
|
|
111
|
-
l2BlockNumber:
|
|
112
|
-
l2BlockHash:
|
|
113
|
-
txIndexInBlock:
|
|
115
|
+
l2BlockNumber: txData.l2BlockNumber,
|
|
116
|
+
l2BlockHash: txData.l2BlockHash,
|
|
117
|
+
txIndexInBlock: txData.txIndexInBlock,
|
|
114
118
|
eventIndexInTx,
|
|
115
119
|
},
|
|
116
120
|
this.jobId,
|
|
117
121
|
);
|
|
118
122
|
}
|
|
119
123
|
}
|
|
124
|
+
|
|
125
|
+
/** The onchain context of the tx an event validation request points at: where it was mined and its nullifiers. */
|
|
126
|
+
export type EventValidationTxData = InBlock & {
|
|
127
|
+
nullifiers: Fr[];
|
|
128
|
+
txIndexInBlock: number;
|
|
129
|
+
};
|