@openzeppelin/miden-multisig-client 0.12.7 → 0.13.0
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 +42 -4
- package/dist/account/builder.d.ts +1 -1
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +7 -5
- package/dist/account/builder.js.map +1 -1
- package/dist/account/masm.d.ts +3 -3
- package/dist/account/masm.d.ts.map +1 -1
- package/dist/account/masm.js +101 -98
- package/dist/account/masm.js.map +1 -1
- package/dist/account/storage.d.ts +1 -1
- package/dist/account/storage.d.ts.map +1 -1
- package/dist/account/storage.js +17 -7
- package/dist/account/storage.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +4 -4
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.d.ts.map +1 -1
- package/dist/inspector.js +16 -10
- package/dist/inspector.js.map +1 -1
- package/dist/inspector.test.js +33 -22
- package/dist/inspector.test.js.map +1 -1
- package/dist/multisig/helpers.d.ts +1 -1
- package/dist/multisig/helpers.d.ts.map +1 -1
- package/dist/multisig/helpers.js +1 -1
- package/dist/multisig/helpers.js.map +1 -1
- package/dist/multisig/helpers.test.js +2 -2
- package/dist/multisig/helpers.test.js.map +1 -1
- package/dist/multisig/proposal/execution.d.ts +18 -0
- package/dist/multisig/proposal/execution.d.ts.map +1 -0
- package/dist/multisig/proposal/execution.js +147 -0
- package/dist/multisig/proposal/execution.js.map +1 -0
- package/dist/multisig/proposal/parser.d.ts +17 -0
- package/dist/multisig/proposal/parser.d.ts.map +1 -0
- package/dist/multisig/proposal/parser.js +147 -0
- package/dist/multisig/proposal/parser.js.map +1 -0
- package/dist/multisig/signing.d.ts +5 -0
- package/dist/multisig/signing.d.ts.map +1 -0
- package/dist/multisig/signing.js +14 -0
- package/dist/multisig/signing.js.map +1 -0
- package/dist/multisig.d.ts +9 -13
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +109 -337
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +8 -4
- package/dist/multisig.test.js.map +1 -1
- package/dist/signer.d.ts +2 -20
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +2 -82
- package/dist/signer.js.map +1 -1
- package/dist/signer.test.js +24 -24
- package/dist/signer.test.js.map +1 -1
- package/dist/signers/ecdsa.d.ts +12 -0
- package/dist/signers/ecdsa.d.ts.map +1 -0
- package/dist/signers/ecdsa.js +30 -0
- package/dist/signers/ecdsa.js.map +1 -0
- package/dist/signers/falcon.d.ts +12 -0
- package/dist/signers/falcon.d.ts.map +1 -0
- package/dist/signers/falcon.js +31 -0
- package/dist/signers/falcon.js.map +1 -0
- package/dist/signers/falcon.test.d.ts +2 -0
- package/dist/signers/falcon.test.d.ts.map +1 -0
- package/dist/signers/falcon.test.js +113 -0
- package/dist/signers/falcon.test.js.map +1 -0
- package/dist/signers/index.d.ts +5 -0
- package/dist/signers/index.d.ts.map +1 -0
- package/dist/signers/index.js +5 -0
- package/dist/signers/index.js.map +1 -0
- package/dist/signers/miden-wallet.d.ts +16 -0
- package/dist/signers/miden-wallet.d.ts.map +1 -0
- package/dist/signers/miden-wallet.js +35 -0
- package/dist/signers/miden-wallet.js.map +1 -0
- package/dist/signers/miden-wallet.test.d.ts +2 -0
- package/dist/signers/miden-wallet.test.d.ts.map +1 -0
- package/dist/signers/miden-wallet.test.js +107 -0
- package/dist/signers/miden-wallet.test.js.map +1 -0
- package/dist/signers/para.d.ts +21 -0
- package/dist/signers/para.d.ts.map +1 -0
- package/dist/signers/para.js +43 -0
- package/dist/signers/para.js.map +1 -0
- package/dist/signers/para.test.d.ts +2 -0
- package/dist/signers/para.test.d.ts.map +1 -0
- package/dist/signers/para.test.js +88 -0
- package/dist/signers/para.test.js.map +1 -0
- package/dist/transaction/consumeNotes.d.ts +3 -3
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +11 -7
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/transaction/options.d.ts +2 -2
- package/dist/transaction/options.d.ts.map +1 -1
- package/dist/transaction/p2id.d.ts +1 -1
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js +5 -5
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/summary.d.ts +1 -1
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js +1 -1
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction/updatePsm.d.ts +1 -1
- package/dist/transaction/updatePsm.d.ts.map +1 -1
- package/dist/transaction/updatePsm.js +5 -5
- package/dist/transaction/updatePsm.js.map +1 -1
- package/dist/transaction/updateSigners.d.ts +1 -1
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js +5 -5
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.test.js +1 -1
- package/dist/transaction.test.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/digest.d.ts +6 -0
- package/dist/utils/digest.d.ts.map +1 -0
- package/dist/utils/digest.js +22 -0
- package/dist/utils/digest.js.map +1 -0
- package/dist/utils/ecdsa.d.ts +8 -0
- package/dist/utils/ecdsa.d.ts.map +1 -0
- package/dist/utils/ecdsa.js +60 -0
- package/dist/utils/ecdsa.js.map +1 -0
- package/dist/utils/ecdsa.test.d.ts +2 -0
- package/dist/utils/ecdsa.test.d.ts.map +1 -0
- package/dist/utils/ecdsa.test.js +85 -0
- package/dist/utils/ecdsa.test.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/key.d.ts +10 -0
- package/dist/utils/key.d.ts.map +1 -0
- package/dist/utils/key.js +38 -0
- package/dist/utils/key.js.map +1 -0
- package/dist/utils/key.test.d.ts +2 -0
- package/dist/utils/key.test.d.ts.map +1 -0
- package/dist/utils/key.test.js +80 -0
- package/dist/utils/key.test.js.map +1 -0
- package/dist/utils/random.d.ts +1 -1
- package/dist/utils/random.d.ts.map +1 -1
- package/dist/utils/random.js +1 -1
- package/dist/utils/random.js.map +1 -1
- package/dist/utils/signature.d.ts +2 -1
- package/dist/utils/signature.d.ts.map +1 -1
- package/dist/utils/signature.js +6 -3
- package/dist/utils/signature.js.map +1 -1
- package/dist/utils/signature.test.js +11 -11
- package/dist/utils/signature.test.js.map +1 -1
- package/dist/utils/word.d.ts +6 -1
- package/dist/utils/word.d.ts.map +1 -1
- package/dist/utils/word.js +11 -0
- package/dist/utils/word.js.map +1 -1
- package/dist/utils/word.test.js +54 -3
- package/dist/utils/word.test.js.map +1 -1
- package/package.json +4 -5
- package/src/account/builder.ts +7 -5
- package/src/account/masm.ts +103 -99
- package/src/account/storage.ts +19 -7
- package/src/client.test.ts +4 -4
- package/src/client.ts +3 -3
- package/src/index.ts +8 -1
- package/src/inspector.test.ts +38 -25
- package/src/inspector.ts +18 -10
- package/src/multisig/helpers.test.ts +2 -2
- package/src/multisig/helpers.ts +2 -1
- package/src/multisig/proposal/execution.ts +320 -0
- package/src/multisig/proposal/parser.ts +191 -0
- package/src/multisig/signing.ts +24 -0
- package/src/multisig.test.ts +8 -4
- package/src/multisig.ts +153 -434
- package/src/signer.ts +2 -95
- package/src/signers/ecdsa.ts +35 -0
- package/src/{signer.test.ts → signers/falcon.test.ts} +29 -33
- package/src/signers/falcon.ts +36 -0
- package/src/signers/index.ts +4 -0
- package/src/signers/miden-wallet.test.ts +119 -0
- package/src/signers/miden-wallet.ts +49 -0
- package/src/signers/para.test.ts +103 -0
- package/src/signers/para.ts +60 -0
- package/src/transaction/consumeNotes.ts +14 -10
- package/src/transaction/options.ts +2 -2
- package/src/transaction/p2id.ts +3 -8
- package/src/transaction/summary.ts +2 -2
- package/src/transaction/updatePsm.ts +5 -6
- package/src/transaction/updateSigners.ts +5 -6
- package/src/transaction.test.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/digest.ts +25 -0
- package/src/utils/ecdsa.test.ts +104 -0
- package/src/utils/ecdsa.ts +77 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/key.test.ts +89 -0
- package/src/utils/key.ts +49 -0
- package/src/utils/random.ts +1 -1
- package/src/utils/signature.test.ts +11 -11
- package/src/utils/signature.ts +10 -3
- package/src/utils/word.test.ts +57 -3
- package/src/utils/word.ts +13 -1
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import type { TransactionRequest, Word } from '@
|
|
2
|
-
import {
|
|
1
|
+
import type { TransactionRequest, Word, WebClient } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import { NoteAndArgs, NoteAndArgsArray, TransactionRequestBuilder, Word as WordType } from '@miden-sdk/miden-sdk';
|
|
3
3
|
import { randomWord } from '../utils/random.js';
|
|
4
4
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
5
5
|
import type { SignatureOptions } from './options.js';
|
|
6
6
|
|
|
7
|
-
export function buildConsumeNotesTransactionRequest(
|
|
7
|
+
export async function buildConsumeNotesTransactionRequest(
|
|
8
|
+
webClient: WebClient,
|
|
8
9
|
noteIds: string[],
|
|
9
10
|
options: SignatureOptions = {},
|
|
10
|
-
): { request: TransactionRequest; salt: Word } {
|
|
11
|
+
): Promise<{ request: TransactionRequest; salt: Word }> {
|
|
11
12
|
if (noteIds.length === 0) {
|
|
12
13
|
throw new Error('At least one note ID is required');
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
+
const noteAndArgsArray = new NoteAndArgsArray();
|
|
16
17
|
for (const noteIdHex of noteIds) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const inputNoteRecord = await webClient.getInputNote(noteIdHex);
|
|
19
|
+
if (!inputNoteRecord) {
|
|
20
|
+
throw new Error(`Note not found in local store: ${noteIdHex}`);
|
|
21
|
+
}
|
|
22
|
+
const note = inputNoteRecord.toNote();
|
|
23
|
+
const noteAndArgs = new NoteAndArgs(note, null);
|
|
24
|
+
noteAndArgsArray.push(noteAndArgs);
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
|
|
@@ -24,7 +29,7 @@ export function buildConsumeNotesTransactionRequest(
|
|
|
24
29
|
const authSaltForBuilder = WordType.fromHex(normalizeHexWord(authSaltHex));
|
|
25
30
|
|
|
26
31
|
let txBuilder = new TransactionRequestBuilder();
|
|
27
|
-
txBuilder = txBuilder.
|
|
32
|
+
txBuilder = txBuilder.withInputNotes(noteAndArgsArray);
|
|
28
33
|
txBuilder = txBuilder.withAuthArg(authSaltForBuilder);
|
|
29
34
|
|
|
30
35
|
if (options.signatureAdviceMap) {
|
|
@@ -38,4 +43,3 @@ export function buildConsumeNotesTransactionRequest(
|
|
|
38
43
|
salt: authSaltForReturn,
|
|
39
44
|
};
|
|
40
45
|
}
|
|
41
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AdviceMap, Word } from '@
|
|
2
|
-
import type { SignatureScheme } from '
|
|
1
|
+
import type { AdviceMap, Word } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import type { SignatureScheme } from '@openzeppelin/psm-client';
|
|
3
3
|
|
|
4
4
|
export interface SignatureOptions {
|
|
5
5
|
salt?: Word;
|
package/src/transaction/p2id.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TransactionRequest, Word } from '@
|
|
1
|
+
import type { TransactionRequest, Word } from '@miden-sdk/miden-sdk';
|
|
2
2
|
import {
|
|
3
3
|
AccountId,
|
|
4
4
|
Felt,
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
MidenArrays,
|
|
8
8
|
Note,
|
|
9
9
|
NoteAssets,
|
|
10
|
-
NoteExecutionHint,
|
|
11
10
|
NoteInputs,
|
|
12
11
|
NoteMetadata,
|
|
13
12
|
NoteRecipient,
|
|
@@ -18,7 +17,7 @@ import {
|
|
|
18
17
|
Rpo256,
|
|
19
18
|
TransactionRequestBuilder,
|
|
20
19
|
Word as WordType,
|
|
21
|
-
} from '@
|
|
20
|
+
} from '@miden-sdk/miden-sdk';
|
|
22
21
|
import { randomWord } from '../utils/random.js';
|
|
23
22
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
24
23
|
import type { SignatureOptions } from './options.js';
|
|
@@ -28,7 +27,6 @@ function buildP2idNote(
|
|
|
28
27
|
recipient: AccountId,
|
|
29
28
|
noteAssets: NoteAssets,
|
|
30
29
|
noteType: NoteType,
|
|
31
|
-
aux: Felt,
|
|
32
30
|
saltHex: string,
|
|
33
31
|
): Note {
|
|
34
32
|
const salt = WordType.fromHex(normalizeHexWord(saltHex));
|
|
@@ -44,14 +42,12 @@ function buildP2idNote(
|
|
|
44
42
|
]));
|
|
45
43
|
|
|
46
44
|
const noteRecipient = new NoteRecipient(serialNum, noteScript, noteInputs);
|
|
47
|
-
const noteTag = NoteTag.
|
|
45
|
+
const noteTag = NoteTag.withAccountTarget(recipient);
|
|
48
46
|
|
|
49
47
|
const noteMetadata = new NoteMetadata(
|
|
50
48
|
sender,
|
|
51
49
|
noteType,
|
|
52
50
|
noteTag,
|
|
53
|
-
NoteExecutionHint.always(),
|
|
54
|
-
aux,
|
|
55
51
|
);
|
|
56
52
|
|
|
57
53
|
return new Note(noteAssets, noteMetadata, noteRecipient);
|
|
@@ -78,7 +74,6 @@ export function buildP2idTransactionRequest(
|
|
|
78
74
|
recipient,
|
|
79
75
|
noteAssets,
|
|
80
76
|
NoteType.Public,
|
|
81
|
-
new Felt(0n),
|
|
82
77
|
authSaltHex,
|
|
83
78
|
);
|
|
84
79
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { TransactionRequest, TransactionSummary, WebClient } from '@
|
|
2
|
-
import { AccountId } from '@
|
|
1
|
+
import type { TransactionRequest, TransactionSummary, WebClient } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import { AccountId } from '@miden-sdk/miden-sdk';
|
|
3
3
|
|
|
4
4
|
export async function executeForSummary(
|
|
5
5
|
client: WebClient,
|
|
@@ -7,19 +7,19 @@ import {
|
|
|
7
7
|
WebClient,
|
|
8
8
|
Word,
|
|
9
9
|
Word as WordType,
|
|
10
|
-
} from '@
|
|
10
|
+
} from '@miden-sdk/miden-sdk';
|
|
11
11
|
import { PSM_ECDSA_MASM, PSM_MASM } from '../account/masm.js';
|
|
12
12
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
13
13
|
import { randomWord } from '../utils/random.js';
|
|
14
14
|
import type { SignatureOptions } from './options.js';
|
|
15
15
|
|
|
16
16
|
function buildUpdatePsmFalconScript(webClient: WebClient): TransactionScript {
|
|
17
|
-
const libBuilder = webClient.
|
|
17
|
+
const libBuilder = webClient.createCodeBuilder();
|
|
18
18
|
const psmLib = libBuilder.buildLibrary('openzeppelin::psm', PSM_MASM);
|
|
19
19
|
libBuilder.linkDynamicLibrary(psmLib);
|
|
20
20
|
|
|
21
21
|
const scriptSource = `
|
|
22
|
-
use
|
|
22
|
+
use openzeppelin::psm
|
|
23
23
|
|
|
24
24
|
begin
|
|
25
25
|
adv.push_mapval
|
|
@@ -32,12 +32,12 @@ end
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function buildUpdatePsmEcdsaScript(webClient: WebClient): TransactionScript {
|
|
35
|
-
const libBuilder = webClient.
|
|
35
|
+
const libBuilder = webClient.createCodeBuilder();
|
|
36
36
|
const psmLib = libBuilder.buildLibrary('openzeppelin::psm_ecdsa', PSM_ECDSA_MASM);
|
|
37
37
|
libBuilder.linkDynamicLibrary(psmLib);
|
|
38
38
|
|
|
39
39
|
const scriptSource = `
|
|
40
|
-
use
|
|
40
|
+
use openzeppelin::psm_ecdsa
|
|
41
41
|
|
|
42
42
|
begin
|
|
43
43
|
adv.push_mapval
|
|
@@ -87,4 +87,3 @@ export async function buildUpdatePsmTransactionRequest(
|
|
|
87
87
|
salt: authSaltForReturn,
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
-
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
WebClient,
|
|
10
10
|
Word,
|
|
11
11
|
Word as WordType,
|
|
12
|
-
} from '@
|
|
12
|
+
} from '@miden-sdk/miden-sdk';
|
|
13
13
|
import { MULTISIG_ECDSA_MASM, MULTISIG_MASM, PSM_ECDSA_MASM, PSM_MASM } from '../account/masm.js';
|
|
14
14
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
15
15
|
import { randomWord } from '../utils/random.js';
|
|
@@ -36,7 +36,7 @@ function buildMultisigConfigAdvice(
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function buildUpdateSignersFalconScript(webClient: WebClient): TransactionScript {
|
|
39
|
-
const libBuilder = webClient.
|
|
39
|
+
const libBuilder = webClient.createCodeBuilder();
|
|
40
40
|
const psmLib = libBuilder.buildLibrary('openzeppelin::psm', PSM_MASM);
|
|
41
41
|
libBuilder.linkStaticLibrary(psmLib);
|
|
42
42
|
|
|
@@ -44,7 +44,7 @@ function buildUpdateSignersFalconScript(webClient: WebClient): TransactionScript
|
|
|
44
44
|
libBuilder.linkDynamicLibrary(multisigLib);
|
|
45
45
|
|
|
46
46
|
const scriptSource = `
|
|
47
|
-
use
|
|
47
|
+
use auth::multisig
|
|
48
48
|
|
|
49
49
|
begin
|
|
50
50
|
call.multisig::update_signers_and_threshold
|
|
@@ -55,7 +55,7 @@ end
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function buildUpdateSignersEcdsaScript(webClient: WebClient): TransactionScript {
|
|
58
|
-
const libBuilder = webClient.
|
|
58
|
+
const libBuilder = webClient.createCodeBuilder();
|
|
59
59
|
const psmLib = libBuilder.buildLibrary('openzeppelin::psm_ecdsa', PSM_ECDSA_MASM);
|
|
60
60
|
libBuilder.linkStaticLibrary(psmLib);
|
|
61
61
|
|
|
@@ -63,7 +63,7 @@ function buildUpdateSignersEcdsaScript(webClient: WebClient): TransactionScript
|
|
|
63
63
|
libBuilder.linkDynamicLibrary(multisigLib);
|
|
64
64
|
|
|
65
65
|
const scriptSource = `
|
|
66
|
-
use
|
|
66
|
+
use auth::multisig
|
|
67
67
|
|
|
68
68
|
begin
|
|
69
69
|
call.multisig::update_signers_and_threshold
|
|
@@ -115,4 +115,3 @@ export async function buildUpdateSignersTransactionRequest(
|
|
|
115
115
|
configHash: configHashForReturn,
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
|
package/src/transaction.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeAll } from 'vitest';
|
|
2
2
|
|
|
3
3
|
// Mock Miden SDK before importing transaction module
|
|
4
|
-
vi.mock('@
|
|
4
|
+
vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
5
5
|
AccountId: { fromHex: vi.fn() },
|
|
6
6
|
AdviceMap: vi.fn(),
|
|
7
7
|
Felt: vi.fn().mockImplementation((v) => ({ value: v })),
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Account } from '@
|
|
1
|
+
import type { Account } from '@miden-sdk/miden-sdk';
|
|
2
2
|
import type { SignatureScheme } from '@openzeppelin/psm-client';
|
|
3
3
|
import type { ProcedureName } from './procedures.js';
|
|
4
4
|
import type { TransactionProposal } from './types/proposal.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AccountId, Felt, FeltArray, Rpo256, Word } from '@miden-sdk/miden-sdk';
|
|
2
|
+
|
|
3
|
+
export class AuthDigest {
|
|
4
|
+
static fromAccountIdWithTimestamp(accountId: string, timestamp: number): Word {
|
|
5
|
+
const paddedHex = accountId.startsWith('0x') ? accountId : `0x${accountId}`;
|
|
6
|
+
const parsedAccountId = AccountId.fromHex(paddedHex);
|
|
7
|
+
const prefix = parsedAccountId.prefix();
|
|
8
|
+
const suffix = parsedAccountId.suffix();
|
|
9
|
+
|
|
10
|
+
const feltArray = new FeltArray([
|
|
11
|
+
prefix,
|
|
12
|
+
suffix,
|
|
13
|
+
new Felt(BigInt(timestamp)),
|
|
14
|
+
new Felt(BigInt(0)),
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
return Rpo256.hashElements(feltArray);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static fromCommitmentHex(commitmentHex: string): Word {
|
|
21
|
+
const paddedHex = commitmentHex.startsWith('0x') ? commitmentHex : `0x${commitmentHex}`;
|
|
22
|
+
const cleanHex = paddedHex.slice(2).padStart(64, '0');
|
|
23
|
+
return Word.fromHex(`0x${cleanHex}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { EcdsaFormat } from './ecdsa.js';
|
|
3
|
+
|
|
4
|
+
describe('EcdsaFormat', () => {
|
|
5
|
+
describe('normalizeRecoveryByte', () => {
|
|
6
|
+
it('should normalize v=27 to recovery_id=0', () => {
|
|
7
|
+
const rHex = 'aa'.repeat(32);
|
|
8
|
+
const sHex = 'bb'.repeat(32);
|
|
9
|
+
const sig = `0x${rHex}${sHex}1b`;
|
|
10
|
+
const result = EcdsaFormat.normalizeRecoveryByte(sig);
|
|
11
|
+
expect(result).toBe(`0x${rHex}${sHex}00`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should normalize v=28 to recovery_id=1', () => {
|
|
15
|
+
const rHex = 'aa'.repeat(32);
|
|
16
|
+
const sHex = 'bb'.repeat(32);
|
|
17
|
+
const sig = `0x${rHex}${sHex}1c`;
|
|
18
|
+
const result = EcdsaFormat.normalizeRecoveryByte(sig);
|
|
19
|
+
expect(result).toBe(`0x${rHex}${sHex}01`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should pass through v=0 unchanged', () => {
|
|
23
|
+
const rHex = 'aa'.repeat(32);
|
|
24
|
+
const sHex = 'bb'.repeat(32);
|
|
25
|
+
const sig = `0x${rHex}${sHex}00`;
|
|
26
|
+
const result = EcdsaFormat.normalizeRecoveryByte(sig);
|
|
27
|
+
expect(result).toBe(sig);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should pass through 64-byte signature unchanged', () => {
|
|
31
|
+
const sig = `0x${'ab'.repeat(64)}`;
|
|
32
|
+
const result = EcdsaFormat.normalizeRecoveryByte(sig);
|
|
33
|
+
expect(result).toBe(sig);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should throw on invalid length', () => {
|
|
37
|
+
expect(() => EcdsaFormat.normalizeRecoveryByte('0x' + 'ab'.repeat(10))).toThrow(
|
|
38
|
+
'Invalid ECDSA signature length',
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle input without 0x prefix', () => {
|
|
43
|
+
const rHex = 'aa'.repeat(32);
|
|
44
|
+
const sHex = 'bb'.repeat(32);
|
|
45
|
+
const sig = `${rHex}${sHex}1b`;
|
|
46
|
+
const result = EcdsaFormat.normalizeRecoveryByte(sig);
|
|
47
|
+
expect(result).toBe(`0x${rHex}${sHex}00`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('compressPublicKey', () => {
|
|
52
|
+
it('should compress a 65-byte uncompressed key with even y', () => {
|
|
53
|
+
const x = 'aa'.repeat(32);
|
|
54
|
+
const yEven = 'bb'.repeat(31) + 'b0';
|
|
55
|
+
const uncompressed = `0x04${x}${yEven}`;
|
|
56
|
+
const result = EcdsaFormat.compressPublicKey(uncompressed);
|
|
57
|
+
expect(result).toBe(`0x02${x}`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should compress a 65-byte uncompressed key with odd y', () => {
|
|
61
|
+
const x = 'aa'.repeat(32);
|
|
62
|
+
const yOdd = 'bb'.repeat(31) + 'b1';
|
|
63
|
+
const uncompressed = `0x04${x}${yOdd}`;
|
|
64
|
+
const result = EcdsaFormat.compressPublicKey(uncompressed);
|
|
65
|
+
expect(result).toBe(`0x03${x}`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should pass through a 33-byte compressed key unchanged', () => {
|
|
69
|
+
const compressed = `0x02${'aa'.repeat(32)}`;
|
|
70
|
+
const result = EcdsaFormat.compressPublicKey(compressed);
|
|
71
|
+
expect(result).toBe(compressed);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should throw on invalid length', () => {
|
|
75
|
+
expect(() => EcdsaFormat.compressPublicKey('0x' + 'ab'.repeat(10))).toThrow(
|
|
76
|
+
'Expected 65-byte uncompressed public key',
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should throw on 65 bytes without 04 prefix', () => {
|
|
81
|
+
expect(() => EcdsaFormat.compressPublicKey('0x05' + 'ab'.repeat(64))).toThrow(
|
|
82
|
+
'Expected 65-byte uncompressed public key',
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('keccakDigestHex', () => {
|
|
88
|
+
it('should return 0x-prefixed hex of keccak-256 hash', () => {
|
|
89
|
+
const result = EcdsaFormat.keccakDigestHex(new Uint8Array(0));
|
|
90
|
+
expect(result).toMatch(/^0x[a-f0-9]{64}$/);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should produce correct hash for empty input', () => {
|
|
94
|
+
const result = EcdsaFormat.keccakDigestHex(new Uint8Array(0));
|
|
95
|
+
expect(result).toBe('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should produce different hashes for different inputs', () => {
|
|
99
|
+
const a = EcdsaFormat.keccakDigestHex(new Uint8Array([1]));
|
|
100
|
+
const b = EcdsaFormat.keccakDigestHex(new Uint8Array([2]));
|
|
101
|
+
expect(a).not.toBe(b);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
2
|
+
import { ensureHexPrefix } from './encoding.js';
|
|
3
|
+
|
|
4
|
+
const ECDSA_SIGNATURE_BYTE_LENGTH = 64;
|
|
5
|
+
const ECDSA_SIGNATURE_WITH_RECOVERY_BYTE_LENGTH = 65;
|
|
6
|
+
|
|
7
|
+
export class EcdsaFormat {
|
|
8
|
+
static normalizeSignatureHex(signatureHex: string): string {
|
|
9
|
+
const clean = ensureHexPrefix(signatureHex).slice(2);
|
|
10
|
+
const byteLength = clean.length / 2;
|
|
11
|
+
|
|
12
|
+
if (byteLength === ECDSA_SIGNATURE_WITH_RECOVERY_BYTE_LENGTH) {
|
|
13
|
+
return `0x${clean.slice(0, ECDSA_SIGNATURE_BYTE_LENGTH * 2)}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (byteLength === ECDSA_SIGNATURE_BYTE_LENGTH) {
|
|
17
|
+
return `0x${clean}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Invalid ECDSA signature length: expected ${ECDSA_SIGNATURE_BYTE_LENGTH} or ${ECDSA_SIGNATURE_WITH_RECOVERY_BYTE_LENGTH} bytes, got ${byteLength}`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static normalizeRecoveryByte(signatureHex: string): string {
|
|
26
|
+
const clean = ensureHexPrefix(signatureHex).slice(2);
|
|
27
|
+
const byteLength = clean.length / 2;
|
|
28
|
+
|
|
29
|
+
if (byteLength === ECDSA_SIGNATURE_WITH_RECOVERY_BYTE_LENGTH) {
|
|
30
|
+
const vByte = parseInt(clean.slice(-2), 16);
|
|
31
|
+
if (vByte === 27 || vByte === 28) {
|
|
32
|
+
const normalized = clean.slice(0, -2) + (vByte - 27).toString(16).padStart(2, '0');
|
|
33
|
+
return `0x${normalized}`;
|
|
34
|
+
}
|
|
35
|
+
return `0x${clean}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (byteLength === ECDSA_SIGNATURE_BYTE_LENGTH) {
|
|
39
|
+
return `0x${clean}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Invalid ECDSA signature length: expected ${ECDSA_SIGNATURE_BYTE_LENGTH} or ${ECDSA_SIGNATURE_WITH_RECOVERY_BYTE_LENGTH} bytes, got ${byteLength}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static validatePublicKeyHex(publicKeyHex: string): boolean {
|
|
48
|
+
const clean = ensureHexPrefix(publicKeyHex).slice(2);
|
|
49
|
+
const byteLength = clean.length / 2;
|
|
50
|
+
return byteLength === 33 || byteLength === 65;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static compressPublicKey(uncompressedHex: string): string {
|
|
54
|
+
const clean = ensureHexPrefix(uncompressedHex).slice(2);
|
|
55
|
+
const byteLength = clean.length / 2;
|
|
56
|
+
|
|
57
|
+
if (byteLength === 33) return `0x${clean}`;
|
|
58
|
+
|
|
59
|
+
if (byteLength !== 65 || !clean.startsWith('04')) {
|
|
60
|
+
throw new Error(`Expected 65-byte uncompressed public key, got ${byteLength} bytes`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const x = clean.slice(2, 66);
|
|
64
|
+
const yLastNibble = parseInt(clean.slice(-1), 16);
|
|
65
|
+
const tag = yLastNibble % 2 === 0 ? '02' : '03';
|
|
66
|
+
return `0x${tag}${x}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static keccakDigestHex(data: Uint8Array): string {
|
|
70
|
+
const hash = keccak_256(data);
|
|
71
|
+
let hex = '0x';
|
|
72
|
+
for (let i = 0; i < hash.length; i++) {
|
|
73
|
+
hex += hash[i].toString(16).padStart(2, '0');
|
|
74
|
+
}
|
|
75
|
+
return hex;
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { PublicKeyFormat } from './key.js';
|
|
3
|
+
|
|
4
|
+
vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
5
|
+
Word: class {
|
|
6
|
+
private u64s: BigUint64Array;
|
|
7
|
+
constructor(u64s: BigUint64Array) {
|
|
8
|
+
this.u64s = u64s;
|
|
9
|
+
}
|
|
10
|
+
toHex() {
|
|
11
|
+
const h3 = this.u64s[3].toString(16).padStart(16, '0');
|
|
12
|
+
const h2 = this.u64s[2].toString(16).padStart(16, '0');
|
|
13
|
+
const h1 = this.u64s[1].toString(16).padStart(16, '0');
|
|
14
|
+
const h0 = this.u64s[0].toString(16).padStart(16, '0');
|
|
15
|
+
return '0x' + h3 + h2 + h1 + h0;
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock('./signature.js', () => ({
|
|
21
|
+
tryComputeCommitmentHex: vi.fn((_hex: string, _scheme: string) => '0x' + 'cc'.repeat(32)),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe('PublicKeyFormat', () => {
|
|
25
|
+
describe('parse', () => {
|
|
26
|
+
it('should treat 32-byte input as Word commitment with falcon scheme', () => {
|
|
27
|
+
const bytes = new Uint8Array(32);
|
|
28
|
+
bytes[0] = 0x42;
|
|
29
|
+
const result = PublicKeyFormat.parse(bytes);
|
|
30
|
+
expect(result.scheme).toBe('falcon');
|
|
31
|
+
expect(result.commitment).toBe(result.publicKeyHex);
|
|
32
|
+
expect(result.publicKeyHex).toMatch(/^0x[a-f0-9]{64}$/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should detect 0x00-prefixed key as falcon', () => {
|
|
36
|
+
const bytes = new Uint8Array(200);
|
|
37
|
+
bytes[0] = 0x00;
|
|
38
|
+
bytes[1] = 0xab;
|
|
39
|
+
const result = PublicKeyFormat.parse(bytes);
|
|
40
|
+
expect(result.scheme).toBe('falcon');
|
|
41
|
+
expect(result.commitment).toBe('0x' + 'cc'.repeat(32));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should detect 0x01-prefixed 34-byte key as ecdsa', () => {
|
|
45
|
+
const bytes = new Uint8Array(34);
|
|
46
|
+
bytes[0] = 0x01;
|
|
47
|
+
bytes[1] = 0x02;
|
|
48
|
+
const result = PublicKeyFormat.parse(bytes);
|
|
49
|
+
expect(result.scheme).toBe('ecdsa');
|
|
50
|
+
expect(result.commitment).toBe('0x' + 'cc'.repeat(32));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should detect 33-byte input as ecdsa', () => {
|
|
54
|
+
const bytes = new Uint8Array(33);
|
|
55
|
+
bytes[0] = 0x02;
|
|
56
|
+
const result = PublicKeyFormat.parse(bytes);
|
|
57
|
+
expect(result.scheme).toBe('ecdsa');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should detect 65-byte input as ecdsa', () => {
|
|
61
|
+
const bytes = new Uint8Array(65);
|
|
62
|
+
bytes[0] = 0x04;
|
|
63
|
+
const result = PublicKeyFormat.parse(bytes);
|
|
64
|
+
expect(result.scheme).toBe('ecdsa');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should default to falcon for unknown lengths', () => {
|
|
68
|
+
const bytes = new Uint8Array(500);
|
|
69
|
+
const result = PublicKeyFormat.parse(bytes);
|
|
70
|
+
expect(result.scheme).toBe('falcon');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('wordBytesToHex', () => {
|
|
75
|
+
it('should convert 32 zero bytes to a valid hex string', () => {
|
|
76
|
+
const bytes = new Uint8Array(32);
|
|
77
|
+
const result = PublicKeyFormat.wordBytesToHex(bytes);
|
|
78
|
+
expect(result).toMatch(/^0x[a-f0-9]{64}$/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should produce consistent output for same input', () => {
|
|
82
|
+
const bytes = new Uint8Array(32);
|
|
83
|
+
bytes[0] = 0xff;
|
|
84
|
+
const a = PublicKeyFormat.wordBytesToHex(bytes);
|
|
85
|
+
const b = PublicKeyFormat.wordBytesToHex(bytes);
|
|
86
|
+
expect(a).toBe(b);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/utils/key.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Word } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import type { SignatureScheme } from '../types.js';
|
|
3
|
+
import { bytesToHex } from './encoding.js';
|
|
4
|
+
import { tryComputeCommitmentHex } from './signature.js';
|
|
5
|
+
|
|
6
|
+
export class PublicKeyFormat {
|
|
7
|
+
static parse(publicKey: Uint8Array): {
|
|
8
|
+
scheme: SignatureScheme;
|
|
9
|
+
publicKeyHex: string;
|
|
10
|
+
commitment: string | null;
|
|
11
|
+
} {
|
|
12
|
+
if (publicKey.length === 32) {
|
|
13
|
+
const commitmentHex = PublicKeyFormat.wordBytesToHex(publicKey);
|
|
14
|
+
return { scheme: 'falcon', publicKeyHex: commitmentHex, commitment: commitmentHex };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const firstByte = publicKey[0];
|
|
18
|
+
|
|
19
|
+
if (firstByte === 0x00 && publicKey.length > 100) {
|
|
20
|
+
const hex = bytesToHex(publicKey.slice(1));
|
|
21
|
+
return { scheme: 'falcon', publicKeyHex: hex, commitment: tryComputeCommitmentHex(hex, 'falcon') };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (firstByte === 0x01 && publicKey.length === 34) {
|
|
25
|
+
const hex = bytesToHex(publicKey.slice(1));
|
|
26
|
+
return { scheme: 'ecdsa', publicKeyHex: hex, commitment: tryComputeCommitmentHex(hex, 'ecdsa') };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (publicKey.length === 33 || publicKey.length === 65) {
|
|
30
|
+
const hex = bytesToHex(publicKey);
|
|
31
|
+
return { scheme: 'ecdsa', publicKeyHex: hex, commitment: tryComputeCommitmentHex(hex, 'ecdsa') };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const hex = bytesToHex(publicKey);
|
|
35
|
+
return { scheme: 'falcon', publicKeyHex: hex, commitment: tryComputeCommitmentHex(hex, 'falcon') };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static wordBytesToHex(bytes: Uint8Array): string {
|
|
39
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
40
|
+
const u64s = new BigUint64Array(4);
|
|
41
|
+
for (let i = 0; i < 4; i++) {
|
|
42
|
+
u64s[i] = view.getBigUint64(i * 8, true);
|
|
43
|
+
}
|
|
44
|
+
const word = new Word(u64s);
|
|
45
|
+
const hex = word.toHex();
|
|
46
|
+
const clean = hex.replace(/^0x/i, '').toLowerCase().padStart(64, '0');
|
|
47
|
+
return `0x${clean}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/utils/random.ts
CHANGED