@openzeppelin/miden-multisig-client 0.13.1 → 0.13.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 +98 -119
- package/dist/account/builder.d.ts +18 -0
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +59 -29
- package/dist/account/builder.js.map +1 -1
- package/dist/account/builder.test.js +108 -32
- package/dist/account/builder.test.js.map +1 -1
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/index.js +1 -1
- package/dist/account/index.js.map +1 -1
- package/dist/account/masm/account-components/auth.d.ts +5 -0
- package/dist/account/masm/account-components/auth.d.ts.map +1 -0
- package/dist/account/masm/account-components/auth.js +67 -0
- package/dist/account/masm/account-components/auth.js.map +1 -0
- package/dist/account/masm/auth.d.ts +5 -0
- package/dist/account/masm/auth.d.ts.map +1 -0
- package/dist/account/masm/auth.js +1395 -0
- package/dist/account/masm/auth.js.map +1 -0
- package/dist/account/masm/index.d.ts +3 -0
- package/dist/account/masm/index.d.ts.map +1 -0
- package/dist/account/masm/index.js +5 -0
- package/dist/account/masm/index.js.map +1 -0
- package/dist/account/masm.d.ts +1 -4
- package/dist/account/masm.d.ts.map +1 -1
- package/dist/account/masm.js +3 -1034
- package/dist/account/masm.js.map +1 -1
- package/dist/account/storage.d.ts +2 -2
- package/dist/account/storage.d.ts.map +1 -1
- package/dist/account/storage.js +18 -16
- package/dist/account/storage.js.map +1 -1
- package/dist/client.d.ts +67 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +73 -22
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +31 -32
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +54 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -9
- package/dist/index.js.map +1 -1
- package/dist/inspector.d.ts +32 -6
- package/dist/inspector.d.ts.map +1 -1
- package/dist/inspector.js +58 -23
- package/dist/inspector.js.map +1 -1
- package/dist/inspector.test.js +18 -18
- package/dist/inspector.test.js.map +1 -1
- package/dist/multisig/proposal/execution.d.ts +4 -4
- package/dist/multisig/proposal/execution.d.ts.map +1 -1
- package/dist/multisig/proposal/execution.js +42 -31
- package/dist/multisig/proposal/execution.js.map +1 -1
- package/dist/multisig/proposal/parser.d.ts +3 -3
- package/dist/multisig/proposal/parser.d.ts.map +1 -1
- package/dist/multisig/proposal/parser.js +23 -23
- package/dist/multisig/proposal/parser.js.map +1 -1
- package/dist/multisig/signing.d.ts +3 -3
- package/dist/multisig/signing.d.ts.map +1 -1
- package/dist/multisig/signing.js +3 -3
- package/dist/multisig/signing.js.map +1 -1
- package/dist/multisig.d.ts +188 -48
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +650 -308
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +1680 -241
- package/dist/multisig.test.js.map +1 -1
- package/dist/procedures.d.ts +46 -24
- package/dist/procedures.d.ts.map +1 -1
- package/dist/procedures.js +46 -21
- package/dist/procedures.js.map +1 -1
- package/dist/proposal/factory.d.ts +18 -0
- package/dist/proposal/factory.d.ts.map +1 -0
- package/dist/proposal/factory.js +99 -0
- package/dist/proposal/factory.js.map +1 -0
- package/dist/proposal/metadata.d.ts +5 -12
- package/dist/proposal/metadata.d.ts.map +1 -1
- package/dist/proposal/metadata.js +118 -174
- package/dist/proposal/metadata.js.map +1 -1
- package/dist/proposal/signatures.d.ts +10 -0
- package/dist/proposal/signatures.d.ts.map +1 -0
- package/dist/proposal/signatures.js +54 -0
- package/dist/proposal/signatures.js.map +1 -0
- package/dist/signer.d.ts +2 -0
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +2 -0
- package/dist/signer.js.map +1 -1
- package/dist/signers/ecdsa.d.ts +3 -0
- package/dist/signers/ecdsa.d.ts.map +1 -1
- package/dist/signers/ecdsa.js +8 -4
- package/dist/signers/ecdsa.js.map +1 -1
- package/dist/signers/ecdsa.test.d.ts +2 -0
- package/dist/signers/ecdsa.test.d.ts.map +1 -0
- package/dist/signers/ecdsa.test.js +65 -0
- package/dist/signers/ecdsa.test.js.map +1 -0
- package/dist/signers/falcon.d.ts +3 -0
- package/dist/signers/falcon.d.ts.map +1 -1
- package/dist/signers/falcon.js +8 -4
- package/dist/signers/falcon.js.map +1 -1
- package/dist/signers/falcon.test.js +12 -0
- package/dist/signers/falcon.test.js.map +1 -1
- package/dist/signers/miden-wallet.d.ts +3 -1
- package/dist/signers/miden-wallet.d.ts.map +1 -1
- package/dist/signers/miden-wallet.js +11 -2
- package/dist/signers/miden-wallet.js.map +1 -1
- package/dist/signers/miden-wallet.test.js +26 -0
- package/dist/signers/miden-wallet.test.js.map +1 -1
- package/dist/signers/para.d.ts +3 -3
- package/dist/signers/para.d.ts.map +1 -1
- package/dist/signers/para.js +15 -6
- package/dist/signers/para.js.map +1 -1
- package/dist/signers/para.test.js +39 -0
- package/dist/signers/para.test.js.map +1 -1
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +1 -0
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/transaction/index.d.ts +2 -1
- package/dist/transaction/index.d.ts.map +1 -1
- package/dist/transaction/index.js +2 -1
- package/dist/transaction/index.js.map +1 -1
- package/dist/transaction/options.d.ts +1 -1
- package/dist/transaction/options.d.ts.map +1 -1
- package/dist/transaction/p2id.d.ts +1 -0
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js +9 -3
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/updateGuardian.d.ts +7 -0
- package/dist/transaction/updateGuardian.d.ts.map +1 -0
- package/dist/transaction/{updatePsm.js → updateGuardian.js} +15 -29
- package/dist/transaction/updateGuardian.js.map +1 -0
- package/dist/transaction/updateProcedureThreshold.d.ts +9 -0
- package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -0
- package/dist/transaction/updateProcedureThreshold.js +55 -0
- package/dist/transaction/updateProcedureThreshold.js.map +1 -0
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js +5 -23
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.d.ts +2 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +2 -1
- package/dist/transaction.js.map +1 -1
- package/dist/types/proposal.d.ts +32 -15
- package/dist/types/proposal.d.ts.map +1 -1
- package/dist/types.d.ts +24 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/digest.d.ts +5 -0
- package/dist/utils/digest.d.ts.map +1 -1
- package/dist/utils/digest.js +34 -1
- package/dist/utils/digest.js.map +1 -1
- package/dist/utils/digest.test.d.ts +2 -0
- package/dist/utils/digest.test.d.ts.map +1 -0
- package/dist/utils/digest.test.js +48 -0
- package/dist/utils/digest.test.js.map +1 -0
- package/dist/utils/signature.d.ts +3 -1
- package/dist/utils/signature.d.ts.map +1 -1
- package/dist/utils/signature.js +31 -6
- package/dist/utils/signature.js.map +1 -1
- package/dist/utils/word.test.js +1 -52
- package/dist/utils/word.test.js.map +1 -1
- package/masm/account_components/auth/multisig.masm +13 -0
- package/masm/account_components/auth/multisig_ecdsa.masm +13 -0
- package/masm/account_components/auth/multisig_guardian.masm +17 -0
- package/masm/account_components/auth/multisig_guardian_ecdsa.masm +17 -0
- package/masm/auth/guardian.masm +177 -0
- package/masm/auth/guardian_ecdsa.masm +178 -0
- package/masm/{multisig.masm → auth/multisig.masm} +149 -55
- package/masm/{multisig_ecdsa.masm → auth/multisig_ecdsa.masm} +148 -56
- package/package.json +11 -9
- package/src/account/builder.test.ts +165 -0
- package/src/account/builder.ts +71 -34
- package/src/account/index.ts +1 -1
- package/src/account/masm/account-components/auth.ts +70 -0
- package/src/account/masm/auth.ts +1398 -0
- package/src/account/masm/index.ts +5 -0
- package/src/account/masm.ts +3 -1040
- package/src/account/storage.ts +18 -16
- package/src/client.test.ts +31 -35
- package/src/client.ts +98 -26
- package/src/index.ts +84 -22
- package/src/inspector.test.ts +17 -17
- package/src/inspector.ts +63 -29
- package/src/multisig/proposal/execution.ts +66 -49
- package/src/multisig/proposal/parser.ts +26 -26
- package/src/multisig/signing.ts +4 -4
- package/src/multisig.test.ts +2097 -248
- package/src/multisig.ts +882 -388
- package/src/procedures.ts +50 -26
- package/src/proposal/factory.ts +171 -0
- package/src/proposal/metadata.ts +165 -0
- package/src/proposal/signatures.ts +68 -0
- package/src/signer.ts +2 -0
- package/src/signers/ecdsa.test.ts +80 -0
- package/src/signers/ecdsa.ts +15 -4
- package/src/signers/falcon.test.ts +19 -0
- package/src/signers/falcon.ts +16 -5
- package/src/signers/miden-wallet.test.ts +47 -0
- package/src/signers/miden-wallet.ts +18 -1
- package/src/signers/para.test.ts +57 -0
- package/src/signers/para.ts +26 -7
- package/src/transaction/consumeNotes.ts +1 -0
- package/src/transaction/index.ts +2 -2
- package/src/transaction/options.ts +1 -2
- package/src/transaction/p2id.test.ts +190 -0
- package/src/transaction/p2id.ts +11 -5
- package/src/transaction/rpoRandomCoin.test.ts +64 -0
- package/src/transaction/rpoRandomCoin.ts +24 -0
- package/src/transaction/{updatePsm.ts → updateGuardian.ts} +19 -32
- package/src/transaction/updateProcedureThreshold.ts +97 -0
- package/src/transaction/updateSigners.ts +12 -27
- package/src/transaction.ts +5 -3
- package/src/types/proposal.ts +43 -11
- package/src/types.ts +29 -11
- package/src/utils/digest.test.ts +55 -0
- package/src/utils/digest.ts +51 -1
- package/src/utils/signature.ts +47 -9
- package/src/utils/word.test.ts +1 -55
- package/dist/account.d.ts +0 -104
- package/dist/account.d.ts.map +0 -1
- package/dist/account.js +0 -136
- package/dist/account.js.map +0 -1
- package/dist/builder.d.ts +0 -54
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -78
- package/dist/builder.js.map +0 -1
- package/dist/http.d.ts +0 -64
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -162
- package/dist/http.js.map +0 -1
- package/dist/keystore.d.ts +0 -65
- package/dist/keystore.d.ts.map +0 -1
- package/dist/keystore.js +0 -121
- package/dist/keystore.js.map +0 -1
- package/dist/miden.d.ts +0 -9
- package/dist/miden.d.ts.map +0 -1
- package/dist/miden.js +0 -26
- package/dist/miden.js.map +0 -1
- package/dist/multisig/metadata.d.ts +0 -18
- package/dist/multisig/metadata.d.ts.map +0 -1
- package/dist/multisig/metadata.js +0 -100
- package/dist/multisig/metadata.js.map +0 -1
- package/dist/multisig/metadata.test.d.ts +0 -2
- package/dist/multisig/metadata.test.d.ts.map +0 -1
- package/dist/multisig/metadata.test.js +0 -323
- package/dist/multisig/metadata.test.js.map +0 -1
- package/dist/proposal/exported.d.ts +0 -12
- package/dist/proposal/exported.d.ts.map +0 -1
- package/dist/proposal/exported.js +0 -88
- package/dist/proposal/exported.js.map +0 -1
- package/dist/signer.test.d.ts +0 -2
- package/dist/signer.test.d.ts.map +0 -1
- package/dist/signer.test.js +0 -123
- package/dist/signer.test.js.map +0 -1
- package/dist/transaction/p2id.interop.test.d.ts +0 -2
- package/dist/transaction/p2id.interop.test.d.ts.map +0 -1
- package/dist/transaction/p2id.interop.test.js +0 -36
- package/dist/transaction/p2id.interop.test.js.map +0 -1
- package/dist/transaction/updatePsm.d.ts +0 -7
- package/dist/transaction/updatePsm.d.ts.map +0 -1
- package/dist/transaction/updatePsm.js.map +0 -1
- package/dist/transport/http.d.ts +0 -64
- package/dist/transport/http.d.ts.map +0 -1
- package/dist/transport/http.js +0 -162
- package/dist/transport/http.js.map +0 -1
- package/dist/transport/index.d.ts +0 -5
- package/dist/transport/index.d.ts.map +0 -1
- package/dist/transport/index.js +0 -5
- package/dist/transport/index.js.map +0 -1
- package/dist/utils/state-validation.d.ts +0 -5
- package/dist/utils/state-validation.d.ts.map +0 -1
- package/dist/utils/state-validation.js +0 -30
- package/dist/utils/state-validation.js.map +0 -1
- package/dist/utils/validation.d.ts +0 -6
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/validation.js +0 -52
- package/dist/utils/validation.js.map +0 -1
- package/masm/psm.masm +0 -178
- package/masm/psm_ecdsa.masm +0 -179
package/src/procedures.ts
CHANGED
|
@@ -1,37 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Static mapping of procedure names to their deterministic roots.
|
|
3
|
+
*
|
|
4
|
+
* These values use the Miden SDK `Word.toHex()` / `Word.fromHex()` encoding, which is the
|
|
5
|
+
* representation used by the TypeScript client when writing and reading storage map keys.
|
|
6
|
+
*
|
|
7
|
+
* Source of truth:
|
|
8
|
+
* `cargo run --quiet --example procedure_roots -p miden-multisig-client -- --json`
|
|
9
|
+
*
|
|
10
|
+
* Note: the Rust example also prints `rust_hex` values for `procedures.rs`. Those are a different
|
|
11
|
+
* human-readable encoding and should not be copied into this table.
|
|
12
|
+
*/
|
|
13
|
+
export const PROCEDURE_ROOTS = {
|
|
14
|
+
update_signers: '0xafdea9abee19b02620520b11a165267b266403fa408f7eebc91fc83c33b507ca',
|
|
15
|
+
update_procedure_threshold: '0xd6616423eebe012655a9db925363cc6751b94109a938e9bc5f488b52f75b7d1c',
|
|
16
|
+
auth_tx: '0x611abcd570631ad98842cb6f0ef891fe8f9ee508b3245c55a5531d5a8f7fdca9',
|
|
17
|
+
update_guardian: '0xcda1f9120a3ab2948d5cdc6b4b2982571c04e3f6af787a6d6b2f88eeedd872d7',
|
|
18
|
+
verify_guardian: '0xe4be6069ba56570afecab5ae2ff77165d5230b703ba2cff1cafe06b92aa19e56',
|
|
8
19
|
send_asset: '0x0e406b067ed2bcd7de745ca6517f519fd1a9be245f913347ac673ca1db30c1d6',
|
|
9
20
|
receive_asset: '0x6f4bdbdc4b13d7ed933d590d88ac9dfb98020c9e917697845b5e169395b76a01',
|
|
10
21
|
} as const;
|
|
11
22
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
verify_psm: '0xbeb9e08a83eca030968f2f137b5673136f21bc16c82fd408c2ce2495ccbdcd15',
|
|
17
|
-
send_asset: '0xd6c130dba13c67ac4733915f24bea9d19f517f51a65c74ded7bcd27e066b400e',
|
|
18
|
-
receive_asset: '0x016ab79593165e5b849776919e0c0298fb9dac880d593d93edd7134bdcdb4b6f',
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
export const PROCEDURE_ROOTS = PROCEDURE_ROOTS_FALCON;
|
|
22
|
-
|
|
23
|
-
export type ProcedureName = keyof typeof PROCEDURE_ROOTS_FALCON;
|
|
23
|
+
/**
|
|
24
|
+
* Valid procedure names that can be used for threshold overrides.
|
|
25
|
+
*/
|
|
26
|
+
export type ProcedureName = keyof typeof PROCEDURE_ROOTS;
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Get the procedure root for a given procedure name.
|
|
30
|
+
*
|
|
31
|
+
* @param name - The procedure name
|
|
32
|
+
* @returns The procedure root as a hex string in SDK `Word.toHex()` format
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const root = getProcedureRoot('send_asset');
|
|
37
|
+
* // '0x0e406b067ed2bcd7de745ca6517f519fd1a9be245f913347ac673ca1db30c1d6'
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function getProcedureRoot(name: ProcedureName): string {
|
|
41
|
+
return PROCEDURE_ROOTS[name];
|
|
28
42
|
}
|
|
29
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Check if a string is a valid procedure name.
|
|
46
|
+
*
|
|
47
|
+
* @param name - The string to check
|
|
48
|
+
* @returns true if the string is a valid procedure name
|
|
49
|
+
*/
|
|
30
50
|
export function isProcedureName(name: string): name is ProcedureName {
|
|
31
|
-
return name in
|
|
51
|
+
return name in PROCEDURE_ROOTS;
|
|
32
52
|
}
|
|
33
53
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Get all available procedure names.
|
|
56
|
+
*
|
|
57
|
+
* @returns Array of all valid procedure names
|
|
58
|
+
*/
|
|
59
|
+
export function getProcedureNames(): ProcedureName[] {
|
|
60
|
+
return Object.keys(PROCEDURE_ROOTS) as ProcedureName[];
|
|
37
61
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { DeltaObject, DeltaStatus } from '@openzeppelin/guardian-client';
|
|
2
|
+
import type {
|
|
3
|
+
ExportedProposal,
|
|
4
|
+
Proposal,
|
|
5
|
+
ProposalMetadata,
|
|
6
|
+
ProposalSignatureEntry,
|
|
7
|
+
ProposalStatus,
|
|
8
|
+
ProposalType,
|
|
9
|
+
} from '../types.js';
|
|
10
|
+
import { computeCommitmentFromTxSummary } from '../multisig/helpers.js';
|
|
11
|
+
import { normalizeHexWord } from '../utils/encoding.js';
|
|
12
|
+
import { ProposalMetadataCodec } from './metadata.js';
|
|
13
|
+
import { ProposalSignatures } from './signatures.js';
|
|
14
|
+
|
|
15
|
+
interface ProposalFactoryOptions {
|
|
16
|
+
accountId: string;
|
|
17
|
+
signerCommitments: string[];
|
|
18
|
+
resolveRequiredSignatures: (proposalType: ProposalType) => number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class ProposalFactory {
|
|
22
|
+
constructor(private readonly options: ProposalFactoryOptions) {}
|
|
23
|
+
|
|
24
|
+
assertAccountId(accountId: string): void {
|
|
25
|
+
if (accountId.toLowerCase() === this.options.accountId.toLowerCase()) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
throw new Error(`Proposal is for a different account: ${accountId}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
assertCommitmentMatchesTxSummary(
|
|
33
|
+
commitment: string,
|
|
34
|
+
txSummaryBase64: string,
|
|
35
|
+
context: string,
|
|
36
|
+
): string {
|
|
37
|
+
const expectedCommitment = normalizeHexWord(computeCommitmentFromTxSummary(txSummaryBase64));
|
|
38
|
+
const actualCommitment = normalizeHexWord(commitment);
|
|
39
|
+
|
|
40
|
+
if (actualCommitment !== expectedCommitment) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`${context}: commitment ${actualCommitment} does not match tx_summary ${expectedCommitment}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return expectedCommitment;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fromDelta(
|
|
50
|
+
delta: DeltaObject,
|
|
51
|
+
proposalId: string,
|
|
52
|
+
metadata?: ProposalMetadata,
|
|
53
|
+
existingSignatures: ProposalSignatureEntry[] = [],
|
|
54
|
+
): Proposal {
|
|
55
|
+
this.assertAccountId(delta.accountId);
|
|
56
|
+
const normalizedProposalId = this.assertCommitmentMatchesTxSummary(
|
|
57
|
+
proposalId,
|
|
58
|
+
delta.deltaPayload.txSummary.data,
|
|
59
|
+
'Invalid proposal',
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const resolvedMetadata = ProposalMetadataCodec.validate(
|
|
63
|
+
metadata ?? ProposalMetadataCodec.fromGuardian(delta.deltaPayload.metadata),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const localSignatures = new ProposalSignatures(
|
|
67
|
+
existingSignatures,
|
|
68
|
+
this.options.signerCommitments,
|
|
69
|
+
`Invalid local proposal signatures for ${normalizedProposalId}`,
|
|
70
|
+
).entries();
|
|
71
|
+
|
|
72
|
+
const serverSignatures =
|
|
73
|
+
delta.status.status === 'pending'
|
|
74
|
+
? new ProposalSignatures(
|
|
75
|
+
delta.status.cosignerSigs.map((signature) => ({
|
|
76
|
+
signerId: signature.signerId,
|
|
77
|
+
signature: signature.signature,
|
|
78
|
+
timestamp: signature.timestamp,
|
|
79
|
+
})),
|
|
80
|
+
this.options.signerCommitments,
|
|
81
|
+
`Invalid server proposal signatures for ${normalizedProposalId}`,
|
|
82
|
+
).entries()
|
|
83
|
+
: [];
|
|
84
|
+
|
|
85
|
+
const signatures = ProposalSignatures.mergeEntries([localSignatures, serverSignatures]);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
id: normalizedProposalId,
|
|
89
|
+
accountId: delta.accountId,
|
|
90
|
+
nonce: delta.nonce,
|
|
91
|
+
status: this.toStatus(delta.status, resolvedMetadata.proposalType, signatures),
|
|
92
|
+
txSummary: delta.deltaPayload.txSummary.data,
|
|
93
|
+
signatures,
|
|
94
|
+
metadata: resolvedMetadata,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
fromExported(exported: ExportedProposal): Proposal {
|
|
99
|
+
if (typeof exported.nonce !== 'number' || !Number.isInteger(exported.nonce) || exported.nonce < 0) {
|
|
100
|
+
throw new Error('Invalid proposal JSON: nonce is required');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.assertAccountId(exported.accountId);
|
|
104
|
+
|
|
105
|
+
const normalizedCommitment = this.assertCommitmentMatchesTxSummary(
|
|
106
|
+
exported.commitment,
|
|
107
|
+
exported.txSummaryBase64,
|
|
108
|
+
'Invalid proposal',
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const metadata = ProposalMetadataCodec.validate(exported.metadata);
|
|
112
|
+
const signatures = new ProposalSignatures(
|
|
113
|
+
exported.signatures.map((signature) => {
|
|
114
|
+
const scheme = signature.scheme ?? 'falcon';
|
|
115
|
+
if (scheme === 'ecdsa' && !signature.publicKey) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Invalid imported proposal signatures: ECDSA signature for ${signature.commitment} is missing publicKey`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
signerId: signature.commitment,
|
|
123
|
+
signature:
|
|
124
|
+
scheme === 'ecdsa'
|
|
125
|
+
? {
|
|
126
|
+
scheme,
|
|
127
|
+
signature: signature.signatureHex,
|
|
128
|
+
publicKey: signature.publicKey,
|
|
129
|
+
}
|
|
130
|
+
: {
|
|
131
|
+
scheme,
|
|
132
|
+
signature: signature.signatureHex,
|
|
133
|
+
},
|
|
134
|
+
timestamp: signature.timestamp ?? new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
}),
|
|
137
|
+
this.options.signerCommitments,
|
|
138
|
+
'Invalid imported proposal signatures',
|
|
139
|
+
).entries();
|
|
140
|
+
|
|
141
|
+
const signaturesRequired = this.options.resolveRequiredSignatures(metadata.proposalType);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
id: normalizedCommitment,
|
|
145
|
+
accountId: exported.accountId,
|
|
146
|
+
nonce: exported.nonce,
|
|
147
|
+
status: signatures.length >= signaturesRequired ? 'ready' : 'pending',
|
|
148
|
+
txSummary: exported.txSummaryBase64,
|
|
149
|
+
signatures,
|
|
150
|
+
metadata,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private toStatus(
|
|
155
|
+
status: DeltaStatus,
|
|
156
|
+
proposalType: ProposalType,
|
|
157
|
+
signatures: ProposalSignatureEntry[],
|
|
158
|
+
): ProposalStatus {
|
|
159
|
+
switch (status.status) {
|
|
160
|
+
case 'pending': {
|
|
161
|
+
const signaturesRequired = this.options.resolveRequiredSignatures(proposalType);
|
|
162
|
+
return signatures.length >= signaturesRequired ? 'ready' : 'pending';
|
|
163
|
+
}
|
|
164
|
+
case 'candidate':
|
|
165
|
+
return 'ready';
|
|
166
|
+
case 'canonical':
|
|
167
|
+
case 'discarded':
|
|
168
|
+
return 'finalized';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { ProposalMetadata as GuardianProposalMetadata } from '@openzeppelin/guardian-client';
|
|
2
|
+
import type { ProposalMetadata } from '../types.js';
|
|
3
|
+
import { isProcedureName } from '../procedures.js';
|
|
4
|
+
|
|
5
|
+
export class ProposalMetadataCodec {
|
|
6
|
+
static toGuardian(metadata: ProposalMetadata): GuardianProposalMetadata {
|
|
7
|
+
const base: GuardianProposalMetadata = {
|
|
8
|
+
proposalType: metadata.proposalType,
|
|
9
|
+
description: metadata.description,
|
|
10
|
+
salt: metadata.saltHex,
|
|
11
|
+
requiredSignatures: metadata.requiredSignatures,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
switch (metadata.proposalType) {
|
|
15
|
+
case 'consume_notes':
|
|
16
|
+
return {
|
|
17
|
+
...base,
|
|
18
|
+
noteIds: metadata.noteIds,
|
|
19
|
+
};
|
|
20
|
+
case 'p2id':
|
|
21
|
+
return {
|
|
22
|
+
...base,
|
|
23
|
+
recipientId: metadata.recipientId,
|
|
24
|
+
faucetId: metadata.faucetId,
|
|
25
|
+
amount: metadata.amount,
|
|
26
|
+
};
|
|
27
|
+
case 'switch_guardian':
|
|
28
|
+
return {
|
|
29
|
+
...base,
|
|
30
|
+
targetThreshold: metadata.targetThreshold,
|
|
31
|
+
signerCommitments: metadata.targetSignerCommitments,
|
|
32
|
+
newGuardianPubkey: metadata.newGuardianPubkey,
|
|
33
|
+
newGuardianEndpoint: metadata.newGuardianEndpoint,
|
|
34
|
+
};
|
|
35
|
+
case 'update_procedure_threshold':
|
|
36
|
+
return {
|
|
37
|
+
...base,
|
|
38
|
+
targetThreshold: metadata.targetThreshold,
|
|
39
|
+
targetProcedure: metadata.targetProcedure,
|
|
40
|
+
};
|
|
41
|
+
case 'add_signer':
|
|
42
|
+
case 'remove_signer':
|
|
43
|
+
case 'change_threshold':
|
|
44
|
+
return {
|
|
45
|
+
...base,
|
|
46
|
+
targetThreshold: metadata.targetThreshold,
|
|
47
|
+
signerCommitments: metadata.targetSignerCommitments,
|
|
48
|
+
};
|
|
49
|
+
case 'unknown':
|
|
50
|
+
return base;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static fromGuardian(guardian?: GuardianProposalMetadata): ProposalMetadata {
|
|
55
|
+
if (!guardian?.proposalType) {
|
|
56
|
+
throw new Error('Missing proposal metadata.proposalType');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const base = {
|
|
60
|
+
description: guardian.description ?? '',
|
|
61
|
+
saltHex: guardian.salt,
|
|
62
|
+
requiredSignatures: guardian.requiredSignatures,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
switch (guardian.proposalType) {
|
|
66
|
+
case 'p2id':
|
|
67
|
+
if (!guardian.recipientId || !guardian.faucetId || !guardian.amount) {
|
|
68
|
+
throw new Error('p2id proposal is missing required metadata fields');
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
...base,
|
|
72
|
+
proposalType: 'p2id',
|
|
73
|
+
recipientId: guardian.recipientId,
|
|
74
|
+
faucetId: guardian.faucetId,
|
|
75
|
+
amount: guardian.amount,
|
|
76
|
+
};
|
|
77
|
+
case 'consume_notes':
|
|
78
|
+
if (!guardian.noteIds || guardian.noteIds.length === 0) {
|
|
79
|
+
throw new Error('consume_notes proposal is missing noteIds');
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
...base,
|
|
83
|
+
proposalType: 'consume_notes',
|
|
84
|
+
noteIds: guardian.noteIds,
|
|
85
|
+
};
|
|
86
|
+
case 'switch_guardian':
|
|
87
|
+
if (!guardian.newGuardianPubkey || !guardian.newGuardianEndpoint) {
|
|
88
|
+
throw new Error('switch_guardian proposal is missing required metadata fields');
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
...base,
|
|
92
|
+
proposalType: 'switch_guardian',
|
|
93
|
+
newGuardianPubkey: guardian.newGuardianPubkey,
|
|
94
|
+
newGuardianEndpoint: guardian.newGuardianEndpoint,
|
|
95
|
+
targetThreshold: guardian.targetThreshold,
|
|
96
|
+
targetSignerCommitments: guardian.signerCommitments,
|
|
97
|
+
};
|
|
98
|
+
case 'update_procedure_threshold':
|
|
99
|
+
if (guardian.targetThreshold === undefined || !guardian.targetProcedure) {
|
|
100
|
+
throw new Error('update_procedure_threshold proposal is missing required metadata fields');
|
|
101
|
+
}
|
|
102
|
+
if (!isProcedureName(guardian.targetProcedure)) {
|
|
103
|
+
throw new Error(`unknown target procedure: ${guardian.targetProcedure}`);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
...base,
|
|
107
|
+
proposalType: 'update_procedure_threshold',
|
|
108
|
+
targetProcedure: guardian.targetProcedure,
|
|
109
|
+
targetThreshold: guardian.targetThreshold,
|
|
110
|
+
};
|
|
111
|
+
case 'add_signer':
|
|
112
|
+
case 'remove_signer':
|
|
113
|
+
case 'change_threshold':
|
|
114
|
+
if (guardian.targetThreshold === undefined || !guardian.signerCommitments || guardian.signerCommitments.length === 0) {
|
|
115
|
+
throw new Error(`${guardian.proposalType} proposal is missing required metadata fields`);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
...base,
|
|
119
|
+
proposalType: guardian.proposalType,
|
|
120
|
+
targetThreshold: guardian.targetThreshold,
|
|
121
|
+
targetSignerCommitments: guardian.signerCommitments,
|
|
122
|
+
};
|
|
123
|
+
default:
|
|
124
|
+
throw new Error(`Unsupported proposal type: ${guardian.proposalType as string}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static validate(metadata: ProposalMetadata): ProposalMetadata {
|
|
129
|
+
switch (metadata.proposalType) {
|
|
130
|
+
case 'add_signer':
|
|
131
|
+
case 'remove_signer':
|
|
132
|
+
case 'change_threshold':
|
|
133
|
+
if (
|
|
134
|
+
metadata.targetThreshold === undefined ||
|
|
135
|
+
!metadata.targetSignerCommitments ||
|
|
136
|
+
metadata.targetSignerCommitments.length === 0
|
|
137
|
+
) {
|
|
138
|
+
throw new Error(`${metadata.proposalType} proposal metadata is incomplete`);
|
|
139
|
+
}
|
|
140
|
+
return metadata;
|
|
141
|
+
case 'switch_guardian':
|
|
142
|
+
if (!metadata.newGuardianPubkey || !metadata.newGuardianEndpoint) {
|
|
143
|
+
throw new Error('switch_guardian proposal metadata is incomplete');
|
|
144
|
+
}
|
|
145
|
+
return metadata;
|
|
146
|
+
case 'update_procedure_threshold':
|
|
147
|
+
if (!metadata.targetProcedure || metadata.targetThreshold === undefined) {
|
|
148
|
+
throw new Error('update_procedure_threshold proposal metadata is incomplete');
|
|
149
|
+
}
|
|
150
|
+
return metadata;
|
|
151
|
+
case 'consume_notes':
|
|
152
|
+
if (!metadata.noteIds || metadata.noteIds.length === 0) {
|
|
153
|
+
throw new Error('consume_notes proposal metadata is incomplete');
|
|
154
|
+
}
|
|
155
|
+
return metadata;
|
|
156
|
+
case 'p2id':
|
|
157
|
+
if (!metadata.recipientId || !metadata.faucetId || !metadata.amount) {
|
|
158
|
+
throw new Error('p2id proposal metadata is incomplete');
|
|
159
|
+
}
|
|
160
|
+
return metadata;
|
|
161
|
+
case 'unknown':
|
|
162
|
+
throw new Error('unknown proposal type is not supported');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ProposalSignatureEntry } from '../types.js';
|
|
2
|
+
import { canonicalizeSignature, normalizeSignerCommitment } from '../utils/signature.js';
|
|
3
|
+
|
|
4
|
+
export class ProposalSignatures {
|
|
5
|
+
private readonly signatures: ProposalSignatureEntry[];
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
signatures: ProposalSignatureEntry[],
|
|
9
|
+
signerCommitments: string[],
|
|
10
|
+
context: string,
|
|
11
|
+
) {
|
|
12
|
+
const expectedSigners = new Set<string>();
|
|
13
|
+
for (const signerCommitment of signerCommitments) {
|
|
14
|
+
let normalizedCommitment: string;
|
|
15
|
+
try {
|
|
16
|
+
normalizedCommitment = normalizeSignerCommitment(signerCommitment);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
+
throw new Error(`${context}: ${message}`);
|
|
20
|
+
}
|
|
21
|
+
expectedSigners.add(normalizedCommitment);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const signaturesBySigner = new Map<string, ProposalSignatureEntry>();
|
|
25
|
+
for (const signature of signatures) {
|
|
26
|
+
let canonicalized: ProposalSignatureEntry;
|
|
27
|
+
try {
|
|
28
|
+
canonicalized = canonicalizeSignature(signature, expectedSigners);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
+
throw new Error(`${context}: ${message}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (signaturesBySigner.has(canonicalized.signerId)) {
|
|
35
|
+
throw new Error(`${context}: duplicate signatures for signer ${canonicalized.signerId}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
signaturesBySigner.set(canonicalized.signerId, canonicalized);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.signatures = Array.from(signaturesBySigner.values());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
entries(): ProposalSignatureEntry[] {
|
|
45
|
+
return [...this.signatures];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
count(): number {
|
|
49
|
+
return this.signatures.length;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
hasSigner(signerId: string): boolean {
|
|
53
|
+
const normalizedSigner = normalizeSignerCommitment(signerId);
|
|
54
|
+
return this.signatures.some((signature) => signature.signerId === normalizedSigner);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static mergeEntries(entryGroups: ProposalSignatureEntry[][]): ProposalSignatureEntry[] {
|
|
58
|
+
const signaturesBySigner = new Map<string, ProposalSignatureEntry>();
|
|
59
|
+
|
|
60
|
+
for (const group of entryGroups) {
|
|
61
|
+
for (const signature of group) {
|
|
62
|
+
signaturesBySigner.set(signature.signerId, signature);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Array.from(signaturesBySigner.values());
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/signer.ts
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { EcdsaSigner } from './ecdsa.js';
|
|
3
|
+
|
|
4
|
+
vi.mock('@miden-sdk/miden-sdk', () => {
|
|
5
|
+
const mockSignature = {
|
|
6
|
+
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5]),
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const mockPublicKey = {
|
|
10
|
+
toCommitment: () => ({
|
|
11
|
+
toHex: () => '0x' + 'a'.repeat(64),
|
|
12
|
+
}),
|
|
13
|
+
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const mockSecretKey = {
|
|
17
|
+
publicKey: vi.fn().mockReturnValue(mockPublicKey),
|
|
18
|
+
sign: vi.fn().mockReturnValue(mockSignature),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
AuthSecretKey: vi.fn(),
|
|
23
|
+
Word: {
|
|
24
|
+
fromHex: vi.fn((hex: string) => ({
|
|
25
|
+
toHex: () => hex,
|
|
26
|
+
toFelts: () => [1, 2, 3, 4],
|
|
27
|
+
})),
|
|
28
|
+
},
|
|
29
|
+
AccountId: {
|
|
30
|
+
fromHex: vi.fn(() => ({
|
|
31
|
+
prefix: () => ({ asInt: () => BigInt(1) }),
|
|
32
|
+
suffix: () => ({ asInt: () => BigInt(2) }),
|
|
33
|
+
})),
|
|
34
|
+
},
|
|
35
|
+
Felt: vi.fn().mockImplementation((value: bigint) => ({ value })),
|
|
36
|
+
FeltArray: vi.fn().mockImplementation((elements: unknown[]) => elements),
|
|
37
|
+
Rpo256: {
|
|
38
|
+
hashElements: vi.fn().mockReturnValue({
|
|
39
|
+
toHex: () => '0x' + 'b'.repeat(64),
|
|
40
|
+
toFelts: () => [1, 2, 3, 4],
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('EcdsaSigner', () => {
|
|
47
|
+
let mockSecretKey: {
|
|
48
|
+
publicKey: ReturnType<typeof vi.fn>;
|
|
49
|
+
sign: ReturnType<typeof vi.fn>;
|
|
50
|
+
};
|
|
51
|
+
let signer: EcdsaSigner;
|
|
52
|
+
|
|
53
|
+
beforeEach(async () => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
mockSecretKey = {
|
|
56
|
+
publicKey: vi.fn().mockReturnValue({
|
|
57
|
+
toCommitment: () => ({ toHex: () => '0x' + 'a'.repeat(64) }),
|
|
58
|
+
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
|
59
|
+
}),
|
|
60
|
+
sign: vi.fn().mockReturnValue({
|
|
61
|
+
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5]),
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
signer = new EcdsaSigner(mockSecretKey as never);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('signs request-bound auth messages for ECDSA', async () => {
|
|
68
|
+
const { Rpo256 } = await import('@miden-sdk/miden-sdk');
|
|
69
|
+
|
|
70
|
+
const signature = await signer.signRequest(
|
|
71
|
+
'0x' + 'a'.repeat(30),
|
|
72
|
+
1700000000,
|
|
73
|
+
{ toBytes: () => new Uint8Array([1, 2, 3, 4]) } as never,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(signature).toBe('0x0102030405');
|
|
77
|
+
expect(mockSecretKey.sign).toHaveBeenCalled();
|
|
78
|
+
expect(Rpo256.hashElements).toHaveBeenCalledTimes(2);
|
|
79
|
+
});
|
|
80
|
+
});
|
package/src/signers/ecdsa.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RequestAuthPayload } from '@openzeppelin/guardian-client';
|
|
1
2
|
import { AuthSecretKey } from '@miden-sdk/miden-sdk';
|
|
2
3
|
import type { Signer, SignatureScheme } from '../types.js';
|
|
3
4
|
import { bytesToHex, normalizeHexWord } from '../utils/encoding.js';
|
|
@@ -19,14 +20,24 @@ export class EcdsaSigner implements Signer {
|
|
|
19
20
|
|
|
20
21
|
async signAccountIdWithTimestamp(accountId: string, timestamp: number): Promise<string> {
|
|
21
22
|
const digest = AuthDigest.fromAccountIdWithTimestamp(accountId, timestamp);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
return this.signWord(digest);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async signRequest(
|
|
27
|
+
accountId: string,
|
|
28
|
+
timestamp: number,
|
|
29
|
+
requestPayload: RequestAuthPayload,
|
|
30
|
+
): Promise<string> {
|
|
31
|
+
const digest = AuthDigest.fromRequest(accountId, timestamp, requestPayload);
|
|
32
|
+
return this.signWord(digest);
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
async signCommitment(commitmentHex: string): Promise<string> {
|
|
29
36
|
const word = AuthDigest.fromCommitmentHex(commitmentHex);
|
|
37
|
+
return this.signWord(word);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private signWord(word: ReturnType<typeof AuthDigest.fromCommitmentHex>): string {
|
|
30
41
|
const signature = this.secretKey.sign(word);
|
|
31
42
|
const signatureBytes = signature.serialize();
|
|
32
43
|
const ecdsaSignature = signatureBytes.slice(1);
|
|
@@ -39,6 +39,7 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
39
39
|
Rpo256: {
|
|
40
40
|
hashElements: vi.fn().mockReturnValue({
|
|
41
41
|
toHex: () => '0x' + 'b'.repeat(64),
|
|
42
|
+
toFelts: () => [1, 2, 3, 4],
|
|
42
43
|
}),
|
|
43
44
|
},
|
|
44
45
|
};
|
|
@@ -102,6 +103,24 @@ describe('FalconSigner', () => {
|
|
|
102
103
|
});
|
|
103
104
|
});
|
|
104
105
|
|
|
106
|
+
describe('signRequest', () => {
|
|
107
|
+
it('signs account ID, timestamp, and request payload for Falcon auth', async () => {
|
|
108
|
+
const { Rpo256 } = await import('@miden-sdk/miden-sdk');
|
|
109
|
+
|
|
110
|
+
const signature = await signer.signRequest(
|
|
111
|
+
'0x' + 'a'.repeat(30),
|
|
112
|
+
1700000000,
|
|
113
|
+
{
|
|
114
|
+
toBytes: () => new Uint8Array([1, 2, 3, 4]),
|
|
115
|
+
} as any,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(signature).toBe('0x010203040506070809');
|
|
119
|
+
expect(mockSecretKey.sign).toHaveBeenCalled();
|
|
120
|
+
expect(Rpo256.hashElements).toHaveBeenCalledTimes(2);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
105
124
|
describe('signCommitment', () => {
|
|
106
125
|
it('handles commitment with 0x prefix', async () => {
|
|
107
126
|
const signature = await signer.signCommitment('0x' + 'c'.repeat(64));
|
package/src/signers/falcon.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RequestAuthPayload } from '@openzeppelin/guardian-client';
|
|
2
|
+
import { AuthSecretKey, type Word } from '@miden-sdk/miden-sdk';
|
|
2
3
|
import type { Signer, SignatureScheme } from '../types.js';
|
|
3
4
|
import { bytesToHex } from '../utils/encoding.js';
|
|
4
5
|
import { AuthDigest } from '../utils/digest.js';
|
|
@@ -20,14 +21,24 @@ export class FalconSigner implements Signer {
|
|
|
20
21
|
|
|
21
22
|
async signAccountIdWithTimestamp(accountId: string, timestamp: number): Promise<string> {
|
|
22
23
|
const digest = AuthDigest.fromAccountIdWithTimestamp(accountId, timestamp);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
return this.signWord(digest);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async signRequest(
|
|
28
|
+
accountId: string,
|
|
29
|
+
timestamp: number,
|
|
30
|
+
requestPayload: RequestAuthPayload,
|
|
31
|
+
): Promise<string> {
|
|
32
|
+
const digest = AuthDigest.fromRequest(accountId, timestamp, requestPayload);
|
|
33
|
+
return this.signWord(digest);
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
async signCommitment(commitmentHex: string): Promise<string> {
|
|
30
37
|
const word = AuthDigest.fromCommitmentHex(commitmentHex);
|
|
38
|
+
return this.signWord(word);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private signWord(word: Word): string {
|
|
31
42
|
const signature = this.secretKey.sign(word);
|
|
32
43
|
const signatureBytes = signature.serialize();
|
|
33
44
|
const falconSignature = signatureBytes.slice(1);
|