@aztec/pxe 0.0.1-commit.b8a057fa → 0.0.1-commit.be03c316

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 (167) hide show
  1. package/dest/bin/check_oracle_version.js +3 -8
  2. package/dest/bin/index.d.ts +2 -2
  3. package/dest/bin/index.d.ts.map +1 -1
  4. package/dest/bin/index.js +1 -1
  5. package/dest/bin/oracle_version_helpers.d.ts +8 -25
  6. package/dest/bin/oracle_version_helpers.d.ts.map +1 -1
  7. package/dest/bin/oracle_version_helpers.js +11 -219
  8. package/dest/contract/helpers.d.ts +1 -1
  9. package/dest/contract/helpers.d.ts.map +1 -1
  10. package/dest/contract/helpers.js +3 -3
  11. package/dest/contract/skip_sync_contracts.d.ts +11 -0
  12. package/dest/contract/skip_sync_contracts.d.ts.map +1 -0
  13. package/dest/contract/skip_sync_contracts.js +18 -0
  14. package/dest/contract_function_simulator/aztec_node_read_cache.d.ts +39 -0
  15. package/dest/contract_function_simulator/aztec_node_read_cache.d.ts.map +1 -0
  16. package/dest/contract_function_simulator/aztec_node_read_cache.js +60 -0
  17. package/dest/contract_function_simulator/index.d.ts +3 -3
  18. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  19. package/dest/contract_function_simulator/index.js +1 -2
  20. package/dest/contract_function_simulator/noir-structs/resolved_tagging_strategy.d.ts +1 -1
  21. package/dest/contract_function_simulator/noir-structs/resolved_tagging_strategy.d.ts.map +1 -1
  22. package/dest/contract_function_simulator/noir-structs/resolved_tagging_strategy.js +7 -0
  23. package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts +7 -9
  24. package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts.map +1 -1
  25. package/dest/contract_function_simulator/noir-structs/resolved_tx.js +3 -44
  26. package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts +5 -4
  27. package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts.map +1 -1
  28. package/dest/contract_function_simulator/noir-structs/tx_effect_data.js +2 -2
  29. package/dest/contract_function_simulator/oracle/acir_callback.js +1 -1
  30. package/dest/contract_function_simulator/oracle/legacy_oracle_registry.d.ts +3 -3
  31. package/dest/contract_function_simulator/oracle/legacy_oracle_registry.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/oracle/legacy_oracle_registry.js +34 -1
  33. package/dest/contract_function_simulator/oracle/oracle_registry.d.ts +7 -4
  34. package/dest/contract_function_simulator/oracle/oracle_registry.d.ts.map +1 -1
  35. package/dest/contract_function_simulator/oracle/oracle_registry.js +40 -13
  36. package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts +87 -35
  37. package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts.map +1 -1
  38. package/dest/contract_function_simulator/oracle/oracle_type_mappings.js +286 -286
  39. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
  40. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  41. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +7 -10
  42. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +14 -8
  43. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  44. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +61 -34
  45. package/dest/entrypoints/client/bundle/index.d.ts +2 -1
  46. package/dest/entrypoints/client/bundle/index.d.ts.map +1 -1
  47. package/dest/entrypoints/client/bundle/index.js +1 -0
  48. package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
  49. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  50. package/dest/entrypoints/client/bundle/utils.js +10 -4
  51. package/dest/entrypoints/client/lazy/index.d.ts +2 -1
  52. package/dest/entrypoints/client/lazy/index.d.ts.map +1 -1
  53. package/dest/entrypoints/client/lazy/index.js +1 -0
  54. package/dest/entrypoints/client/lazy/utils.d.ts +1 -1
  55. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  56. package/dest/entrypoints/client/lazy/utils.js +10 -4
  57. package/dest/entrypoints/client/store.d.ts +14 -0
  58. package/dest/entrypoints/client/store.d.ts.map +1 -0
  59. package/dest/entrypoints/client/store.js +42 -0
  60. package/dest/entrypoints/server/index.d.ts +2 -1
  61. package/dest/entrypoints/server/index.d.ts.map +1 -1
  62. package/dest/entrypoints/server/index.js +1 -0
  63. package/dest/entrypoints/server/store.d.ts +18 -0
  64. package/dest/entrypoints/server/store.d.ts.map +1 -0
  65. package/dest/entrypoints/server/store.js +28 -0
  66. package/dest/entrypoints/server/utils.d.ts +1 -1
  67. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  68. package/dest/entrypoints/server/utils.js +8 -2
  69. package/dest/hooks/index.d.ts +2 -2
  70. package/dest/hooks/index.d.ts.map +1 -1
  71. package/dest/hooks/index.js +1 -0
  72. package/dest/hooks/resolve_tagging_secret_strategy.d.ts +3 -1
  73. package/dest/hooks/resolve_tagging_secret_strategy.d.ts.map +1 -1
  74. package/dest/hooks/resolve_tagging_secret_strategy.js +3 -3
  75. package/dest/logs/log_service.d.ts +3 -2
  76. package/dest/logs/log_service.d.ts.map +1 -1
  77. package/dest/logs/log_service.js +12 -3
  78. package/dest/messages/tx_resolver_service.d.ts +2 -2
  79. package/dest/messages/tx_resolver_service.d.ts.map +1 -1
  80. package/dest/messages/tx_resolver_service.js +7 -2
  81. package/dest/notes/note_service.d.ts +1 -1
  82. package/dest/notes/note_service.js +1 -1
  83. package/dest/oracle_version.d.ts +2 -2
  84. package/dest/oracle_version.js +3 -3
  85. package/dest/pxe.d.ts +1 -1
  86. package/dest/pxe.d.ts.map +1 -1
  87. package/dest/pxe.js +8 -5
  88. package/dest/storage/backwards_compatibility_tests/schema_tests.d.ts +1 -1
  89. package/dest/storage/backwards_compatibility_tests/schema_tests.d.ts.map +1 -1
  90. package/dest/storage/backwards_compatibility_tests/schema_tests.js +26 -2
  91. package/dest/storage/fact_store/index.d.ts +2 -2
  92. package/dest/storage/fact_store/index.d.ts.map +1 -1
  93. package/dest/storage/fact_store/index.js +1 -1
  94. package/dest/storage/fact_store/origin_state.d.ts +3 -1
  95. package/dest/storage/fact_store/origin_state.d.ts.map +1 -1
  96. package/dest/storage/fact_store/origin_state.js +7 -0
  97. package/dest/storage/metadata.d.ts +1 -1
  98. package/dest/storage/metadata.js +1 -1
  99. package/dest/storage/store_identity.d.ts +35 -0
  100. package/dest/storage/store_identity.d.ts.map +1 -0
  101. package/dest/storage/store_identity.js +44 -0
  102. package/dest/storage/tagging_store/sender_tagging_store.d.ts +19 -3
  103. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  104. package/dest/storage/tagging_store/sender_tagging_store.js +48 -11
  105. package/dest/tagging/constants.d.ts +3 -2
  106. package/dest/tagging/constants.d.ts.map +1 -1
  107. package/dest/tagging/constants.js +26 -5
  108. package/dest/tagging/index.d.ts +2 -2
  109. package/dest/tagging/index.d.ts.map +1 -1
  110. package/dest/tagging/index.js +1 -1
  111. package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts +9 -4
  112. package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts.map +1 -1
  113. package/dest/tagging/recipient_sync/sync_tagged_private_logs.js +60 -25
  114. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +1 -1
  115. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +1 -1
  116. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  117. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +6 -2
  118. package/dest/tagging/testing/tag_query_test_utils.d.ts +6 -0
  119. package/dest/tagging/testing/tag_query_test_utils.d.ts.map +1 -0
  120. package/dest/tagging/testing/tag_query_test_utils.js +10 -0
  121. package/package.json +17 -17
  122. package/src/bin/check_oracle_version.ts +3 -10
  123. package/src/bin/index.ts +1 -5
  124. package/src/bin/oracle_version_helpers.ts +11 -268
  125. package/src/contract/helpers.ts +3 -3
  126. package/src/contract/skip_sync_contracts.ts +23 -0
  127. package/src/contract_function_simulator/aztec_node_read_cache.ts +89 -0
  128. package/src/contract_function_simulator/index.ts +23 -3
  129. package/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts +8 -0
  130. package/src/contract_function_simulator/noir-structs/resolved_tx.ts +11 -45
  131. package/src/contract_function_simulator/noir-structs/tx_effect_data.ts +4 -3
  132. package/src/contract_function_simulator/oracle/acir_callback.ts +1 -1
  133. package/src/contract_function_simulator/oracle/legacy_oracle_registry.ts +30 -3
  134. package/src/contract_function_simulator/oracle/oracle_registry.ts +32 -11
  135. package/src/contract_function_simulator/oracle/oracle_type_mappings.ts +285 -197
  136. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +17 -12
  137. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +92 -39
  138. package/src/entrypoints/client/bundle/index.ts +1 -0
  139. package/src/entrypoints/client/bundle/utils.ts +15 -3
  140. package/src/entrypoints/client/lazy/index.ts +1 -0
  141. package/src/entrypoints/client/lazy/utils.ts +15 -3
  142. package/src/entrypoints/client/store.ts +54 -0
  143. package/src/entrypoints/server/index.ts +1 -0
  144. package/src/entrypoints/server/store.ts +47 -0
  145. package/src/entrypoints/server/utils.ts +21 -7
  146. package/src/hooks/index.ts +1 -0
  147. package/src/hooks/resolve_tagging_secret_strategy.ts +3 -0
  148. package/src/logs/log_service.ts +11 -2
  149. package/src/messages/tx_resolver_service.ts +8 -8
  150. package/src/notes/note_service.ts +1 -1
  151. package/src/oracle_version.ts +3 -3
  152. package/src/pxe.ts +8 -5
  153. package/src/storage/backwards_compatibility_tests/__snapshots__/RecipientTaggingStore.json +11 -3
  154. package/src/storage/backwards_compatibility_tests/__snapshots__/SenderTaggingStore.json +12 -4
  155. package/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json +1 -1
  156. package/src/storage/backwards_compatibility_tests/schema_tests.ts +30 -1
  157. package/src/storage/fact_store/index.ts +1 -0
  158. package/src/storage/fact_store/origin_state.ts +9 -0
  159. package/src/storage/metadata.ts +1 -1
  160. package/src/storage/store_identity.ts +72 -0
  161. package/src/storage/tagging_store/sender_tagging_store.ts +62 -15
  162. package/src/tagging/constants.ts +27 -5
  163. package/src/tagging/index.ts +1 -1
  164. package/src/tagging/recipient_sync/sync_tagged_private_logs.ts +103 -46
  165. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +1 -1
  166. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +6 -2
  167. package/src/tagging/testing/tag_query_test_utils.ts +11 -0
@@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import type { AztecNode } from '@aztec/stdlib/interfaces/server';
4
4
  import { type IndexedTxEffect, TxHash } from '@aztec/stdlib/tx';
5
5
 
6
- import { ResolvedTx } from '../contract_function_simulator/noir-structs/resolved_tx.js';
6
+ import type { ResolvedTx } from '../contract_function_simulator/noir-structs/resolved_tx.js';
7
7
 
8
8
  /** Resolves transaction hashes into their on-chain context (note hashes, first nullifier, and mined block). */
9
9
  export class TxResolverService {
@@ -57,13 +57,13 @@ export class TxResolverService {
57
57
  throw new Error(`Tx effect for ${txHash} has no nullifiers`);
58
58
  }
59
59
 
60
- return new ResolvedTx(
61
- data.txHash,
62
- data.noteHashes,
63
- data.nullifiers[0],
64
- txEffect.l2BlockNumber,
65
- txEffect.l2BlockHash.toFr(),
66
- );
60
+ return {
61
+ txHash: data.txHash,
62
+ uniqueNoteHashesInTx: data.noteHashes,
63
+ firstNullifierInTx: data.nullifiers[0],
64
+ blockNumber: txEffect.l2BlockNumber,
65
+ blockHash: txEffect.l2BlockHash.toFr(),
66
+ };
67
67
  });
68
68
  }
69
69
  }
@@ -26,7 +26,7 @@ export class NoteService {
26
26
  *
27
27
  * @param owner - The owner of the notes. If undefined, returns notes for all known owners.
28
28
  * @param status - The status of notes to fetch.
29
- * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
29
+ * @param scopes - The accounts whose notes we can access in this call. An empty list matches no notes.
30
30
  */
31
31
  public async getNotes(
32
32
  contractAddress: AztecAddress,
@@ -11,12 +11,12 @@
11
11
  /// if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency without actually
12
12
  /// using any of the new oracles then there is no reason to throw.
13
13
  export const ORACLE_VERSION_MAJOR = 30;
14
- export const ORACLE_VERSION_MINOR = 5;
14
+ export const ORACLE_VERSION_MINOR = 8;
15
15
 
16
- /// This hash is computed from the `ORACLE_REGISTRY` declaration (each oracle's name, ordered parameter names and
16
+ /// This hash is computed from `ORACLE_REGISTRY` (each oracle's name, ordered parameter names and
17
17
  /// types, and return type) and is used to detect when the oracle interface changes. When it does, you need to either:
18
18
  /// - increment `ORACLE_VERSION_MAJOR` and reset `ORACLE_VERSION_MINOR` to zero if the change is breaking, or
19
19
  /// - increment only `ORACLE_VERSION_MINOR` if the change is additive (a new oracle was added).
20
20
  ///
21
21
  /// These constants must be kept in sync between this file and `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
22
- export const ORACLE_INTERFACE_HASH = 'f71b9662ec851e74593764a87792b0a91c181e1410aac49a4507f0bba949f6be';
22
+ export const ORACLE_INTERFACE_HASH = 'fec4d3a95fc57a62a20bfeb659bb3bfd91b31cd93e07f8cf97b368f9f55cc67b';
package/src/pxe.ts CHANGED
@@ -37,7 +37,7 @@ import type {
37
37
  PrivateKernelTailCircuitPublicInputs,
38
38
  } from '@aztec/stdlib/kernel';
39
39
  import { computeAddressSecret } from '@aztec/stdlib/keys';
40
- import { deriveEcdhSharedSecretPoint } from '@aztec/stdlib/logs';
40
+ import { deriveEcdhSharedSecretPoint, protectFromForgery } from '@aztec/stdlib/logs';
41
41
  import {
42
42
  BlockHeader,
43
43
  type ContractOverrides,
@@ -890,9 +890,11 @@ export class PXE {
890
890
  }
891
891
 
892
892
  /**
893
- * Derives the shared secret of a handshake from its ephemeral public key: `S = addressSecret(recipient) * ephPk`,
894
- * the same derivation the recipient-side scan uses for handshakes discovered onchain. The ephemeral key is
895
- * reconstructed from its x-coordinate with a positive y, matching how the protocol generates it.
893
+ * Derives the shared secret of a handshake from its ephemeral public key: the raw ECDH secret
894
+ * `S = addressSecret(recipient) * ephPk` with the forgery protection applied,
895
+ * matching the protected secret the registry stores and the derivation the recipient-side scan uses for
896
+ * handshakes discovered onchain. The ephemeral key is reconstructed from its x-coordinate with a positive y,
897
+ * matching how the protocol generates it.
896
898
  *
897
899
  * @throws If `ephPk` is not the x-coordinate of a Grumpkin curve point, or if `recipient` is not an account of
898
900
  * this PXE, since the derivation needs its keys.
@@ -914,7 +916,8 @@ export class PXE {
914
916
 
915
917
  const ivskM = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
916
918
  const addressSecret = await computeAddressSecret(await completeAddress.getPreaddress(), ivskM);
917
- return deriveEcdhSharedSecretPoint(addressSecret, ephPkPoint);
919
+ const rawSecret = await deriveEcdhSharedSecretPoint(addressSecret, ephPkPoint);
920
+ return protectFromForgery(rawSecret, ephPkPoint, await recipient.toAddressPoint());
918
921
  }
919
922
 
920
923
  /** Registers a resolved shared secret scoped to a recipient. Returns whether it was newly added. */
@@ -1,17 +1,25 @@
1
1
  {
2
2
  "highest_aged_index": [
3
3
  {
4
- "key": "utf8:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
4
+ "key": "utf8:constrained:0x0000000000000000000000000000000000000000000000000000000000000013:0x0000000000000000000000000000000000000000000000000000000000000017",
5
+ "value": "num:13"
6
+ },
7
+ {
8
+ "key": "utf8:unconstrained:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
5
9
  "value": "num:13"
6
10
  }
7
11
  ],
8
12
  "highest_finalized_index": [
9
13
  {
10
- "key": "utf8:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
14
+ "key": "utf8:constrained:0x0000000000000000000000000000000000000000000000000000000000000013:0x0000000000000000000000000000000000000000000000000000000000000017",
15
+ "value": "num:11"
16
+ },
17
+ {
18
+ "key": "utf8:unconstrained:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
11
19
  "value": "num:11"
12
20
  },
13
21
  {
14
- "key": "utf8:0x0000000000000000000000000000000000000000000000000000000000000005:0x0000000000000000000000000000000000000000000000000000000000000007",
22
+ "key": "utf8:unconstrained:0x0000000000000000000000000000000000000000000000000000000000000005:0x0000000000000000000000000000000000000000000000000000000000000007",
15
23
  "value": "num:17"
16
24
  }
17
25
  ]
@@ -1,21 +1,29 @@
1
1
  {
2
2
  "pending_indexes": [
3
3
  {
4
- "key": "utf8:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
4
+ "key": "utf8:constrained:0x0000000000000000000000000000000000000000000000000000000000000013:0x0000000000000000000000000000000000000000000000000000000000000017",
5
+ "value": "[{\"lowestIndex\":4,\"highestIndex\":7,\"txHash\":\"0x0000000000000000000000000000000000000000000000000000000000000025\"}]"
6
+ },
7
+ {
8
+ "key": "utf8:unconstrained:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
5
9
  "value": "[{\"lowestIndex\":4,\"highestIndex\":7,\"txHash\":\"0x0000000000000000000000000000000000000000000000000000000000000013\"},{\"lowestIndex\":8,\"highestIndex\":11,\"txHash\":\"0x0000000000000000000000000000000000000000000000000000000000000017\"}]"
6
10
  },
7
11
  {
8
- "key": "utf8:0x000000000000000000000000000000000000000000000000000000000000000b:0x000000000000000000000000000000000000000000000000000000000000000d",
12
+ "key": "utf8:unconstrained:0x000000000000000000000000000000000000000000000000000000000000000b:0x000000000000000000000000000000000000000000000000000000000000000d",
9
13
  "value": "[{\"lowestIndex\":1,\"highestIndex\":9,\"txHash\":\"0x000000000000000000000000000000000000000000000000000000000000001d\"}]"
10
14
  }
11
15
  ],
12
16
  "last_finalized_indexes": [
13
17
  {
14
- "key": "utf8:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
18
+ "key": "utf8:constrained:0x0000000000000000000000000000000000000000000000000000000000000013:0x0000000000000000000000000000000000000000000000000000000000000017",
19
+ "value": "num:3"
20
+ },
21
+ {
22
+ "key": "utf8:unconstrained:0x0000000000000000000000000000000000000000000000000000000000000002:0x0000000000000000000000000000000000000000000000000000000000000003",
15
23
  "value": "num:3"
16
24
  },
17
25
  {
18
- "key": "utf8:0x0000000000000000000000000000000000000000000000000000000000000005:0x0000000000000000000000000000000000000000000000000000000000000007",
26
+ "key": "utf8:unconstrained:0x0000000000000000000000000000000000000000000000000000000000000005:0x0000000000000000000000000000000000000000000000000000000000000007",
19
27
  "value": "num:5"
20
28
  }
21
29
  ]
@@ -1,5 +1,5 @@
1
1
  {
2
- "schemaVersion": 12,
2
+ "schemaVersion": 13,
3
3
  "stores": [
4
4
  {
5
5
  "name": "capsules",
@@ -18,6 +18,7 @@ import { GasFees } from '@aztec/stdlib/gas';
18
18
  import { PublicKey, PublicKeys, deriveKeys } from '@aztec/stdlib/keys';
19
19
  import {
20
20
  AppTaggingSecret,
21
+ AppTaggingSecretKind,
21
22
  ContractClassLog,
22
23
  ContractClassLogFields,
23
24
  PrivateLog,
@@ -530,10 +531,18 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [
530
531
  const jobId = 'fixture-job';
531
532
  const secretA = new AppTaggingSecret(new Fr(2n), AztecAddress.fromBigIntUnsafe(3n));
532
533
  const secretB = new AppTaggingSecret(new Fr(5n), AztecAddress.fromBigIntUnsafe(7n));
534
+ // A constrained secret keys under the `constrained:` prefix, so the snapshot pins both kinds side by side.
535
+ const secretConstrained = new AppTaggingSecret(
536
+ new Fr(19n),
537
+ AztecAddress.fromBigIntUnsafe(23n),
538
+ AppTaggingSecretKind.CONSTRAINED,
539
+ );
533
540
 
534
541
  await recipientTaggingStore.updateHighestFinalizedIndex(secretA, 11, jobId);
535
542
  await recipientTaggingStore.updateHighestAgedIndex(secretA, 13, jobId);
536
543
  await recipientTaggingStore.updateHighestFinalizedIndex(secretB, 17, jobId);
544
+ await recipientTaggingStore.updateHighestFinalizedIndex(secretConstrained, 11, jobId);
545
+ await recipientTaggingStore.updateHighestAgedIndex(secretConstrained, 13, jobId);
537
546
  await kvStore.transactionAsync(() => recipientTaggingStore.commit(jobId));
538
547
  },
539
548
  snapshotStore: async kvStore => ({
@@ -584,10 +593,17 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [
584
593
  const secretA = new AppTaggingSecret(new Fr(2n), AztecAddress.fromBigIntUnsafe(3n));
585
594
  const secretB = new AppTaggingSecret(new Fr(5n), AztecAddress.fromBigIntUnsafe(7n));
586
595
  const secretC = new AppTaggingSecret(new Fr(11n), AztecAddress.fromBigIntUnsafe(13n));
596
+ const secretConstrained = new AppTaggingSecret(
597
+ new Fr(19n),
598
+ AztecAddress.fromBigIntUnsafe(23n),
599
+ AppTaggingSecretKind.CONSTRAINED,
600
+ );
587
601
  const txHashA = TxHash.fromBigInt(17n);
588
602
  const txHashB = TxHash.fromBigInt(19n);
589
603
  const txHashC = TxHash.fromBigInt(23n);
590
604
  const txHashD = TxHash.fromBigInt(29n);
605
+ const txHashE = TxHash.fromBigInt(31n);
606
+ const txHashF = TxHash.fromBigInt(37n);
591
607
 
592
608
  // secretA receives three pending ranges (one per tx); secretB receives one. After finalizing txHashA below,
593
609
  // secretA's array shrinks to two elements (the txHashB and txHashC ranges, both with highestIndex > 3) which
@@ -626,7 +642,20 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [
626
642
  jobId,
627
643
  );
628
644
 
629
- await senderTaggingStore.finalizePendingIndexes([txHashA], jobId);
645
+ // secretConstrained gets a finalized range (txHashE) plus a surviving higher pending range (txHashF), so the
646
+ // `constrained:` key lands in both pending_indexes and last_finalized_indexes.
647
+ await senderTaggingStore.storePendingIndexes(
648
+ [{ extendedSecret: secretConstrained, lowestIndex: 1, highestIndex: 3 }],
649
+ txHashE,
650
+ jobId,
651
+ );
652
+ await senderTaggingStore.storePendingIndexes(
653
+ [{ extendedSecret: secretConstrained, lowestIndex: 4, highestIndex: 7 }],
654
+ txHashF,
655
+ jobId,
656
+ );
657
+
658
+ await senderTaggingStore.finalizePendingIndexes([txHashA, txHashE], jobId);
630
659
 
631
660
  await kvStore.transactionAsync(() => senderTaggingStore.commit(jobId));
632
661
  },
@@ -5,6 +5,7 @@ export type { Fact } from './stored_fact.js';
5
5
  export {
6
6
  OriginBlockState,
7
7
  classifyOriginBlockState,
8
+ originBlockStateFromNumber,
8
9
  anchoredTipBlockNumbers,
9
10
  toFactWithOriginState,
10
11
  type TipBlockNumbers,
@@ -21,6 +21,15 @@ export enum OriginBlockState {
21
21
  Finalized = 3,
22
22
  }
23
23
 
24
+ /** Parses a numeric origin-block-state discriminant, rejecting unknown values. */
25
+ export function originBlockStateFromNumber(value: number): OriginBlockState {
26
+ if (!(value in OriginBlockState)) {
27
+ const validNames = Object.keys(OriginBlockState).filter(k => isNaN(Number(k)));
28
+ throw new Error(`Invalid OriginBlockState value ${value}, expected one of ${validNames.join(', ')}`);
29
+ }
30
+ return value as OriginBlockState;
31
+ }
32
+
24
33
  /** The two chain-tip block numbers needed to classify an origin block (`finalized <= proven` always holds). */
25
34
  export type TipBlockNumbers = { provenBlockNumber: number; finalizedBlockNumber: number };
26
35
 
@@ -1 +1 @@
1
- export const PXE_DATA_SCHEMA_VERSION = 12;
1
+ export const PXE_DATA_SCHEMA_VERSION = 13;
@@ -0,0 +1,72 @@
1
+ import type { EthAddress } from '@aztec/foundation/eth-address';
2
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
3
+ import { DatabaseVersion } from '@aztec/stdlib/database-version/version';
4
+
5
+ /** The triple that determine which physical store a logical store name maps to. */
6
+ export type StoreIdentity = {
7
+ /** Chain ID of the L1 the rollup is deployed to. */
8
+ l1ChainId: number;
9
+ /** Address of the rollup contract the store's data pertains to. */
10
+ rollupAddress: EthAddress;
11
+ /** Schema version of the data held in the store. */
12
+ schemaVersion: number;
13
+ };
14
+
15
+ /**
16
+ * Composes the store-name discriminator for a store identity. Two identities map to the same physical store iff
17
+ * their slugs are equal, so the format must stay stable: `<l1ChainId>-<rollupAddress>-v<schemaVersion>` — changing
18
+ * it orphans every existing store.
19
+ */
20
+ export function storeIdentitySlug({ l1ChainId, rollupAddress, schemaVersion }: StoreIdentity): string {
21
+ return `${l1ChainId}-${rollupAddress.toString()}-v${schemaVersion}`;
22
+ }
23
+
24
+ /** Composes the physical store name for a logical store name and identity. */
25
+ export function effectiveStoreName(name: string, identity: StoreIdentity): string {
26
+ return `${name}_${storeIdentitySlug(identity)}`;
27
+ }
28
+
29
+ /**
30
+ * Invariant check for stores whose physical name carries their identity: the recorded version can
31
+ * only disagree with the expected one if there is a store-naming bug.
32
+ */
33
+ export async function assertStoreIdentity(
34
+ store: AztecAsyncKVStore,
35
+ storeName: string,
36
+ identity: StoreIdentity,
37
+ ): Promise<void> {
38
+ const expected = new DatabaseVersion(identity.schemaVersion, identity.rollupAddress);
39
+ const singleton = store.openSingleton<string>('dbVersion');
40
+ const stored = await singleton.getAsync();
41
+ if (stored === undefined) {
42
+ await singleton.set(expected.toBuffer().toString('utf-8'));
43
+ return;
44
+ }
45
+ let storedVersion: DatabaseVersion;
46
+ try {
47
+ storedVersion = DatabaseVersion.fromBuffer(Buffer.from(stored, 'utf-8'));
48
+ } catch {
49
+ throw new StoreIdentityMismatchError(storeName, expected.toString(), stored);
50
+ }
51
+ if (!storedVersion.equals(expected)) {
52
+ throw new StoreIdentityMismatchError(storeName, expected.toString(), storedVersion.toString());
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Thrown when a store's recorded identity does not match the identity it was opened under. Since the identity is
58
+ * part of the physical store name, this can only indicate a store-naming bug; the store is left untouched.
59
+ */
60
+ export class StoreIdentityMismatchError extends Error {
61
+ constructor(
62
+ public readonly storeName: string,
63
+ public readonly expected: string,
64
+ public readonly actual: string,
65
+ ) {
66
+ super(
67
+ `Store '${storeName}' records identity ${actual} but was opened as ${expected}. ` +
68
+ `Refusing to open; data was NOT modified.`,
69
+ );
70
+ this.name = 'StoreIdentityMismatchError';
71
+ }
72
+ }
@@ -128,10 +128,11 @@ export class SenderTaggingStore implements StagedStore {
128
128
  }
129
129
 
130
130
  /**
131
- * Stores pending index ranges.
131
+ * Stores pending index ranges, rejecting any range that disagrees with an already-stored one.
132
132
  * @remarks If the same (secret, txHash) pair already exists in the db with an equal range, it's a no-op. This is
133
133
  * expected to happen because whenever we start sync we start from the last finalized index and we can have pending
134
- * ranges already stored from previous syncs. If the ranges differ, it throws an error as that indicates a bug.
134
+ * ranges already stored from previous syncs. If the ranges differ, it throws an error as that indicates a bug in
135
+ * callers that record indexes at prove time. Discovery from onchain logs must use `mergePendingIndexes` instead.
135
136
  * @param ranges - The tagging index ranges containing the directional app tagging secrets and the index ranges that are
136
137
  * to be stored in the db.
137
138
  * @param txHash - The tx in which the tagging indexes were used in private logs.
@@ -141,6 +142,33 @@ export class SenderTaggingStore implements StagedStore {
141
142
  * @throws If a different range already exists for the same (secret, txHash) pair.
142
143
  */
143
144
  storePendingIndexes(ranges: TaggingIndexRange[], txHash: TxHash, jobId: string): Promise<void> {
145
+ return this.#storePendingIndexes(ranges, txHash, jobId, false);
146
+ }
147
+
148
+ /**
149
+ * Stores pending index ranges, widening an existing entry for the same (secret, txHash) pair to the union of the
150
+ * stored and incoming ranges instead of throwing on a mismatch. Discovery from onchain logs needs this: it may see
151
+ * only the surviving (non-revertible phase) sub-range of a partially reverted tx recorded at prove time (the
152
+ * finalized receipt step of the sync resolves that difference), or indexes beyond a partially discovered entry
153
+ * when a tx from another PXE straddles a sync window boundary. Callers that record indexes at prove time must use
154
+ * `storePendingIndexes` instead, so that a range disagreement surfaces as a bug rather than being absorbed.
155
+ * @param ranges - The tagging index ranges containing the directional app tagging secrets and the index ranges that are
156
+ * to be stored in the db.
157
+ * @param txHash - The tx in which the tagging indexes were used in private logs.
158
+ * @param jobId - job context for staged writes to this store. See `JobCoordinator` for more details.
159
+ * @throws If the highestIndex is further than window length from the highest finalized index for the same secret.
160
+ * @throws If the lowestIndex is lower than or equal to the last finalized index for the same secret.
161
+ */
162
+ mergePendingIndexes(ranges: TaggingIndexRange[], txHash: TxHash, jobId: string): Promise<void> {
163
+ return this.#storePendingIndexes(ranges, txHash, jobId, true);
164
+ }
165
+
166
+ #storePendingIndexes(
167
+ ranges: TaggingIndexRange[],
168
+ txHash: TxHash,
169
+ jobId: string,
170
+ mergeExisting: boolean,
171
+ ): Promise<void> {
144
172
  if (ranges.length === 0) {
145
173
  return Promise.resolve();
146
174
  }
@@ -187,21 +215,40 @@ export class SenderTaggingStore implements StagedStore {
187
215
  // Check if an entry with the same txHash already exists
188
216
  const existingEntry = pendingData.find(entry => entry.txHash === txHashStr);
189
217
 
190
- if (existingEntry) {
191
- // Assert that the ranges are equal — different ranges for the same (secret, txHash) indicates a bug
192
- if (existingEntry.lowestIndex !== range.lowestIndex || existingEntry.highestIndex !== range.highestIndex) {
193
- throw new Error(
194
- `Conflicting range for secret ${secretStr} and txHash ${txHashStr}: ` +
195
- `existing [${existingEntry.lowestIndex}, ${existingEntry.highestIndex}] vs ` +
196
- `new [${range.lowestIndex}, ${range.highestIndex}]`,
197
- );
198
- }
199
- // Exact duplicate — skip
200
- } else {
201
- this.#writePendingIndexes(jobId, secretStr, [
218
+ let updatedPending: PendingIndexesEntry[] | undefined;
219
+ if (!existingEntry) {
220
+ updatedPending = [
202
221
  ...pendingData,
203
222
  { lowestIndex: range.lowestIndex, highestIndex: range.highestIndex, txHash: txHashStr },
204
- ]);
223
+ ];
224
+ } else if (mergeExisting) {
225
+ // Widen the entry to the union of both ranges, never shrink it: replacing would drop prove-time
226
+ // indexes the chain doesn't show (partially reverted tx), and skipping would drop onchain indexes
227
+ // discovered in a later sync window (tx straddling the window boundary).
228
+ const lowestIndex = Math.min(existingEntry.lowestIndex, range.lowestIndex);
229
+ const highestIndex = Math.max(existingEntry.highestIndex, range.highestIndex);
230
+ if (lowestIndex !== existingEntry.lowestIndex || highestIndex !== existingEntry.highestIndex) {
231
+ updatedPending = pendingData.map(entry =>
232
+ entry === existingEntry ? { lowestIndex, highestIndex, txHash: entry.txHash } : entry,
233
+ );
234
+ }
235
+ } else if (
236
+ existingEntry.lowestIndex !== range.lowestIndex ||
237
+ existingEntry.highestIndex !== range.highestIndex
238
+ ) {
239
+ // Different ranges for the same (secret, txHash) indicate a bug in callers that record indexes at prove
240
+ // time.
241
+ throw new Error(
242
+ `Conflicting range for secret ${secretStr} and txHash ${txHashStr}: ` +
243
+ `existing [${existingEntry.lowestIndex}, ${existingEntry.highestIndex}] vs ` +
244
+ `new [${range.lowestIndex}, ${range.highestIndex}]`,
245
+ );
246
+ }
247
+ // Remaining cases (a merge whose union equals the stored range, or an identical range without
248
+ // mergeExisting): duplicate evidence, nothing to write.
249
+
250
+ if (updatedPending) {
251
+ this.#writePendingIndexes(jobId, secretStr, updatedPending);
205
252
  }
206
253
  }
207
254
  });
@@ -1,8 +1,30 @@
1
1
  import { MAX_PRIVATE_LOGS_PER_TX } from '@aztec/constants';
2
2
 
3
- // This window has to cover the largest expected number of unfinalized logs emitted for a given directional app tagging
4
- // secret. If more tag indexes are consumed than this window, an error is thrown in `PXE::proveTx`.
3
+ // This window bounds how far a sender's next tag index for a directional app tagging secret can run ahead of that
4
+ // secret's highest finalized index before `PXE::proveTx` throws.
5
5
  //
6
- // Having a large window significantly slowed down `e2e_l1_with_wall_time` test as there we perform sync for more than
7
- // 1000 secrets, so keep this bounded to the per-tx private log limit.
8
- export const UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN = MAX_PRIVATE_LOGS_PER_TX;
6
+ // MAX_PRIVATE_LOGS_PER_TX is a floor for ordinary transaction patterns: the private kernel accumulates every
7
+ // surviving private log into a single tx-wide array of exactly this size, so a fresh secret's very first
8
+ // ordinary tx can consume that many indexes before anything is finalized.
9
+ //
10
+ // The +20 on top is headroom for multiple pending ordinary txs to the same counterparty stacking up
11
+ // before the first one is observed as mined. Only a run of near-MAX_PRIVATE_LOGS_PER_TX txs back-to-back would
12
+ // exhaust this; ordinary transfers use far fewer logs per tx, so the same margin covers many more of those. Keep the
13
+ // margin additive rather than another multiple of the floor. A larger window also makes recipient-side sync more
14
+ // expensive, since discovery has to probe ahead of the last finalized index for every active secret.
15
+ //
16
+ // TODO(F-783): MAX_PRIVATE_LOGS_PER_TX is not a hard ceiling in general — a tx that creates and nullifies (squashes)
17
+ // many notes/logs to the same secret can drive that secret's raw tag index arbitrarily higher, since indexes are
18
+ // reserved at log emission time, before squashing is decided, and the kernel's reset/squash loop is not bounded by
19
+ // MAX_PRIVATE_LOGS_PER_TX. No fixed window value closes that gap.
20
+ export const UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN = MAX_PRIVATE_LOGS_PER_TX + 20;
21
+
22
+ // The number of tags probed per constrained secret in the first round.
23
+ //
24
+ // The probe doubles each round (2, 4, 8, ..., capped at UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN) while every probed
25
+ // index is a hit, stopping at the first missing tag. Constrained delivery is gapless, so a single missing tag proves
26
+ // the stream has ended: at steady state this turns a full WINDOW_LEN probe into two tags. A secret K logs behind
27
+ // catches up in ~log2(K) round-trips while the probe is still doubling (2, 4, 8, 16, 32), but once it saturates the cap
28
+ // and advances WINDOW_LEN tags per round, deeper catch-up is linear at ~K/WINDOW_LEN rounds. Either way it beats both
29
+ // the full window every round and one round per log.
30
+ export const INITIAL_CONSTRAINED_PROBE_LEN = 2;
@@ -12,7 +12,7 @@
12
12
  export { syncTaggedPrivateLogs } from './recipient_sync/sync_tagged_private_logs.js';
13
13
  export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
14
14
  export { persistSenderTaggingIndexRangesForTx } from './persist_sender_tagging_index_ranges.js';
15
- export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
15
+ export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN, INITIAL_CONSTRAINED_PROBE_LEN } from './constants.js';
16
16
  export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
17
17
 
18
18
  // Re-export tagging-related types from stdlib