@unlink-xyz/core 0.1.2 → 0.1.3-canary.0877bfe

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 (350) hide show
  1. package/dist/account/{zkAccount.d.ts → account.d.ts} +7 -5
  2. package/dist/account/account.d.ts.map +1 -0
  3. package/dist/account/{zkAccount.js → account.js} +57 -43
  4. package/dist/browser/index.js +108202 -0
  5. package/dist/browser/index.js.map +1 -0
  6. package/dist/circuits.json +74 -0
  7. package/dist/clients/broadcaster.d.ts +7 -2
  8. package/dist/clients/broadcaster.d.ts.map +1 -1
  9. package/dist/clients/broadcaster.js +9 -1
  10. package/dist/clients/http.d.ts +6 -0
  11. package/dist/clients/http.d.ts.map +1 -1
  12. package/dist/clients/http.js +24 -9
  13. package/dist/clients/indexer.d.ts +11 -0
  14. package/dist/clients/indexer.d.ts.map +1 -1
  15. package/dist/clients/indexer.js +40 -11
  16. package/dist/config.d.ts +28 -9
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +33 -26
  19. package/dist/constants.d.ts +6 -0
  20. package/dist/constants.d.ts.map +1 -0
  21. package/dist/constants.js +5 -0
  22. package/dist/core.d.ts.map +1 -1
  23. package/dist/core.js +5 -2
  24. package/dist/crypto-adapters/auto-init.d.ts +2 -0
  25. package/dist/crypto-adapters/auto-init.d.ts.map +1 -0
  26. package/dist/crypto-adapters/auto-init.js +7 -0
  27. package/dist/crypto-adapters/index.d.ts +22 -0
  28. package/dist/crypto-adapters/index.d.ts.map +1 -0
  29. package/dist/crypto-adapters/index.js +47 -0
  30. package/dist/crypto-adapters/polyfills.d.ts +5 -0
  31. package/dist/crypto-adapters/polyfills.d.ts.map +1 -0
  32. package/dist/crypto-adapters/polyfills.js +8 -0
  33. package/dist/errors.d.ts +9 -0
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/errors.js +18 -0
  36. package/dist/history/index.d.ts +3 -0
  37. package/dist/history/index.d.ts.map +1 -0
  38. package/dist/history/index.js +2 -0
  39. package/dist/history/service.d.ts +46 -0
  40. package/dist/history/service.d.ts.map +1 -0
  41. package/dist/history/service.js +354 -0
  42. package/dist/history/types.d.ts +21 -0
  43. package/dist/history/types.d.ts.map +1 -0
  44. package/dist/index.d.ts +12 -5
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +11 -4
  47. package/dist/keys/address.d.ts +13 -0
  48. package/dist/keys/address.d.ts.map +1 -0
  49. package/dist/keys/address.js +55 -0
  50. package/dist/keys/derive.d.ts +37 -0
  51. package/dist/keys/derive.d.ts.map +1 -0
  52. package/dist/keys/derive.js +112 -0
  53. package/dist/keys/hex.d.ts +17 -0
  54. package/dist/keys/hex.d.ts.map +1 -0
  55. package/dist/keys/hex.js +66 -0
  56. package/dist/keys/index.d.ts +5 -0
  57. package/dist/keys/index.d.ts.map +1 -0
  58. package/dist/keys/index.js +4 -0
  59. package/dist/keys/mnemonic.d.ts +8 -0
  60. package/dist/keys/mnemonic.d.ts.map +1 -0
  61. package/dist/keys/mnemonic.js +23 -0
  62. package/dist/keys.d.ts +4 -1
  63. package/dist/keys.d.ts.map +1 -1
  64. package/dist/keys.js +4 -0
  65. package/dist/prover/config.d.ts +1 -15
  66. package/dist/prover/config.d.ts.map +1 -1
  67. package/dist/prover/config.js +1 -11
  68. package/dist/prover/prover.d.ts +15 -4
  69. package/dist/prover/prover.d.ts.map +1 -1
  70. package/dist/prover/prover.js +115 -98
  71. package/dist/prover/registry.d.ts +3 -30
  72. package/dist/prover/registry.d.ts.map +1 -1
  73. package/dist/prover/registry.js +12 -51
  74. package/dist/state/merkle/hydrator.d.ts.map +1 -1
  75. package/dist/state/merkle/hydrator.js +3 -2
  76. package/dist/state/merkle/index.d.ts +1 -1
  77. package/dist/state/merkle/index.d.ts.map +1 -1
  78. package/dist/state/merkle/index.js +1 -1
  79. package/dist/state/merkle/merkle-tree.d.ts +8 -0
  80. package/dist/state/merkle/merkle-tree.d.ts.map +1 -1
  81. package/dist/state/merkle/merkle-tree.js +16 -7
  82. package/dist/state/store/ciphertext-store.d.ts +4 -0
  83. package/dist/state/store/ciphertext-store.d.ts.map +1 -1
  84. package/dist/state/store/ciphertext-store.js +12 -0
  85. package/dist/state/store/history-store.d.ts +24 -0
  86. package/dist/state/store/history-store.d.ts.map +1 -0
  87. package/dist/state/store/history-store.js +53 -0
  88. package/dist/state/store/index.d.ts +3 -2
  89. package/dist/state/store/index.d.ts.map +1 -1
  90. package/dist/state/store/index.js +1 -0
  91. package/dist/state/store/job-store.d.ts +7 -7
  92. package/dist/state/store/job-store.d.ts.map +1 -1
  93. package/dist/state/store/job-store.js +65 -39
  94. package/dist/state/store/jobs.d.ts +65 -18
  95. package/dist/state/store/jobs.d.ts.map +1 -1
  96. package/dist/state/store/leaf-store.d.ts.map +1 -1
  97. package/dist/state/store/leaf-store.js +0 -3
  98. package/dist/state/store/note-store.d.ts +7 -7
  99. package/dist/state/store/note-store.d.ts.map +1 -1
  100. package/dist/state/store/note-store.js +38 -34
  101. package/dist/state/store/nullifier-store.d.ts +9 -0
  102. package/dist/state/store/nullifier-store.d.ts.map +1 -1
  103. package/dist/state/store/nullifier-store.js +32 -2
  104. package/dist/state/store/records.d.ts +31 -2
  105. package/dist/state/store/records.d.ts.map +1 -1
  106. package/dist/state/store/root-store.d.ts.map +1 -1
  107. package/dist/state/store/root-store.js +0 -4
  108. package/dist/state/store/store.d.ts +61 -27
  109. package/dist/state/store/store.d.ts.map +1 -1
  110. package/dist/state/store/store.js +92 -1
  111. package/dist/storage/indexeddb.js +1 -1
  112. package/dist/storage/memory.d.ts.map +1 -1
  113. package/dist/storage/memory.js +5 -1
  114. package/dist/transactions/deposit.d.ts +12 -15
  115. package/dist/transactions/deposit.d.ts.map +1 -1
  116. package/dist/transactions/deposit.js +203 -152
  117. package/dist/transactions/index.d.ts +7 -4
  118. package/dist/transactions/index.d.ts.map +1 -1
  119. package/dist/transactions/index.js +7 -2
  120. package/dist/transactions/note-selection.d.ts +17 -0
  121. package/dist/transactions/note-selection.d.ts.map +1 -0
  122. package/dist/transactions/note-selection.js +201 -0
  123. package/dist/transactions/note-sync.d.ts +5 -33
  124. package/dist/transactions/note-sync.d.ts.map +1 -1
  125. package/dist/transactions/note-sync.js +320 -155
  126. package/dist/transactions/reconcile.d.ts +10 -12
  127. package/dist/transactions/reconcile.d.ts.map +1 -1
  128. package/dist/transactions/reconcile.js +53 -7
  129. package/dist/transactions/transact.d.ts +13 -24
  130. package/dist/transactions/transact.d.ts.map +1 -1
  131. package/dist/transactions/transact.js +393 -507
  132. package/dist/transactions/transaction-planner.d.ts +34 -0
  133. package/dist/transactions/transaction-planner.d.ts.map +1 -0
  134. package/dist/transactions/transaction-planner.js +116 -0
  135. package/dist/transactions/transfer-planner.d.ts +36 -0
  136. package/dist/transactions/transfer-planner.d.ts.map +1 -0
  137. package/dist/transactions/transfer-planner.js +85 -0
  138. package/dist/transactions/types/deposit.d.ts +67 -0
  139. package/dist/transactions/types/deposit.d.ts.map +1 -0
  140. package/dist/transactions/types/domain.d.ts +67 -0
  141. package/dist/transactions/types/domain.d.ts.map +1 -0
  142. package/dist/transactions/types/domain.js +4 -0
  143. package/dist/transactions/types/index.d.ts +18 -0
  144. package/dist/transactions/types/index.d.ts.map +1 -0
  145. package/dist/transactions/types/index.js +17 -0
  146. package/dist/transactions/types/options.d.ts +45 -0
  147. package/dist/transactions/types/options.d.ts.map +1 -0
  148. package/dist/transactions/types/options.js +1 -0
  149. package/dist/transactions/types/planning.d.ts +80 -0
  150. package/dist/transactions/types/planning.d.ts.map +1 -0
  151. package/dist/transactions/types/planning.js +1 -0
  152. package/dist/transactions/types/state-stores.d.ts +103 -0
  153. package/dist/transactions/types/state-stores.d.ts.map +1 -0
  154. package/dist/transactions/types/state-stores.js +1 -0
  155. package/dist/transactions/types/transact.d.ts +76 -0
  156. package/dist/transactions/types/transact.d.ts.map +1 -0
  157. package/dist/transactions/types/transact.js +1 -0
  158. package/dist/transactions/withdrawal-planner.d.ts +58 -0
  159. package/dist/transactions/withdrawal-planner.d.ts.map +1 -0
  160. package/dist/transactions/withdrawal-planner.js +128 -0
  161. package/dist/tsconfig.tsbuildinfo +1 -1
  162. package/dist/tsup.browser.config.d.ts +7 -0
  163. package/dist/tsup.browser.config.d.ts.map +1 -0
  164. package/dist/tsup.browser.config.js +34 -0
  165. package/dist/utils/amounts.d.ts +39 -0
  166. package/dist/utils/amounts.d.ts.map +1 -0
  167. package/dist/utils/amounts.js +89 -0
  168. package/dist/utils/async.d.ts +9 -0
  169. package/dist/utils/async.d.ts.map +1 -1
  170. package/dist/utils/async.js +24 -0
  171. package/dist/utils/bigint.js +7 -7
  172. package/dist/utils/crypto.d.ts +11 -5
  173. package/dist/utils/crypto.d.ts.map +1 -1
  174. package/dist/utils/crypto.js +12 -6
  175. package/dist/utils/format.d.ts +25 -0
  176. package/dist/utils/format.d.ts.map +1 -0
  177. package/dist/utils/format.js +33 -0
  178. package/dist/utils/json-codec.js +1 -1
  179. package/dist/utils/notes.d.ts +15 -0
  180. package/dist/utils/notes.d.ts.map +1 -0
  181. package/dist/utils/notes.js +14 -0
  182. package/dist/utils/polling.d.ts +5 -0
  183. package/dist/utils/polling.d.ts.map +1 -1
  184. package/dist/utils/polling.js +5 -0
  185. package/dist/utils/random.d.ts +13 -0
  186. package/dist/utils/random.d.ts.map +1 -0
  187. package/dist/utils/random.js +27 -0
  188. package/dist/utils/secure-memory.d.ts +25 -0
  189. package/dist/utils/secure-memory.d.ts.map +1 -0
  190. package/dist/utils/secure-memory.js +28 -0
  191. package/dist/utils/signature.d.ts +6 -0
  192. package/dist/utils/signature.d.ts.map +1 -1
  193. package/dist/utils/signature.js +8 -6
  194. package/dist/utils/validators.d.ts +21 -10
  195. package/dist/utils/validators.d.ts.map +1 -1
  196. package/dist/utils/validators.js +37 -11
  197. package/dist/vitest.config.d.ts +3 -0
  198. package/dist/vitest.config.d.ts.map +1 -0
  199. package/dist/vitest.config.js +13 -0
  200. package/package.json +28 -11
  201. package/.eslintrc.json +0 -4
  202. package/account/zkAccount.test.ts +0 -316
  203. package/account/zkAccount.ts +0 -222
  204. package/clients/broadcaster.ts +0 -67
  205. package/clients/http.ts +0 -94
  206. package/clients/indexer.ts +0 -150
  207. package/config.ts +0 -39
  208. package/core.ts +0 -17
  209. package/dist/account/railgun-imports-prototype.d.ts +0 -12
  210. package/dist/account/railgun-imports-prototype.d.ts.map +0 -1
  211. package/dist/account/railgun-imports-prototype.js +0 -30
  212. package/dist/account/zkAccount.d.ts.map +0 -1
  213. package/dist/key-derivation/babyjubjub.d.ts +0 -9
  214. package/dist/key-derivation/babyjubjub.d.ts.map +0 -1
  215. package/dist/key-derivation/babyjubjub.js +0 -9
  216. package/dist/key-derivation/bech32.d.ts +0 -22
  217. package/dist/key-derivation/bech32.d.ts.map +0 -1
  218. package/dist/key-derivation/bech32.js +0 -86
  219. package/dist/key-derivation/bip32.d.ts +0 -17
  220. package/dist/key-derivation/bip32.d.ts.map +0 -1
  221. package/dist/key-derivation/bip32.js +0 -41
  222. package/dist/key-derivation/bip39.d.ts +0 -22
  223. package/dist/key-derivation/bip39.d.ts.map +0 -1
  224. package/dist/key-derivation/bip39.js +0 -56
  225. package/dist/key-derivation/bytes.d.ts +0 -19
  226. package/dist/key-derivation/bytes.d.ts.map +0 -1
  227. package/dist/key-derivation/bytes.js +0 -92
  228. package/dist/key-derivation/hash.d.ts +0 -3
  229. package/dist/key-derivation/hash.d.ts.map +0 -1
  230. package/dist/key-derivation/hash.js +0 -10
  231. package/dist/key-derivation/index.d.ts +0 -8
  232. package/dist/key-derivation/index.d.ts.map +0 -1
  233. package/dist/key-derivation/index.js +0 -7
  234. package/dist/key-derivation/wallet-node.d.ts +0 -45
  235. package/dist/key-derivation/wallet-node.d.ts.map +0 -1
  236. package/dist/key-derivation/wallet-node.js +0 -109
  237. package/dist/state/ciphertext-store.d.ts +0 -12
  238. package/dist/state/ciphertext-store.d.ts.map +0 -1
  239. package/dist/state/ciphertext-store.js +0 -25
  240. package/dist/state/hydrator.d.ts +0 -16
  241. package/dist/state/hydrator.d.ts.map +0 -1
  242. package/dist/state/hydrator.js +0 -18
  243. package/dist/state/job-store.d.ts +0 -12
  244. package/dist/state/job-store.d.ts.map +0 -1
  245. package/dist/state/job-store.js +0 -118
  246. package/dist/state/jobs.d.ts +0 -50
  247. package/dist/state/jobs.d.ts.map +0 -1
  248. package/dist/state/jobs.js +0 -1
  249. package/dist/state/leaf-store.d.ts +0 -17
  250. package/dist/state/leaf-store.d.ts.map +0 -1
  251. package/dist/state/leaf-store.js +0 -35
  252. package/dist/state/merkle-tree.d.ts +0 -34
  253. package/dist/state/merkle-tree.d.ts.map +0 -1
  254. package/dist/state/merkle-tree.js +0 -104
  255. package/dist/state/note-store.d.ts +0 -37
  256. package/dist/state/note-store.d.ts.map +0 -1
  257. package/dist/state/note-store.js +0 -133
  258. package/dist/state/nullifier-store.d.ts +0 -13
  259. package/dist/state/nullifier-store.d.ts.map +0 -1
  260. package/dist/state/nullifier-store.js +0 -21
  261. package/dist/state/records.d.ts +0 -57
  262. package/dist/state/records.d.ts.map +0 -1
  263. package/dist/state/root-store.d.ts +0 -13
  264. package/dist/state/root-store.d.ts.map +0 -1
  265. package/dist/state/root-store.js +0 -30
  266. package/dist/state/store.d.ts +0 -26
  267. package/dist/state/store.d.ts.map +0 -1
  268. package/dist/state/store.js +0 -19
  269. package/dist/state.d.ts +0 -83
  270. package/dist/state.d.ts.map +0 -1
  271. package/dist/state.js +0 -171
  272. package/dist/transactions/shield.d.ts +0 -5
  273. package/dist/transactions/shield.d.ts.map +0 -1
  274. package/dist/transactions/shield.js +0 -93
  275. package/dist/transactions/types.d.ts +0 -114
  276. package/dist/transactions/types.d.ts.map +0 -1
  277. package/dist/transactions/utils.d.ts +0 -10
  278. package/dist/transactions/utils.d.ts.map +0 -1
  279. package/dist/transactions/utils.js +0 -17
  280. package/dist/utils/time.d.ts +0 -2
  281. package/dist/utils/time.d.ts.map +0 -1
  282. package/dist/utils/time.js +0 -3
  283. package/dist/utils/witness.d.ts +0 -11
  284. package/dist/utils/witness.d.ts.map +0 -1
  285. package/dist/utils/witness.js +0 -19
  286. package/errors.ts +0 -20
  287. package/index.ts +0 -17
  288. package/key-derivation/babyjubjub.ts +0 -11
  289. package/key-derivation/bech32.test.ts +0 -90
  290. package/key-derivation/bech32.ts +0 -124
  291. package/key-derivation/bip32.ts +0 -56
  292. package/key-derivation/bip39.ts +0 -76
  293. package/key-derivation/bytes.ts +0 -118
  294. package/key-derivation/hash.ts +0 -13
  295. package/key-derivation/index.ts +0 -7
  296. package/key-derivation/wallet-node.ts +0 -155
  297. package/keys.ts +0 -47
  298. package/prover/config.ts +0 -104
  299. package/prover/index.ts +0 -1
  300. package/prover/prover.integration.test.ts +0 -162
  301. package/prover/prover.test.ts +0 -309
  302. package/prover/prover.ts +0 -405
  303. package/prover/registry.test.ts +0 -90
  304. package/prover/registry.ts +0 -82
  305. package/schema.ts +0 -17
  306. package/setup-artifacts.sh +0 -57
  307. package/state/index.ts +0 -2
  308. package/state/merkle/hydrator.ts +0 -69
  309. package/state/merkle/index.ts +0 -12
  310. package/state/merkle/merkle-tree.test.ts +0 -50
  311. package/state/merkle/merkle-tree.ts +0 -163
  312. package/state/store/ciphertext-store.ts +0 -28
  313. package/state/store/index.ts +0 -24
  314. package/state/store/job-store.ts +0 -162
  315. package/state/store/jobs.ts +0 -64
  316. package/state/store/leaf-store.ts +0 -39
  317. package/state/store/note-store.ts +0 -177
  318. package/state/store/nullifier-store.ts +0 -39
  319. package/state/store/records.ts +0 -61
  320. package/state/store/root-store.ts +0 -34
  321. package/state/store/store.ts +0 -25
  322. package/state.test.ts +0 -235
  323. package/storage/index.ts +0 -3
  324. package/storage/indexeddb.test.ts +0 -99
  325. package/storage/indexeddb.ts +0 -235
  326. package/storage/memory.test.ts +0 -59
  327. package/storage/memory.ts +0 -93
  328. package/transactions/deposit.test.ts +0 -160
  329. package/transactions/deposit.ts +0 -227
  330. package/transactions/index.ts +0 -20
  331. package/transactions/note-sync.test.ts +0 -155
  332. package/transactions/note-sync.ts +0 -452
  333. package/transactions/reconcile.ts +0 -73
  334. package/transactions/transact.test.ts +0 -451
  335. package/transactions/transact.ts +0 -811
  336. package/transactions/types.ts +0 -141
  337. package/tsconfig.json +0 -14
  338. package/types/global.d.ts +0 -15
  339. package/types.ts +0 -24
  340. package/utils/async.ts +0 -15
  341. package/utils/bigint.ts +0 -34
  342. package/utils/crypto.test.ts +0 -69
  343. package/utils/crypto.ts +0 -58
  344. package/utils/json-codec.ts +0 -38
  345. package/utils/polling.ts +0 -6
  346. package/utils/signature.ts +0 -16
  347. package/utils/validators.test.ts +0 -64
  348. package/utils/validators.ts +0 -86
  349. /package/dist/{transactions → history}/types.js +0 -0
  350. /package/dist/{state/records.js → transactions/types/deposit.js} +0 -0
@@ -1,46 +1,18 @@
1
- import type { ZkAccount } from "../account/zkAccount.js";
2
- import type { FetchLike } from "../clients/http.js";
3
- import { createIndexerClient } from "../clients/indexer.js";
4
- import { type LeafRecord, type LocalMerkleTrees, type NoteRecord, type RootRecord } from "../state/index.js";
5
- export type NoteSyncStateStore = {
6
- putLeaf(record: LeafRecord): Promise<void>;
7
- getLeaf(chainId: number, index: number): Promise<LeafRecord | null>;
8
- clearLeaves(chainId: number): Promise<void>;
9
- putRoot(record: RootRecord): Promise<void>;
10
- putNote(record: NoteRecord): Promise<void>;
11
- getNote(chainId: number, index: number): Promise<NoteRecord | null>;
12
- markNoteSpent(chainId: number, index: number, spentAt?: number): Promise<NoteRecord>;
13
- putCiphertext(chainId: number, index: number, payload: Uint8Array): Promise<void>;
14
- countNullifiers(chainId: number): Promise<number>;
15
- putNullifier(record: {
16
- chainId: number;
17
- nullifier: string;
18
- noteIndex?: number;
19
- }): Promise<void>;
20
- };
21
- export type NoteSyncOptions = {
22
- merkleTrees?: LocalMerkleTrees;
23
- indexerClient?: ReturnType<typeof createIndexerClient>;
24
- fetch?: FetchLike;
25
- limit?: number;
26
- };
1
+ import type { Account } from "../account/account.js";
2
+ import type { NoteSyncOptions, NoteSyncStateStore } from "./types/index.js";
3
+ export type { NoteSyncOptions, NoteSyncStateStore };
27
4
  type ChainSyncStatus = {
28
5
  inFlight: boolean;
29
6
  lastSuccess: number | null;
30
7
  lastError?: string;
31
8
  };
32
9
  export declare function createNoteSyncService(stateStore: NoteSyncStateStore, options?: NoteSyncOptions): {
33
- sync: (chainId: number, account: ZkAccount, opts?: {
34
- start?: number;
35
- forceFullScan?: boolean;
36
- }) => Promise<NoteRecord[]>;
37
- syncChain: (chainId: number, account: ZkAccount, opts?: {
10
+ syncChain: (chainId: number, account: Account, opts?: {
38
11
  forceFullResync?: boolean;
39
12
  }) => Promise<void>;
40
- syncChains: (chainIds: number[], account: ZkAccount, opts?: {
13
+ syncChains: (chainIds: number[], account: Account, opts?: {
41
14
  forceFullResync?: boolean;
42
15
  }) => Promise<void>;
43
16
  getStatus: () => Map<number, ChainSyncStatus>;
44
17
  };
45
- export {};
46
18
  //# sourceMappingURL=note-sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"note-sync.d.ts","sourceRoot":"","sources":["../../transactions/note-sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAK5D,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC;AAQ3B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACpE,aAAa,CACX,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,aAAa,CACX,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,YAAY,CAAC,MAAM,EAAE;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;IACvD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,kBAAkB,EAC9B,OAAO,GAAE,eAAoB;oBAsTlB,MAAM,WACN,SAAS,SACX;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,KACjD,OAAO,CAAC,UAAU,EAAE,CAAC;yBAnEb,MAAM,WACN,SAAS,SACZ;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,KAClC,OAAO,CAAC,IAAI,CAAC;2BA+CJ,MAAM,EAAE,WACT,SAAS,SACX;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,KACnC,OAAO,CAAC,IAAI,CAAC;;EAqEjB"}
1
+ {"version":3,"file":"note-sync.d.ts","sourceRoot":"","sources":["../../transactions/note-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAkBrD,OAAO,KAAK,EAEV,eAAe,EACf,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAI1B,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC;AAEpD,KAAK,eAAe,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,kBAAkB,EAC9B,OAAO,GAAE,eAAoB;yBA8elB,MAAM,WACN,OAAO,SACV;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,KAClC,OAAO,CAAC,IAAI,CAAC;2BA6FJ,MAAM,EAAE,WACT,OAAO,SACT;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,KACnC,OAAO,CAAC,IAAI,CAAC;;EAejB"}
@@ -1,8 +1,8 @@
1
- import { poseidon } from "@railgun-community/circomlibjs";
2
1
  import { createIndexerClient } from "../clients/indexer.js";
3
- import { serviceConfig } from "../config.js";
4
- import { CoreError } from "../errors.js";
5
- import { ByteLength, ByteUtils } from "../key-derivation/bytes.js";
2
+ import { createServiceConfig } from "../config.js";
3
+ import { poseidon } from "../crypto-adapters/index.js";
4
+ import { CoreError, InitializationError } from "../errors.js";
5
+ import { FieldSize, Hex } from "../keys/hex.js";
6
6
  import { createMerkleTrees, rebuildTreeFromStore, } from "../state/index.js";
7
7
  import { formatUint256, parseHexToBigInt } from "../utils/bigint.js";
8
8
  import { decryptNote } from "../utils/crypto.js";
@@ -18,10 +18,10 @@ function buildCiphertext(bytes) {
18
18
  };
19
19
  }
20
20
  function encodeCiphertext(ciphertext) {
21
- const payload = new Uint8Array(ByteLength.UINT_256 * ciphertext.data.length);
21
+ const payload = new Uint8Array(FieldSize.SCALAR * ciphertext.data.length);
22
22
  ciphertext.data.forEach((value, index) => {
23
- const start = index * ByteLength.UINT_256;
24
- payload.set(ByteUtils.hexStringToBytes(formatUint256(value)), start);
23
+ const start = index * FieldSize.SCALAR;
24
+ payload.set(Hex.toBytes(formatUint256(value)), start);
25
25
  });
26
26
  return payload;
27
27
  }
@@ -34,22 +34,18 @@ export function createNoteSyncService(stateStore, options = {}) {
34
34
  if (!indexerClient) {
35
35
  const fetchImpl = options.fetch ?? (typeof fetch === "function" ? fetch : undefined);
36
36
  if (!fetchImpl) {
37
- throw new Error("fetch dependency is required to sync indexer commitments");
37
+ throw new InitializationError("fetch dependency is required to sync indexer commitments");
38
38
  }
39
+ if (!options.rpcUrl) {
40
+ throw new InitializationError("rpcUrl is required to sync indexer commitments");
41
+ }
42
+ const serviceConfig = createServiceConfig(options.rpcUrl);
39
43
  indexerClient = createIndexerClient(serviceConfig.indexerBaseUrl, {
40
44
  fetch: fetchImpl,
41
45
  });
42
46
  }
43
- // Count-based optimization: avoid per-nullifier lookups when counts are unchanged
44
- async function shouldQueryNullifiers(chainId) {
45
- const [onChainCount, localCount] = await Promise.all([
46
- indexerClient.getNullifierCount(chainId),
47
- stateStore.countNullifiers(chainId),
48
- ]);
49
- return onChainCount !== localCount;
50
- }
51
47
  function createNoteRecord(params) {
52
- const { chainId, index, commitment, token, amount, npk, mpk, random, nullifier, spentAt } = params;
48
+ const { chainId, index, commitment, token, amount, npk, mpk, random, nullifier, createdTxHash, createdEventType, createdAt, spentAt, spentTxHash, } = params;
53
49
  const base = {
54
50
  chainId,
55
51
  index,
@@ -60,26 +56,15 @@ export function createNoteSyncService(stateStore, options = {}) {
60
56
  mpk: formatUint256(mpk),
61
57
  random: formatUint256(random),
62
58
  nullifier,
59
+ createdTxHash,
60
+ createdEventType,
61
+ createdAt,
62
+ spentTxHash,
63
63
  };
64
64
  return spentAt === undefined ? base : { ...base, spentAt };
65
65
  }
66
- async function recordCommitment(chainId, record, allowRelaxedOrder = false) {
67
- const value = parseHexToBigInt(record.commitment);
68
- const currentLeafCount = trees.getLeafCount(chainId);
69
- if (record.index >= currentLeafCount) {
70
- const { index } = trees.addLeaf(chainId, value);
71
- if (index !== record.index && !allowRelaxedOrder) {
72
- throw new CoreError("indexed commitments out of order");
73
- }
74
- await stateStore.putLeaf({
75
- chainId,
76
- index: record.index,
77
- commitment: record.commitment,
78
- });
79
- }
80
- await stateStore.putRoot({ chainId, root: record.root });
81
- }
82
- async function tryStoreNote(chainId, record, account, shouldCheckNullifiers) {
66
+ // Try to decrypt a note. Returns the note record if successful, null otherwise.
67
+ function tryDecryptNoteRecord(chainId, record, account) {
83
68
  const ciphertext = buildCiphertext(record.ciphertext);
84
69
  let note;
85
70
  try {
@@ -101,9 +86,7 @@ export function createNoteSyncService(stateStore, options = {}) {
101
86
  }
102
87
  const nullifier = poseidon([account.nullifyingKey, BigInt(record.index)]);
103
88
  const nullifierHex = formatUint256(nullifier);
104
- const ciphertextBytes = encodeCiphertext(ciphertext);
105
- await stateStore.putCiphertext(chainId, record.index, ciphertextBytes);
106
- const baseNoteParams = {
89
+ return createNoteRecord({
107
90
  chainId,
108
91
  index: record.index,
109
92
  commitment: record.commitment,
@@ -113,65 +96,14 @@ export function createNoteSyncService(stateStore, options = {}) {
113
96
  mpk: account.masterPublicKey,
114
97
  random,
115
98
  nullifier: nullifierHex,
116
- };
117
- // Check if this note already exists in local storage
118
- const existingNote = await stateStore.getNote(chainId, record.index);
119
- // If note is already marked as spent locally, don't query indexer
120
- // to ask for nullifier because we already know it's spent
121
- if (existingNote?.spentAt !== undefined) {
122
- // Ensure nullifier is stored for count optimization (idempotent)
123
- await stateStore.putNullifier({
124
- chainId,
125
- nullifier: nullifierHex,
126
- noteIndex: record.index,
127
- });
128
- return existingNote;
129
- }
130
- // Skip indexer nullifier query if nullifier counts match
131
- if (!shouldCheckNullifiers) {
132
- if (existingNote) {
133
- return existingNote;
134
- }
135
- const stored = createNoteRecord(baseNoteParams);
136
- await stateStore.putNote(stored);
137
- return stored;
138
- }
139
- // Counts mismatched: query individual nullifier to check if this note was spent
140
- const onChainNullifier = await indexerClient.getNullifier({
141
- chainId,
142
- nullifier: nullifierHex,
99
+ createdTxHash: record.txHash,
100
+ createdEventType: record.eventType,
101
+ createdAt: record.insertedAt != null ? record.insertedAt * 1000 : undefined,
143
102
  });
144
- if (onChainNullifier) {
145
- const spentAtMs = onChainNullifier.spentAt * 1000;
146
- // Store nullifier
147
- await stateStore.putNullifier({
148
- chainId,
149
- nullifier: nullifierHex,
150
- noteIndex: record.index,
151
- });
152
- if (existingNote) {
153
- // Update existing unspent note to mark as spent
154
- await stateStore.markNoteSpent(chainId, record.index, spentAtMs);
155
- return existingNote;
156
- }
157
- const stored = createNoteRecord({
158
- ...baseNoteParams,
159
- spentAt: spentAtMs,
160
- });
161
- await stateStore.putNote(stored);
162
- return stored;
163
- }
164
- // Skip re-storing unspent notes that already exist
165
- if (existingNote) {
166
- return existingNote;
167
- }
168
- const stored = createNoteRecord(baseNoteParams);
169
- await stateStore.putNote(stored);
170
- return stored;
171
103
  }
172
- // Pulls commitments from the indexer starting at `start`, updating local tree and notes.
104
+ // Pulls commitments from the indexer, updating local tree and notes atomically.
105
+ // Uses batch nullifier check to avoid individual 404-generating queries.
173
106
  async function ingestFrom(chainId, start, account) {
174
- const shouldCheckNullifiers = await shouldQueryNullifiers(chainId);
175
107
  let cursor = start;
176
108
  for (;;) {
177
109
  const batch = await indexerClient.fetchCommitmentBatch({
@@ -182,40 +114,276 @@ export function createNoteSyncService(stateStore, options = {}) {
182
114
  if (batch.commitments.length === 0) {
183
115
  break;
184
116
  }
117
+ // Phase 1: Ingest commitments into tree + store
118
+ const decryptedNotes = [];
185
119
  for (const record of batch.commitments) {
186
120
  const expectedIndex = trees.getLeafCount(chainId);
187
121
  if (record.index !== expectedIndex) {
188
- throw new Error("indexed commitments out of order");
122
+ throw new CoreError(`indexed commitments out of order: expected index ${expectedIndex}, got ${record.index} (cursor=${cursor})`);
189
123
  }
190
- const value = ByteUtils.hexToBigInt(record.commitment);
124
+ const value = Hex.toBigInt(record.commitment);
191
125
  const { index } = trees.addLeaf(chainId, value);
192
126
  if (index !== record.index) {
193
- throw new Error("local merkle tree desynchronized");
127
+ throw new CoreError("local merkle tree desynchronized");
194
128
  }
195
- await stateStore.putLeaf({
196
- chainId,
197
- index: record.index,
198
- commitment: record.commitment,
129
+ const noteRecord = tryDecryptNoteRecord(chainId, record, account);
130
+ const ciphertext = buildCiphertext(record.ciphertext);
131
+ const ciphertextBytes = encodeCiphertext(ciphertext);
132
+ await stateStore.syncCommitment({
133
+ leaf: {
134
+ chainId,
135
+ index: record.index,
136
+ commitment: record.commitment,
137
+ txHash: record.txHash,
138
+ eventType: record.eventType,
139
+ insertedAt: record.insertedAt,
140
+ },
141
+ ciphertext: {
142
+ chainId,
143
+ index: record.index,
144
+ payload: ciphertextBytes,
145
+ },
146
+ root: { chainId, root: record.root },
147
+ note: noteRecord ?? undefined,
199
148
  });
200
- await stateStore.putRoot({ chainId, root: record.root });
201
- await tryStoreNote(chainId, record, account, shouldCheckNullifiers);
149
+ if (noteRecord) {
150
+ decryptedNotes.push(noteRecord);
151
+ }
202
152
  cursor = record.index + 1;
203
153
  }
204
- if (batch.commitments.length < limit) {
205
- break;
154
+ // Phase 2: Batch nullifier check for decrypted notes
155
+ if (decryptedNotes.length > 0) {
156
+ const notesToCheck = [];
157
+ for (const note of decryptedNotes) {
158
+ const existing = await stateStore.getNote(chainId, note.index);
159
+ if (existing?.spentAt !== undefined) {
160
+ await stateStore.putNullifier({
161
+ chainId,
162
+ nullifier: note.nullifier,
163
+ noteIndex: note.index,
164
+ });
165
+ }
166
+ else {
167
+ notesToCheck.push(note);
168
+ }
169
+ }
170
+ if (notesToCheck.length > 0) {
171
+ const spentNullifiers = await indexerClient.checkNullifiers({
172
+ chainId,
173
+ nullifiers: notesToCheck.map((n) => n.nullifier),
174
+ });
175
+ const spentMap = new Map(spentNullifiers.map((n) => [n.nullifier, n]));
176
+ for (const note of notesToCheck) {
177
+ const spent = spentMap.get(note.nullifier);
178
+ if (spent) {
179
+ const spentAtMs = spent.spentAt * 1000;
180
+ await stateStore.putNullifier({
181
+ chainId,
182
+ nullifier: note.nullifier,
183
+ noteIndex: note.index,
184
+ });
185
+ await stateStore.markNoteSpent(chainId, note.index, spentAtMs, spent.txHash);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ async function diagnoseChainState(chainId, localCount) {
193
+ // Tier 1: compare first commitment
194
+ const firstBatch = await indexerClient.fetchCommitmentBatch({
195
+ chainId,
196
+ start: 0,
197
+ limit: 1,
198
+ });
199
+ const localFirst = await stateStore.getLeaf(chainId, 0);
200
+ if (firstBatch.commitments.length === 0 || !localFirst) {
201
+ return { kind: "full-divergence" };
202
+ }
203
+ if (firstBatch.commitments[0].commitment !== localFirst.commitment) {
204
+ return { kind: "full-divergence" };
205
+ }
206
+ // Single-leaf shortcut: first matches and there's only one local leaf.
207
+ if (localCount === 1) {
208
+ return { kind: "consistent" };
209
+ }
210
+ // Compare last local commitment
211
+ const lastBatch = await indexerClient.fetchCommitmentBatch({
212
+ chainId,
213
+ start: localCount - 1,
214
+ limit: 1,
215
+ });
216
+ const localLast = await stateStore.getLeaf(chainId, localCount - 1);
217
+ if (lastBatch.commitments.length > 0 &&
218
+ localLast &&
219
+ lastBatch.commitments[0].commitment === localLast.commitment) {
220
+ return { kind: "consistent" };
221
+ }
222
+ // Tier 2: binary search for divergence point
223
+ const divergePoint = await findDivergencePoint(chainId, 1, localCount - 1);
224
+ return { kind: "partial-divergence", validPrefix: divergePoint };
225
+ }
226
+ async function findDivergencePoint(chainId, low, high) {
227
+ while (low < high) {
228
+ const mid = Math.floor((low + high) / 2);
229
+ const batch = await indexerClient.fetchCommitmentBatch({
230
+ chainId,
231
+ start: mid,
232
+ limit: 1,
233
+ });
234
+ const localLeaf = await stateStore.getLeaf(chainId, mid);
235
+ if (batch.commitments.length > 0 &&
236
+ localLeaf &&
237
+ batch.commitments[0].commitment === localLeaf.commitment) {
238
+ low = mid + 1;
239
+ }
240
+ else {
241
+ high = mid;
206
242
  }
207
243
  }
244
+ return low;
245
+ }
246
+ // Clears stale tail data and re-ingests from the divergence point.
247
+ async function partialResync(chainId, fromIndex, account) {
248
+ await Promise.all([
249
+ stateStore.clearChainDataFromIndex(chainId, fromIndex),
250
+ stateStore.clearNullifiersFromIndex(chainId, fromIndex),
251
+ ]);
252
+ trees.reset(chainId);
253
+ await rebuildTreeFromStore({
254
+ chainId,
255
+ trees,
256
+ loadLeaf: stateStore.getLeaf.bind(stateStore),
257
+ });
258
+ await ingestFrom(chainId, fromIndex, account);
208
259
  }
209
260
  // Clears local state for a chain and rehydrates fully from the indexer.
210
261
  async function fullResync(chainId, account) {
211
262
  trees.reset(chainId);
212
- await stateStore.clearLeaves(chainId);
263
+ await Promise.all([
264
+ stateStore.clearLeaves(chainId),
265
+ stateStore.clearNotes(chainId),
266
+ stateStore.clearCiphertexts(chainId),
267
+ stateStore.clearNullifiers(chainId),
268
+ ]);
213
269
  await ingestFrom(chainId, 0, account);
214
270
  }
271
+ // Decode a stored ciphertext (3 x 32-byte values) into a Ciphertext object
272
+ function decodeCiphertext(payload) {
273
+ const data = [];
274
+ for (let i = 0; i < 3; i++) {
275
+ const start = i * FieldSize.SCALAR;
276
+ const end = start + FieldSize.SCALAR;
277
+ const slice = payload.slice(start, end);
278
+ data.push(Hex.toBigInt(Hex.fromBytes(slice)));
279
+ }
280
+ return { data: data };
281
+ }
282
+ // Re-scan stored ciphertexts for a new account, using batch nullifier check.
283
+ async function rescanCiphertextsForAccount(chainId, account, leafCount) {
284
+ const accountMpk = formatUint256(account.masterPublicKey);
285
+ const pendingNotes = [];
286
+ for (let index = 0; index < leafCount; index++) {
287
+ const existingNote = await stateStore.getNote(chainId, index);
288
+ if (existingNote && existingNote.mpk === accountMpk) {
289
+ continue;
290
+ }
291
+ const ciphertextBytes = await stateStore.getCiphertext(chainId, index);
292
+ if (!ciphertextBytes)
293
+ continue;
294
+ const leaf = await stateStore.getLeaf(chainId, index);
295
+ if (!leaf)
296
+ continue;
297
+ const ciphertext = decodeCiphertext(ciphertextBytes);
298
+ let note;
299
+ try {
300
+ note = decryptNote(ciphertext, account.masterPublicKey);
301
+ }
302
+ catch (err) {
303
+ if (err instanceof CoreError)
304
+ continue;
305
+ throw err;
306
+ }
307
+ const npk = poseidon([account.masterPublicKey, note.random]);
308
+ const commitment = poseidon([npk, BigInt(note.token), note.amount]);
309
+ if (commitment !== parseHexToBigInt(leaf.commitment))
310
+ continue;
311
+ const nullifier = poseidon([account.nullifyingKey, BigInt(index)]);
312
+ const nullifierHex = formatUint256(nullifier);
313
+ const baseNoteParams = {
314
+ chainId,
315
+ index,
316
+ commitment: leaf.commitment,
317
+ token: note.token,
318
+ amount: note.amount,
319
+ npk,
320
+ mpk: account.masterPublicKey,
321
+ random: note.random,
322
+ nullifier: nullifierHex,
323
+ createdTxHash: leaf.txHash,
324
+ createdEventType: leaf.eventType,
325
+ createdAt: leaf.insertedAt != null ? leaf.insertedAt * 1000 : undefined,
326
+ };
327
+ pendingNotes.push({
328
+ note: createNoteRecord(baseNoteParams),
329
+ baseParams: baseNoteParams,
330
+ });
331
+ }
332
+ if (pendingNotes.length > 0) {
333
+ const spentNullifiers = await indexerClient.checkNullifiers({
334
+ chainId,
335
+ nullifiers: pendingNotes.map((p) => p.note.nullifier),
336
+ });
337
+ const spentMap = new Map(spentNullifiers.map((n) => [n.nullifier, n]));
338
+ for (const { note, baseParams } of pendingNotes) {
339
+ const spent = spentMap.get(note.nullifier);
340
+ if (spent) {
341
+ await stateStore.putNullifier({
342
+ chainId,
343
+ nullifier: note.nullifier,
344
+ noteIndex: note.index,
345
+ });
346
+ await stateStore.putNote(createNoteRecord({
347
+ ...baseParams,
348
+ spentAt: spent.spentAt * 1000,
349
+ spentTxHash: spent.txHash,
350
+ }));
351
+ }
352
+ else {
353
+ await stateStore.putNote(note);
354
+ }
355
+ }
356
+ }
357
+ }
358
+ // Check existing unspent notes for nullification using batch check.
359
+ async function checkExistingNotesForNullification(chainId, account) {
360
+ const accountMpk = formatUint256(account.masterPublicKey);
361
+ const unspentNotes = await stateStore.listNotes({
362
+ chainId,
363
+ mpk: accountMpk,
364
+ includeSpent: false,
365
+ });
366
+ if (unspentNotes.length === 0)
367
+ return;
368
+ const spentNullifiers = await indexerClient.checkNullifiers({
369
+ chainId,
370
+ nullifiers: unspentNotes.map((n) => n.nullifier),
371
+ });
372
+ const spentMap = new Map(spentNullifiers.map((n) => [n.nullifier, n]));
373
+ for (const note of unspentNotes) {
374
+ const spent = spentMap.get(note.nullifier);
375
+ if (spent) {
376
+ await stateStore.putNullifier({
377
+ chainId,
378
+ nullifier: note.nullifier,
379
+ noteIndex: note.index,
380
+ });
381
+ await stateStore.markNoteSpent(chainId, note.index, spent.spentAt * 1000, spent.txHash);
382
+ }
383
+ }
384
+ }
215
385
  /**
216
386
  * Syncs a single chain into local state, optionally forcing a full rebuild.
217
- * Skips overlapping runs via `inFlight` and falls back to full resync on
218
- * ordering/root inconsistencies.
219
387
  */
220
388
  async function syncChain(chainId, account, opts = {}) {
221
389
  ensureChainId(chainId);
@@ -223,9 +391,8 @@ export function createNoteSyncService(stateStore, options = {}) {
223
391
  inFlight: false,
224
392
  lastSuccess: null,
225
393
  };
226
- if (current.inFlight) {
394
+ if (current.inFlight)
227
395
  return;
228
- }
229
396
  status.set(chainId, { ...current, inFlight: true, lastError: undefined });
230
397
  try {
231
398
  const start = opts.forceFullResync
@@ -238,19 +405,57 @@ export function createNoteSyncService(stateStore, options = {}) {
238
405
  if (opts.forceFullResync) {
239
406
  await fullResync(chainId, account);
240
407
  }
408
+ else if (start > 0) {
409
+ try {
410
+ const diagnosis = await diagnoseChainState(chainId, start);
411
+ switch (diagnosis.kind) {
412
+ case "consistent":
413
+ try {
414
+ await ingestFrom(chainId, start, account);
415
+ }
416
+ catch (err) {
417
+ console.warn(`[note-sync] incremental ingest failed for chain ${chainId}, falling back to full resync:`, err);
418
+ await fullResync(chainId, account);
419
+ }
420
+ break;
421
+ case "partial-divergence":
422
+ await partialResync(chainId, diagnosis.validPrefix, account);
423
+ break;
424
+ case "full-divergence":
425
+ await fullResync(chainId, account);
426
+ break;
427
+ }
428
+ }
429
+ catch (err) {
430
+ console.warn(`[note-sync] chain diagnosis failed for chain ${chainId}, falling back to full resync:`, err);
431
+ await fullResync(chainId, account);
432
+ }
433
+ }
241
434
  else {
435
+ // start === 0, fresh first sync
242
436
  try {
243
- await ingestFrom(chainId, start, account);
437
+ await ingestFrom(chainId, 0, account);
244
438
  }
245
439
  catch (err) {
246
- // Fallback to full resync on ordering/root mismatch.
440
+ console.warn(`[note-sync] initial ingest failed for chain ${chainId}, falling back to full resync:`, err);
247
441
  await fullResync(chainId, account);
248
442
  }
249
443
  }
250
- status.set(chainId, {
251
- inFlight: false,
252
- lastSuccess: Date.now(),
253
- });
444
+ // Rescan ciphertexts for multi-account support
445
+ const accountMpk = formatUint256(account.masterPublicKey);
446
+ const leafCount = trees.getLeafCount(chainId);
447
+ if (leafCount > 0 && !opts.forceFullResync) {
448
+ const accountNotes = await stateStore.listNotes({
449
+ chainId,
450
+ mpk: accountMpk,
451
+ });
452
+ if (accountNotes.length < leafCount) {
453
+ await rescanCiphertextsForAccount(chainId, account, leafCount);
454
+ }
455
+ }
456
+ // Check if any existing unspent notes have been nullified
457
+ await checkExistingNotesForNullification(chainId, account);
458
+ status.set(chainId, { inFlight: false, lastSuccess: Date.now() });
254
459
  }
255
460
  catch (err) {
256
461
  status.set(chainId, {
@@ -269,50 +474,10 @@ export function createNoteSyncService(stateStore, options = {}) {
269
474
  await syncChain(chainId, account, opts);
270
475
  }
271
476
  }
272
- /**
273
- * Legacy sync method that returns synced notes.
274
- * Kept for backward compatibility.
275
- */
276
- async function sync(chainId, account, opts) {
277
- ensureChainId(chainId);
278
- await rebuildTreeFromStore({
279
- chainId,
280
- trees,
281
- loadLeaf: stateStore.getLeaf.bind(stateStore),
282
- });
283
- const synced = [];
284
- let start = opts?.start ?? 0;
285
- const shouldCheckNullifiers = await shouldQueryNullifiers(chainId);
286
- let keepFetching = true;
287
- while (keepFetching) {
288
- const batch = await indexerClient.fetchCommitmentBatch({
289
- chainId,
290
- start,
291
- limit,
292
- });
293
- if (batch.commitments.length === 0) {
294
- keepFetching = false;
295
- continue;
296
- }
297
- for (const record of batch.commitments) {
298
- await recordCommitment(chainId, record, !!opts?.forceFullScan);
299
- const storedNote = await tryStoreNote(chainId, record, account, shouldCheckNullifiers);
300
- if (storedNote) {
301
- synced.push(storedNote);
302
- }
303
- start = record.index + 1;
304
- }
305
- if (batch.commitments.length < limit) {
306
- keepFetching = false;
307
- }
308
- }
309
- return synced;
310
- }
311
477
  function getStatus() {
312
478
  return status;
313
479
  }
314
480
  return {
315
- sync,
316
481
  syncChain,
317
482
  syncChains,
318
483
  getStatus,
@@ -1,20 +1,18 @@
1
- import type { JobStatus, PendingJobKind } from "../state/index.js";
2
- import type { BaseStateStore, DepositSyncResult, TransactSyncResult } from "./types.js";
3
- type DepositClient = {
4
- syncPendingDeposit(relayId: string): Promise<DepositSyncResult>;
5
- };
6
- type TransactService = {
7
- syncPendingTransact(relayId: string): Promise<TransactSyncResult>;
8
- };
1
+ import type { JobKind, JobStatus } from "../state/index.js";
2
+ import type { BaseStateStore, DepositSyncResult, TransactSyncResult } from "./types/index.js";
9
3
  type ReconcilerDeps = {
10
4
  stateStore: BaseStateStore;
11
- depositClient: DepositClient;
12
- transactService: TransactService;
5
+ depositClient: {
6
+ syncPendingDeposit: (relayId: string) => Promise<DepositSyncResult>;
7
+ };
8
+ transactService: {
9
+ syncPendingTransact: (relayId: string) => Promise<TransactSyncResult>;
10
+ };
13
11
  };
14
12
  export declare function createJobReconciler(deps: ReconcilerDeps): {
15
- reconcileRelay: (relayId: string) => Promise<DepositSyncResult | TransactSyncResult>;
13
+ reconcileRelay: (relayId: string) => Promise<Error | DepositSyncResult | TransactSyncResult | null>;
16
14
  reconcileAll: (filter?: {
17
- kind?: PendingJobKind;
15
+ kind?: JobKind;
18
16
  statuses?: JobStatus[];
19
17
  }) => Promise<unknown[]>;
20
18
  };