1+ import { uniqueBy } from '@aztec/foundation/collection' ;
12import { Fr } from '@aztec/foundation/curves/bn254' ;
23import type { AztecNode } from '@aztec/stdlib/interfaces/server' ;
34import { MessageContext } from '@aztec/stdlib/logs' ;
4- import { TxHash } from '@aztec/stdlib/tx' ;
5+ import { type IndexedTxEffect , TxHash } from '@aztec/stdlib/tx' ;
56
67/** Resolves transaction hashes into the context needed to process messages. */
78export class MessageContextService {
@@ -14,31 +15,31 @@ export class MessageContextService {
1415 * process messages that originated from that transaction. Returns `null` for tx hashes that are zero, not yet
1516 * available, or in blocks beyond the anchor block.
1617 */
17- getMessageContextsByTxHash ( txHashes : Fr [ ] , anchorBlockNumber : number ) : Promise < ( MessageContext | null ) [ ] > {
18- // TODO: optimize, we might be hitting the node to get the same txHash repeatedly
19- return Promise . all (
20- txHashes . map ( async txHashField => {
21- // A zero tx hash indicates a tx-less offchain message (e.g. one not tied to any onchain transaction).
22- // These messages don't have a transaction context to resolve, so we return null.
23- if ( txHashField . isZero ( ) ) {
24- return null ;
25- }
18+ async getMessageContextsByTxHash ( txHashes : Fr [ ] , anchorBlockNumber : number ) : Promise < ( MessageContext | null ) [ ] > {
19+ const nonZeroTxHashes = txHashes . filter ( h => ! h . isZero ( ) ) . map ( h => TxHash . fromField ( h ) ) ;
20+ const uniqueTxHashes = uniqueBy ( nonZeroTxHashes , h => h . toString ( ) ) ;
21+ const fetched = await Promise . all ( uniqueTxHashes . map ( h => this . aztecNode . getTxEffect ( h ) ) ) ;
22+ const txEffects = new Map (
23+ uniqueTxHashes
24+ . map ( ( h , i ) : [ string , IndexedTxEffect | undefined ] => [ h . toString ( ) , fetched [ i ] ] )
25+ . filter ( ( entry ) : entry is [ string , IndexedTxEffect ] => entry [ 1 ] !== undefined ) ,
26+ ) ;
2627
27- const txHash = TxHash . fromField ( txHashField ) ;
28- const txEffect = await this . aztecNode . getTxEffect ( txHash ) ;
29- if ( ! txEffect || txEffect . l2BlockNumber > anchorBlockNumber ) {
30- return null ;
31- }
28+ return txHashes . map ( txHashField => {
29+ const txHash = TxHash . fromField ( txHashField ) ;
30+ const txEffect = txEffects . get ( txHash . toString ( ) ) ;
31+ if ( ! txEffect || txEffect . l2BlockNumber > anchorBlockNumber ) {
32+ return null ;
33+ }
3234
33- // Every tx has at least one nullifier (the first nullifier derived from the tx hash). Hitting this condition
34- // would mean a buggy node, but since we need to access data.nullifiers[0], the defensive check does no harm.
35- const data = txEffect . data ;
36- if ( data . nullifiers . length === 0 ) {
37- throw new Error ( `Tx effect for ${ txHash } has no nullifiers` ) ;
38- }
35+ // Every tx has at least one nullifier (the first nullifier derived from the tx hash). Hitting this condition
36+ // would mean a buggy node, but since we need to access data.nullifiers[0], the defensive check does no harm.
37+ const data = txEffect . data ;
38+ if ( data . nullifiers . length === 0 ) {
39+ throw new Error ( `Tx effect for ${ txHash } has no nullifiers` ) ;
40+ }
3941
40- return new MessageContext ( data . txHash , data . noteHashes , data . nullifiers [ 0 ] ) ;
41- } ) ,
42- ) ;
42+ return new MessageContext ( data . txHash , data . noteHashes , data . nullifiers [ 0 ] ) ;
43+ } ) ;
4344 }
4445}
0 commit comments