@openzeppelin/miden-multisig-client 0.18.0-rc.2 → 0.18.0-rc.3
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.
- package/README.md +46 -4
- package/dist/account/builder.d.ts +3 -4
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +6 -13
- package/dist/account/builder.js.map +1 -1
- package/dist/client.d.ts +11 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +8 -3
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/multisig.d.ts +40 -9
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +97 -108
- package/dist/multisig.js.map +1 -1
- package/dist/procedures.d.ts +5 -5
- package/dist/procedures.js +5 -5
- package/dist/recovery/proposalNoteImport.d.ts +5 -6
- package/dist/recovery/proposalNoteImport.d.ts.map +1 -1
- package/dist/recovery/proposalNoteImport.js +12 -13
- package/dist/recovery/proposalNoteImport.js.map +1 -1
- package/dist/recovery/publicNoteBackfill.d.ts +2 -3
- package/dist/recovery/publicNoteBackfill.d.ts.map +1 -1
- package/dist/recovery/publicNoteBackfill.js +4 -5
- package/dist/recovery/publicNoteBackfill.js.map +1 -1
- package/dist/transaction/authArgs.d.ts +2 -3
- package/dist/transaction/authArgs.d.ts.map +1 -1
- package/dist/transaction/authArgs.js +7 -14
- package/dist/transaction/authArgs.js.map +1 -1
- package/dist/transaction/consumeNotes.d.ts +4 -12
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +10 -4
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/transaction/noteAuthentication.d.ts +2 -3
- package/dist/transaction/noteAuthentication.d.ts.map +1 -1
- package/dist/transaction/noteAuthentication.js +11 -11
- package/dist/transaction/noteAuthentication.js.map +1 -1
- package/dist/transaction/options.d.ts +0 -7
- package/dist/transaction/options.d.ts.map +1 -1
- package/dist/transaction/p2id.d.ts +2 -3
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/summary.d.ts +8 -14
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js +64 -20
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction/updateGuardian.d.ts +3 -7
- package/dist/transaction/updateGuardian.d.ts.map +1 -1
- package/dist/transaction/updateGuardian.js +3 -4
- package/dist/transaction/updateGuardian.js.map +1 -1
- package/dist/transaction/updateProcedureThreshold.d.ts +3 -8
- package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
- package/dist/transaction/updateProcedureThreshold.js +3 -4
- package/dist/transaction/updateProcedureThreshold.js.map +1 -1
- package/dist/transaction/updateSigners.d.ts +3 -8
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js +3 -4
- package/dist/transaction/updateSigners.js.map +1 -1
- package/package.json +4 -4
- package/src/account/builder.ts +6 -13
- package/src/client.ts +13 -10
- package/src/config.ts +14 -0
- package/src/index.ts +2 -3
- package/src/multisig.ts +125 -143
- package/src/procedures.ts +5 -5
- package/src/recovery/proposalNoteImport.ts +13 -15
- package/src/recovery/publicNoteBackfill.ts +6 -6
- package/src/transaction/authArgs.ts +15 -21
- package/src/transaction/consumeNotes.ts +6 -34
- package/src/transaction/noteAuthentication.ts +18 -13
- package/src/transaction/options.ts +0 -9
- package/src/transaction/p2id.ts +2 -3
- package/src/transaction/summary.ts +27 -69
- package/src/transaction/updateGuardian.ts +5 -23
- package/src/transaction/updateProcedureThreshold.ts +5 -25
- package/src/transaction/updateSigners.ts +5 -22
- package/dist/raw-client.d.ts +0 -14
- package/dist/raw-client.d.ts.map +0 -1
- package/dist/raw-client.js +0 -60
- package/dist/raw-client.js.map +0 -1
- package/src/raw-client.ts +0 -89
package/dist/multisig.js
CHANGED
|
@@ -27,7 +27,7 @@ import { importNotesFromProposals as importNotesFromProposalsStandalone, throwIf
|
|
|
27
27
|
import { backfillPublicNotesByTag as backfillPublicNotesByTagStandalone, } from './recovery/publicNoteBackfill.js';
|
|
28
28
|
import { drainPrivateNoteBacklog } from './recovery/transportDrain.js';
|
|
29
29
|
import { GUARDIAN_SWITCH_RECOVERY_OPTIONS, runNoteRecovery, } from './recovery/recoverNotes.js';
|
|
30
|
-
import {
|
|
30
|
+
import { requireMidenRpcEndpoint } from './config.js';
|
|
31
31
|
import { resolveProverConfig, } from './prover/config.js';
|
|
32
32
|
import { ProverWorkflow } from './prover/workflow.js';
|
|
33
33
|
import { resolveRpcConfig, } from './rpc/config.js';
|
|
@@ -141,7 +141,6 @@ export class Multisig {
|
|
|
141
141
|
guardian;
|
|
142
142
|
signer;
|
|
143
143
|
midenClient;
|
|
144
|
-
rawClientPromise;
|
|
145
144
|
proverWorkflow;
|
|
146
145
|
rpcConfig;
|
|
147
146
|
_accountId;
|
|
@@ -177,16 +176,12 @@ export class Multisig {
|
|
|
177
176
|
this.midenClient = midenClient;
|
|
178
177
|
this._accountId = accountId ?? (account ? accountIdToHex(account) : '');
|
|
179
178
|
this.midenRpcEndpoint = requireMidenRpcEndpoint(midenRpcEndpoint);
|
|
180
|
-
this.
|
|
181
|
-
this.proverWorkflow = new ProverWorkflow(this.midenClient, proverConfig ?? resolveProverConfig(undefined, getTransactionProver(midenClient)));
|
|
179
|
+
this.proverWorkflow = new ProverWorkflow(this.midenClient, proverConfig ?? resolveProverConfig(undefined, midenClient.defaultProver));
|
|
182
180
|
this.rpcConfig = rpcConfig ?? resolveRpcConfig(undefined);
|
|
183
181
|
}
|
|
184
182
|
getMidenRpcEndpoint() {
|
|
185
183
|
return this.midenRpcEndpoint;
|
|
186
184
|
}
|
|
187
|
-
async getRawClient() {
|
|
188
|
-
return this.rawClientPromise;
|
|
189
|
-
}
|
|
190
185
|
proposalFactory() {
|
|
191
186
|
return new ProposalFactory({
|
|
192
187
|
accountId: this._accountId,
|
|
@@ -215,7 +210,7 @@ export class Multisig {
|
|
|
215
210
|
return this.signer.commitment;
|
|
216
211
|
}
|
|
217
212
|
/**
|
|
218
|
-
* Resolve the account from the
|
|
213
|
+
* Resolve the account from the Miden client's store, falling back to the
|
|
219
214
|
* `account` snapshot when the store has no record.
|
|
220
215
|
*
|
|
221
216
|
* Transaction execution reads the store, and other flows (e.g. consume-notes
|
|
@@ -223,9 +218,10 @@ export class Multisig {
|
|
|
223
218
|
* source from the store to see the same state execution will.
|
|
224
219
|
*/
|
|
225
220
|
async getStoreAccount() {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
221
|
+
return (await this.readStoreAccount()) ?? this.account;
|
|
222
|
+
}
|
|
223
|
+
readStoreAccount() {
|
|
224
|
+
return retryRpcRead(() => this.midenClient.accounts.get(this._accountId), this.rpcConfig);
|
|
229
225
|
}
|
|
230
226
|
/**
|
|
231
227
|
* Read the current ordered signer public-key commitments from account
|
|
@@ -375,9 +371,16 @@ export class Multisig {
|
|
|
375
371
|
/**
|
|
376
372
|
* Sync account state from GUARDIAN into the local Miden client store.
|
|
377
373
|
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* is
|
|
374
|
+
* Starts with the canonical-nonce pre-check (OpenZeppelin/guardian#191):
|
|
375
|
+
* GUARDIAN reports the nonce and commitment of its canonical state, and
|
|
376
|
+
* when that nonce is not above the local account's — at a matching
|
|
377
|
+
* commitment when equal — nothing newer exists to pull, so the state
|
|
378
|
+
* fetch is skipped and the local account stands (`source: 'local'`).
|
|
379
|
+
*
|
|
380
|
+
* Otherwise the state is fetched (`source: 'guardian'`). If the GUARDIAN
|
|
381
|
+
* commitment differs from the local commitment (or the account is missing
|
|
382
|
+
* locally) and the GUARDIAN state is safe to import, the local store is
|
|
383
|
+
* overwritten with the GUARDIAN state. When the GUARDIAN is merely *behind*
|
|
381
384
|
* local — e.g. the pushed execution delta has not been canonicalized yet
|
|
382
385
|
* (see OpenZeppelin/guardian#316) — the local state is already ahead and
|
|
383
386
|
* on-chain-verifiable, so it is kept as authoritative. Either way, config is
|
|
@@ -385,11 +388,18 @@ export class Multisig {
|
|
|
385
388
|
* (e.g. the UI) observe the current state instead of a stale snapshot.
|
|
386
389
|
*/
|
|
387
390
|
async syncState() {
|
|
391
|
+
const localAccount = await this.readStoreAccount();
|
|
392
|
+
if (localAccount) {
|
|
393
|
+
const head = await this.guardian.getCanonicalNonce(this._accountId);
|
|
394
|
+
const localNonce = localAccount.nonce().asInt();
|
|
395
|
+
const guardianNonce = BigInt(head.nonce);
|
|
396
|
+
if (this.guardianIsNotAhead(localAccount, localNonce, head.commitment, guardianNonce)) {
|
|
397
|
+
this.refreshConfigFromAccount(localAccount);
|
|
398
|
+
return { source: 'local', localNonce, guardianNonce };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
388
401
|
const state = await this.fetchState();
|
|
389
|
-
|
|
390
|
-
const webClient = await this.getRawClient();
|
|
391
|
-
const localAccount = await retryRpcRead(() => webClient.getAccount(accountId), this.rpcConfig);
|
|
392
|
-
let accountForConfigRefresh = localAccount ?? null;
|
|
402
|
+
let accountForConfigRefresh = localAccount;
|
|
393
403
|
const guardianCommitment = normalizeHexWord(state.commitment);
|
|
394
404
|
const localCommitment = localAccount
|
|
395
405
|
? normalizeHexWord(localAccount.to_commitment().toHex())
|
|
@@ -397,18 +407,33 @@ export class Multisig {
|
|
|
397
407
|
if (!localAccount || localCommitment !== guardianCommitment) {
|
|
398
408
|
const accountBytes = base64ToUint8Array(state.stateDataBase64);
|
|
399
409
|
const incomingAccount = Account.deserialize(accountBytes);
|
|
400
|
-
if (await this.isSafeToOverwriteLocalState(incomingAccount, localAccount)) {
|
|
401
|
-
await
|
|
410
|
+
if (await this.isSafeToOverwriteLocalState(incomingAccount, localAccount ?? undefined)) {
|
|
411
|
+
await this.midenClient.accounts.insert({ account: incomingAccount, overwrite: true });
|
|
402
412
|
accountForConfigRefresh = incomingAccount;
|
|
403
413
|
}
|
|
404
414
|
}
|
|
405
415
|
this.refreshConfigFromAccount(accountForConfigRefresh);
|
|
406
|
-
return state;
|
|
416
|
+
return { source: 'guardian', state };
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* The pre-check decision: GUARDIAN has nothing newer when its canonical
|
|
420
|
+
* nonce is below the local nonce, or equal to it at the same commitment.
|
|
421
|
+
* An equal nonce with a different commitment is divergence, which the
|
|
422
|
+
* full fetch reports (see `isSafeToOverwriteLocalState`).
|
|
423
|
+
*/
|
|
424
|
+
guardianIsNotAhead(localAccount, localNonce, guardianCommitment, guardianNonce) {
|
|
425
|
+
if (guardianNonce < localNonce) {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
if (guardianNonce > localNonce) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
return (normalizeHexWord(guardianCommitment) ===
|
|
432
|
+
normalizeHexWord(localAccount.to_commitment().toHex()));
|
|
407
433
|
}
|
|
408
434
|
async verifyStateCommitment() {
|
|
409
435
|
const accountId = AccountId.fromHex(this._accountId);
|
|
410
|
-
const
|
|
411
|
-
const localAccount = await retryRpcRead(() => webClient.getAccount(accountId), this.rpcConfig);
|
|
436
|
+
const localAccount = await this.readStoreAccount();
|
|
412
437
|
if (!localAccount) {
|
|
413
438
|
throw new Error(`Local account state not found for account ${this._accountId}. Sync the account before verifying.`);
|
|
414
439
|
}
|
|
@@ -464,11 +489,10 @@ export class Multisig {
|
|
|
464
489
|
* fail.
|
|
465
490
|
*/
|
|
466
491
|
async syncNetworkOnly() {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
return account ?? null;
|
|
492
|
+
await this.syncChain();
|
|
493
|
+
const account = await this.readStoreAccount();
|
|
494
|
+
this.refreshConfigFromAccount(account);
|
|
495
|
+
return account;
|
|
472
496
|
}
|
|
473
497
|
refreshConfigFromAccount(account) {
|
|
474
498
|
if (!account) {
|
|
@@ -736,7 +760,6 @@ export class Multisig {
|
|
|
736
760
|
*/
|
|
737
761
|
async createAddSignerProposal(newCommitment, options = {}, ...legacyArgs) {
|
|
738
762
|
const proposalNonce = resolveProposalNonce('createAddSignerProposal', options, legacyArgs);
|
|
739
|
-
const webClient = await this.getRawClient();
|
|
740
763
|
const targetThreshold = options.newThreshold ?? this.threshold;
|
|
741
764
|
const targetSignerCommitments = [...this.signerCommitments, newCommitment];
|
|
742
765
|
// What `update_signers_and_threshold` rejects on-chain, and what the auth
|
|
@@ -748,8 +771,8 @@ export class Multisig {
|
|
|
748
771
|
guardianCommitment: this.guardianCommitment,
|
|
749
772
|
});
|
|
750
773
|
this.warnOnOverrideDilution(targetSignerCommitments.length);
|
|
751
|
-
const { request, salt } = await buildUpdateSignersTransactionRequest(
|
|
752
|
-
const { summary, anchor } = await executeForSummary(
|
|
774
|
+
const { request, salt } = await buildUpdateSignersTransactionRequest(this.midenClient, targetThreshold, targetSignerCommitments, this.proposalRequestOptions(options));
|
|
775
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
753
776
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
754
777
|
anchor.free();
|
|
755
778
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -773,7 +796,6 @@ export class Multisig {
|
|
|
773
796
|
*/
|
|
774
797
|
async createRemoveSignerProposal(signerToRemove, options = {}, ...legacyArgs) {
|
|
775
798
|
const proposalNonce = resolveProposalNonce('createRemoveSignerProposal', options, legacyArgs);
|
|
776
|
-
const webClient = await this.getRawClient();
|
|
777
799
|
const normalizedRemove = signerToRemove.toLowerCase();
|
|
778
800
|
const targetSignerCommitments = this.signerCommitments.filter((c) => c.toLowerCase() !== normalizedRemove);
|
|
779
801
|
if (targetSignerCommitments.length === this.signerCommitments.length) {
|
|
@@ -786,8 +808,8 @@ export class Multisig {
|
|
|
786
808
|
if (targetThreshold < 1 || targetThreshold > targetSignerCommitments.length) {
|
|
787
809
|
throw new Error(`Invalid threshold ${targetThreshold}. Must be between 1 and ${targetSignerCommitments.length}`);
|
|
788
810
|
}
|
|
789
|
-
const { request, salt } = await buildUpdateSignersTransactionRequest(
|
|
790
|
-
const { summary, anchor } = await executeForSummary(
|
|
811
|
+
const { request, salt } = await buildUpdateSignersTransactionRequest(this.midenClient, targetThreshold, targetSignerCommitments, this.proposalRequestOptions(options));
|
|
812
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
791
813
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
792
814
|
anchor.free();
|
|
793
815
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -810,15 +832,14 @@ export class Multisig {
|
|
|
810
832
|
*/
|
|
811
833
|
async createChangeThresholdProposal(newThreshold, options = {}) {
|
|
812
834
|
const proposalNonce = resolveProposalNonce('createChangeThresholdProposal', options);
|
|
813
|
-
const webClient = await this.getRawClient();
|
|
814
835
|
if (newThreshold < 1 || newThreshold > this.signerCommitments.length) {
|
|
815
836
|
throw new Error(`Invalid threshold ${newThreshold}. Must be between 1 and ${this.signerCommitments.length}`);
|
|
816
837
|
}
|
|
817
838
|
if (newThreshold === this.threshold) {
|
|
818
839
|
throw new Error('New threshold is the same as current threshold');
|
|
819
840
|
}
|
|
820
|
-
const { request, salt } = await buildUpdateSignersTransactionRequest(
|
|
821
|
-
const { summary, anchor } = await executeForSummary(
|
|
841
|
+
const { request, salt } = await buildUpdateSignersTransactionRequest(this.midenClient, newThreshold, this.signerCommitments, this.proposalRequestOptions(options));
|
|
842
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
822
843
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
823
844
|
anchor.free();
|
|
824
845
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -835,7 +856,6 @@ export class Multisig {
|
|
|
835
856
|
}
|
|
836
857
|
async createUpdateProcedureThresholdProposal(targetProcedure, targetThreshold, options = {}) {
|
|
837
858
|
const proposalNonce = resolveProposalNonce('createUpdateProcedureThresholdProposal', options);
|
|
838
|
-
const webClient = await this.getRawClient();
|
|
839
859
|
if (targetThreshold < 0 || targetThreshold > this.signerCommitments.length) {
|
|
840
860
|
throw new Error(`Invalid threshold ${targetThreshold}. Must be between 0 and ${this.signerCommitments.length}`);
|
|
841
861
|
}
|
|
@@ -846,8 +866,8 @@ export class Multisig {
|
|
|
846
866
|
if (currentOverride !== undefined && currentOverride === targetThreshold) {
|
|
847
867
|
throw new Error(`Procedure ${targetProcedure} already has threshold override ${targetThreshold}`);
|
|
848
868
|
}
|
|
849
|
-
const { request, salt } = await buildUpdateProcedureThresholdTransactionRequest(
|
|
850
|
-
const { summary, anchor } = await executeForSummary(
|
|
869
|
+
const { request, salt } = await buildUpdateProcedureThresholdTransactionRequest(this.midenClient, targetProcedure, targetThreshold, this.proposalRequestOptions(options));
|
|
870
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
851
871
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
852
872
|
anchor.free();
|
|
853
873
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -887,7 +907,6 @@ export class Multisig {
|
|
|
887
907
|
* proposals for the same operation can never drift apart.
|
|
888
908
|
*/
|
|
889
909
|
async buildSwitchGuardianSummary(newGuardianEndpoint, newGuardianPubkey, approvalExpirationDelta) {
|
|
890
|
-
const webClient = await this.getRawClient();
|
|
891
910
|
// What `auth_tx_guarded_multisig` asserts after a guardian rotation, checked
|
|
892
911
|
// before any signature is collected.
|
|
893
912
|
validateMultisigConfig({
|
|
@@ -896,12 +915,12 @@ export class Multisig {
|
|
|
896
915
|
guardianCommitment: newGuardianPubkey,
|
|
897
916
|
});
|
|
898
917
|
await this.verifyGuardianEndpointCommitment(newGuardianEndpoint, newGuardianPubkey);
|
|
899
|
-
const { request, salt } = await buildUpdateGuardianTransactionRequest(
|
|
918
|
+
const { request, salt } = await buildUpdateGuardianTransactionRequest(this.midenClient, newGuardianPubkey, {
|
|
900
919
|
accountId: this._accountId,
|
|
901
920
|
approvalExpirationDelta,
|
|
902
921
|
signatureScheme: this.signer.scheme,
|
|
903
922
|
});
|
|
904
|
-
const { summary, anchor } = await executeForSummary(
|
|
923
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
905
924
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
906
925
|
anchor.free();
|
|
907
926
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -973,15 +992,13 @@ export class Multisig {
|
|
|
973
992
|
*/
|
|
974
993
|
async createConsumeNotesProposal(noteIds, options = {}) {
|
|
975
994
|
const proposalNonce = resolveProposalNonce('createConsumeNotesProposal', options);
|
|
976
|
-
const webClient = await this.getRawClient();
|
|
977
995
|
if (noteIds.length === 0) {
|
|
978
996
|
throw new Error('At least one note ID is required');
|
|
979
997
|
}
|
|
980
998
|
// Fetch notes locally (proposer has them per FR-012); embed for v2 verification.
|
|
981
|
-
const rawClient = await getRawMidenClient(webClient);
|
|
982
999
|
const fetchedNotes = [];
|
|
983
1000
|
for (const noteIdHex of noteIds) {
|
|
984
|
-
const inputNoteRecord = await
|
|
1001
|
+
const inputNoteRecord = await this.midenClient.notes.get(noteIdHex);
|
|
985
1002
|
if (!inputNoteRecord) {
|
|
986
1003
|
throw new LegacyConsumeNotesNoteMissingError(noteIdHex);
|
|
987
1004
|
}
|
|
@@ -992,8 +1009,8 @@ export class Multisig {
|
|
|
992
1009
|
// the notes are authenticated here first, before the anchor is captured.
|
|
993
1010
|
await this.ensureNotesAuthenticated(fetchedNotes);
|
|
994
1011
|
const embeddedNotes = fetchedNotes.map((n) => noteToBase64(n));
|
|
995
|
-
const { request, salt } = await buildConsumeNotesTransactionRequestFromNotes(
|
|
996
|
-
const { summary, anchor } = await executeForSummary(
|
|
1012
|
+
const { request, salt } = await buildConsumeNotesTransactionRequestFromNotes(this.midenClient, fetchedNotes, this.proposalRequestOptions(options));
|
|
1013
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
997
1014
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
998
1015
|
anchor.free();
|
|
999
1016
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -1030,15 +1047,14 @@ export class Multisig {
|
|
|
1030
1047
|
*/
|
|
1031
1048
|
async createP2idProposal(recipientId, faucetId, amount, options = {}, ...legacyArgs) {
|
|
1032
1049
|
const proposalNonce = resolveProposalNonce('createP2idProposal', options, legacyArgs);
|
|
1033
|
-
const webClient = await this.getRawClient();
|
|
1034
1050
|
if (amount <= 0n) {
|
|
1035
1051
|
throw new Error('Amount must be greater than 0');
|
|
1036
1052
|
}
|
|
1037
1053
|
// Forward everything but the nonce, so a note option added to
|
|
1038
1054
|
// CreateP2idProposalOptions can't be silently dropped before the builder.
|
|
1039
1055
|
const { nonce: _nonce, ...noteOptions } = options;
|
|
1040
|
-
const { request, salt } = await buildP2idTransactionRequest(
|
|
1041
|
-
const { summary, anchor } = await executeForSummary(
|
|
1056
|
+
const { request, salt } = await buildP2idTransactionRequest(this.midenClient, this._accountId, recipientId, faucetId, amount, noteOptions);
|
|
1057
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
1042
1058
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
1043
1059
|
anchor.free();
|
|
1044
1060
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -1062,41 +1078,24 @@ export class Multisig {
|
|
|
1062
1078
|
/**
|
|
1063
1079
|
* Get notes that can be consumed by this multisig account.
|
|
1064
1080
|
*
|
|
1065
|
-
* Returns
|
|
1066
|
-
*
|
|
1081
|
+
* Returns the notes the Miden client's last sync screened as consumable by
|
|
1082
|
+
* this account right now; block-locked notes are left out.
|
|
1067
1083
|
*/
|
|
1068
1084
|
async getConsumableNotes() {
|
|
1069
|
-
const
|
|
1070
|
-
const webClient = await this.getRawClient();
|
|
1071
|
-
// Get consumable notes for this account
|
|
1072
|
-
const consumableRecords = await webClient.getConsumableNotes(accountId);
|
|
1073
|
-
// Convert to our simplified ConsumableNote type
|
|
1085
|
+
const records = await this.midenClient.notes.listAvailable({ account: this._accountId });
|
|
1074
1086
|
const notes = [];
|
|
1075
|
-
for (const
|
|
1076
|
-
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
c.consumptionStatus().consumableAfterBlock() === undefined);
|
|
1081
|
-
if (canConsumeNow) {
|
|
1082
|
-
// Miden 0.15: InputNoteRecord.id() is `NoteId | undefined`; skip id-less records.
|
|
1083
|
-
const id = inputNote.id();
|
|
1084
|
-
if (id === undefined) {
|
|
1085
|
-
continue;
|
|
1086
|
-
}
|
|
1087
|
-
const noteId = id.toString();
|
|
1088
|
-
const details = inputNote.details();
|
|
1089
|
-
const fungibleAssets = details.assets().fungibleAssets();
|
|
1090
|
-
// Extract assets
|
|
1091
|
-
const assets = [];
|
|
1092
|
-
for (const asset of fungibleAssets) {
|
|
1093
|
-
assets.push({
|
|
1094
|
-
faucetId: asset.faucetId().toString(),
|
|
1095
|
-
amount: asset.amount(),
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
|
-
notes.push({ id: noteId, assets });
|
|
1087
|
+
for (const inputNote of records) {
|
|
1088
|
+
// Miden 0.15: InputNoteRecord.id() is `NoteId | undefined`; skip id-less records.
|
|
1089
|
+
const id = inputNote.id();
|
|
1090
|
+
if (id === undefined) {
|
|
1091
|
+
continue;
|
|
1099
1092
|
}
|
|
1093
|
+
const assets = inputNote
|
|
1094
|
+
.details()
|
|
1095
|
+
.assets()
|
|
1096
|
+
.fungibleAssets()
|
|
1097
|
+
.map((asset) => ({ faucetId: asset.faucetId().toString(), amount: asset.amount() }));
|
|
1098
|
+
notes.push({ id: id.toString(), assets });
|
|
1100
1099
|
}
|
|
1101
1100
|
return notes;
|
|
1102
1101
|
}
|
|
@@ -1118,11 +1117,10 @@ export class Multisig {
|
|
|
1118
1117
|
* @returns Serialized note file bytes
|
|
1119
1118
|
*/
|
|
1120
1119
|
async exportNoteToBytes(noteId) {
|
|
1121
|
-
const webClient = await this.getRawClient();
|
|
1122
1120
|
const trimmedNoteId = noteId.trim();
|
|
1123
1121
|
let record;
|
|
1124
1122
|
try {
|
|
1125
|
-
record = await
|
|
1123
|
+
[record] = await this.midenClient.notes.listSent({ ids: [trimmedNoteId] });
|
|
1126
1124
|
}
|
|
1127
1125
|
catch (err) {
|
|
1128
1126
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -1134,7 +1132,7 @@ export class Multisig {
|
|
|
1134
1132
|
const format = record.inclusionProof()
|
|
1135
1133
|
? NoteExportFormat.Full
|
|
1136
1134
|
: NoteExportFormat.Details;
|
|
1137
|
-
const noteFile = await
|
|
1135
|
+
const noteFile = await this.midenClient.notes.export(trimmedNoteId, { format });
|
|
1138
1136
|
return noteFile.serialize();
|
|
1139
1137
|
}
|
|
1140
1138
|
/**
|
|
@@ -1178,7 +1176,6 @@ export class Multisig {
|
|
|
1178
1176
|
* commitment for a details-only file
|
|
1179
1177
|
*/
|
|
1180
1178
|
async importNoteFromBytes(noteBytes) {
|
|
1181
|
-
const webClient = await this.getRawClient();
|
|
1182
1179
|
let noteFile;
|
|
1183
1180
|
try {
|
|
1184
1181
|
noteFile = NoteFile.deserialize(noteBytes);
|
|
@@ -1187,7 +1184,7 @@ export class Multisig {
|
|
|
1187
1184
|
const detail = err instanceof Error ? err.message : String(err);
|
|
1188
1185
|
throw new Error(`failed to decode note file: ${detail}`);
|
|
1189
1186
|
}
|
|
1190
|
-
return
|
|
1187
|
+
return this.midenClient.notes.import(noteFile);
|
|
1191
1188
|
}
|
|
1192
1189
|
/**
|
|
1193
1190
|
* Import a note file received out-of-band from a browser
|
|
@@ -1600,8 +1597,7 @@ export class Multisig {
|
|
|
1600
1597
|
* syncing first when this client is still below the block the request binds.
|
|
1601
1598
|
*/
|
|
1602
1599
|
async submitAtTip(request) {
|
|
1603
|
-
|
|
1604
|
-
await prepareTipExecution(webClient, request, () => retryRpcRead(() => webClient.syncState(), this.rpcConfig));
|
|
1600
|
+
await prepareTipExecution(this.midenClient, request, () => this.syncChain());
|
|
1605
1601
|
await this.proverWorkflow.submit(AccountId.fromHex(this._accountId), request);
|
|
1606
1602
|
}
|
|
1607
1603
|
/**
|
|
@@ -1610,13 +1606,11 @@ export class Multisig {
|
|
|
1610
1606
|
* store's sync height, which a node prunes about 50 blocks later.
|
|
1611
1607
|
*/
|
|
1612
1608
|
async syncChain() {
|
|
1613
|
-
|
|
1614
|
-
await retryRpcRead(() => webClient.syncState(), this.rpcConfig);
|
|
1609
|
+
await retryRpcRead(() => this.midenClient.syncChain(), this.rpcConfig);
|
|
1615
1610
|
}
|
|
1616
1611
|
/** {@link syncToBoundBlock} with this client's RPC retry policy. */
|
|
1617
1612
|
async syncToBoundBlock(boundBlockNum) {
|
|
1618
|
-
|
|
1619
|
-
await syncToBoundBlock(webClient, boundBlockNum, () => retryRpcRead(() => webClient.syncState(), this.rpcConfig));
|
|
1613
|
+
await syncToBoundBlock(this.midenClient, boundBlockNum, () => this.syncChain());
|
|
1620
1614
|
}
|
|
1621
1615
|
/**
|
|
1622
1616
|
* Create a proposal from a producer-built transaction the SDK does not model.
|
|
@@ -1637,9 +1631,8 @@ export class Multisig {
|
|
|
1637
1631
|
if (BUILTIN_PROPOSAL_TYPES.has(label)) {
|
|
1638
1632
|
throw new Error(`'${label}' is a built-in proposal type; use the typed proposal API instead`);
|
|
1639
1633
|
}
|
|
1640
|
-
const webClient = await this.getRawClient();
|
|
1641
1634
|
const request = deserializeTransactionRequest(transactionRequestBytes);
|
|
1642
|
-
const { summary, anchor } = await executeForSummary(
|
|
1635
|
+
const { summary, anchor } = await executeForSummary(this.midenClient, this._accountId, request);
|
|
1643
1636
|
const chainAnchor = chainAnchorToBase64(anchor);
|
|
1644
1637
|
anchor.free();
|
|
1645
1638
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
@@ -1695,8 +1688,7 @@ export class Multisig {
|
|
|
1695
1688
|
finally {
|
|
1696
1689
|
anchor.free();
|
|
1697
1690
|
}
|
|
1698
|
-
const
|
|
1699
|
-
const derived = await executeForSummaryAtTip(webClient, this._accountId, bindingRequest);
|
|
1691
|
+
const derived = await executeForSummaryAtTip(this.midenClient, this._accountId, bindingRequest);
|
|
1700
1692
|
const derivedCommitmentHex = normalizeHexWord(derived.toCommitment().toHex());
|
|
1701
1693
|
if (derivedCommitmentHex !== signedCommitmentHex) {
|
|
1702
1694
|
throw new Error(`Custom proposal binding mismatch: expected ${signedCommitmentHex}, got ${derivedCommitmentHex}`);
|
|
@@ -2124,7 +2116,6 @@ export class Multisig {
|
|
|
2124
2116
|
// to the block the summary binds; a cosigner that has only just loaded
|
|
2125
2117
|
// the account has not.
|
|
2126
2118
|
await this.syncToBoundBlock(binding.boundBlockNum);
|
|
2127
|
-
const webClient = await this.getRawClient();
|
|
2128
2119
|
// A consume-notes summary commits to *authenticated* consumption (see
|
|
2129
2120
|
// ensureNotesAuthenticated), which miden-client decides from this store
|
|
2130
2121
|
// alone. Put the store in that mode before the rebuild, or a cosigner
|
|
@@ -2134,7 +2125,7 @@ export class Multisig {
|
|
|
2134
2125
|
await this.ensureNotesAuthenticated(decodeEmbeddedConsumeNotes(proposal.metadata));
|
|
2135
2126
|
}
|
|
2136
2127
|
const request = await this.buildTransactionRequestFromMetadata(proposal.metadata, binding);
|
|
2137
|
-
const reconstructed = await executeForSummaryAtTip(
|
|
2128
|
+
const reconstructed = await executeForSummaryAtTip(this.midenClient, this._accountId, request);
|
|
2138
2129
|
const reconstructedCommitment = normalizeHexWord(reconstructed.toCommitment().toHex());
|
|
2139
2130
|
if (reconstructedCommitment !== txSummaryCommitment) {
|
|
2140
2131
|
throw new Error(`Invalid proposal: metadata does not match tx_summary for ${proposal.id}`);
|
|
@@ -2209,8 +2200,7 @@ export class Multisig {
|
|
|
2209
2200
|
if (expirationBlockNum === undefined) {
|
|
2210
2201
|
return;
|
|
2211
2202
|
}
|
|
2212
|
-
const
|
|
2213
|
-
const syncHeight = await webClient.getSyncHeight();
|
|
2203
|
+
const syncHeight = await this.midenClient.getSyncHeight();
|
|
2214
2204
|
if (syncHeight >= expirationBlockNum) {
|
|
2215
2205
|
throw new Error(`Proposal ${proposalId} approval expired at block ${expirationBlockNum}; the chain is at ` +
|
|
2216
2206
|
`block ${syncHeight}, so the collected signatures no longer authorize it`);
|
|
@@ -2239,20 +2229,19 @@ export class Multisig {
|
|
|
2239
2229
|
}
|
|
2240
2230
|
}
|
|
2241
2231
|
async buildTransactionRequestWithOptions(metadata, requestOptions) {
|
|
2242
|
-
const webClient = await this.getRawClient();
|
|
2243
2232
|
switch (metadata.proposalType) {
|
|
2244
2233
|
case 'add_signer':
|
|
2245
2234
|
case 'remove_signer':
|
|
2246
2235
|
case 'change_threshold': {
|
|
2247
|
-
const { request } = await buildUpdateSignersTransactionRequest(
|
|
2236
|
+
const { request } = await buildUpdateSignersTransactionRequest(this.midenClient, metadata.targetThreshold, metadata.targetSignerCommitments, requestOptions);
|
|
2248
2237
|
return request;
|
|
2249
2238
|
}
|
|
2250
2239
|
case 'switch_guardian': {
|
|
2251
|
-
const { request } = await buildUpdateGuardianTransactionRequest(
|
|
2240
|
+
const { request } = await buildUpdateGuardianTransactionRequest(this.midenClient, metadata.newGuardianPubkey, requestOptions);
|
|
2252
2241
|
return request;
|
|
2253
2242
|
}
|
|
2254
2243
|
case 'update_procedure_threshold': {
|
|
2255
|
-
const { request } = await buildUpdateProcedureThresholdTransactionRequest(
|
|
2244
|
+
const { request } = await buildUpdateProcedureThresholdTransactionRequest(this.midenClient, metadata.targetProcedure, metadata.targetThreshold, requestOptions);
|
|
2256
2245
|
return request;
|
|
2257
2246
|
}
|
|
2258
2247
|
case 'consume_notes': {
|
|
@@ -2260,7 +2249,7 @@ export class Multisig {
|
|
|
2260
2249
|
const version = metadata.metadataVersion;
|
|
2261
2250
|
if (version === CONSUME_NOTES_METADATA_VERSION_V2) {
|
|
2262
2251
|
const decoded = decodeEmbeddedConsumeNotes(metadata);
|
|
2263
|
-
const { request } = await buildConsumeNotesTransactionRequestFromNotes(
|
|
2252
|
+
const { request } = await buildConsumeNotesTransactionRequestFromNotes(this.midenClient, decoded, requestOptions);
|
|
2264
2253
|
return request;
|
|
2265
2254
|
}
|
|
2266
2255
|
if (version === undefined || version === 1) {
|
|
@@ -2269,13 +2258,13 @@ export class Multisig {
|
|
|
2269
2258
|
// operator which legacy shape was rejected.
|
|
2270
2259
|
throw new UnsupportedMetadataVersionError(version);
|
|
2271
2260
|
}
|
|
2272
|
-
const { request } = await buildConsumeNotesTransactionRequest(
|
|
2261
|
+
const { request } = await buildConsumeNotesTransactionRequest(this.midenClient, metadata.noteIds, requestOptions);
|
|
2273
2262
|
return request;
|
|
2274
2263
|
}
|
|
2275
2264
|
throw new UnsupportedMetadataVersionError(version);
|
|
2276
2265
|
}
|
|
2277
2266
|
case 'p2id': {
|
|
2278
|
-
const { request } = await buildP2idTransactionRequest(
|
|
2267
|
+
const { request } = await buildP2idTransactionRequest(this.midenClient, this._accountId, metadata.recipientId, metadata.faucetId, BigInt(metadata.amount), {
|
|
2279
2268
|
...requestOptions,
|
|
2280
2269
|
noteType: parseP2idNoteType(metadata.noteType),
|
|
2281
2270
|
reclaimHeight: metadata.reclaimHeight,
|