@openzeppelin/miden-multisig-client 0.13.4 → 0.14.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 +10 -13
- package/dist/account/builder.d.ts +3 -3
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +10 -10
- package/dist/account/builder.js.map +1 -1
- package/dist/account/builder.test.js +16 -14
- package/dist/account/builder.test.js.map +1 -1
- package/dist/account/masm/account-components/auth.d.ts +4 -4
- package/dist/account/masm/account-components/auth.d.ts.map +1 -1
- package/dist/account/masm/account-components/auth.js +8 -12
- package/dist/account/masm/account-components/auth.js.map +1 -1
- package/dist/account/masm/auth.d.ts +4 -4
- package/dist/account/masm/auth.d.ts.map +1 -1
- package/dist/account/masm/auth.js +226 -124
- package/dist/account/masm/auth.js.map +1 -1
- package/dist/account/storage.d.ts.map +1 -1
- package/dist/account/storage.js +24 -6
- package/dist/account/storage.js.map +1 -1
- package/dist/account/storage.test.d.ts +2 -0
- package/dist/account/storage.test.d.ts.map +1 -0
- package/dist/account/storage.test.js +73 -0
- package/dist/account/storage.test.js.map +1 -0
- package/dist/client.d.ts +9 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +24 -14
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +45 -8
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +6 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -12
- package/dist/index.js.map +1 -1
- package/dist/multisig/proposal/execution.d.ts +3 -2
- package/dist/multisig/proposal/execution.d.ts.map +1 -1
- package/dist/multisig/proposal/execution.js +8 -12
- package/dist/multisig/proposal/execution.js.map +1 -1
- package/dist/multisig.d.ts +7 -4
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +53 -35
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +17 -29
- package/dist/multisig.test.js.map +1 -1
- package/dist/procedures.d.ts +8 -8
- package/dist/procedures.js +8 -8
- package/dist/raw-client.d.ts +13 -0
- package/dist/raw-client.d.ts.map +1 -0
- package/dist/raw-client.js +42 -0
- package/dist/raw-client.js.map +1 -0
- package/dist/raw-client.test.d.ts +2 -0
- package/dist/raw-client.test.d.ts.map +1 -0
- package/dist/raw-client.test.js +89 -0
- package/dist/raw-client.test.js.map +1 -0
- package/dist/signers/ecdsa.d.ts +3 -1
- package/dist/signers/ecdsa.d.ts.map +1 -1
- package/dist/signers/ecdsa.js +15 -1
- package/dist/signers/ecdsa.js.map +1 -1
- package/dist/signers/ecdsa.test.js +27 -4
- package/dist/signers/ecdsa.test.js.map +1 -1
- package/dist/signers/falcon.d.ts +3 -1
- package/dist/signers/falcon.d.ts.map +1 -1
- package/dist/signers/falcon.js +15 -1
- package/dist/signers/falcon.js.map +1 -1
- package/dist/signers/falcon.test.js +39 -3
- package/dist/signers/falcon.test.js.map +1 -1
- package/dist/transaction/consumeNotes.d.ts +2 -2
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +5 -4
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/transaction/options.d.ts +1 -0
- package/dist/transaction/options.d.ts.map +1 -1
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js +7 -10
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/p2id.test.js +19 -9
- package/dist/transaction/p2id.test.js.map +1 -1
- package/dist/transaction/summary.d.ts +2 -2
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js +4 -2
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction/updateGuardian.d.ts +2 -2
- package/dist/transaction/updateGuardian.d.ts.map +1 -1
- package/dist/transaction/updateGuardian.js +8 -11
- package/dist/transaction/updateGuardian.js.map +1 -1
- package/dist/transaction/updateProcedureThreshold.d.ts +2 -2
- package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
- package/dist/transaction/updateProcedureThreshold.js +8 -10
- package/dist/transaction/updateProcedureThreshold.js.map +1 -1
- package/dist/transaction/updateSigners.d.ts +2 -2
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js +8 -10
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.test.js +6 -6
- package/dist/transaction.test.js.map +1 -1
- package/dist/utils/key.d.ts.map +1 -1
- package/dist/utils/key.js +3 -3
- package/dist/utils/key.js.map +1 -1
- package/dist/utils/key.test.js +4 -2
- package/dist/utils/key.test.js.map +1 -1
- package/dist/utils/signature.d.ts +2 -0
- package/dist/utils/signature.d.ts.map +1 -1
- package/dist/utils/signature.js +10 -5
- package/dist/utils/signature.js.map +1 -1
- package/dist/utils/signature.test.js +9 -9
- package/dist/utils/signature.test.js.map +1 -1
- package/masm/account_components/auth/multisig.masm +2 -3
- package/masm/account_components/auth/multisig_ecdsa.masm +2 -3
- package/masm/account_components/auth/multisig_guardian.masm +2 -3
- package/masm/account_components/auth/multisig_guardian_ecdsa.masm +2 -3
- package/masm/auth/guardian.masm +30 -8
- package/masm/auth/guardian_ecdsa.masm +24 -7
- package/masm/auth/multisig.masm +86 -55
- package/masm/auth/multisig_ecdsa.masm +86 -54
- package/package.json +3 -3
- package/src/account/builder.test.ts +16 -14
- package/src/account/builder.ts +13 -12
- package/src/account/masm/account-components/auth.ts +8 -12
- package/src/account/masm/auth.ts +226 -124
- package/src/account/storage.test.ts +90 -0
- package/src/account/storage.ts +27 -6
- package/src/client.test.ts +52 -8
- package/src/client.ts +39 -16
- package/src/index.ts +6 -12
- package/src/multisig/proposal/execution.ts +18 -14
- package/src/multisig.test.ts +17 -29
- package/src/multisig.ts +61 -36
- package/src/procedures.ts +8 -8
- package/src/raw-client.test.ts +133 -0
- package/src/raw-client.ts +75 -0
- package/src/signers/ecdsa.test.ts +35 -4
- package/src/signers/ecdsa.ts +18 -2
- package/src/signers/falcon.test.ts +50 -3
- package/src/signers/falcon.ts +18 -2
- package/src/transaction/consumeNotes.ts +16 -5
- package/src/transaction/options.ts +1 -0
- package/src/transaction/p2id.test.ts +19 -9
- package/src/transaction/p2id.ts +8 -13
- package/src/transaction/summary.ts +11 -4
- package/src/transaction/updateGuardian.ts +22 -14
- package/src/transaction/updateProcedureThreshold.ts +20 -15
- package/src/transaction/updateSigners.ts +22 -14
- package/src/transaction.test.ts +6 -6
- package/src/utils/key.test.ts +4 -2
- package/src/utils/key.ts +7 -3
- package/src/utils/signature.test.ts +10 -10
- package/src/utils/signature.ts +12 -5
package/src/multisig.ts
CHANGED
|
@@ -16,7 +16,12 @@ import type {
|
|
|
16
16
|
ProposalType,
|
|
17
17
|
} from './types.js';
|
|
18
18
|
import type { ProcedureName } from './procedures.js';
|
|
19
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
MidenClient,
|
|
21
|
+
TransactionProver,
|
|
22
|
+
TransactionRequest,
|
|
23
|
+
WasmWebClient,
|
|
24
|
+
} from '@miden-sdk/miden-sdk';
|
|
20
25
|
import {
|
|
21
26
|
Account,
|
|
22
27
|
AccountId,
|
|
@@ -53,6 +58,7 @@ import { AccountInspector } from './inspector.js';
|
|
|
53
58
|
import { ProposalFactory } from './proposal/factory.js';
|
|
54
59
|
import { ProposalMetadataCodec } from './proposal/metadata.js';
|
|
55
60
|
import { ProposalSignatures } from './proposal/signatures.js';
|
|
61
|
+
import { getRawMidenClient, getTransactionProver } from './raw-client.js';
|
|
56
62
|
|
|
57
63
|
/**
|
|
58
64
|
* Result of fetching account state from GUARDIAN.
|
|
@@ -87,7 +93,9 @@ export class Multisig {
|
|
|
87
93
|
|
|
88
94
|
private guardian: GuardianHttpClient;
|
|
89
95
|
private readonly signer: Signer;
|
|
90
|
-
private readonly
|
|
96
|
+
private readonly midenClient: MidenClient;
|
|
97
|
+
private readonly rawClientPromise: Promise<WasmWebClient>;
|
|
98
|
+
private readonly transactionProver: TransactionProver | null;
|
|
91
99
|
private readonly _accountId: string;
|
|
92
100
|
private readonly midenRpcEndpoint?: string;
|
|
93
101
|
private proposals: Map<string, Proposal> = new Map();
|
|
@@ -97,7 +105,7 @@ export class Multisig {
|
|
|
97
105
|
config: MultisigConfig,
|
|
98
106
|
guardian: GuardianHttpClient,
|
|
99
107
|
signer: Signer,
|
|
100
|
-
|
|
108
|
+
midenClient: MidenClient,
|
|
101
109
|
accountId?: string,
|
|
102
110
|
midenRpcEndpoint?: string
|
|
103
111
|
) {
|
|
@@ -111,9 +119,11 @@ export class Multisig {
|
|
|
111
119
|
);
|
|
112
120
|
this.guardian = guardian;
|
|
113
121
|
this.signer = signer;
|
|
114
|
-
this.
|
|
122
|
+
this.midenClient = midenClient;
|
|
115
123
|
this._accountId = accountId ?? (account ? accountIdToHex(account) : '');
|
|
116
124
|
this.midenRpcEndpoint = midenRpcEndpoint;
|
|
125
|
+
this.rawClientPromise = getRawMidenClient(midenClient, midenRpcEndpoint);
|
|
126
|
+
this.transactionProver = getTransactionProver(midenClient);
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
private getMidenRpcEndpoint(): string {
|
|
@@ -123,6 +133,10 @@ export class Multisig {
|
|
|
123
133
|
return this.midenRpcEndpoint;
|
|
124
134
|
}
|
|
125
135
|
|
|
136
|
+
private async getRawClient(): Promise<WasmWebClient> {
|
|
137
|
+
return this.rawClientPromise;
|
|
138
|
+
}
|
|
139
|
+
|
|
126
140
|
private proposalFactory(): ProposalFactory {
|
|
127
141
|
return new ProposalFactory({
|
|
128
142
|
accountId: this._accountId,
|
|
@@ -228,7 +242,7 @@ export class Multisig {
|
|
|
228
242
|
}
|
|
229
243
|
|
|
230
244
|
/**
|
|
231
|
-
* Sync account state from GUARDIAN into the local
|
|
245
|
+
* Sync account state from GUARDIAN into the local Miden client store.
|
|
232
246
|
*
|
|
233
247
|
* If the GUARDIAN commitment differs from the local commitment (or the account
|
|
234
248
|
* is missing locally), the local store is overwritten with the GUARDIAN state.
|
|
@@ -236,19 +250,20 @@ export class Multisig {
|
|
|
236
250
|
async syncState(): Promise<AccountState> {
|
|
237
251
|
const state = await this.fetchState();
|
|
238
252
|
const accountId = AccountId.fromHex(this._accountId);
|
|
239
|
-
const
|
|
253
|
+
const webClient = await this.getRawClient();
|
|
254
|
+
const localAccount = await webClient.getAccount(accountId);
|
|
240
255
|
let accountForConfigRefresh: Account | null = localAccount ?? null;
|
|
241
256
|
|
|
242
257
|
const guardianCommitment = normalizeHexWord(state.commitment);
|
|
243
258
|
const localCommitment = localAccount
|
|
244
|
-
? normalizeHexWord(localAccount.
|
|
259
|
+
? normalizeHexWord(localAccount.to_commitment().toHex())
|
|
245
260
|
: null;
|
|
246
261
|
|
|
247
262
|
if (!localAccount || localCommitment !== guardianCommitment) {
|
|
248
263
|
const accountBytes = base64ToUint8Array(state.stateDataBase64);
|
|
249
264
|
const incomingAccount = Account.deserialize(accountBytes);
|
|
250
265
|
await this.ensureSafeToOverwriteLocalState(incomingAccount, localAccount);
|
|
251
|
-
await
|
|
266
|
+
await webClient.newAccount(incomingAccount, true);
|
|
252
267
|
accountForConfigRefresh = incomingAccount;
|
|
253
268
|
}
|
|
254
269
|
|
|
@@ -259,7 +274,8 @@ export class Multisig {
|
|
|
259
274
|
|
|
260
275
|
async verifyStateCommitment(): Promise<AccountStateVerificationResult> {
|
|
261
276
|
const accountId = AccountId.fromHex(this._accountId);
|
|
262
|
-
const
|
|
277
|
+
const webClient = await this.getRawClient();
|
|
278
|
+
const localAccount = await webClient.getAccount(accountId);
|
|
263
279
|
|
|
264
280
|
if (!localAccount) {
|
|
265
281
|
throw new Error(
|
|
@@ -267,7 +283,7 @@ export class Multisig {
|
|
|
267
283
|
);
|
|
268
284
|
}
|
|
269
285
|
|
|
270
|
-
const localCommitment = normalizeHexWord(localAccount.
|
|
286
|
+
const localCommitment = normalizeHexWord(localAccount.to_commitment().toHex());
|
|
271
287
|
const onChainCommitment = await this.getOnChainCommitment(accountId);
|
|
272
288
|
|
|
273
289
|
if (!onChainCommitment) {
|
|
@@ -308,7 +324,7 @@ export class Multisig {
|
|
|
308
324
|
return;
|
|
309
325
|
}
|
|
310
326
|
|
|
311
|
-
const incomingCommitment = normalizeHexWord(incomingAccount.
|
|
327
|
+
const incomingCommitment = normalizeHexWord(incomingAccount.to_commitment().toHex());
|
|
312
328
|
if (incomingCommitment !== onChainCommitment) {
|
|
313
329
|
throw new Error(
|
|
314
330
|
`Refusing to overwrite local state: incoming commitment does not match on-chain commitment for account ${this._accountId}`
|
|
@@ -472,17 +488,18 @@ export class Multisig {
|
|
|
472
488
|
nonce?: number,
|
|
473
489
|
newThreshold?: number,
|
|
474
490
|
): Promise<Proposal> {
|
|
491
|
+
const webClient = await this.getRawClient();
|
|
475
492
|
const targetThreshold = newThreshold ?? this.threshold;
|
|
476
493
|
const targetSignerCommitments = [...this.signerCommitments, newCommitment];
|
|
477
494
|
|
|
478
495
|
const { request, salt } = await buildUpdateSignersTransactionRequest(
|
|
479
|
-
|
|
496
|
+
webClient,
|
|
480
497
|
targetThreshold,
|
|
481
498
|
targetSignerCommitments,
|
|
482
499
|
{ signatureScheme: this.signer.scheme },
|
|
483
500
|
);
|
|
484
501
|
|
|
485
|
-
const summary = await executeForSummary(
|
|
502
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
486
503
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
487
504
|
const proposalNonce = nonce ?? Date.now();
|
|
488
505
|
|
|
@@ -510,6 +527,7 @@ export class Multisig {
|
|
|
510
527
|
nonce?: number,
|
|
511
528
|
newThreshold?: number,
|
|
512
529
|
): Promise<Proposal> {
|
|
530
|
+
const webClient = await this.getRawClient();
|
|
513
531
|
const normalizedRemove = signerToRemove.toLowerCase();
|
|
514
532
|
const targetSignerCommitments = this.signerCommitments.filter(
|
|
515
533
|
(c) => c.toLowerCase() !== normalizedRemove
|
|
@@ -531,13 +549,13 @@ export class Multisig {
|
|
|
531
549
|
}
|
|
532
550
|
|
|
533
551
|
const { request, salt } = await buildUpdateSignersTransactionRequest(
|
|
534
|
-
|
|
552
|
+
webClient,
|
|
535
553
|
targetThreshold,
|
|
536
554
|
targetSignerCommitments,
|
|
537
555
|
{ signatureScheme: this.signer.scheme },
|
|
538
556
|
);
|
|
539
557
|
|
|
540
|
-
const summary = await executeForSummary(
|
|
558
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
541
559
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
542
560
|
const proposalNonce = nonce ?? Date.now();
|
|
543
561
|
|
|
@@ -563,6 +581,7 @@ export class Multisig {
|
|
|
563
581
|
newThreshold: number,
|
|
564
582
|
nonce?: number,
|
|
565
583
|
): Promise<Proposal> {
|
|
584
|
+
const webClient = await this.getRawClient();
|
|
566
585
|
if (newThreshold < 1 || newThreshold > this.signerCommitments.length) {
|
|
567
586
|
throw new Error(
|
|
568
587
|
`Invalid threshold ${newThreshold}. Must be between 1 and ${this.signerCommitments.length}`
|
|
@@ -574,13 +593,13 @@ export class Multisig {
|
|
|
574
593
|
}
|
|
575
594
|
|
|
576
595
|
const { request, salt } = await buildUpdateSignersTransactionRequest(
|
|
577
|
-
|
|
596
|
+
webClient,
|
|
578
597
|
newThreshold,
|
|
579
598
|
this.signerCommitments,
|
|
580
599
|
{ signatureScheme: this.signer.scheme },
|
|
581
600
|
);
|
|
582
601
|
|
|
583
|
-
const summary = await executeForSummary(
|
|
602
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
584
603
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
585
604
|
const proposalNonce = nonce ?? Date.now();
|
|
586
605
|
|
|
@@ -601,6 +620,7 @@ export class Multisig {
|
|
|
601
620
|
targetThreshold: number,
|
|
602
621
|
nonce?: number,
|
|
603
622
|
): Promise<Proposal> {
|
|
623
|
+
const webClient = await this.getRawClient();
|
|
604
624
|
if (targetThreshold < 0 || targetThreshold > this.signerCommitments.length) {
|
|
605
625
|
throw new Error(
|
|
606
626
|
`Invalid threshold ${targetThreshold}. Must be between 0 and ${this.signerCommitments.length}`
|
|
@@ -619,13 +639,13 @@ export class Multisig {
|
|
|
619
639
|
}
|
|
620
640
|
|
|
621
641
|
const { request, salt } = await buildUpdateProcedureThresholdTransactionRequest(
|
|
622
|
-
|
|
642
|
+
webClient,
|
|
623
643
|
targetProcedure,
|
|
624
644
|
targetThreshold,
|
|
625
645
|
{ signatureScheme: this.signer.scheme },
|
|
626
646
|
);
|
|
627
647
|
|
|
628
|
-
const summary = await executeForSummary(
|
|
648
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
629
649
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
630
650
|
const proposalNonce = nonce ?? Date.now();
|
|
631
651
|
const action = targetThreshold === 0
|
|
@@ -656,15 +676,16 @@ export class Multisig {
|
|
|
656
676
|
newGuardianPubkey: string,
|
|
657
677
|
nonce?: number,
|
|
658
678
|
): Promise<Proposal> {
|
|
679
|
+
const webClient = await this.getRawClient();
|
|
659
680
|
await this.verifyGuardianEndpointCommitment(newGuardianEndpoint, newGuardianPubkey);
|
|
660
681
|
|
|
661
682
|
const { request, salt } = await buildUpdateGuardianTransactionRequest(
|
|
662
|
-
|
|
683
|
+
webClient,
|
|
663
684
|
newGuardianPubkey,
|
|
664
685
|
{ signatureScheme: this.signer.scheme },
|
|
665
686
|
);
|
|
666
687
|
|
|
667
|
-
const summary = await executeForSummary(
|
|
688
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
668
689
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
669
690
|
const proposalNonce = nonce ?? Date.now();
|
|
670
691
|
|
|
@@ -702,13 +723,14 @@ export class Multisig {
|
|
|
702
723
|
noteIds: string[],
|
|
703
724
|
nonce?: number,
|
|
704
725
|
): Promise<Proposal> {
|
|
726
|
+
const webClient = await this.getRawClient();
|
|
705
727
|
if (noteIds.length === 0) {
|
|
706
728
|
throw new Error('At least one note ID is required');
|
|
707
729
|
}
|
|
708
730
|
|
|
709
|
-
const { request, salt } = await buildConsumeNotesTransactionRequest(
|
|
731
|
+
const { request, salt } = await buildConsumeNotesTransactionRequest(webClient, noteIds);
|
|
710
732
|
|
|
711
|
-
const summary = await executeForSummary(
|
|
733
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
712
734
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
713
735
|
const proposalNonce = nonce ?? Date.now();
|
|
714
736
|
|
|
@@ -737,6 +759,7 @@ export class Multisig {
|
|
|
737
759
|
amount: bigint,
|
|
738
760
|
nonce?: number,
|
|
739
761
|
): Promise<Proposal> {
|
|
762
|
+
const webClient = await this.getRawClient();
|
|
740
763
|
if (amount <= 0n) {
|
|
741
764
|
throw new Error('Amount must be greater than 0');
|
|
742
765
|
}
|
|
@@ -748,7 +771,7 @@ export class Multisig {
|
|
|
748
771
|
amount,
|
|
749
772
|
);
|
|
750
773
|
|
|
751
|
-
const summary = await executeForSummary(
|
|
774
|
+
const summary = await executeForSummary(webClient, this._accountId, request);
|
|
752
775
|
const summaryBase64 = uint8ArrayToBase64(summary.serialize());
|
|
753
776
|
const proposalNonce = nonce ?? Date.now();
|
|
754
777
|
|
|
@@ -773,9 +796,10 @@ export class Multisig {
|
|
|
773
796
|
*/
|
|
774
797
|
async getConsumableNotes(): Promise<ConsumableNote[]> {
|
|
775
798
|
const accountId = AccountId.fromHex(this._accountId);
|
|
799
|
+
const webClient = await this.getRawClient();
|
|
776
800
|
|
|
777
801
|
// Get consumable notes for this account
|
|
778
|
-
const consumableRecords = await
|
|
802
|
+
const consumableRecords = await webClient.getConsumableNotes(accountId);
|
|
779
803
|
|
|
780
804
|
// Convert to our simplified ConsumableNote type
|
|
781
805
|
const notes: ConsumableNote[] = [];
|
|
@@ -880,10 +904,7 @@ export class Multisig {
|
|
|
880
904
|
const { metadata, finalRequest, proposal } = await this.prepareProposalExecution(proposalId);
|
|
881
905
|
|
|
882
906
|
const accountId = AccountId.fromHex(this._accountId);
|
|
883
|
-
|
|
884
|
-
const proven = await this.webClient.proveTransaction(result, null);
|
|
885
|
-
const submissionHeight = await this.webClient.submitProvenTransaction(proven, result);
|
|
886
|
-
await this.webClient.applyTransaction(result, submissionHeight);
|
|
907
|
+
await this.midenClient.transactions.submit(accountId, finalRequest);
|
|
887
908
|
|
|
888
909
|
if (metadata.proposalType === 'switch_guardian') {
|
|
889
910
|
if (!metadata.newGuardianEndpoint || !metadata.newGuardianPubkey) {
|
|
@@ -891,9 +912,10 @@ export class Multisig {
|
|
|
891
912
|
}
|
|
892
913
|
|
|
893
914
|
try {
|
|
894
|
-
await this.
|
|
915
|
+
const webClient = await this.getRawClient();
|
|
916
|
+
await webClient.syncState();
|
|
895
917
|
|
|
896
|
-
const updatedAccount = await
|
|
918
|
+
const updatedAccount = await webClient.getAccount(accountId);
|
|
897
919
|
if (!updatedAccount) {
|
|
898
920
|
throw new Error(
|
|
899
921
|
`Updated account ${this._accountId} is missing from local client`
|
|
@@ -1249,7 +1271,8 @@ export class Multisig {
|
|
|
1249
1271
|
: summary.salt();
|
|
1250
1272
|
|
|
1251
1273
|
const request = await this.buildTransactionRequestFromMetadata(proposal.metadata, salt);
|
|
1252
|
-
const
|
|
1274
|
+
const webClient = await this.getRawClient();
|
|
1275
|
+
const reconstructed = await executeForSummary(webClient, this._accountId, request);
|
|
1253
1276
|
const reconstructedCommitment = normalizeHexWord(reconstructed.toCommitment().toHex());
|
|
1254
1277
|
|
|
1255
1278
|
if (reconstructedCommitment !== txSummaryCommitment) {
|
|
@@ -1264,12 +1287,14 @@ export class Multisig {
|
|
|
1264
1287
|
salt: Word,
|
|
1265
1288
|
signatureAdviceMap?: AdviceMap,
|
|
1266
1289
|
): Promise<TransactionRequest> {
|
|
1290
|
+
const webClient = await this.getRawClient();
|
|
1291
|
+
|
|
1267
1292
|
switch (metadata.proposalType) {
|
|
1268
1293
|
case 'add_signer':
|
|
1269
1294
|
case 'remove_signer':
|
|
1270
1295
|
case 'change_threshold': {
|
|
1271
1296
|
const { request } = await buildUpdateSignersTransactionRequest(
|
|
1272
|
-
|
|
1297
|
+
webClient,
|
|
1273
1298
|
metadata.targetThreshold,
|
|
1274
1299
|
metadata.targetSignerCommitments,
|
|
1275
1300
|
{ salt, signatureAdviceMap, signatureScheme: this.signer.scheme }
|
|
@@ -1278,7 +1303,7 @@ export class Multisig {
|
|
|
1278
1303
|
}
|
|
1279
1304
|
case 'switch_guardian': {
|
|
1280
1305
|
const { request } = await buildUpdateGuardianTransactionRequest(
|
|
1281
|
-
|
|
1306
|
+
webClient,
|
|
1282
1307
|
metadata.newGuardianPubkey,
|
|
1283
1308
|
{ salt, signatureAdviceMap, signatureScheme: this.signer.scheme }
|
|
1284
1309
|
);
|
|
@@ -1286,7 +1311,7 @@ export class Multisig {
|
|
|
1286
1311
|
}
|
|
1287
1312
|
case 'update_procedure_threshold': {
|
|
1288
1313
|
const { request } = await buildUpdateProcedureThresholdTransactionRequest(
|
|
1289
|
-
|
|
1314
|
+
webClient,
|
|
1290
1315
|
metadata.targetProcedure,
|
|
1291
1316
|
metadata.targetThreshold,
|
|
1292
1317
|
{ salt, signatureAdviceMap, signatureScheme: this.signer.scheme }
|
|
@@ -1295,7 +1320,7 @@ export class Multisig {
|
|
|
1295
1320
|
}
|
|
1296
1321
|
case 'consume_notes': {
|
|
1297
1322
|
const { request } = await buildConsumeNotesTransactionRequest(
|
|
1298
|
-
|
|
1323
|
+
webClient,
|
|
1299
1324
|
metadata.noteIds,
|
|
1300
1325
|
{ salt, signatureAdviceMap }
|
|
1301
1326
|
);
|
package/src/procedures.ts
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* human-readable encoding and should not be copied into this table.
|
|
12
12
|
*/
|
|
13
13
|
export const PROCEDURE_ROOTS = {
|
|
14
|
-
update_signers: '
|
|
15
|
-
update_procedure_threshold: '
|
|
16
|
-
auth_tx: '
|
|
17
|
-
update_guardian: '
|
|
18
|
-
verify_guardian: '
|
|
19
|
-
send_asset: '
|
|
20
|
-
receive_asset: '
|
|
14
|
+
update_signers: '0x3d382ad461f9914c487c6fe908991d088eb54ecbd4aa8560ef79c66c3746bf19',
|
|
15
|
+
update_procedure_threshold: '0x1f43e9d56ceff5d547ffdcb89896fb38cae0be1b74d9235ed2b4aa525df85f8d',
|
|
16
|
+
auth_tx: '0x415530d7169f849d7219e810065f9119bba9af2c55070de0bf4f082a1c0aea5c',
|
|
17
|
+
update_guardian: '0xc8ea876f1837e5cd1d6031becdbd40ce262ecd55930d65400f6890a37149d80c',
|
|
18
|
+
verify_guardian: '0x9bc6e7b25c8dbaa29d6ad41e354a545dd0a4bac7f3a521bb5195ba101f0213cc',
|
|
19
|
+
send_asset: '0x6d30df4312a2c44ec842db1bee227cc045396ca91e2c47d756dcb607f2bf5f89',
|
|
20
|
+
receive_asset: '0x75f638c65584d058542bcf4674b066ae394183021bc9b44dc2fdd97d52f9bcfb',
|
|
21
21
|
} as const;
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -34,7 +34,7 @@ export type ProcedureName = keyof typeof PROCEDURE_ROOTS;
|
|
|
34
34
|
* @example
|
|
35
35
|
* ```typescript
|
|
36
36
|
* const root = getProcedureRoot('send_asset');
|
|
37
|
-
* // '
|
|
37
|
+
* // '0x6d30df4312a2c44ec842db1bee227cc045396ca91e2c47d756dcb607f2bf5f89'
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
40
|
export function getProcedureRoot(name: ProcedureName): string {
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const { mockCreateClient } = vi.hoisted(() => ({
|
|
4
|
+
mockCreateClient: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
8
|
+
WasmWebClient: {
|
|
9
|
+
createClient: mockCreateClient,
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
compileTxScript,
|
|
15
|
+
getRawMidenClient,
|
|
16
|
+
getTransactionProver,
|
|
17
|
+
resolveMidenRpcEndpoint,
|
|
18
|
+
} from './raw-client.js';
|
|
19
|
+
|
|
20
|
+
describe('raw-client', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
mockCreateClient.mockReset();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('defaults RPC endpoint resolution to devnet', () => {
|
|
26
|
+
expect(resolveMidenRpcEndpoint()).toBe('https://rpc.devnet.miden.io');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns the default prover from a public MidenClient', () => {
|
|
30
|
+
const prover = { kind: 'devnet-prover' };
|
|
31
|
+
const client = {
|
|
32
|
+
accounts: {},
|
|
33
|
+
sync: vi.fn(),
|
|
34
|
+
defaultProver: prover,
|
|
35
|
+
storeIdentifier: vi.fn(() => 'browser-db'),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
expect(getTransactionProver(client as any)).toBe(prover);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns null for raw web clients', () => {
|
|
42
|
+
const rawClient = {
|
|
43
|
+
executeTransaction: vi.fn(),
|
|
44
|
+
proveTransaction: vi.fn(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
expect(getTransactionProver(rawClient as any)).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('creates and caches a raw client for a public MidenClient', async () => {
|
|
51
|
+
const rawClient = { kind: 'raw-client' };
|
|
52
|
+
const client = {
|
|
53
|
+
accounts: {},
|
|
54
|
+
sync: vi.fn(),
|
|
55
|
+
defaultProver: null,
|
|
56
|
+
storeIdentifier: vi.fn(() => 'browser-db'),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
mockCreateClient.mockResolvedValue(rawClient);
|
|
60
|
+
|
|
61
|
+
await expect(
|
|
62
|
+
getRawMidenClient(client as any, 'https://rpc.devnet.miden.io'),
|
|
63
|
+
).resolves.toBe(rawClient);
|
|
64
|
+
await expect(
|
|
65
|
+
getRawMidenClient(client as any, 'https://rpc.devnet.miden.io'),
|
|
66
|
+
).resolves.toBe(rawClient);
|
|
67
|
+
|
|
68
|
+
expect(mockCreateClient).toHaveBeenCalledTimes(1);
|
|
69
|
+
expect(mockCreateClient).toHaveBeenCalledWith(
|
|
70
|
+
'https://rpc.devnet.miden.io',
|
|
71
|
+
undefined,
|
|
72
|
+
undefined,
|
|
73
|
+
'browser-db',
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('uses the public compile resource when available', async () => {
|
|
78
|
+
const script = { kind: 'compiled-script' };
|
|
79
|
+
const client = {
|
|
80
|
+
accounts: {},
|
|
81
|
+
sync: vi.fn(),
|
|
82
|
+
compile: {
|
|
83
|
+
txScript: vi.fn().mockResolvedValue(script),
|
|
84
|
+
},
|
|
85
|
+
storeIdentifier: vi.fn(() => 'browser-db'),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
await expect(
|
|
89
|
+
compileTxScript(
|
|
90
|
+
client as any,
|
|
91
|
+
'begin end',
|
|
92
|
+
[{ namespace: 'auth::multisig', code: 'export.foo' }],
|
|
93
|
+
),
|
|
94
|
+
).resolves.toBe(script);
|
|
95
|
+
|
|
96
|
+
expect(client.compile.txScript).toHaveBeenCalledWith({
|
|
97
|
+
code: 'begin end',
|
|
98
|
+
libraries: [{ namespace: 'auth::multisig', code: 'export.foo' }],
|
|
99
|
+
});
|
|
100
|
+
expect(mockCreateClient).not.toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('falls back to raw client compilation for low-level callers', async () => {
|
|
104
|
+
const compiledScript = { kind: 'compiled-script' };
|
|
105
|
+
const builtLibrary = { kind: 'built-library' };
|
|
106
|
+
const builder = {
|
|
107
|
+
buildLibrary: vi.fn().mockReturnValue(builtLibrary),
|
|
108
|
+
linkDynamicLibrary: vi.fn(),
|
|
109
|
+
linkStaticLibrary: vi.fn(),
|
|
110
|
+
compileTxScript: vi.fn().mockReturnValue(compiledScript),
|
|
111
|
+
};
|
|
112
|
+
const rawClient = {
|
|
113
|
+
createCodeBuilder: vi.fn().mockReturnValue(builder),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
await expect(
|
|
117
|
+
compileTxScript(
|
|
118
|
+
rawClient as any,
|
|
119
|
+
'begin end',
|
|
120
|
+
[
|
|
121
|
+
{ namespace: 'auth::multisig', code: 'export.foo' },
|
|
122
|
+
{ namespace: 'auth::guardian', code: 'export.bar', linking: 'static' },
|
|
123
|
+
],
|
|
124
|
+
),
|
|
125
|
+
).resolves.toBe(compiledScript);
|
|
126
|
+
|
|
127
|
+
expect(builder.buildLibrary).toHaveBeenNthCalledWith(1, 'auth::multisig', 'export.foo');
|
|
128
|
+
expect(builder.buildLibrary).toHaveBeenNthCalledWith(2, 'auth::guardian', 'export.bar');
|
|
129
|
+
expect(builder.linkDynamicLibrary).toHaveBeenCalledWith(builtLibrary);
|
|
130
|
+
expect(builder.linkStaticLibrary).toHaveBeenCalledWith(builtLibrary);
|
|
131
|
+
expect(builder.compileTxScript).toHaveBeenCalledWith('begin end');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MidenClient,
|
|
3
|
+
type TransactionProver,
|
|
4
|
+
type TransactionScript,
|
|
5
|
+
WasmWebClient,
|
|
6
|
+
} from '@miden-sdk/miden-sdk';
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_MIDEN_RPC_URL = 'https://rpc.devnet.miden.io';
|
|
9
|
+
|
|
10
|
+
export type RawClientSource = MidenClient | WasmWebClient;
|
|
11
|
+
export interface ScriptLibrarySource {
|
|
12
|
+
namespace: string;
|
|
13
|
+
code: string;
|
|
14
|
+
linking?: 'dynamic' | 'static';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const rawClientCache = new WeakMap<MidenClient, Promise<WasmWebClient>>();
|
|
18
|
+
|
|
19
|
+
export function resolveMidenRpcEndpoint(endpoint?: string): string {
|
|
20
|
+
return endpoint ?? DEFAULT_MIDEN_RPC_URL;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isPublicMidenClient(client: RawClientSource): client is MidenClient {
|
|
24
|
+
return 'accounts' in client && 'sync' in client;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getRawMidenClient(
|
|
28
|
+
client: RawClientSource,
|
|
29
|
+
rpcUrl?: string,
|
|
30
|
+
): Promise<WasmWebClient> {
|
|
31
|
+
if (!isPublicMidenClient(client)) {
|
|
32
|
+
return Promise.resolve(client);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cached = rawClientCache.get(client);
|
|
36
|
+
if (cached) {
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const rawClient = WasmWebClient.createClient(
|
|
41
|
+
resolveMidenRpcEndpoint(rpcUrl),
|
|
42
|
+
undefined,
|
|
43
|
+
undefined,
|
|
44
|
+
client.storeIdentifier(),
|
|
45
|
+
);
|
|
46
|
+
rawClientCache.set(client, rawClient);
|
|
47
|
+
return rawClient;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getTransactionProver(client: RawClientSource): TransactionProver | null {
|
|
51
|
+
return isPublicMidenClient(client) ? client.defaultProver : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function compileTxScript(
|
|
55
|
+
client: RawClientSource,
|
|
56
|
+
code: string,
|
|
57
|
+
libraries: ScriptLibrarySource[] = [],
|
|
58
|
+
rpcUrl?: string,
|
|
59
|
+
): Promise<TransactionScript> {
|
|
60
|
+
if (isPublicMidenClient(client)) {
|
|
61
|
+
return client.compile.txScript({ code, libraries });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const rawClient = await getRawMidenClient(client, rpcUrl);
|
|
65
|
+
const builder = rawClient.createCodeBuilder();
|
|
66
|
+
for (const library of libraries) {
|
|
67
|
+
const builtLibrary = builder.buildLibrary(library.namespace, library.code);
|
|
68
|
+
if (library.linking === 'static') {
|
|
69
|
+
builder.linkStaticLibrary(builtLibrary);
|
|
70
|
+
} else {
|
|
71
|
+
builder.linkDynamicLibrary(builtLibrary);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return builder.compileTxScript(code);
|
|
75
|
+
}
|
|
@@ -6,10 +6,12 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
6
6
|
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5]),
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
const mockCommitment = {
|
|
10
|
+
toHex: () => '0x' + 'a'.repeat(64),
|
|
11
|
+
};
|
|
12
|
+
|
|
9
13
|
const mockPublicKey = {
|
|
10
|
-
toCommitment: () =>
|
|
11
|
-
toHex: () => '0x' + 'a'.repeat(64),
|
|
12
|
-
}),
|
|
14
|
+
toCommitment: () => mockCommitment,
|
|
13
15
|
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
|
14
16
|
};
|
|
15
17
|
|
|
@@ -27,7 +29,8 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
27
29
|
})),
|
|
28
30
|
},
|
|
29
31
|
AccountId: {
|
|
30
|
-
fromHex: vi.fn(() => ({
|
|
32
|
+
fromHex: vi.fn((hex: string) => ({
|
|
33
|
+
toString: () => hex,
|
|
31
34
|
prefix: () => ({ asInt: () => BigInt(1) }),
|
|
32
35
|
suffix: () => ({ asInt: () => BigInt(2) }),
|
|
33
36
|
})),
|
|
@@ -77,4 +80,32 @@ describe('EcdsaSigner', () => {
|
|
|
77
80
|
expect(mockSecretKey.sign).toHaveBeenCalled();
|
|
78
81
|
expect(Rpo256.hashElements).toHaveBeenCalledTimes(2);
|
|
79
82
|
});
|
|
83
|
+
|
|
84
|
+
it('skips rebinding when the signer key is already attached to the same account', async () => {
|
|
85
|
+
const accountId = '0x' + 'd'.repeat(30);
|
|
86
|
+
const midenClient = {
|
|
87
|
+
keystore: {
|
|
88
|
+
getAccountId: vi.fn().mockResolvedValue({ toString: () => accountId }),
|
|
89
|
+
insert: vi.fn().mockResolvedValue(undefined),
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
await signer.bindAccountKey(midenClient as never, accountId);
|
|
94
|
+
|
|
95
|
+
expect(midenClient.keystore.insert).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('throws when the signer key is already attached to a different account', async () => {
|
|
99
|
+
const midenClient = {
|
|
100
|
+
keystore: {
|
|
101
|
+
getAccountId: vi.fn().mockResolvedValue({ toString: () => '0x' + 'e'.repeat(30) }),
|
|
102
|
+
insert: vi.fn().mockResolvedValue(undefined),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
await expect(
|
|
107
|
+
signer.bindAccountKey(midenClient as never, '0x' + 'd'.repeat(30)),
|
|
108
|
+
).rejects.toThrow('already bound to account');
|
|
109
|
+
expect(midenClient.keystore.insert).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
80
111
|
});
|