@openzeppelin/miden-multisig-client 0.17.0 → 0.18.0-rc.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 (146) hide show
  1. package/README.md +159 -46
  2. package/dist/account/builder.d.ts +4 -4
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +17 -7
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/layout.d.ts +5 -5
  7. package/dist/account/layout.d.ts.map +1 -1
  8. package/dist/account/layout.js +5 -5
  9. package/dist/account/layout.js.map +1 -1
  10. package/dist/client.d.ts +18 -1
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +79 -6
  13. package/dist/client.js.map +1 -1
  14. package/dist/index.d.ts +6 -6
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +5 -5
  17. package/dist/index.js.map +1 -1
  18. package/dist/multisig/authArgErrors.d.ts +14 -31
  19. package/dist/multisig/authArgErrors.d.ts.map +1 -1
  20. package/dist/multisig/authArgErrors.js +22 -46
  21. package/dist/multisig/authArgErrors.js.map +1 -1
  22. package/dist/multisig/consumeNotesErrors.d.ts +13 -1
  23. package/dist/multisig/consumeNotesErrors.d.ts.map +1 -1
  24. package/dist/multisig/consumeNotesErrors.js +17 -0
  25. package/dist/multisig/consumeNotesErrors.js.map +1 -1
  26. package/dist/multisig/signing.d.ts +1 -1
  27. package/dist/multisig/signing.d.ts.map +1 -1
  28. package/dist/multisig/signing.js +8 -3
  29. package/dist/multisig/signing.js.map +1 -1
  30. package/dist/multisig.d.ts +118 -11
  31. package/dist/multisig.d.ts.map +1 -1
  32. package/dist/multisig.js +421 -141
  33. package/dist/multisig.js.map +1 -1
  34. package/dist/procedures.d.ts +7 -7
  35. package/dist/procedures.js +7 -7
  36. package/dist/proposal/factory.d.ts.map +1 -1
  37. package/dist/proposal/factory.js +7 -0
  38. package/dist/proposal/factory.js.map +1 -1
  39. package/dist/raw-client.d.ts +1 -0
  40. package/dist/raw-client.d.ts.map +1 -1
  41. package/dist/raw-client.js +10 -2
  42. package/dist/raw-client.js.map +1 -1
  43. package/dist/recovery/publicNoteBackfill.js +1 -1
  44. package/dist/recovery/publicNoteBackfill.js.map +1 -1
  45. package/dist/retry/classify.d.ts +3 -0
  46. package/dist/retry/classify.d.ts.map +1 -1
  47. package/dist/retry/classify.js +2 -2
  48. package/dist/retry/classify.js.map +1 -1
  49. package/dist/signer.d.ts +1 -0
  50. package/dist/signer.d.ts.map +1 -1
  51. package/dist/signer.js +1 -0
  52. package/dist/signer.js.map +1 -1
  53. package/dist/signers/index.d.ts +1 -0
  54. package/dist/signers/index.d.ts.map +1 -1
  55. package/dist/signers/index.js +1 -0
  56. package/dist/signers/index.js.map +1 -1
  57. package/dist/signers/ledger.d.ts +25 -0
  58. package/dist/signers/ledger.d.ts.map +1 -0
  59. package/dist/signers/ledger.js +96 -0
  60. package/dist/signers/ledger.js.map +1 -0
  61. package/dist/state/adopt.d.ts +45 -0
  62. package/dist/state/adopt.d.ts.map +1 -0
  63. package/dist/state/adopt.js +101 -0
  64. package/dist/state/adopt.js.map +1 -0
  65. package/dist/transaction/authArgs.d.ts +57 -0
  66. package/dist/transaction/authArgs.d.ts.map +1 -0
  67. package/dist/transaction/authArgs.js +108 -0
  68. package/dist/transaction/authArgs.js.map +1 -0
  69. package/dist/transaction/consumeNotes.d.ts +9 -5
  70. package/dist/transaction/consumeNotes.d.ts.map +1 -1
  71. package/dist/transaction/consumeNotes.js +8 -23
  72. package/dist/transaction/consumeNotes.js.map +1 -1
  73. package/dist/transaction/noteAuthentication.d.ts +39 -0
  74. package/dist/transaction/noteAuthentication.d.ts.map +1 -0
  75. package/dist/transaction/noteAuthentication.js +94 -0
  76. package/dist/transaction/noteAuthentication.js.map +1 -0
  77. package/dist/transaction/options.d.ts +25 -0
  78. package/dist/transaction/options.d.ts.map +1 -1
  79. package/dist/transaction/p2id.d.ts +3 -2
  80. package/dist/transaction/p2id.d.ts.map +1 -1
  81. package/dist/transaction/p2id.js +36 -27
  82. package/dist/transaction/p2id.js.map +1 -1
  83. package/dist/transaction/summary.d.ts +41 -15
  84. package/dist/transaction/summary.d.ts.map +1 -1
  85. package/dist/transaction/summary.js +71 -19
  86. package/dist/transaction/summary.js.map +1 -1
  87. package/dist/transaction/updateGuardian.d.ts +3 -3
  88. package/dist/transaction/updateGuardian.d.ts.map +1 -1
  89. package/dist/transaction/updateGuardian.js +5 -16
  90. package/dist/transaction/updateGuardian.js.map +1 -1
  91. package/dist/transaction/updateProcedureThreshold.d.ts +3 -3
  92. package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
  93. package/dist/transaction/updateProcedureThreshold.js +6 -16
  94. package/dist/transaction/updateProcedureThreshold.js.map +1 -1
  95. package/dist/transaction/updateSigners.d.ts +3 -3
  96. package/dist/transaction/updateSigners.d.ts.map +1 -1
  97. package/dist/transaction/updateSigners.js +9 -16
  98. package/dist/transaction/updateSigners.js.map +1 -1
  99. package/dist/transaction.d.ts +3 -2
  100. package/dist/transaction.d.ts.map +1 -1
  101. package/dist/transaction.js +3 -2
  102. package/dist/transaction.js.map +1 -1
  103. package/dist/types/proposal.d.ts +31 -0
  104. package/dist/types/proposal.d.ts.map +1 -1
  105. package/dist/types/proposal.js +8 -0
  106. package/dist/types/proposal.js.map +1 -1
  107. package/dist/utils/eip712.d.ts +80 -0
  108. package/dist/utils/eip712.d.ts.map +1 -0
  109. package/dist/utils/eip712.js +49 -0
  110. package/dist/utils/eip712.js.map +1 -0
  111. package/dist/utils/signature.d.ts +4 -0
  112. package/dist/utils/signature.d.ts.map +1 -1
  113. package/dist/utils/signature.js +49 -1
  114. package/dist/utils/signature.js.map +1 -1
  115. package/package.json +11 -6
  116. package/src/account/builder.ts +18 -7
  117. package/src/account/layout.ts +5 -5
  118. package/src/client.ts +94 -6
  119. package/src/index.ts +21 -3
  120. package/src/multisig/authArgErrors.ts +23 -56
  121. package/src/multisig/consumeNotesErrors.ts +20 -1
  122. package/src/multisig/signing.ts +8 -2
  123. package/src/multisig.ts +530 -183
  124. package/src/procedures.ts +7 -7
  125. package/src/proposal/factory.ts +7 -0
  126. package/src/raw-client.ts +11 -7
  127. package/src/recovery/publicNoteBackfill.ts +1 -1
  128. package/src/retry/classify.ts +3 -3
  129. package/src/signer.ts +1 -0
  130. package/src/signers/index.ts +1 -0
  131. package/src/signers/ledger.ts +122 -0
  132. package/src/state/adopt.ts +132 -0
  133. package/src/transaction/authArgs.ts +142 -0
  134. package/src/transaction/consumeNotes.ts +23 -30
  135. package/src/transaction/noteAuthentication.ts +136 -0
  136. package/src/transaction/options.ts +27 -0
  137. package/src/transaction/p2id.ts +45 -34
  138. package/src/transaction/summary.ts +86 -22
  139. package/src/transaction/updateGuardian.ts +8 -22
  140. package/src/transaction/updateProcedureThreshold.ts +8 -20
  141. package/src/transaction/updateSigners.ts +11 -22
  142. package/src/transaction.ts +12 -1
  143. package/src/types/proposal.ts +30 -0
  144. package/src/utils/eip712.ts +57 -0
  145. package/src/utils/signature.ts +57 -0
  146. package/src/prover/test-node.d.ts +0 -6
package/README.md CHANGED
@@ -17,9 +17,14 @@ Miden multisig accounts store their authentication logic on-chain, but **their s
17
17
  ## Installation
18
18
 
19
19
  ```bash
20
- npm install @openzeppelin/miden-multisig-client @miden-sdk/miden-sdk@0.16.0
20
+ npm install @openzeppelin/miden-multisig-client@0.18.0-rc.1 @miden-sdk/miden-sdk@0.17.0-rc.3
21
21
  ```
22
22
 
23
+ Miden 0.17 requires a new client database: a store created under 0.16 does
24
+ not open. Pass a fresh `storeName` when creating the `MidenClient`, or delete
25
+ the existing IndexedDB database first. Accounts and pending proposals from the
26
+ 0.16 line do not carry over; see the compatibility document below.
27
+
23
28
  > **Why the peer version is exact**: the transaction-summary layout and the
24
29
  > guarded-multisig procedure roots are only byte-compatible between one
25
30
  > `@miden-sdk/miden-sdk` build and the `miden-standards` version its WASM
@@ -32,6 +37,7 @@ matches your Miden node:
32
37
 
33
38
  | This package | Miden protocol |
34
39
  |---|---|
40
+ | 0.18.x | 0.17.x (pre-release) |
35
41
  | 0.17.x | 0.16.x |
36
42
  | 0.16.x | 0.15.x |
37
43
  | 0.15.x | 0.15.x |
@@ -69,6 +75,42 @@ const client = new MultisigClient(midenClient, {
69
75
  });
70
76
  ```
71
77
 
78
+ For an ECDSA cosigner using an EIP-1193 wallet (including Ledger), call
79
+ `Eip712Signer.connect(provider)` once to select the Ethereum address and
80
+ recover its secp256k1 public key from a dedicated key-discovery signature.
81
+ Alternatively, pass an already-enrolled public key and its matching address
82
+ to `new Eip712Signer(provider, publicKeyHex, address)`. `LedgerSigner` remains
83
+ an alias. Use the resulting signer with the same `client.load(accountId, signer)`,
84
+ proposal-creation methods, and
85
+ `multisig.signProposal(id)` flow as a raw signer. The Ledger user can create
86
+ the proposal, then approve it. The approval and Guardian submission each
87
+ require a separate `eth_signTypedData_v4` signature; authenticated reads in
88
+ the flow also prompt the device. All signatures use the enrolled key; no
89
+ separate Guardian signing endpoint is needed. The device displays hashes,
90
+ not human-readable transferred assets. `recoverByKey` uses a separate
91
+ `GuardianLookup(bytes32 lookupHash)` typed-data signature to discover accounts.
92
+ EIP-712 execution requires an account compiled with the Miden 0.17 multisig
93
+ authentication component; changing Guardian's signature format does not upgrade
94
+ an older account's code root.
95
+
96
+ For example, a Ledger-backed proposer follows the same proposal lifecycle as a
97
+ raw signer:
98
+
99
+ ```typescript
100
+ import { Eip712Signer } from '@openzeppelin/miden-multisig-client';
101
+
102
+ const signer = await Eip712Signer.connect(provider); // key-discovery prompt
103
+ const multisig = await client.load(accountId, signer); // authenticated reads may prompt
104
+ const proposal = await multisig.createAddSignerProposal(newSignerCommitment); // request-auth prompt
105
+ await multisig.signProposal(proposal.id); // EIP-712 approval and request-auth prompts
106
+ await multisig.executeProposal(proposal.id); // authenticated calls may prompt again
107
+ ```
108
+
109
+ The proposal ID is the transaction-summary commitment. The wallet signs an
110
+ EIP-712 digest derived from that commitment, so the digest displayed by the
111
+ wallet need not equal the proposal ID. The approval signature and the
112
+ Guardian request-authentication signature are separate.
113
+
72
114
  The nested `prover` configuration is optional. Without it, the injected Miden
73
115
  client's prover is preserved. By default, cloneable remote provers get two total
74
116
  attempts; endpoint-less injected provers, including local and callback provers,
@@ -232,6 +274,13 @@ the min of the current threshold and the remaining signer count on remove.
232
274
  The option shapes are exported as `CreateProposalOptions`,
233
275
  `CreateSignerProposalOptions`, and `CreateP2idProposalOptions`.
234
276
 
277
+ All methods also accept `approvalExpirationDelta` (1 to 65535): the number of blocks after
278
+ the proposal's anchor block by which the transaction must be included. Past
279
+ that block the approvers' signatures no longer authorize it, the SDK refuses to
280
+ execute it, and the node rejects it as expired. The summary binds the value, so
281
+ the executing party cannot change it. Omitted, the approval never expires,
282
+ which is the Miden default.
283
+
235
284
  > **Breaking change (issue #387):** these methods previously took `nonce` (and
236
285
  > `newThreshold`) as positional parameters. Passing the old positional form
237
286
  > now throws at runtime instead of silently applying defaults.
@@ -264,7 +313,7 @@ console.log('Signatures:', signedProposal.signatures.length);
264
313
 
265
314
  ### Sync Proposals
266
315
 
267
- Fetches proposals from the GUARDIAN server and updates local state:
316
+ Fetches proposals from the GUARDIAN server and reconciles local state. A proposal GUARDIAN reported on an earlier sync but no longer reports (executed, canonicalized, or abandoned) is pruned from the cache. A proposal GUARDIAN holds but has not listed yet (a freshly pushed create) survives the first listing that omits it, absorbing a read-your-writes lag, and is pruned once a second consecutive listing omits it. Proposals GUARDIAN never received (offline switch-guardian creations and imports) are not pruned by listings. The response is parsed in full before the cache changes (a malformed payload rejects and leaves the cache untouched), a proposal that fails metadata-binding verification is still cached and returned with `verification.status === 'failed'` (see [Proposal Verification Status](#proposal-verification-status)), a proposal executed or signed offline while a sync is in flight keeps that local outcome (it is neither reverted to the listed state nor pruned by that sync), and overlapping calls share a single in-flight sync:
268
317
 
269
318
  ```typescript
270
319
  const proposals = await multisig.syncProposals();
@@ -325,6 +374,45 @@ for (const p of proposals) {
325
374
  }
326
375
  ```
327
376
 
377
+ ### Proposal Verification Status
378
+
379
+ `syncProposals()` checks every proposal's metadata against its signed
380
+ summary and records the outcome in `proposal.verification`:
381
+ `{ status: 'unchecked' }`, `{ status: 'verified' }`, or
382
+ `{ status: 'failed', retryable, message }`. A proposal that fails the
383
+ check is still returned, so one stale or corrupt proposal cannot hide the
384
+ others; only the check itself writes `verified`, and a freshly parsed or
385
+ imported proposal is `unchecked`. `retryable: true` means the
386
+ re-execution hit a transient node error and the proposal may verify on
387
+ the next sync; `retryable: false` means it cannot be reproduced (tampered
388
+ metadata, or an anchor block the node has pruned) and has to be
389
+ re-proposed. Verification is deliberately not part of `status`: a fully
390
+ signed proposal can be dead, so `'ready'` keeps meaning "threshold met"
391
+ and `isProposalActionable(proposal)` answers "verified and ready".
392
+ `signProposal` and `executeProposal` re-verify the one proposal they act
393
+ on and throw the real error. A payload that does not parse at all still
394
+ rejects, so malformed GUARDIAN data is never silently dropped.
395
+
396
+ ```typescript
397
+ import { isProposalActionable } from '@openzeppelin/miden-multisig-client';
398
+
399
+ for (const p of await multisig.syncProposals()) {
400
+ if (p.verification.status === 'failed') {
401
+ const { retryable, message } = p.verification;
402
+ console.log(`${p.id}: ${retryable ? 'retry later' : 're-propose'} — ${message}`);
403
+ } else if (isProposalActionable(p)) {
404
+ await multisig.executeProposal(p.id);
405
+ }
406
+ }
407
+ ```
408
+
409
+ Anchored re-execution needs the node to serve account state at the
410
+ proposal's reference block, and nodes keep that history only briefly
411
+ (devnet: about 50 blocks); once it is gone the proposal is reported as
412
+ `failed` with `retryable: false` for everyone, the proposer included.
413
+ Collect signatures and execute promptly, and re-propose once a proposal
414
+ has aged out.
415
+
328
416
  ### Execute a Proposal
329
417
 
330
418
  When a proposal has enough signatures:
@@ -401,7 +489,7 @@ exported/imported through the normal flow, but the SDK cannot build its on-chain
401
489
  transaction — the integration owns that recipe and submits it itself.
402
490
 
403
491
  ```typescript
404
- import { buildP2idTransactionRequest } from '@openzeppelin/miden-multisig-client';
492
+ import { buildP2idTransactionRequest, chainAnchorBlockNum } from '@openzeppelin/miden-multisig-client';
405
493
 
406
494
  // Producer: build a transaction and propose it under a custom label.
407
495
  // The options object accepts `noteType` (`NoteType.Public` (default) or
@@ -412,7 +500,12 @@ import { buildP2idTransactionRequest } from '@openzeppelin/miden-multisig-client
412
500
  // The typed path is `createP2idProposal(recipient, faucet, amount,
413
501
  // { nonce, noteType, reclaimHeight, timelockHeight })`, which persists the
414
502
  // choices in signed metadata.
415
- const { request, salt } = buildP2idTransactionRequest(senderId, recipientId, faucetId, amount);
503
+ //
504
+ // The builder takes the Miden client because the executing account decides
505
+ // the auth args the request has to carry (see below).
506
+ const { request, salt } = await buildP2idTransactionRequest(
507
+ midenClient, senderId, recipientId, faucetId, amount, { midenRpcEndpoint },
508
+ );
416
509
  const proposal = await multisig.createCustomProposal(request.serialize(), 'b2agg');
417
510
 
418
511
  // Cosigners review and sign through the usual signProposal flow.
@@ -423,51 +516,54 @@ const proposal = await multisig.createCustomProposal(request.serialize(), 'b2agg
423
516
  const advice = await multisig.prepareCustomExecution(proposal.id, request.serialize());
424
517
 
425
518
  // The browser TransactionRequest is immutable, so rebuild from the same recipe
426
- // (inputs + salt) with the advice, then submit. `submitTransaction` takes the
427
- // proposal id to execute at the proposal's anchored reference block, since the
428
- // collected signatures only authorize the summary produced there.
429
- const { request: finalRequest } = buildP2idTransactionRequest(
430
- senderId, recipientId, faucetId, amount, { salt, signatureAdviceMap: advice },
519
+ // (inputs + salt + bound block) with the advice, then submit. `submitTransaction`
520
+ // takes the proposal id to execute at the proposal's anchored reference block,
521
+ // since the collected signatures only authorize the summary produced there.
522
+ const boundBlockNum = chainAnchorBlockNum(proposal.metadata.chainAnchor);
523
+ const { request: finalRequest } = await buildP2idTransactionRequest(
524
+ midenClient, senderId, recipientId, faucetId, amount,
525
+ { salt, boundBlockNum, signatureAdviceMap: advice, midenRpcEndpoint },
431
526
  );
432
527
  await multisig.submitTransaction(proposal.id, finalRequest);
433
528
  ```
434
529
 
435
- The exported transaction builders declare the proposal salt through
436
- `TransactionRequestBuilder.withFeeConversionSalt`. Miden-client derives the
437
- native 1:1 conversion info from the execution reference header. Integrations
438
- that build a request directly must retain the original salt and call
439
- `withFeeConversionSalt(salt)` on their builder when they create and rebuild the
440
- request.
441
-
442
- The integration keeps only its own recipe (build inputs + salt) so it can
443
- reproduce the exact transaction at execute time — the SDK does not store the
444
- serialized request. The binding check guarantees the rebuilt transaction matches the
445
- commitment the cosigners signed.
446
-
447
- The salt cannot be recovered from the summary. Once the auth arg became the
448
- commitment `hash(CONVERSION_INFO || SALT)` it stopped being invertible, so a
449
- recipe that was not retained cannot be reconstructed from the signed summary —
450
- keep the salt, or read it from the proposal's `saltHex` metadata, which is what
451
- the SDK's own execution path does.
452
-
453
- The summary exposes the committed auth argument for inspection:
530
+ Since Miden 0.17 a multisig auth procedure reads three words out of the
531
+ transaction's auth arg: the block the summary binds together with the approval
532
+ expiration, the salt, and the fee conversion info. The exported builders get
533
+ them from `client.feeAwareTransactionRequestBuilder(account, { feeConversionSalt,
534
+ boundBlockNum })`, which sets the commitment as the request's auth arg and puts
535
+ the preimage in its advice map. An integration that assembles a request itself
536
+ must start from that builder for a multisig account, and must not call
537
+ `withFeeConversionSalt` or `withAuthArg` on it: the two setters clear each other
538
+ and either one discards the auth args. The approval never expires unless the
539
+ integration asks for one through `approvalExpirationDelta`, which the
540
+ `create*Proposal` methods forward from their options.
541
+
542
+ The integration keeps its own recipe (build inputs + salt) and reads the bound
543
+ block from the proposal's chain anchor, so it can reproduce the exact
544
+ transaction at execute time — the SDK does not store the serialized request.
545
+ The binding check guarantees the rebuilt transaction matches the commitment the
546
+ cosigners signed. A request built at one sync height and anchored at another is
547
+ refused by `executeForSummary` with `SummaryAnchorMismatchError`; rebuild and
548
+ retry.
549
+
550
+ The summary binds the salt itself, so the value the cosigners signed over is
551
+ readable back out of it:
454
552
 
455
553
  ```typescript
456
- import { summaryAuthArg } from '@openzeppelin/miden-multisig-client';
554
+ import { summarySalt, summaryApprovalExpirationBlockNum } from '@openzeppelin/miden-multisig-client';
457
555
  import { TransactionSummary } from '@miden-sdk/miden-sdk';
458
556
 
459
- const signedAuthArg = summaryAuthArg(TransactionSummary.deserialize(bytes));
557
+ const summary = TransactionSummary.deserialize(bytes);
558
+ const salt = summarySalt(summary);
559
+ const expiresAt = summaryApprovalExpirationBlockNum(summary); // undefined: never
460
560
  ```
461
561
 
462
- Do not use `signedAuthArg` as the salt when rebuilding the request.
463
- `withFeeConversionSalt` would derive and commit a second value from it, and the
464
- rebuilt summary would not match the summary that the cosigners signed.
465
-
466
- On the Miden 0.16 line a summary binds seven user-defined elements,
467
- and the guarded-multisig auth component zeroes the leading three and passes the
468
- auth arg as the trailing four. `summaryAuthArg` reads that convention, so prefer
469
- it over indexing `userParams()` by hand. It replaced `summarySalt`, whose name
470
- claimed an inversion that no longer exists.
562
+ The SDK's own verification path compares `summarySalt(summary)` with the
563
+ proposal's `saltHex` metadata before rebuilding, so a proposal GUARDIAN serves
564
+ with a summary and metadata that disagree fails by name rather than as a
565
+ generic summary mismatch. A request carries the same two values in its auth
566
+ args; `requestSaltHex(request)` and `requestBoundBlockNum(request)` read them.
471
567
 
472
568
  > **Rust ↔ TS parity:** both SDKs expose the same producer surface —
473
569
  > `createCustomProposal` / `propose_custom_transaction`, `prepareCustomExecution` /
@@ -497,8 +593,8 @@ if (recovered.length === 0) {
497
593
  ```
498
594
 
499
595
  The `Signer` passed to `recoverByKey` MUST implement `signLookupMessage`
500
- (the bundled `FalconSigner` and `EcdsaSigner` both do). The lookup endpoint
501
- authenticates by proof-of-possession of the queried commitment — same key
596
+ (the bundled `FalconSigner`, `EcdsaSigner`, and `Eip712Signer` do). The lookup
597
+ endpoint authenticates by proof-of-possession of the queried commitment — same key
502
598
  that already authenticates per-account requests, so revealing the account ID
503
599
  does not grant any new capability. See the design doc for the security
504
600
  analysis.
@@ -628,11 +724,26 @@ discriminator.
628
724
  [issue #229](https://github.com/OpenZeppelin/guardian/issues/229).
629
725
  - **v2 (self-contained)** — `metadataVersion: 2` plus a `notes` array
630
726
  of base64-encoded `Note.serialize()` bytes, aligned by index with
631
- `noteIds`. Verification rebuilds the request from the embedded notes
632
- alone — no `getInputNote`, no network call. Restores the same
633
- "rebuild from signed metadata" invariant every other proposal type
634
- already satisfied (and that audit finding **M-08** remediated for
635
- `p2id`).
727
+ `noteIds`. Verification rebuilds the request from the embedded notes,
728
+ never from whatever notes the verifier's store happens to hold.
729
+ Restores the same "rebuild from signed metadata" invariant every other
730
+ proposal type already satisfied (and that audit finding **M-08**
731
+ remediated for `p2id`).
732
+
733
+ The rebuild is not store-independent by itself, though: miden-client
734
+ consumes each input note as *authenticated* when the local store holds
735
+ its inclusion proof and as *unauthenticated* otherwise, and the two
736
+ commit differently into the signed summary (issue #409). Authenticated
737
+ is the canonical mode, so before every rebuild (`syncProposals`,
738
+ `signProposal`, `executeProposal`) the verifier authenticates the
739
+ embedded notes: notes already authenticated locally are left alone,
740
+ the rest get their inclusion proofs from the Miden node in one round
741
+ trip and are imported into the local store as committed (with one
742
+ `syncState` if the store is behind the note's block). Verification
743
+ therefore reads and writes the local store and contacts the node.
744
+ `createConsumeNotesProposal` does the same before the summary and its
745
+ chain anchor are captured, and refuses a note that is not yet
746
+ committed on chain.
636
747
 
637
748
  `createConsumeNotesProposal` always emits v2 starting with this
638
749
  release; the proposer is the one party guaranteed to hold the notes
@@ -645,6 +756,7 @@ signature collection begins.
645
756
  import {
646
757
  MAX_CONSUME_NOTES_METADATA_BYTES,
647
758
  CONSUME_NOTES_METADATA_VERSION_V2,
759
+ ConsumeNoteNotAuthenticatedError,
648
760
  ConsumeNotesMetadataOversizeError,
649
761
  LegacyConsumeNotesNoteMissingError,
650
762
  NoteBindingMismatchError,
@@ -665,6 +777,7 @@ dashboards can branch on one taxonomy.
665
777
  | `UnsupportedMetadataVersionError` | `consume_notes_unsupported_metadata_version` | Unrecognized version (including v1 on a cut-over build) |
666
778
  | `ConsumeNotesMetadataOversizeError` | `consume_notes_metadata_oversize` | v2 metadata serialization exceeds 256 KiB at creation |
667
779
  | `LegacyConsumeNotesNoteMissingError` | `consume_notes_legacy_note_missing` | v1 path: local store does not contain the referenced note |
780
+ | `ConsumeNoteNotAuthenticatedError` | `consume_notes_note_not_authenticated` | A note could not be authenticated: not committed on chain yet, the node served no proof, the import failed, or the store could not verify it after a sync |
668
781
 
669
782
  ### Cut-over policy
670
783
 
@@ -3,17 +3,17 @@
3
3
  *
4
4
  * This module provides functionality to create multisig accounts.
5
5
  */
6
- import { type MidenClient } from "@miden-sdk/miden-sdk";
6
+ import { type RawClientSource } from "../raw-client.js";
7
7
  import type { MultisigConfig, CreateAccountResult } from "../types.js";
8
8
  /**
9
9
  * Creates a multisig account with GUARDIAN authentication.
10
10
  *
11
- * @param midenClient - Initialized MidenClient
11
+ * @param client - Initialized MidenClient, or the WASM client behind one
12
12
  * @param config - Multisig configuration
13
- * @param midenRpcEndpoint - RPC endpoint for the MidenClient's network
13
+ * @param midenRpcEndpoint - RPC endpoint for the client's network
14
14
  * @returns The created account and seed
15
15
  */
16
- export declare function createMultisigAccount(midenClient: MidenClient, config: MultisigConfig, midenRpcEndpoint: string): Promise<CreateAccountResult>;
16
+ export declare function createMultisigAccount(client: RawClientSource, config: MultisigConfig, midenRpcEndpoint: string): Promise<CreateAccountResult>;
17
17
  /**
18
18
  * Validates a multisig configuration.
19
19
  *
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAgEvE;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,cAAc,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,mBAAmB,CAAC,CAiC9B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CA2EnE"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAgEvE;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,cAAc,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,mBAAmB,CAAC,CAqC9B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAgFnE"}
@@ -5,6 +5,8 @@
5
5
  */
6
6
  import { AccountBuilder, AccountStorageMode, AuthGuardedMultisigConfig, ProcedureThreshold, Word, createAuthGuardedMultisig, } from "@miden-sdk/miden-sdk";
7
7
  import { getProcedureRoot } from "../procedures.js";
8
+ import { isPublicMidenClient } from "../raw-client.js";
9
+ import { MAX_SIGNERS } from "./layout.js";
8
10
  import { normalizeSignerCommitment } from "../utils/signature.js";
9
11
  /**
10
12
  * Discriminants of the SDK's wasm `AuthScheme` enum, which `AuthGuardedMultisigConfig` takes.
@@ -44,12 +46,12 @@ function buildGuardedMultisigComponent(config) {
44
46
  /**
45
47
  * Creates a multisig account with GUARDIAN authentication.
46
48
  *
47
- * @param midenClient - Initialized MidenClient
49
+ * @param client - Initialized MidenClient, or the WASM client behind one
48
50
  * @param config - Multisig configuration
49
- * @param midenRpcEndpoint - RPC endpoint for the MidenClient's network
51
+ * @param midenRpcEndpoint - RPC endpoint for the client's network
50
52
  * @returns The created account and seed
51
53
  */
52
- export async function createMultisigAccount(midenClient, config, midenRpcEndpoint) {
54
+ export async function createMultisigAccount(client, config, midenRpcEndpoint) {
53
55
  validateMultisigConfig(config);
54
56
  const authComponent = buildGuardedMultisigComponent(config);
55
57
  let seed = config.seed;
@@ -67,10 +69,15 @@ export async function createMultisigAccount(midenClient, config, midenRpcEndpoin
67
69
  .withAuthComponent(authComponent)
68
70
  .withBasicWalletComponent();
69
71
  const result = accountBuilder.buildWithoutSchemaCommitment();
70
- await midenClient.accounts.insert({
71
- account: result.account,
72
- overwrite: false,
73
- });
72
+ if (isPublicMidenClient(client)) {
73
+ await client.accounts.insert({
74
+ account: result.account,
75
+ overwrite: false,
76
+ });
77
+ }
78
+ else {
79
+ await client.newAccount(result.account, false);
80
+ }
74
81
  return {
75
82
  account: result.account,
76
83
  seed,
@@ -97,6 +104,9 @@ export function validateMultisigConfig(config) {
97
104
  }
98
105
  signerCommitments.add(normalizedCommitment);
99
106
  }
107
+ if (config.signerCommitments.length > MAX_SIGNERS) {
108
+ throw new Error(`too many signers (${config.signerCommitments.length}): a multisig account holds at most ${MAX_SIGNERS}`);
109
+ }
100
110
  if (config.threshold > config.signerCommitments.length) {
101
111
  throw new Error(`threshold (${config.threshold}) cannot exceed number of signers (${config.signerCommitments.length})`);
102
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"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"}
@@ -22,10 +22,10 @@ export declare const GUARDIAN_SLOT_NAMES: {
22
22
  readonly SCHEME_ID: "miden::standards::auth::guardian::scheme";
23
23
  };
24
24
  /**
25
- * Sanity ceiling for the signer count read from `threshold_config`. The
26
- * contract has no on-chain maximum (the felt is only asserted to be a u32),
27
- * so a corrupt or adversarial account could report an arbitrarily large
28
- * count; readers bound their loops with this instead of trusting it.
25
+ * The most approvers a multisig account can hold: `ApproverSet::MAX_APPROVERS`
26
+ * in miden-standards, enforced at account creation and again on-chain by
27
+ * `update_signers_and_threshold`. Readers bound their loops with it, so a
28
+ * corrupt `threshold_config` cannot drive an unbounded storage walk.
29
29
  */
30
- export declare const MAX_SIGNERS = 1000;
30
+ export declare const MAX_SIGNERS = 64;
31
31
  //# sourceMappingURL=layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/account/layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,WAAW,OAAO,CAAC"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/account/layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,WAAW,KAAK,CAAC"}
@@ -22,10 +22,10 @@ export const GUARDIAN_SLOT_NAMES = {
22
22
  SCHEME_ID: 'miden::standards::auth::guardian::scheme',
23
23
  };
24
24
  /**
25
- * Sanity ceiling for the signer count read from `threshold_config`. The
26
- * contract has no on-chain maximum (the felt is only asserted to be a u32),
27
- * so a corrupt or adversarial account could report an arbitrarily large
28
- * count; readers bound their loops with this instead of trusting it.
25
+ * The most approvers a multisig account can hold: `ApproverSet::MAX_APPROVERS`
26
+ * in miden-standards, enforced at account creation and again on-chain by
27
+ * `update_signers_and_threshold`. Readers bound their loops with it, so a
28
+ * corrupt `threshold_config` cannot drive an unbounded storage walk.
29
29
  */
30
- export const MAX_SIGNERS = 1000;
30
+ export const MAX_SIGNERS = 64;
31
31
  //# sourceMappingURL=layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/account/layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,gBAAgB,EAAE,oDAAoD;IACtE,kBAAkB,EAAE,wDAAwD;IAC5E,iBAAiB,EAAE,oDAAoD;IACvE,qBAAqB,EAAE,yDAAyD;IAChF,oBAAoB,EAAE,wDAAwD;CACtE,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,UAAU,EAAE,2CAA2C;IACvD,SAAS,EAAE,0CAA0C;CAC7C,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/account/layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,gBAAgB,EAAE,oDAAoD;IACtE,kBAAkB,EAAE,wDAAwD;IAC5E,iBAAiB,EAAE,oDAAoD;IACvE,qBAAqB,EAAE,yDAAyD;IAChF,oBAAoB,EAAE,wDAAwD;CACtE,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,UAAU,EAAE,2CAA2C;IACvD,SAAS,EAAE,0CAA0C;CAC7C,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC"}
package/dist/client.d.ts CHANGED
@@ -93,7 +93,7 @@ export declare class MultisigClient {
93
93
  * which would fail authentication first).
94
94
  *
95
95
  * @throws if `signer` does not implement `signLookupMessage`. The bundled
96
- * `FalconSigner` and `EcdsaSigner` both do.
96
+ * `FalconSigner`, `EcdsaSigner`, and `LedgerSigner` do.
97
97
  */
98
98
  recoverByKey(signer: Signer): Promise<RecoveredAccount[]>;
99
99
  /**
@@ -112,5 +112,22 @@ export declare class MultisigClient {
112
112
  * @returns A Multisig instance for the loaded account
113
113
  */
114
114
  load(accountId: string, signer: Signer): Promise<Multisig>;
115
+ /**
116
+ * Reconcile the account GUARDIAN returned with the local store, and answer
117
+ * with the one that is actually current.
118
+ *
119
+ * Writing GUARDIAN's account only when the store held nothing meant a caller
120
+ * that already had the account kept its own copy while the returned
121
+ * `Multisig` carried a config derived from GUARDIAN's. One object, two
122
+ * sources, silently disagreeing: after a membership change the config said
123
+ * one thing and every account read said another, with no error.
124
+ *
125
+ * Overwriting unconditionally is not the fix either. Between pushing a delta
126
+ * and GUARDIAN canonicalizing it, local is legitimately ahead, and clobbering
127
+ * it would build the next transaction on a stale nonce. So this applies the
128
+ * same rule `syncState` does, and returns whichever account wins so the
129
+ * caller describes the state it will actually read.
130
+ */
131
+ private adoptGuardianAccount;
115
132
  }
116
133
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAsB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,SAAS,EAEf,MAAM,iBAAiB,CAAC;AAiBzB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,eAAe,CAAqB;gBAEhC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB;IAUlE;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAM3C;;OAEG;IACH,IAAI,cAAc,IAAI,kBAAkB,CAEvC;IAED;;;;;;;;;OASG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAe/D;;;;;;OAMG;IACG,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAwBvE;;;;;;OAMG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAgDjE"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAsB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGzD,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,SAAS,EAEf,MAAM,iBAAiB,CAAC;AAiBzB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,eAAe,CAAqB;gBAEhC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB;IAUlE;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAM3C;;OAEG;IACH,IAAI,cAAc,IAAI,kBAAkB,CAEvC;IAED;;;;;;;;;OASG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAe/D;;;;;;OAMG;IACG,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAwBvE;;;;;;OAMG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+ChE;;;;;;;;;;;;;;;OAeG;YACW,oBAAoB;CAuEnC"}
package/dist/client.js CHANGED
@@ -10,6 +10,8 @@ import { Multisig } from './multisig.js';
10
10
  import { createMultisigAccount } from './account/index.js';
11
11
  import { AccountInspector, assertCompleteDetectedConfig } from './inspector.js';
12
12
  import { requireConfigValue, requireMidenRpcEndpoint } from './raw-client.js';
13
+ import { isSafeToAdoptGuardianState, readOnChainCommitment } from './state/adopt.js';
14
+ import { normalizeHexWord } from './utils/encoding.js';
13
15
  import { resolveProverConfig, } from './prover/config.js';
14
16
  import { resolveRpcConfig, } from './rpc/config.js';
15
17
  async function bindSignerAccountKey(signer, midenClient, accountId) {
@@ -84,7 +86,7 @@ export class MultisigClient {
84
86
  * which would fail authentication first).
85
87
  *
86
88
  * @throws if `signer` does not implement `signLookupMessage`. The bundled
87
- * `FalconSigner` and `EcdsaSigner` both do.
89
+ * `FalconSigner`, `EcdsaSigner`, and `LedgerSigner` do.
88
90
  */
89
91
  async recoverByKey(signer) {
90
92
  this._guardianClient.setSigner(signer);
@@ -129,7 +131,8 @@ export class MultisigClient {
129
131
  for (let i = 0; i < binaryString.length; i++) {
130
132
  accountBytes[i] = binaryString.charCodeAt(i);
131
133
  }
132
- const account = Account.deserialize(accountBytes);
134
+ const incomingAccount = Account.deserialize(accountBytes);
135
+ const account = await this.adoptGuardianAccount(accountId, incomingAccount);
133
136
  const detected = AccountInspector.fromAccount(account);
134
137
  // Fail closed on a partial read: the detected signer set becomes the
135
138
  // authoritative config that membership proposals rewrite on-chain.
@@ -140,12 +143,82 @@ export class MultisigClient {
140
143
  guardianCommitment: detected.guardianCommitment,
141
144
  procedureThresholds: Array.from(detected.procedureThresholds.entries()).map(([procedure, threshold]) => ({ procedure, threshold })),
142
145
  };
143
- const existingAccount = await this.midenClient.accounts.get(AccountId.fromHex(accountId));
144
- if (!existingAccount) {
145
- await this.midenClient.accounts.insert({ account, overwrite: true });
146
- }
147
146
  await bindSignerAccountKey(signer, this.midenClient, accountId);
148
147
  return new Multisig(account, config, this._guardianClient, signer, this.midenClient, accountId, this.midenRpcEndpoint, this.proverConfig, this.rpcConfig);
149
148
  }
149
+ /**
150
+ * Reconcile the account GUARDIAN returned with the local store, and answer
151
+ * with the one that is actually current.
152
+ *
153
+ * Writing GUARDIAN's account only when the store held nothing meant a caller
154
+ * that already had the account kept its own copy while the returned
155
+ * `Multisig` carried a config derived from GUARDIAN's. One object, two
156
+ * sources, silently disagreeing: after a membership change the config said
157
+ * one thing and every account read said another, with no error.
158
+ *
159
+ * Overwriting unconditionally is not the fix either. Between pushing a delta
160
+ * and GUARDIAN canonicalizing it, local is legitimately ahead, and clobbering
161
+ * it would build the next transaction on a stale nonce. So this applies the
162
+ * same rule `syncState` does, and returns whichever account wins so the
163
+ * caller describes the state it will actually read.
164
+ */
165
+ async adoptGuardianAccount(accountId, incomingAccount) {
166
+ const localAccount = await this.midenClient.accounts.get(AccountId.fromHex(accountId));
167
+ if (!localAccount) {
168
+ // Still checked against chain, and through the same rule the other path
169
+ // uses. An empty store is the ordinary shape for loading an account this
170
+ // client has never held, so inserting without the check would make it the
171
+ // one path where GUARDIAN's word is taken on its own. Checking only that
172
+ // *some* commitment exists is not the check: it admits state that
173
+ // disagrees with chain, which is the thing being guarded against.
174
+ // Only for an account that has transacted. One that has not is not
175
+ // deployed, so there is no on-chain commitment to agree or disagree with,
176
+ // and reading the node for one would make loading a fresh account depend
177
+ // on the node as well as on GUARDIAN.
178
+ const transacted = incomingAccount.nonce().asInt() > BigInt(0);
179
+ const onChain = transacted
180
+ ? await readOnChainCommitment(this.midenRpcEndpoint, AccountId.fromHex(accountId), this.rpcConfig)
181
+ : null;
182
+ // The one rule the shared predicate cannot apply here. It keys its
183
+ // null-commitment guard on the *local* nonce, because `syncState`
184
+ // legitimately meets a higher incoming nonce with no local history. With
185
+ // no local record at all, the incoming nonce is the only evidence there
186
+ // is: an account GUARDIAN reports as having transacted is deployed, so a
187
+ // node reporting nothing for it is an RPC or gateway failure rather than
188
+ // an undeployed account. `readOnChainCommitment` reports a missing
189
+ // account by matching `not found`, which a 404 also says.
190
+ if (transacted && !onChain) {
191
+ throw new Error(`Refusing to adopt GUARDIAN state for ${accountId}: it has transacted (nonce ${incomingAccount.nonce().asInt().toString()}) but the node reported no on-chain commitment, so it could not be checked against chain`);
192
+ }
193
+ // Handed the commitment already read, rather than letting the predicate
194
+ // read it again: two reads can disagree, and the guard above would then
195
+ // have passed on a different answer than the comparison rejects.
196
+ await isSafeToAdoptGuardianState({
197
+ accountId,
198
+ incomingAccount,
199
+ readCommitment: () => Promise.resolve(onChain),
200
+ });
201
+ await this.midenClient.accounts.insert({ account: incomingAccount, overwrite: true });
202
+ return incomingAccount;
203
+ }
204
+ const localCommitment = normalizeHexWord(localAccount.to_commitment().toHex());
205
+ const incomingCommitment = normalizeHexWord(incomingAccount.to_commitment().toHex());
206
+ // Equal commitments are the same state, and the safety rule reads an equal
207
+ // nonce as divergence, so it must not be asked about a pair that agrees.
208
+ if (localCommitment === incomingCommitment) {
209
+ return localAccount;
210
+ }
211
+ const adopt = await isSafeToAdoptGuardianState({
212
+ accountId,
213
+ incomingAccount,
214
+ localAccount,
215
+ readCommitment: () => readOnChainCommitment(this.midenRpcEndpoint, AccountId.fromHex(accountId), this.rpcConfig),
216
+ });
217
+ if (!adopt) {
218
+ return localAccount;
219
+ }
220
+ await this.midenClient.accounts.insert({ account: incomingAccount, overwrite: true });
221
+ return incomingAccount;
222
+ }
150
223
  }
151
224
  //# sourceMappingURL=client.js.map