@aztec/stdlib 5.0.0-rc.2 → 5.0.1

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.
Files changed (197) hide show
  1. package/dest/block/attestation_info.d.ts +1 -1
  2. package/dest/block/attestation_info.d.ts.map +1 -1
  3. package/dest/block/attestation_info.js +7 -4
  4. package/dest/block/l2_block_source.d.ts +34 -3
  5. package/dest/block/l2_block_source.d.ts.map +1 -1
  6. package/dest/block/l2_block_source.js +15 -0
  7. package/dest/block/l2_block_stream/event_driven_l2_block_stream.d.ts +39 -0
  8. package/dest/block/l2_block_stream/event_driven_l2_block_stream.d.ts.map +1 -0
  9. package/dest/block/l2_block_stream/event_driven_l2_block_stream.js +165 -0
  10. package/dest/block/l2_block_stream/index.d.ts +2 -1
  11. package/dest/block/l2_block_stream/index.d.ts.map +1 -1
  12. package/dest/block/l2_block_stream/index.js +1 -0
  13. package/dest/block/l2_block_stream/interfaces.d.ts +16 -2
  14. package/dest/block/l2_block_stream/interfaces.d.ts.map +1 -1
  15. package/dest/block/l2_block_stream/interfaces.js +25 -1
  16. package/dest/block/l2_block_stream/l2_block_stream.d.ts +21 -35
  17. package/dest/block/l2_block_stream/l2_block_stream.d.ts.map +1 -1
  18. package/dest/block/l2_block_stream/l2_block_stream.js +5 -30
  19. package/dest/block/proposal/attestations_and_signers.d.ts +19 -1
  20. package/dest/block/proposal/attestations_and_signers.d.ts.map +1 -1
  21. package/dest/block/proposal/attestations_and_signers.js +47 -7
  22. package/dest/block/validate_block_result.d.ts +16 -1
  23. package/dest/block/validate_block_result.d.ts.map +1 -1
  24. package/dest/block/validate_block_result.js +18 -3
  25. package/dest/config/network-consensus-config.d.ts +1 -1
  26. package/dest/config/network-consensus-config.d.ts.map +1 -1
  27. package/dest/config/network-consensus-config.js +2 -10
  28. package/dest/contract/contract_address.d.ts +5 -4
  29. package/dest/contract/contract_address.d.ts.map +1 -1
  30. package/dest/contract/contract_instance.d.ts +16 -2
  31. package/dest/contract/contract_instance.d.ts.map +1 -1
  32. package/dest/contract/contract_instance.js +45 -0
  33. package/dest/contract/interfaces/contract_instance.d.ts +71 -10
  34. package/dest/contract/interfaces/contract_instance.d.ts.map +1 -1
  35. package/dest/contract/interfaces/contract_instance.js +7 -2
  36. package/dest/contract/partial_address.d.ts +9 -3
  37. package/dest/contract/partial_address.d.ts.map +1 -1
  38. package/dest/contract/partial_address.js +8 -2
  39. package/dest/database-version/version_manager.d.ts +18 -4
  40. package/dest/database-version/version_manager.d.ts.map +1 -1
  41. package/dest/database-version/version_manager.js +76 -6
  42. package/dest/ha-signing/config.d.ts +19 -5
  43. package/dest/ha-signing/config.d.ts.map +1 -1
  44. package/dest/ha-signing/config.js +9 -3
  45. package/dest/ha-signing/local_config.d.ts +10 -2
  46. package/dest/ha-signing/local_config.d.ts.map +1 -1
  47. package/dest/ha-signing/local_config.js +8 -2
  48. package/dest/hash/hash.d.ts +1 -2
  49. package/dest/hash/hash.d.ts.map +1 -1
  50. package/dest/hash/hash.js +0 -7
  51. package/dest/interfaces/archiver.d.ts +8 -1
  52. package/dest/interfaces/archiver.d.ts.map +1 -1
  53. package/dest/interfaces/archiver.js +2 -1
  54. package/dest/interfaces/aztec-node-admin.d.ts +5 -2
  55. package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
  56. package/dest/interfaces/configs.d.ts +4 -1
  57. package/dest/interfaces/configs.d.ts.map +1 -1
  58. package/dest/interfaces/configs.js +1 -0
  59. package/dest/interfaces/private_kernel_prover.d.ts +9 -11
  60. package/dest/interfaces/private_kernel_prover.d.ts.map +1 -1
  61. package/dest/interfaces/proving-job.d.ts +87 -74
  62. package/dest/interfaces/proving-job.d.ts.map +1 -1
  63. package/dest/interfaces/proving-job.js +13 -4
  64. package/dest/interfaces/server_circuit_prover.d.ts +3 -3
  65. package/dest/interfaces/server_circuit_prover.d.ts.map +1 -1
  66. package/dest/interfaces/validator.d.ts +7 -3
  67. package/dest/interfaces/validator.d.ts.map +1 -1
  68. package/dest/interfaces/world_state.d.ts +14 -1
  69. package/dest/interfaces/world_state.d.ts.map +1 -1
  70. package/dest/kernel/hints/find_private_kernel_reset_dimensions.d.ts +8 -2
  71. package/dest/kernel/hints/find_private_kernel_reset_dimensions.d.ts.map +1 -1
  72. package/dest/kernel/hints/find_private_kernel_reset_dimensions.js +12 -74
  73. package/dest/kernel/index.d.ts +2 -2
  74. package/dest/kernel/index.d.ts.map +1 -1
  75. package/dest/kernel/index.js +1 -1
  76. package/dest/kernel/private_circuit_public_inputs.d.ts +13 -3
  77. package/dest/kernel/private_circuit_public_inputs.d.ts.map +1 -1
  78. package/dest/kernel/private_circuit_public_inputs.js +14 -7
  79. package/dest/kernel/private_context_inputs.d.ts +4 -3
  80. package/dest/kernel/private_context_inputs.d.ts.map +1 -1
  81. package/dest/kernel/private_context_inputs.js +7 -3
  82. package/dest/kernel/private_kernel_circuit_public_inputs.d.ts +7 -1
  83. package/dest/kernel/private_kernel_circuit_public_inputs.d.ts.map +1 -1
  84. package/dest/kernel/private_kernel_circuit_public_inputs.js +7 -0
  85. package/dest/kernel/private_kernel_init_2_circuit_private_inputs.d.ts +2 -3
  86. package/dest/kernel/private_kernel_init_2_circuit_private_inputs.d.ts.map +1 -1
  87. package/dest/kernel/private_kernel_init_2_circuit_private_inputs.js +3 -5
  88. package/dest/kernel/private_kernel_init_3_circuit_private_inputs.d.ts +2 -3
  89. package/dest/kernel/private_kernel_init_3_circuit_private_inputs.d.ts.map +1 -1
  90. package/dest/kernel/private_kernel_init_3_circuit_private_inputs.js +3 -5
  91. package/dest/kernel/private_kernel_init_circuit_private_inputs.d.ts +1 -9
  92. package/dest/kernel/private_kernel_init_circuit_private_inputs.d.ts.map +1 -1
  93. package/dest/kernel/private_kernel_init_circuit_private_inputs.js +2 -6
  94. package/dest/kernel/private_kernel_reset_dimensions.d.ts +7 -8
  95. package/dest/kernel/private_kernel_reset_dimensions.d.ts.map +1 -1
  96. package/dest/kernel/private_kernel_reset_tail_circuit_private_inputs.d.ts +29 -0
  97. package/dest/kernel/private_kernel_reset_tail_circuit_private_inputs.d.ts.map +1 -0
  98. package/dest/kernel/private_kernel_reset_tail_circuit_private_inputs.js +36 -0
  99. package/dest/keys/derivation.d.ts +41 -6
  100. package/dest/keys/derivation.d.ts.map +1 -1
  101. package/dest/keys/derivation.js +49 -26
  102. package/dest/logs/app_tagging_secret.d.ts +16 -8
  103. package/dest/logs/app_tagging_secret.d.ts.map +1 -1
  104. package/dest/logs/app_tagging_secret.js +24 -27
  105. package/dest/logs/index.d.ts +1 -3
  106. package/dest/logs/index.d.ts.map +1 -1
  107. package/dest/logs/index.js +0 -2
  108. package/dest/logs/shared_secret_derivation.d.ts +29 -6
  109. package/dest/logs/shared_secret_derivation.d.ts.map +1 -1
  110. package/dest/logs/shared_secret_derivation.js +44 -11
  111. package/dest/messaging/l1_to_l2_message.d.ts +23 -2
  112. package/dest/messaging/l1_to_l2_message.d.ts.map +1 -1
  113. package/dest/messaging/l1_to_l2_message.js +36 -11
  114. package/dest/p2p/attestation_utils.d.ts +1 -1
  115. package/dest/p2p/attestation_utils.d.ts.map +1 -1
  116. package/dest/p2p/attestation_utils.js +8 -1
  117. package/dest/p2p/checkpoint_attestation.d.ts +9 -1
  118. package/dest/p2p/checkpoint_attestation.d.ts.map +1 -1
  119. package/dest/p2p/checkpoint_attestation.js +10 -0
  120. package/dest/p2p/signature_utils.d.ts +10 -1
  121. package/dest/p2p/signature_utils.d.ts.map +1 -1
  122. package/dest/p2p/signature_utils.js +9 -1
  123. package/dest/rollup/avm_proof_data.d.ts +3 -3
  124. package/dest/rollup/avm_proof_data.d.ts.map +1 -1
  125. package/dest/stats/stats.d.ts +2 -2
  126. package/dest/stats/stats.d.ts.map +1 -1
  127. package/dest/tests/factories.d.ts +1 -1
  128. package/dest/tests/factories.d.ts.map +1 -1
  129. package/dest/tests/factories.js +4 -3
  130. package/dest/tx/private_tx_constant_data.d.ts +18 -2
  131. package/dest/tx/private_tx_constant_data.d.ts.map +1 -1
  132. package/dest/tx/private_tx_constant_data.js +13 -4
  133. package/dest/tx/simulated_tx.d.ts +10 -6
  134. package/dest/tx/simulated_tx.d.ts.map +1 -1
  135. package/dest/world-state/genesis_data.d.ts +9 -1
  136. package/dest/world-state/genesis_data.d.ts.map +1 -1
  137. package/dest/world-state/genesis_data.js +1 -0
  138. package/package.json +8 -8
  139. package/src/block/attestation_info.ts +7 -2
  140. package/src/block/l2_block_source.ts +54 -2
  141. package/src/block/l2_block_stream/event_driven_l2_block_stream.ts +207 -0
  142. package/src/block/l2_block_stream/index.ts +1 -0
  143. package/src/block/l2_block_stream/interfaces.ts +35 -1
  144. package/src/block/l2_block_stream/l2_block_stream.ts +35 -62
  145. package/src/block/proposal/attestations_and_signers.ts +53 -7
  146. package/src/block/validate_block_result.ts +48 -2
  147. package/src/config/network-consensus-config.ts +2 -13
  148. package/src/contract/contract_address.ts +4 -3
  149. package/src/contract/contract_instance.ts +62 -1
  150. package/src/contract/interfaces/contract_instance.ts +26 -8
  151. package/src/contract/partial_address.ts +8 -2
  152. package/src/database-version/version_manager.ts +96 -6
  153. package/src/gas/README.md +108 -36
  154. package/src/ha-signing/config.ts +23 -5
  155. package/src/ha-signing/local_config.ts +20 -1
  156. package/src/hash/hash.ts +0 -8
  157. package/src/interfaces/archiver.ts +8 -0
  158. package/src/interfaces/configs.ts +3 -0
  159. package/src/interfaces/private_kernel_prover.ts +10 -12
  160. package/src/interfaces/proving-job.ts +15 -3
  161. package/src/interfaces/server_circuit_prover.ts +2 -2
  162. package/src/interfaces/world_state.ts +14 -0
  163. package/src/kernel/hints/find_private_kernel_reset_dimensions.ts +22 -112
  164. package/src/kernel/index.ts +1 -1
  165. package/src/kernel/private_circuit_public_inputs.ts +12 -1
  166. package/src/kernel/private_context_inputs.ts +10 -3
  167. package/src/kernel/private_kernel_circuit_public_inputs.ts +9 -0
  168. package/src/kernel/private_kernel_init_2_circuit_private_inputs.ts +0 -3
  169. package/src/kernel/private_kernel_init_3_circuit_private_inputs.ts +0 -3
  170. package/src/kernel/private_kernel_init_circuit_private_inputs.ts +0 -6
  171. package/src/kernel/private_kernel_reset_dimensions.ts +15 -5
  172. package/src/kernel/private_kernel_reset_tail_circuit_private_inputs.ts +69 -0
  173. package/src/keys/derivation.ts +69 -24
  174. package/src/logs/app_tagging_secret.ts +34 -27
  175. package/src/logs/index.ts +0 -2
  176. package/src/logs/shared_secret_derivation.ts +47 -14
  177. package/src/messaging/l1_to_l2_message.ts +51 -13
  178. package/src/p2p/attestation_utils.ts +8 -1
  179. package/src/p2p/checkpoint_attestation.ts +12 -0
  180. package/src/p2p/signature_utils.ts +9 -0
  181. package/src/rollup/avm_proof_data.ts +2 -2
  182. package/src/stats/stats.ts +2 -2
  183. package/src/tests/factories.ts +3 -6
  184. package/src/tx/private_tx_constant_data.ts +25 -2
  185. package/src/world-state/genesis_data.ts +10 -0
  186. package/dest/kernel/private_kernel_tail_circuit_private_inputs.d.ts +0 -47
  187. package/dest/kernel/private_kernel_tail_circuit_private_inputs.d.ts.map +0 -1
  188. package/dest/kernel/private_kernel_tail_circuit_private_inputs.js +0 -38
  189. package/dest/logs/message_context.d.ts +0 -17
  190. package/dest/logs/message_context.d.ts.map +0 -1
  191. package/dest/logs/message_context.js +0 -9
  192. package/dest/logs/pending_tagged_log.d.ts +0 -11
  193. package/dest/logs/pending_tagged_log.d.ts.map +0 -1
  194. package/dest/logs/pending_tagged_log.js +0 -4
  195. package/src/kernel/private_kernel_tail_circuit_private_inputs.ts +0 -58
  196. package/src/logs/message_context.ts +0 -18
  197. package/src/logs/pending_tagged_log.ts +0 -12
@@ -0,0 +1,207 @@
1
+ import { type Logger, createLogger } from '@aztec/foundation/log';
2
+ import { RunningPromise } from '@aztec/foundation/running-promise';
3
+
4
+ import type { BlockData } from '../block_data.js';
5
+ import type { L2Block } from '../l2_block.js';
6
+ import {
7
+ type ArchiverEmitter,
8
+ type BlockQuery,
9
+ type BlocksQuery,
10
+ type L2BlockSource,
11
+ type L2BlockSourceEventEmitter,
12
+ L2BlockSourceEvents,
13
+ type L2BlockSourceUpdatedEvent,
14
+ type L2Tips,
15
+ } from '../l2_block_source.js';
16
+ import { type L2BlockStreamEventHandler, type L2BlockStreamLocalDataProvider, localTipsMatch } from './interfaces.js';
17
+ import { L2BlockStream, type L2BlockStreamOptions, type L2BlockStreamSource } from './l2_block_stream.js';
18
+
19
+ /** Derives the metadata-only {@link BlockData} view of a hydrated {@link L2Block}. */
20
+ async function l2BlockToBlockData(block: L2Block): Promise<BlockData> {
21
+ return {
22
+ header: block.header,
23
+ archive: block.archive,
24
+ blockHash: await block.hash(),
25
+ checkpointNumber: block.checkpointNumber,
26
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
27
+ };
28
+ }
29
+
30
+ /** Returns the event emitter of a source that exposes one, or undefined for plain (e.g. RPC-backed) sources. */
31
+ function getEmitter(source: L2BlockSource | L2BlockSourceEventEmitter): ArchiverEmitter | undefined {
32
+ return 'events' in source ? source.events : undefined;
33
+ }
34
+
35
+ /** Fast-path context for a single sync pass: blocks to serve by number, plus the tips to report as the source's. */
36
+ type ActiveUpdate = { byNumber: Map<number, L2Block>; toTips: L2Tips };
37
+
38
+ /**
39
+ * Wraps a block source so a single sync pass can be served from blocks delivered by an aggregate update event,
40
+ * avoiding round-trips to archiver storage. The fast path is only armed (via {@link activate}) for a pass that is
41
+ * confirmed caught up to the event's pre-pass tips: in that case the event's `blocksAdded` contiguously cover the
42
+ * pass's download range and `toTips` is the exact post-pass tip, so `getL2Tips` can report it without querying the
43
+ * source. When the fast path is not armed, every read delegates to the source, so a stale or partial cache never
44
+ * changes the sync outcome.
45
+ */
46
+ class HotBlockSourceAdapter implements L2BlockStreamSource {
47
+ /** Set for the duration of one fast-path pass; undefined when reads must delegate to the source. */
48
+ private active: ActiveUpdate | undefined;
49
+
50
+ constructor(
51
+ private readonly source: L2BlockStreamSource,
52
+ private readonly log: Logger,
53
+ ) {}
54
+
55
+ /** Arms the fast path for the current pass: serve these blocks and report `toTips` as the source tips. */
56
+ public activate(blocks: readonly L2Block[], toTips: L2Tips): void {
57
+ const byNumber = new Map<number, L2Block>();
58
+ for (const block of blocks) {
59
+ byNumber.set(block.number, block);
60
+ }
61
+ this.active = { byNumber, toTips };
62
+ this.log.trace(`Armed hot-block fast path`, { blocks: byNumber.size, toTips });
63
+ }
64
+
65
+ /** Disarms the fast path so subsequent reads delegate to the source again. */
66
+ public deactivate(): void {
67
+ this.active = undefined;
68
+ }
69
+
70
+ public getL2Tips(): Promise<L2Tips> {
71
+ return this.active ? Promise.resolve(this.active.toTips) : this.source.getL2Tips();
72
+ }
73
+
74
+ public getBlocks(query: BlocksQuery): Promise<L2Block[]> {
75
+ const served = this.active ? this.tryServeBlocksFromCache(query) : undefined;
76
+ return served ? Promise.resolve(served) : this.source.getBlocks(query);
77
+ }
78
+
79
+ public getBlockData(query: BlockQuery): Promise<BlockData | undefined> {
80
+ if (this.active && 'number' in query) {
81
+ const block = this.active.byNumber.get(query.number);
82
+ if (block) {
83
+ return l2BlockToBlockData(block);
84
+ }
85
+ }
86
+ return this.source.getBlockData(query);
87
+ }
88
+
89
+ /** Serves a block range from the cache only when it is fully covered by contiguous cached blocks. */
90
+ private tryServeBlocksFromCache(query: BlocksQuery): L2Block[] | undefined {
91
+ // Only the by-range form is cacheable, and only for the full (not checkpointed-only) chain: the cache may hold
92
+ // uncheckpointed blocks that an onlyCheckpointed query must not receive.
93
+ if (!this.active || !('from' in query) || query.onlyCheckpointed) {
94
+ return undefined;
95
+ }
96
+ const from = query.from;
97
+ const to = from + query.limit - 1;
98
+ const blocks: L2Block[] = [];
99
+ for (let n = from; n <= to; n++) {
100
+ const block = this.active.byNumber.get(n);
101
+ if (!block) {
102
+ // A gap inside the requested range: bail out and let the source serve the whole range.
103
+ return undefined;
104
+ }
105
+ blocks.push(block);
106
+ }
107
+ return blocks;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Event-driven wrapper around {@link L2BlockStream}. Subscribes to the source's aggregate `l2BlockSourceUpdated`
113
+ * event (when the source exposes one) to trigger an immediate reconciliation, while keeping the periodic poll as
114
+ * the correctness fallback. Subsystems keep consuming the same {@link L2BlockStreamEvent}s; the archiver aggregate
115
+ * event is handled entirely here.
116
+ *
117
+ * The event is passed through to the sync pass via {@link RunningPromise.trigger}. If, at the time the pass runs,
118
+ * the stream's local tips match the event's `fromTips` (it is caught up to where the event began), the event's
119
+ * hydrated blocks are served back through a hot-block cache and the event's `toTips` is reported as the source tips
120
+ * — so the triggered sync re-reads neither block bodies nor tips from the archiver. Otherwise the pass delegates
121
+ * entirely to the source, and the periodic poll guarantees eventual catch-up.
122
+ */
123
+ export class EventDrivenL2BlockStream {
124
+ private readonly adapter: HotBlockSourceAdapter;
125
+ private readonly blockStream: L2BlockStream;
126
+ private readonly runningPromise: RunningPromise<L2BlockSourceUpdatedEvent>;
127
+ private readonly emitter: ArchiverEmitter | undefined;
128
+ private started = false;
129
+
130
+ private readonly onSourceUpdated = (event: L2BlockSourceUpdatedEvent) => {
131
+ // Fire-and-forget: trigger coalesces with any in-flight or periodic pass (see RunningPromise.trigger), so a
132
+ // burst of events does not run passes concurrently. Errors are swallowed by the inner stream's own handler.
133
+ void this.runningPromise
134
+ .trigger(event)
135
+ .catch(err => this.log.error(`Error in event-triggered block stream sync`, err));
136
+ };
137
+
138
+ constructor(
139
+ source: L2BlockSource | L2BlockSourceEventEmitter,
140
+ private readonly localData: L2BlockStreamLocalDataProvider,
141
+ handler: L2BlockStreamEventHandler,
142
+ private readonly log = createLogger('types:event_driven_block_stream'),
143
+ opts: L2BlockStreamOptions = {},
144
+ ) {
145
+ this.adapter = new HotBlockSourceAdapter(source, log);
146
+ // The inner stream's own RunningPromise is never started; this wrapper owns the polling loop and drives the
147
+ // stream through `sync()` (which runs `work()` directly when the inner loop is stopped).
148
+ this.blockStream = new L2BlockStream(this.adapter, localData, handler, log, opts);
149
+ this.runningPromise = new RunningPromise<L2BlockSourceUpdatedEvent>(
150
+ this.runPass.bind(this),
151
+ log,
152
+ opts.pollIntervalMS ?? 1000,
153
+ );
154
+ this.emitter = getEmitter(source);
155
+ }
156
+
157
+ public start(): void {
158
+ if (this.started) {
159
+ this.log.warn(`Attempted to start an already-started event-driven block stream`);
160
+ return;
161
+ }
162
+ this.started = true;
163
+ this.emitter?.on(L2BlockSourceEvents.L2BlockSourceUpdated, this.onSourceUpdated);
164
+ this.runningPromise.start();
165
+ }
166
+
167
+ public async stop(): Promise<void> {
168
+ this.started = false;
169
+ this.emitter?.off(L2BlockSourceEvents.L2BlockSourceUpdated, this.onSourceUpdated);
170
+ await this.runningPromise.stop();
171
+ await this.blockStream.stop();
172
+ }
173
+
174
+ public isRunning(): boolean {
175
+ return this.runningPromise.isRunning();
176
+ }
177
+
178
+ /**
179
+ * Runs a synchronization pass now, bypassing the poll interval, and resolves once that pass completes. Concurrent
180
+ * callers and periodic ticks coalesce onto a single pass; a caller that coalesces onto an already in-flight pass
181
+ * can resolve against a pass that began just before it, so this guarantees freshness only up to that coalescing
182
+ * window. The periodic poll and per-pass reorg handling make the gap a latency effect, never a correctness one.
183
+ */
184
+ public sync(): Promise<void> {
185
+ return this.runningPromise.trigger();
186
+ }
187
+
188
+ /**
189
+ * Runs a single pass over the underlying block stream. When triggered by an aggregate event and the stream is
190
+ * caught up to the event's pre-pass tips, the event's blocks and tips serve the pass directly; the fast path is
191
+ * always disarmed afterwards so a subsequent poll-driven pass reads from the source.
192
+ */
193
+ private async runPass(event?: L2BlockSourceUpdatedEvent): Promise<void> {
194
+ if (event) {
195
+ const localTips = await this.localData.getL2Tips();
196
+ if (localTipsMatch(localTips, event.fromTips)) {
197
+ this.adapter.activate(event.blocksAdded, event.toTips);
198
+ }
199
+ }
200
+
201
+ try {
202
+ await this.blockStream.sync();
203
+ } finally {
204
+ this.adapter.deactivate();
205
+ }
206
+ }
207
+ }
@@ -1,3 +1,4 @@
1
+ export * from './event_driven_l2_block_stream.js';
1
2
  export * from './interfaces.js';
2
3
  export * from './l2_block_stream.js';
3
4
  export * from './l2_tips_memory_store.js';
@@ -1,7 +1,7 @@
1
1
  import type { BlockNumber } from '@aztec/foundation/branded-types';
2
2
 
3
3
  import type { L2Block } from '../l2_block.js';
4
- import type { CheckpointId, L2BlockId, L2TipId, LocalL2Tips } from '../l2_block_source.js';
4
+ import type { CheckpointId, L2BlockId, L2TipId, L2Tips, LocalL2Tips } from '../l2_block_source.js';
5
5
 
6
6
  /** Provides the current chain tips. Implemented by world-state, l2-tips-store, and AztecNode. */
7
7
  export interface L2TipsProvider {
@@ -35,6 +35,40 @@ export interface L2BlockStreamLocalDataProvider {
35
35
  getL2BlockHash(number: number): Promise<string | undefined>;
36
36
  }
37
37
 
38
+ /**
39
+ * Returns whether a local block id differs from a source block id. Compares block number and, when the local hash is
40
+ * known, block hash. The hash comparison is skipped when the local hash is undefined: world-state legitimately
41
+ * reports `undefined` hashes for tips ahead of its synced range, and comparing against an undefined hash would treat
42
+ * such a tip as different on every poll. An `undefined` local block (no local tip yet) always counts as differing.
43
+ */
44
+ export function localBlockIdDiffers(localBlock: LocalL2BlockId | undefined, sourceBlock: L2BlockId): boolean {
45
+ if (localBlock === undefined) {
46
+ return true;
47
+ }
48
+ if (sourceBlock.number !== localBlock.number) {
49
+ return true;
50
+ }
51
+ if (localBlock.hash === undefined) {
52
+ return false;
53
+ }
54
+ return sourceBlock.hash !== localBlock.hash;
55
+ }
56
+
57
+ /**
58
+ * Returns whether the local chain tips agree with the given source tips on every tier the local provider exposes.
59
+ * Each tier is compared at the block level via {@link localBlockIdDiffers} (so an unresolved local hash matches on
60
+ * number alone); checkpoint ids are not compared, mirroring the stream's own tier reconciliation. The optional
61
+ * `checkpointed` tier is only compared when present (it is absent when the stream ignores checkpoints).
62
+ */
63
+ export function localTipsMatch(local: LocalChainTips, source: L2Tips): boolean {
64
+ return (
65
+ !localBlockIdDiffers(local.proposed, source.proposed) &&
66
+ (local.checkpointed === undefined || !localBlockIdDiffers(local.checkpointed.block, source.checkpointed.block)) &&
67
+ !localBlockIdDiffers(local.proven.block, source.proven.block) &&
68
+ !localBlockIdDiffers(local.finalized.block, source.finalized.block)
69
+ );
70
+ }
71
+
38
72
  /** Interface to a handler of events emitted. */
39
73
  export interface L2BlockStreamEventHandler {
40
74
  handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
@@ -4,22 +4,34 @@ import { createLogger } from '@aztec/foundation/log';
4
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
5
5
 
6
6
  import type { L2Block } from '../l2_block.js';
7
+ import { type L2BlockId, type L2BlockSource, type LocalL2Tips, makeL2BlockId } from '../l2_block_source.js';
7
8
  import {
8
- type L2BlockId,
9
- type L2BlockSource,
10
- type L2TipId,
11
- type LocalL2Tips,
12
- makeL2BlockId,
13
- } from '../l2_block_source.js';
14
- import type {
15
- L2BlockStreamEvent,
16
- L2BlockStreamEventHandler,
17
- L2BlockStreamLocalDataProvider,
18
- LocalL2BlockId,
9
+ type L2BlockStreamEvent,
10
+ type L2BlockStreamEventHandler,
11
+ type L2BlockStreamLocalDataProvider,
12
+ localBlockIdDiffers,
19
13
  } from './interfaces.js';
20
14
 
21
15
  /** Subset of the block source the stream depends on. Checkpoint payloads are no longer fetched here. */
22
- type L2BlockStreamSource = Pick<L2BlockSource, 'getBlocks' | 'getBlockData' | 'getL2Tips'>;
16
+ export type L2BlockStreamSource = Pick<L2BlockSource, 'getBlocks' | 'getBlockData' | 'getL2Tips'>;
17
+
18
+ /** Options accepted by {@link L2BlockStream} and {@link EventDrivenL2BlockStream}. */
19
+ export type L2BlockStreamOptions = {
20
+ pollIntervalMS?: number;
21
+ batchSize?: number;
22
+ startingBlock?: number;
23
+ /** Instead of downloading all blocks, only fetch the smallest subset that results in reliable reorg detection. */
24
+ skipFinalized?: boolean;
25
+ /** When true, checkpoint events will not be emitted. Blocks are still fetched but only blocks-added events are emitted. */
26
+ ignoreCheckpoints?: boolean;
27
+ /**
28
+ * When true, the block download loop is skipped entirely: `getBlocks` is never called and `blocks-added` is
29
+ * never emitted. Only the tip events (`chain-proposed`/`chain-checkpointed`/`chain-proven`/`chain-finalized`)
30
+ * and `chain-pruned` are emitted, driven by the `getL2Tips` snapshot. For consumers that track tips but never
31
+ * consume block payloads.
32
+ */
33
+ tipsOnly?: boolean;
34
+ };
23
35
 
24
36
  /** Creates a stream of events for new blocks, chain tips updates, and reorgs, out of polling an archiver or a node. */
25
37
  export class L2BlockStream {
@@ -32,22 +44,7 @@ export class L2BlockStream {
32
44
  private localData: L2BlockStreamLocalDataProvider,
33
45
  private handler: L2BlockStreamEventHandler,
34
46
  private readonly log = createLogger('types:block_stream'),
35
- private opts: {
36
- pollIntervalMS?: number;
37
- batchSize?: number;
38
- startingBlock?: number;
39
- /** Instead of downloading all blocks, only fetch the smallest subset that results in reliable reorg detection. */
40
- skipFinalized?: boolean;
41
- /** When true, checkpoint events will not be emitted. Blocks are still fetched but only blocks-added events are emitted. */
42
- ignoreCheckpoints?: boolean;
43
- /**
44
- * When true, the block download loop is skipped entirely: `getBlocks` is never called and `blocks-added` is
45
- * never emitted. Only the tip events (`chain-proposed`/`chain-checkpointed`/`chain-proven`/`chain-finalized`)
46
- * and `chain-pruned` are emitted, driven by the `getL2Tips` snapshot. For consumers that track tips but never
47
- * consume block payloads.
48
- */
49
- tipsOnly?: boolean;
50
- } = {},
47
+ private opts: L2BlockStreamOptions = {},
51
48
  ) {
52
49
  if (opts.tipsOnly && (opts.startingBlock !== undefined || opts.batchSize !== undefined || opts.skipFinalized)) {
53
50
  throw new Error(
@@ -181,19 +178,25 @@ export class L2BlockStream {
181
178
  // End-of-pass reconciliation: chain-proposed fires against the pre-pass baseline (a post-prune re-read would
182
179
  // equal the source tip and suppress it), then the tiers highest-to-lowest so the finalized <= proven <=
183
180
  // checkpointed <= proposed invariant holds mid-pass.
184
- if (this.blockTipDiffers(prePassProposed, sourceTips.proposed)) {
181
+ if (localBlockIdDiffers(prePassProposed, sourceTips.proposed)) {
185
182
  await this.emitEvent({ type: 'chain-proposed', block: sourceTips.proposed });
186
183
  }
187
184
 
188
185
  const reconcileTips = pruned ? await this.localData.getL2Tips() : localTips;
189
- if (!this.opts.ignoreCheckpoints && this.tipDiffers(reconcileTips.checkpointed?.block, sourceTips.checkpointed)) {
186
+ if (
187
+ !this.opts.ignoreCheckpoints &&
188
+ localBlockIdDiffers(reconcileTips.checkpointed?.block, sourceTips.checkpointed.block)
189
+ ) {
190
190
  await this.emitEvent({
191
191
  type: 'chain-checkpointed',
192
192
  block: sourceTips.checkpointed.block,
193
193
  checkpoint: sourceTips.checkpointed.checkpoint,
194
194
  });
195
195
  }
196
- if (reconcileTips.proven !== undefined && this.tipDiffers(reconcileTips.proven.block, sourceTips.proven)) {
196
+ if (
197
+ reconcileTips.proven !== undefined &&
198
+ localBlockIdDiffers(reconcileTips.proven.block, sourceTips.proven.block)
199
+ ) {
197
200
  await this.emitEvent({
198
201
  type: 'chain-proven',
199
202
  block: sourceTips.proven.block,
@@ -202,7 +205,7 @@ export class L2BlockStream {
202
205
  }
203
206
  if (
204
207
  reconcileTips.finalized !== undefined &&
205
- this.tipDiffers(reconcileTips.finalized.block, sourceTips.finalized)
208
+ localBlockIdDiffers(reconcileTips.finalized.block, sourceTips.finalized.block)
206
209
  ) {
207
210
  await this.emitEvent({
208
211
  type: 'chain-finalized',
@@ -286,36 +289,6 @@ export class L2BlockStream {
286
289
  return true;
287
290
  }
288
291
 
289
- /**
290
- * Returns whether the source tip differs from the local one and therefore warrants a tier event. Compares block
291
- * number and, when both hashes are known, block hash. The hash comparison is skipped when the local hash is
292
- * undefined or missing: world-state legitimately reports `undefined` hashes for tips ahead of its synced range,
293
- * and comparing against an undefined hash would re-emit the event on every poll.
294
- */
295
- private tipDiffers(localBlock: LocalL2BlockId | undefined, sourceTip: L2TipId): boolean {
296
- return this.blockTipDiffers(localBlock, sourceTip.block);
297
- }
298
-
299
- /**
300
- * Block-only variant of {@link tipDiffers} for the proposed tip (an {@link L2BlockId}, with no checkpoint). Compares
301
- * block number and, when the local hash is known, block hash. The hash comparison is skipped when the local hash is
302
- * undefined: world-state reports `undefined` for its proposed hash, and a strict comparison would re-emit
303
- * `chain-proposed` on every poll for it. ({@link L2TipsStoreBase} consumers always carry a hash, so the leniency is
304
- * inert for them.)
305
- */
306
- private blockTipDiffers(localBlock: LocalL2BlockId | undefined, sourceBlock: L2BlockId): boolean {
307
- if (localBlock === undefined) {
308
- return true;
309
- }
310
- if (sourceBlock.number !== localBlock.number) {
311
- return true;
312
- }
313
- if (localBlock.hash === undefined) {
314
- return false;
315
- }
316
- return sourceBlock.hash !== localBlock.hash;
317
- }
318
-
319
292
  /**
320
293
  * Returns whether the source and local agree on the block hash at a given height.
321
294
  * @param blockNumber - The block number to test.
@@ -1,5 +1,5 @@
1
1
  import type { ViemCommitteeAttestations } from '@aztec/ethereum/contracts';
2
- import { hexToBuffer } from '@aztec/foundation/string';
2
+ import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
3
3
 
4
4
  import { encodeAbiParameters, parseAbiParameters } from 'viem';
5
5
  import { z } from 'zod';
@@ -76,8 +76,10 @@ export class CommitteeAttestationsAndSigners implements Signable {
76
76
  let totalDataSize = 0;
77
77
  for (const attestation of viemAttestations) {
78
78
  const signature = attestation.signature;
79
- // Check if signature is empty (v = 0)
80
- const isEmpty = signature.v === 0;
79
+ // A slot is empty (a non-signing member, packed as its address) only when r, s and v are all zero
80
+ // matching Signature.isEmpty() and getSigners(), so the bitmap popcount and the signers list can
81
+ // never disagree (which would revert propose() with SignersSizeMismatch).
82
+ const isEmpty = signature.v === 0 && BigInt(signature.r) === 0n && BigInt(signature.s) === 0n;
81
83
 
82
84
  if (!isEmpty) {
83
85
  totalDataSize += 65; // v (1) + r (32) + s (32)
@@ -93,8 +95,8 @@ export class CommitteeAttestationsAndSigners implements Signable {
93
95
  for (const [i, attestation] of viemAttestations.entries()) {
94
96
  const signature = attestation.signature;
95
97
 
96
- // Check if signature is empty
97
- const isEmpty = signature.v === 0;
98
+ // Empty iff r, s and v are all zero (see the size-tally loop above).
99
+ const isEmpty = signature.v === 0 && BigInt(signature.r) === 0n && BigInt(signature.s) === 0n;
98
100
 
99
101
  if (!isEmpty) {
100
102
  // Set bit in bitmap (bit 7-0 in each byte, left to right)
@@ -102,8 +104,10 @@ export class CommitteeAttestationsAndSigners implements Signable {
102
104
  const bitIndex = 7 - (i % 8);
103
105
  signatureIndices[byteIndex] = (signatureIndices[byteIndex] ?? 0) | (1 << bitIndex);
104
106
 
105
- // Pack signature: v + r + s
106
- signaturesOrAddresses[dataIndex] = signature.v;
107
+ // Pack signature: v + r + s. Canonicalize a yParity recovery byte (v = 0/1) to 27/28 — it
108
+ // recovers to the same signer, but L1 ECDSA.recover only accepts 27/28. Any other value is left
109
+ // as-is so a genuinely malformed signature still fails on L1 rather than being silently rewritten.
110
+ signaturesOrAddresses[dataIndex] = signature.v === 0 || signature.v === 1 ? signature.v + 27 : signature.v;
107
111
  dataIndex++;
108
112
 
109
113
  // Pack r (32 bytes)
@@ -153,3 +157,45 @@ export class MaliciousCommitteeAttestationsAndSigners extends CommitteeAttestati
153
157
  return this.signers;
154
158
  }
155
159
  }
160
+
161
+ /**
162
+ * Malicious extension of CommitteeAttestationsAndSigners that rewrites every non-proposer signature slot's
163
+ * recovery byte to yParity form (v ∈ {0, 1}) in the packed output, after the honest `packAttestations` has
164
+ * already canonicalized it to v ∈ {27, 28}. Models a malicious selected proposer that hand-crafts
165
+ * `propose()` calldata L1 accepts but no honest node can byte-replay: each rewritten signature still
166
+ * recovers to the same member (r, s and the recovery parity are preserved), the bitmap bits stay set, and
167
+ * `getSigners()` stays consistent, so `propose()` does not revert `SignersSizeMismatch` -- yet the
168
+ * checkpoint can never be proven (`ECDSA.recover` rejects v ∉ {27, 28}). The proposer's own slot is left
169
+ * canonical so L1 `verifyProposer` (which recovers that slot) still accepts the checkpoint. For testing only.
170
+ */
171
+ export class MaliciousYParityCommitteeAttestationsAndSigners extends CommitteeAttestationsAndSigners {
172
+ constructor(
173
+ attestations: CommitteeAttestation[],
174
+ /** Committee index of the proposer's own slot, left canonical so `propose()` passes `verifyProposer`. */
175
+ private proposerIndex: number,
176
+ signatureContext: CoordinationSignatureContext,
177
+ ) {
178
+ super(attestations, signatureContext);
179
+ }
180
+
181
+ override getPackedAttestations(): ViemCommitteeAttestations {
182
+ const packed = super.getPackedAttestations();
183
+ const data = hexToBuffer(packed.signaturesOrAddresses);
184
+
185
+ // Walk the packed byte-vector and rewrite every non-proposer signed slot's v-byte to yParity form. A
186
+ // signed slot occupies 65 bytes (v, r, s); an empty slot occupies 20 bytes (address only).
187
+ let offset = 0;
188
+ for (let i = 0; i < this.attestations.length; i++) {
189
+ const isSigned = !this.attestations[i].signature.isEmpty();
190
+ if (isSigned && i !== this.proposerIndex) {
191
+ // `packAttestations` canonicalized v to 27/28; rewrite back to the equivalent yParity byte (0/1),
192
+ // preserving the recovery parity so the signature still recovers to the same member.
193
+ const v = data[offset];
194
+ data[offset] = v >= 27 ? v - 27 : v;
195
+ }
196
+ offset += isSigned ? 65 : 20;
197
+ }
198
+
199
+ return { signatureIndices: packed.signatureIndices, signaturesOrAddresses: bufferToHex(data) };
200
+ }
201
+ }
@@ -1,7 +1,9 @@
1
+ import type { ViemCommitteeAttestations } from '@aztec/ethereum/contracts';
1
2
  import { EpochNumber, EpochNumberSchema } from '@aztec/foundation/branded-types';
2
3
  import { EthAddress } from '@aztec/foundation/eth-address';
3
4
  import { type ZodFor, schemas } from '@aztec/foundation/schemas';
4
5
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
+ import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
5
7
 
6
8
  import { z } from 'zod';
7
9
 
@@ -30,6 +32,14 @@ export type ValidateCheckpointNegativeResult =
30
32
  attestors: EthAddress[];
31
33
  /** Committee attestations for this checkpoint as they were posted to L1 */
32
34
  attestations: CommitteeAttestation[];
35
+ /**
36
+ * The exact packed `CommitteeAttestations` tuple as posted to L1 calldata, carried verbatim so the
37
+ * invalidation evidence is byte-faithful to the stored `attestationsHash`. A repack via
38
+ * `packAttestations` is not a round-trip inverse of `fromPacked`, so it would diverge from the hash
39
+ * and revert `invalidateBadAttestation`/`invalidateInsufficientAttestations`. Always populated on the
40
+ * calldata validation path, the only production source of invalidation evidence.
41
+ */
42
+ verbatimAttestations: ViemCommitteeAttestations;
33
43
  /** Reason for the checkpoint being invalid: not enough attestations were posted */
34
44
  reason: 'insufficient-attestations';
35
45
  }
@@ -47,6 +57,12 @@ export type ValidateCheckpointNegativeResult =
47
57
  attestors: EthAddress[];
48
58
  /** Committee attestations for this checkpoint as they were posted to L1 */
49
59
  attestations: CommitteeAttestation[];
60
+ /**
61
+ * The exact packed `CommitteeAttestations` tuple as posted to L1 calldata, carried verbatim so the
62
+ * invalidation evidence is byte-faithful to the stored `attestationsHash`. See the same field on the
63
+ * insufficient-attestations variant for why a repack cannot be used.
64
+ */
65
+ verbatimAttestations: ViemCommitteeAttestations;
50
66
  /** Reason for the checkpoint being invalid: an invalid attestation was posted */
51
67
  reason: 'invalid-attestation';
52
68
  /** Index in the attestations array of the invalid attestation posted */
@@ -56,6 +72,12 @@ export type ValidateCheckpointNegativeResult =
56
72
  /** Result type for validating checkpoint attestations */
57
73
  export type ValidateCheckpointResult = { valid: true } | ValidateCheckpointNegativeResult;
58
74
 
75
+ /** Zod schema for the raw packed `CommitteeAttestations` viem tuple (two 0x-prefixed hex strings). */
76
+ const ViemCommitteeAttestationsSchema: ZodFor<ViemCommitteeAttestations> = z.object({
77
+ signatureIndices: schemas.HexStringWith0x,
78
+ signaturesOrAddresses: schemas.HexStringWith0x,
79
+ });
80
+
59
81
  export const ValidateCheckpointResultSchema: ZodFor<ValidateCheckpointResult> = z.union([
60
82
  z.object({ valid: z.literal(true) }),
61
83
  z.object({
@@ -66,6 +88,7 @@ export const ValidateCheckpointResultSchema: ZodFor<ValidateCheckpointResult> =
66
88
  seed: schemas.BigInt,
67
89
  attestors: z.array(schemas.EthAddress),
68
90
  attestations: z.array(CommitteeAttestation.schema),
91
+ verbatimAttestations: ViemCommitteeAttestationsSchema,
69
92
  reason: z.literal('insufficient-attestations'),
70
93
  }),
71
94
  z.object({
@@ -76,6 +99,7 @@ export const ValidateCheckpointResultSchema: ZodFor<ValidateCheckpointResult> =
76
99
  seed: schemas.BigInt,
77
100
  attestors: z.array(schemas.EthAddress),
78
101
  attestations: z.array(CommitteeAttestation.schema),
102
+ verbatimAttestations: ViemCommitteeAttestationsSchema,
79
103
  reason: z.literal('invalid-attestation'),
80
104
  invalidIndex: z.number(),
81
105
  }),
@@ -87,6 +111,8 @@ export function serializeValidateCheckpointResult(result: ValidateCheckpointResu
87
111
  }
88
112
 
89
113
  const checkpointBuffer = serializeCheckpointInfo(result.checkpoint);
114
+ const signatureIndices = hexToBuffer(result.verbatimAttestations.signatureIndices);
115
+ const signaturesOrAddresses = hexToBuffer(result.verbatimAttestations.signaturesOrAddresses);
90
116
  return serializeToBuffer(
91
117
  result.valid,
92
118
  result.reason,
@@ -101,6 +127,10 @@ export function serializeValidateCheckpointResult(result: ValidateCheckpointResu
101
127
  result.attestations.length,
102
128
  result.attestations,
103
129
  result.reason === 'invalid-attestation' ? result.invalidIndex : 0,
130
+ signatureIndices.length,
131
+ signatureIndices,
132
+ signaturesOrAddresses.length,
133
+ signaturesOrAddresses,
104
134
  );
105
135
  }
106
136
 
@@ -118,10 +148,26 @@ export function deserializeValidateCheckpointResult(bufferOrReader: Buffer | Buf
118
148
  const attestors = reader.readVector(EthAddress, MAX_COMMITTEE_SIZE);
119
149
  const attestations = reader.readVector(CommitteeAttestation, MAX_COMMITTEE_SIZE);
120
150
  const invalidIndex = reader.readNumber();
151
+ const verbatimAttestations: ViemCommitteeAttestations = {
152
+ signatureIndices: bufferToHex(reader.readBuffer()),
153
+ signaturesOrAddresses: bufferToHex(reader.readBuffer()),
154
+ };
155
+
121
156
  if (reason === 'insufficient-attestations') {
122
- return { valid, reason, checkpoint, committee, epoch, seed, attestors, attestations };
157
+ return { valid, reason, checkpoint, committee, epoch, seed, attestors, attestations, verbatimAttestations };
123
158
  } else if (reason === 'invalid-attestation') {
124
- return { valid, reason, checkpoint, committee, epoch, seed, attestors, invalidIndex, attestations };
159
+ return {
160
+ valid,
161
+ reason,
162
+ checkpoint,
163
+ committee,
164
+ epoch,
165
+ seed,
166
+ attestors,
167
+ invalidIndex,
168
+ attestations,
169
+ verbatimAttestations,
170
+ };
125
171
  } else {
126
172
  const _: never = reason;
127
173
  throw new Error(`Unknown reason: ${reason}`);
@@ -1,4 +1,4 @@
1
- import { type L1ContractsConfig, l1ContractsConfigMappings } from '@aztec/ethereum/config';
1
+ import { type L1ContractsConfig, l1ContractsConfigMappings, validateSlotDurations } from '@aztec/ethereum/config';
2
2
  import { type EnvVar, pickConfigMappings } from '@aztec/foundation/config';
3
3
 
4
4
  import type { SequencerConfig } from '../interfaces/configs.js';
@@ -156,21 +156,10 @@ export function validateNetworkConsensusConfig(config: NetworkConsensusConfig):
156
156
  return errors;
157
157
  }
158
158
 
159
- if (config.ethereumSlotDuration <= 0) {
160
- errors.push(`ethereumSlotDuration must be positive (got ${config.ethereumSlotDuration})`);
161
- }
159
+ errors.push(...validateSlotDurations(config));
162
160
  if (config.blockDurationMs <= 0) {
163
161
  errors.push(`blockDurationMs must be positive (got ${config.blockDurationMs})`);
164
162
  }
165
- if (config.aztecSlotDuration <= 0) {
166
- errors.push(`aztecSlotDuration must be positive (got ${config.aztecSlotDuration})`);
167
- }
168
- if (config.ethereumSlotDuration > 0 && config.aztecSlotDuration % config.ethereumSlotDuration !== 0) {
169
- errors.push(
170
- `aztecSlotDuration (${config.aztecSlotDuration}s) must be a multiple of ethereumSlotDuration ` +
171
- `(${config.ethereumSlotDuration}s)`,
172
- );
173
- }
174
163
  if (config.blockDurationMs / 1000 > config.aztecSlotDuration) {
175
164
  errors.push(
176
165
  `blockDurationMs (${config.blockDurationMs}ms) exceeds aztecSlotDuration (${config.aztecSlotDuration}s)`,
@@ -6,7 +6,8 @@ import { type FunctionAbi, FunctionSelector, encodeArguments } from '../abi/inde
6
6
  import type { AztecAddress } from '../aztec-address/index.js';
7
7
  import { computeVarArgsHash } from '../hash/hash.js';
8
8
  import { computeAddress } from '../keys/index.js';
9
- import type { ContractInstance } from './interfaces/contract_instance.js';
9
+ import type { ContractInstance, ContractInstancePreimage } from './interfaces/contract_instance.js';
10
+ import type { PartialAddress } from './partial_address.js';
10
11
 
11
12
  // TODO(@spalladino): Review all generator indices in this file
12
13
 
@@ -21,7 +22,7 @@ import type { ContractInstance } from './interfaces/contract_instance.js';
21
22
  */
22
23
  export async function computeContractAddressFromInstance(
23
24
  instance:
24
- | ContractInstance
25
+ | ContractInstancePreimage
25
26
  | ({ originalContractClassId: Fr; saltedInitializationHash: Fr } & Pick<ContractInstance, 'publicKeys'>),
26
27
  ): Promise<AztecAddress> {
27
28
  const partialAddress = await computePartialAddress(instance);
@@ -36,7 +37,7 @@ export async function computePartialAddress(
36
37
  instance:
37
38
  | Pick<ContractInstance, 'originalContractClassId' | 'initializationHash' | 'salt' | 'deployer' | 'immutablesHash'>
38
39
  | { originalContractClassId: Fr; saltedInitializationHash: Fr },
39
- ): Promise<Fr> {
40
+ ): Promise<PartialAddress> {
40
41
  const saltedInitializationHash =
41
42
  'saltedInitializationHash' in instance
42
43
  ? instance.saltedInitializationHash