@openzeppelin/miden-multisig-client 0.13.3 → 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 +10 -4
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +97 -71
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +481 -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 +572 -29
- package/src/multisig.ts +121 -83
- 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
|
@@ -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
|
});
|
package/src/signers/ecdsa.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequestAuthPayload } from '@openzeppelin/guardian-client';
|
|
2
|
-
import { AuthSecretKey } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import { AccountId, AuthSecretKey, type MidenClient, type Word } from '@miden-sdk/miden-sdk';
|
|
3
3
|
import type { Signer, SignatureScheme } from '../types.js';
|
|
4
4
|
import { bytesToHex, normalizeHexWord } from '../utils/encoding.js';
|
|
5
5
|
import { AuthDigest } from '../utils/digest.js';
|
|
@@ -9,13 +9,15 @@ export class EcdsaSigner implements Signer {
|
|
|
9
9
|
readonly publicKey: string;
|
|
10
10
|
readonly scheme: SignatureScheme = 'ecdsa';
|
|
11
11
|
private readonly secretKey: AuthSecretKey;
|
|
12
|
+
private readonly publicKeyCommitment: Word;
|
|
12
13
|
|
|
13
14
|
constructor(secretKey: AuthSecretKey) {
|
|
14
15
|
this.secretKey = secretKey;
|
|
15
16
|
const pubKey = secretKey.publicKey();
|
|
16
17
|
const serialized = pubKey.serialize();
|
|
17
18
|
this.publicKey = bytesToHex(serialized.slice(1));
|
|
18
|
-
this.
|
|
19
|
+
this.publicKeyCommitment = pubKey.toCommitment();
|
|
20
|
+
this.commitment = normalizeHexWord(this.publicKeyCommitment.toHex());
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
async signAccountIdWithTimestamp(accountId: string, timestamp: number): Promise<string> {
|
|
@@ -37,6 +39,20 @@ export class EcdsaSigner implements Signer {
|
|
|
37
39
|
return this.signWord(word);
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
async bindAccountKey(midenClient: MidenClient, accountId: string): Promise<void> {
|
|
43
|
+
const targetAccountId = AccountId.fromHex(accountId);
|
|
44
|
+
const existingAccountId = await midenClient.keystore.getAccountId(this.publicKeyCommitment);
|
|
45
|
+
if (existingAccountId) {
|
|
46
|
+
if (existingAccountId.toString().toLowerCase() === accountId.toLowerCase()) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Signer commitment ${this.commitment} is already bound to account ${existingAccountId.toString()}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
await midenClient.keystore.insert(targetAccountId, this.secretKey);
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
private signWord(word: ReturnType<typeof AuthDigest.fromCommitmentHex>): string {
|
|
41
57
|
const signature = this.secretKey.sign(word);
|
|
42
58
|
const signatureBytes = signature.serialize();
|
|
@@ -6,10 +6,12 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
6
6
|
serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
|
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, 10, 11, 12, 13, 14, 15]),
|
|
14
16
|
};
|
|
15
17
|
|
|
@@ -30,6 +32,7 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
30
32
|
},
|
|
31
33
|
AccountId: {
|
|
32
34
|
fromHex: vi.fn((hex: string) => ({
|
|
35
|
+
toString: () => hex,
|
|
33
36
|
prefix: () => ({ asInt: () => BigInt(1) }),
|
|
34
37
|
suffix: () => ({ asInt: () => BigInt(2) }),
|
|
35
38
|
})),
|
|
@@ -147,4 +150,48 @@ describe('FalconSigner', () => {
|
|
|
147
150
|
expect(signature).toBe('0x010203040506070809');
|
|
148
151
|
});
|
|
149
152
|
});
|
|
153
|
+
|
|
154
|
+
describe('bindAccountKey', () => {
|
|
155
|
+
it('inserts the signer key when the commitment is not bound yet', async () => {
|
|
156
|
+
const midenClient = {
|
|
157
|
+
keystore: {
|
|
158
|
+
getAccountId: vi.fn().mockResolvedValue(null),
|
|
159
|
+
insert: vi.fn().mockResolvedValue(undefined),
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
await signer.bindAccountKey(midenClient as never, '0x' + 'd'.repeat(30));
|
|
164
|
+
|
|
165
|
+
expect(midenClient.keystore.getAccountId).toHaveBeenCalledTimes(1);
|
|
166
|
+
expect(midenClient.keystore.insert).toHaveBeenCalledTimes(1);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('skips insert when the signer key is already bound to the same account', async () => {
|
|
170
|
+
const accountId = '0x' + 'd'.repeat(30);
|
|
171
|
+
const midenClient = {
|
|
172
|
+
keystore: {
|
|
173
|
+
getAccountId: vi.fn().mockResolvedValue({ toString: () => accountId }),
|
|
174
|
+
insert: vi.fn().mockResolvedValue(undefined),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
await signer.bindAccountKey(midenClient as never, accountId);
|
|
179
|
+
|
|
180
|
+
expect(midenClient.keystore.insert).not.toHaveBeenCalled();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('throws when the signer key is already bound to a different account', async () => {
|
|
184
|
+
const midenClient = {
|
|
185
|
+
keystore: {
|
|
186
|
+
getAccountId: vi.fn().mockResolvedValue({ toString: () => '0x' + 'e'.repeat(30) }),
|
|
187
|
+
insert: vi.fn().mockResolvedValue(undefined),
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
await expect(
|
|
192
|
+
signer.bindAccountKey(midenClient as never, '0x' + 'd'.repeat(30)),
|
|
193
|
+
).rejects.toThrow('already bound to account');
|
|
194
|
+
expect(midenClient.keystore.insert).not.toHaveBeenCalled();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
150
197
|
});
|
package/src/signers/falcon.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequestAuthPayload } from '@openzeppelin/guardian-client';
|
|
2
|
-
import { AuthSecretKey, type Word } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import { AccountId, AuthSecretKey, type MidenClient, type Word } from '@miden-sdk/miden-sdk';
|
|
3
3
|
import type { Signer, SignatureScheme } from '../types.js';
|
|
4
4
|
import { bytesToHex } from '../utils/encoding.js';
|
|
5
5
|
import { AuthDigest } from '../utils/digest.js';
|
|
@@ -9,11 +9,13 @@ export class FalconSigner implements Signer {
|
|
|
9
9
|
readonly publicKey: string;
|
|
10
10
|
readonly scheme: SignatureScheme = 'falcon';
|
|
11
11
|
private readonly secretKey: AuthSecretKey;
|
|
12
|
+
private readonly publicKeyCommitment: Word;
|
|
12
13
|
|
|
13
14
|
constructor(secretKey: AuthSecretKey) {
|
|
14
15
|
this.secretKey = secretKey;
|
|
15
16
|
const pubKey = secretKey.publicKey();
|
|
16
|
-
this.
|
|
17
|
+
this.publicKeyCommitment = pubKey.toCommitment();
|
|
18
|
+
this.commitment = this.publicKeyCommitment.toHex();
|
|
17
19
|
const serialized = pubKey.serialize();
|
|
18
20
|
const falconPubKey = serialized.slice(1);
|
|
19
21
|
this.publicKey = bytesToHex(falconPubKey);
|
|
@@ -38,6 +40,20 @@ export class FalconSigner implements Signer {
|
|
|
38
40
|
return this.signWord(word);
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
async bindAccountKey(midenClient: MidenClient, accountId: string): Promise<void> {
|
|
44
|
+
const targetAccountId = AccountId.fromHex(accountId);
|
|
45
|
+
const existingAccountId = await midenClient.keystore.getAccountId(this.publicKeyCommitment);
|
|
46
|
+
if (existingAccountId) {
|
|
47
|
+
if (existingAccountId.toString().toLowerCase() === accountId.toLowerCase()) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Signer commitment ${this.commitment} is already bound to account ${existingAccountId.toString()}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
await midenClient.keystore.insert(targetAccountId, this.secretKey);
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
private signWord(word: Word): string {
|
|
42
58
|
const signature = this.secretKey.sign(word);
|
|
43
59
|
const signatureBytes = signature.serialize();
|
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
MidenClient,
|
|
3
|
+
TransactionRequest,
|
|
4
|
+
WasmWebClient,
|
|
5
|
+
Word,
|
|
6
|
+
} from '@miden-sdk/miden-sdk';
|
|
7
|
+
import {
|
|
8
|
+
NoteAndArgs,
|
|
9
|
+
NoteAndArgsArray,
|
|
10
|
+
TransactionRequestBuilder,
|
|
11
|
+
Word as WordType,
|
|
12
|
+
} from '@miden-sdk/miden-sdk';
|
|
13
|
+
import { getRawMidenClient } from '../raw-client.js';
|
|
3
14
|
import { randomWord } from '../utils/random.js';
|
|
4
15
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
5
16
|
import type { SignatureOptions } from './options.js';
|
|
6
17
|
|
|
7
18
|
export async function buildConsumeNotesTransactionRequest(
|
|
8
|
-
|
|
19
|
+
client: MidenClient | WasmWebClient,
|
|
9
20
|
noteIds: string[],
|
|
10
21
|
options: SignatureOptions = {},
|
|
11
22
|
): Promise<{ request: TransactionRequest; salt: Word }> {
|
|
@@ -13,10 +24,10 @@ export async function buildConsumeNotesTransactionRequest(
|
|
|
13
24
|
throw new Error('At least one note ID is required');
|
|
14
25
|
}
|
|
15
26
|
|
|
16
|
-
|
|
27
|
+
const rawClient = await getRawMidenClient(client, options.midenRpcEndpoint);
|
|
17
28
|
const noteAndArgsArray = new NoteAndArgsArray();
|
|
18
29
|
for (const noteIdHex of noteIds) {
|
|
19
|
-
const inputNoteRecord = await
|
|
30
|
+
const inputNoteRecord = await rawClient.getInputNote(noteIdHex);
|
|
20
31
|
if (!inputNoteRecord) {
|
|
21
32
|
throw new Error(`Note not found in local store: ${noteIdHex}`);
|
|
22
33
|
}
|
|
@@ -21,10 +21,20 @@ const {
|
|
|
21
21
|
mockRandomWord: vi.fn().mockReturnValue({
|
|
22
22
|
toHex: () => '0x' + 'aa'.repeat(32),
|
|
23
23
|
}),
|
|
24
|
-
mockWordFromHex: vi.fn((hex: string) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
mockWordFromHex: vi.fn((hex: string) => {
|
|
25
|
+
const normalized = hex.toLowerCase();
|
|
26
|
+
return {
|
|
27
|
+
toHex: () => hex,
|
|
28
|
+
toFelts: () => normalized === `0x${'00'.repeat(32)}`
|
|
29
|
+
? [
|
|
30
|
+
{ value: 0n },
|
|
31
|
+
{ value: 0n },
|
|
32
|
+
{ value: 0n },
|
|
33
|
+
{ value: 0n },
|
|
34
|
+
]
|
|
35
|
+
: saltFelts,
|
|
36
|
+
};
|
|
37
|
+
}),
|
|
28
38
|
saltFelts,
|
|
29
39
|
};
|
|
30
40
|
});
|
|
@@ -50,7 +60,7 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
50
60
|
constructor(_assets: unknown[]) {}
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
class
|
|
63
|
+
class NoteStorage {
|
|
54
64
|
constructor(_inputs: FeltArray) {}
|
|
55
65
|
}
|
|
56
66
|
|
|
@@ -82,7 +92,7 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
82
92
|
constructor(_faucet: unknown, _amount: bigint) {}
|
|
83
93
|
}
|
|
84
94
|
|
|
85
|
-
class
|
|
95
|
+
class NoteArray {
|
|
86
96
|
constructor(_notes: unknown[]) {}
|
|
87
97
|
}
|
|
88
98
|
|
|
@@ -116,13 +126,13 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
116
126
|
FeltArray,
|
|
117
127
|
FungibleAsset,
|
|
118
128
|
MidenArrays: {
|
|
119
|
-
|
|
129
|
+
NoteArray,
|
|
120
130
|
},
|
|
121
131
|
Note,
|
|
122
132
|
NoteAssets,
|
|
123
|
-
NoteInputs,
|
|
124
133
|
NoteMetadata,
|
|
125
134
|
NoteRecipient,
|
|
135
|
+
NoteStorage,
|
|
126
136
|
NoteScript: {
|
|
127
137
|
p2id: vi.fn(() => ({ kind: 'p2id-script' })),
|
|
128
138
|
},
|
|
@@ -135,7 +145,7 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
135
145
|
OutputNote: {
|
|
136
146
|
full: vi.fn((note: unknown) => ({ note })),
|
|
137
147
|
},
|
|
138
|
-
|
|
148
|
+
Poseidon2: {
|
|
139
149
|
hashElements: mockHashElements,
|
|
140
150
|
},
|
|
141
151
|
TransactionRequestBuilder,
|
package/src/transaction/p2id.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import type { TransactionRequest, Word } from '@miden-sdk/miden-sdk';
|
|
2
2
|
import {
|
|
3
3
|
AccountId,
|
|
4
|
-
Felt,
|
|
5
4
|
FeltArray,
|
|
6
5
|
FungibleAsset,
|
|
7
6
|
MidenArrays,
|
|
8
7
|
Note,
|
|
9
8
|
NoteAssets,
|
|
10
|
-
NoteInputs,
|
|
11
9
|
NoteMetadata,
|
|
12
10
|
NoteRecipient,
|
|
13
11
|
NoteScript,
|
|
12
|
+
NoteStorage,
|
|
14
13
|
NoteTag,
|
|
15
14
|
NoteType,
|
|
16
|
-
|
|
17
|
-
Rpo256,
|
|
15
|
+
Poseidon2,
|
|
18
16
|
TransactionRequestBuilder,
|
|
19
17
|
Word as WordType,
|
|
20
18
|
} from '@miden-sdk/miden-sdk';
|
|
@@ -23,12 +21,10 @@ import { normalizeHexWord } from '../utils/encoding.js';
|
|
|
23
21
|
import type { SignatureOptions } from './options.js';
|
|
24
22
|
|
|
25
23
|
export function deriveP2idSerialNumber(salt: Word): Word {
|
|
26
|
-
|
|
24
|
+
const zeroWord = WordType.fromHex(`0x${'00'.repeat(32)}`);
|
|
25
|
+
return Poseidon2.hashElements(new FeltArray([
|
|
27
26
|
...salt.toFelts(),
|
|
28
|
-
|
|
29
|
-
new Felt(0n),
|
|
30
|
-
new Felt(0n),
|
|
31
|
-
new Felt(0n),
|
|
27
|
+
...zeroWord.toFelts(),
|
|
32
28
|
]));
|
|
33
29
|
}
|
|
34
30
|
|
|
@@ -43,12 +39,12 @@ function buildP2idNote(
|
|
|
43
39
|
const serialNum = deriveP2idSerialNumber(salt);
|
|
44
40
|
|
|
45
41
|
const noteScript = NoteScript.p2id();
|
|
46
|
-
const
|
|
42
|
+
const noteStorage = new NoteStorage(new FeltArray([
|
|
47
43
|
recipient.suffix(),
|
|
48
44
|
recipient.prefix(),
|
|
49
45
|
]));
|
|
50
46
|
|
|
51
|
-
const noteRecipient = new NoteRecipient(serialNum, noteScript,
|
|
47
|
+
const noteRecipient = new NoteRecipient(serialNum, noteScript, noteStorage);
|
|
52
48
|
const noteTag = NoteTag.withAccountTarget(recipient);
|
|
53
49
|
|
|
54
50
|
const noteMetadata = new NoteMetadata(
|
|
@@ -84,8 +80,7 @@ export function buildP2idTransactionRequest(
|
|
|
84
80
|
authSaltHex,
|
|
85
81
|
);
|
|
86
82
|
|
|
87
|
-
const
|
|
88
|
-
const outputNotes = new MidenArrays.OutputNoteArray([outputNote]);
|
|
83
|
+
const outputNotes = new MidenArrays.NoteArray([note]);
|
|
89
84
|
|
|
90
85
|
const authSaltForBuilder = WordType.fromHex(normalizeHexWord(authSaltHex));
|
|
91
86
|
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
MidenClient,
|
|
3
|
+
TransactionRequest,
|
|
4
|
+
TransactionSummary,
|
|
5
|
+
WasmWebClient,
|
|
6
|
+
} from '@miden-sdk/miden-sdk';
|
|
2
7
|
import { AccountId } from '@miden-sdk/miden-sdk';
|
|
8
|
+
import { getRawMidenClient } from '../raw-client.js';
|
|
3
9
|
|
|
4
10
|
export async function executeForSummary(
|
|
5
|
-
client:
|
|
11
|
+
client: MidenClient | WasmWebClient,
|
|
6
12
|
accountId: string,
|
|
7
13
|
txRequest: TransactionRequest,
|
|
14
|
+
midenRpcEndpoint?: string,
|
|
8
15
|
): Promise<TransactionSummary> {
|
|
9
16
|
const acc = AccountId.fromHex(accountId);
|
|
10
|
-
|
|
17
|
+
const rawClient = await getRawMidenClient(client, midenRpcEndpoint);
|
|
18
|
+
return rawClient.executeForSummary(acc, txRequest);
|
|
11
19
|
}
|
|
12
|
-
|
|
@@ -1,50 +1,58 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AdviceMap,
|
|
3
3
|
FeltArray,
|
|
4
|
+
type MidenClient,
|
|
4
5
|
TransactionRequest,
|
|
5
6
|
TransactionRequestBuilder,
|
|
6
7
|
TransactionScript,
|
|
7
|
-
|
|
8
|
+
type WasmWebClient,
|
|
8
9
|
Word,
|
|
9
10
|
Word as WordType,
|
|
10
11
|
} from '@miden-sdk/miden-sdk';
|
|
11
12
|
import { GUARDIAN_ECDSA_MASM, GUARDIAN_MASM } from '../account/masm/auth.js';
|
|
13
|
+
import { compileTxScript } from '../raw-client.js';
|
|
12
14
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
13
15
|
import { randomWord } from '../utils/random.js';
|
|
14
16
|
import type { SignatureOptions } from './options.js';
|
|
15
17
|
import type { SignatureScheme } from '../types.js';
|
|
16
18
|
|
|
17
|
-
function buildUpdateGuardianScript(
|
|
18
|
-
|
|
19
|
+
async function buildUpdateGuardianScript(
|
|
20
|
+
client: MidenClient | WasmWebClient,
|
|
19
21
|
signatureScheme: SignatureScheme,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const guardianLibraryPath =
|
|
22
|
+
midenRpcEndpoint?: string,
|
|
23
|
+
): Promise<TransactionScript> {
|
|
24
|
+
const guardianLibraryPath = 'oz_guardian::guardian';
|
|
23
25
|
const guardianMasm = signatureScheme === 'ecdsa' ? GUARDIAN_ECDSA_MASM : GUARDIAN_MASM;
|
|
24
|
-
const guardianProcedure = signatureScheme === 'ecdsa' ? 'guardian_ecdsa' : 'guardian';
|
|
25
|
-
const guardianLib = libBuilder.buildLibrary(guardianLibraryPath, guardianMasm);
|
|
26
|
-
libBuilder.linkDynamicLibrary(guardianLib);
|
|
27
26
|
|
|
28
27
|
const scriptSource = `
|
|
29
|
-
use
|
|
28
|
+
use oz_guardian::guardian
|
|
30
29
|
|
|
31
30
|
begin
|
|
32
31
|
adv.push_mapval
|
|
33
32
|
dropw
|
|
34
|
-
call
|
|
33
|
+
call.guardian::update_guardian_public_key
|
|
35
34
|
end
|
|
36
35
|
`;
|
|
37
36
|
|
|
38
|
-
return
|
|
37
|
+
return compileTxScript(
|
|
38
|
+
client,
|
|
39
|
+
scriptSource,
|
|
40
|
+
[{ namespace: guardianLibraryPath, code: guardianMasm }],
|
|
41
|
+
midenRpcEndpoint,
|
|
42
|
+
);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export async function buildUpdateGuardianTransactionRequest(
|
|
42
|
-
|
|
46
|
+
client: MidenClient | WasmWebClient,
|
|
43
47
|
newGuardianPubkey: string,
|
|
44
48
|
options: SignatureOptions = {},
|
|
45
49
|
): Promise<{ request: TransactionRequest; salt: Word }> {
|
|
46
50
|
const signatureScheme = options.signatureScheme ?? 'falcon';
|
|
47
|
-
const script = buildUpdateGuardianScript(
|
|
51
|
+
const script = await buildUpdateGuardianScript(
|
|
52
|
+
client,
|
|
53
|
+
signatureScheme,
|
|
54
|
+
options.midenRpcEndpoint,
|
|
55
|
+
);
|
|
48
56
|
|
|
49
57
|
const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
|
|
50
58
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Felt,
|
|
3
3
|
FeltArray,
|
|
4
|
-
|
|
4
|
+
type MidenClient,
|
|
5
|
+
Poseidon2,
|
|
5
6
|
TransactionRequest,
|
|
6
7
|
TransactionRequestBuilder,
|
|
7
8
|
TransactionScript,
|
|
8
|
-
|
|
9
|
+
type WasmWebClient,
|
|
9
10
|
Word,
|
|
10
11
|
Word as WordType,
|
|
11
12
|
} from '@miden-sdk/miden-sdk';
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
MULTISIG_MASM,
|
|
15
16
|
} from '../account/masm/auth.js';
|
|
16
17
|
import { getProcedureRoot, type ProcedureName } from '../procedures.js';
|
|
18
|
+
import { compileTxScript } from '../raw-client.js';
|
|
17
19
|
import { normalizeHexWord } from '../utils/encoding.js';
|
|
18
20
|
import { randomWord } from '../utils/random.js';
|
|
19
21
|
import type { SignatureOptions } from './options.js';
|
|
@@ -31,25 +33,22 @@ function buildProcedureThresholdAdvice(
|
|
|
31
33
|
new Felt(0n),
|
|
32
34
|
new Felt(0n),
|
|
33
35
|
]);
|
|
34
|
-
const configHash =
|
|
36
|
+
const configHash = Poseidon2.hashElements(payload);
|
|
35
37
|
return { configHash, payload };
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
function buildUpdateProcedureThresholdScript(
|
|
39
|
-
|
|
40
|
+
async function buildUpdateProcedureThresholdScript(
|
|
41
|
+
client: MidenClient | WasmWebClient,
|
|
40
42
|
procedure: ProcedureName,
|
|
41
43
|
threshold: number,
|
|
42
44
|
signatureScheme: SignatureScheme,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
midenRpcEndpoint?: string,
|
|
46
|
+
): Promise<TransactionScript> {
|
|
45
47
|
const multisigMasm = signatureScheme === 'ecdsa' ? MULTISIG_ECDSA_MASM : MULTISIG_MASM;
|
|
46
|
-
|
|
47
|
-
const multisigLib = libBuilder.buildLibrary('auth::multisig', multisigMasm);
|
|
48
|
-
libBuilder.linkDynamicLibrary(multisigLib);
|
|
49
48
|
const procedureRoot = normalizeHexWord(getProcedureRoot(procedure));
|
|
50
49
|
|
|
51
50
|
const scriptSource = `
|
|
52
|
-
use
|
|
51
|
+
use oz_multisig::multisig
|
|
53
52
|
|
|
54
53
|
begin
|
|
55
54
|
push.${procedureRoot}
|
|
@@ -60,11 +59,16 @@ begin
|
|
|
60
59
|
end
|
|
61
60
|
`;
|
|
62
61
|
|
|
63
|
-
return
|
|
62
|
+
return compileTxScript(
|
|
63
|
+
client,
|
|
64
|
+
scriptSource,
|
|
65
|
+
[{ namespace: 'oz_multisig::multisig', code: multisigMasm }],
|
|
66
|
+
midenRpcEndpoint,
|
|
67
|
+
);
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
export async function buildUpdateProcedureThresholdTransactionRequest(
|
|
67
|
-
|
|
71
|
+
client: MidenClient | WasmWebClient,
|
|
68
72
|
procedure: ProcedureName,
|
|
69
73
|
threshold: number,
|
|
70
74
|
options: SignatureOptions = {},
|
|
@@ -72,11 +76,12 @@ export async function buildUpdateProcedureThresholdTransactionRequest(
|
|
|
72
76
|
const signatureScheme = options.signatureScheme ?? 'falcon';
|
|
73
77
|
const { configHash } = buildProcedureThresholdAdvice(procedure, threshold);
|
|
74
78
|
|
|
75
|
-
const script = buildUpdateProcedureThresholdScript(
|
|
76
|
-
|
|
79
|
+
const script = await buildUpdateProcedureThresholdScript(
|
|
80
|
+
client,
|
|
77
81
|
procedure,
|
|
78
82
|
threshold,
|
|
79
83
|
signatureScheme,
|
|
84
|
+
options.midenRpcEndpoint,
|
|
80
85
|
);
|
|
81
86
|
const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
|
|
82
87
|
const authSalt = WordType.fromHex(normalizeHexWord(authSaltHex));
|