@calimero-network/mero-js 24.1.3 → 24.2.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/dist/account/create-context.d.ts +2 -4
- package/dist/account/create-context.js +34 -14
- package/dist/account/session.d.ts +3 -2
- package/dist/admin-api/admin-client.d.ts +6 -6
- package/dist/admin-api/admin-client.js +6 -6
- package/dist/admin-api/admin-types.d.ts +10 -11
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +84 -42
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +84 -42
- package/dist/index.mjs.map +2 -2
- package/dist/relay/index.d.ts +1 -1
- package/dist/relay/relay-client.d.ts +24 -27
- package/dist/relay/relay-client.js +39 -31
- package/dist/warrant/creation-warrant.d.ts +5 -1
- package/dist/warrant/creation-warrant.js +5 -1
- package/dist/warrant/governance-warrant.d.ts +5 -1
- package/dist/warrant/governance-warrant.js +5 -1
- package/dist/warrant/warrant.d.ts +7 -1
- package/dist/warrant/warrant.js +5 -1
- package/package.json +1 -1
package/dist/relay/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { RelayClient, IntentRefusedError } from './relay-client.js';
|
|
2
|
-
export type { RelayClientConfig, RelayDescription, IntentResult, CreationDescription, CreateContextInput, CreatedContext, GovernanceDescription, GovernInput, GovernResult, FoundNamespaceInput, FoundedNamespace, } from './relay-client.js';
|
|
2
|
+
export type { RelayClientConfig, RelayExecutor, RelayDescription, IntentResult, CreationDescription, CreateContextInput, CreatedContext, GovernanceDescription, GovernInput, GovernResult, FoundNamespaceInput, FoundedNamespace, } from './relay-client.js';
|
|
3
3
|
export { createMemoryNonceSource, createLocalStorageNonceSource, createRecoveringNonceSource, authorNonceLookup, WarrantNonceExhaustedError, } from './nonce-source.js';
|
|
4
4
|
export type { NonceSource, WarrantNonceLookup, RecoveringNonceSourceOptions, } from './nonce-source.js';
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* # The three preconditions, and why they are checked in this order
|
|
19
19
|
*
|
|
20
20
|
* `describe()` answers the first two in one call, before anything is signed:
|
|
21
|
-
* the relay's executor account (which the warrant must name)
|
|
22
|
-
* holds `CAN_AUTHOR_ON_BEHALF` on the owning group. The third
|
|
23
|
-
* author's account is a member
|
|
24
|
-
* refusal.
|
|
21
|
+
* the relay's executor account and signing key (which the warrant must name)
|
|
22
|
+
* and whether it holds `CAN_AUTHOR_ON_BEHALF` on the owning group. The third,
|
|
23
|
+
* that the author's account is a member, only the group knows, and it surfaces
|
|
24
|
+
* as a refusal.
|
|
25
25
|
*
|
|
26
26
|
* Checking before signing is not politeness. A warrant consumes a nonce from a
|
|
27
27
|
* monotonic per-device sequence, and one minted against the wrong executor is
|
|
@@ -42,9 +42,8 @@ export interface RelayClientConfig {
|
|
|
42
42
|
/**
|
|
43
43
|
* The account the relay writes as, hex — a warrant's `executor`.
|
|
44
44
|
*
|
|
45
|
-
*
|
|
46
|
-
* signing key
|
|
47
|
-
* first `execute` learns it from {@link RelayClient.describe}.
|
|
45
|
+
* Only checked against what the relay answers: a warrant also names the
|
|
46
|
+
* relay's signing key, which only discovery reports, so the first call asks.
|
|
48
47
|
*/
|
|
49
48
|
executorAccount?: string;
|
|
50
49
|
/** The author's account, hex — whose consent the warrant carries. */
|
|
@@ -101,19 +100,22 @@ export interface RelayClientConfig {
|
|
|
101
100
|
*/
|
|
102
101
|
rateLimitRetries?: number;
|
|
103
102
|
}
|
|
104
|
-
/**
|
|
105
|
-
export interface
|
|
103
|
+
/** The relay a warrant names: its account, and the one key of it that may spend the warrant. */
|
|
104
|
+
export interface RelayExecutor {
|
|
106
105
|
/** The account a warrant must name as `executor`, hex. */
|
|
107
106
|
executorAccount: string;
|
|
107
|
+
/** The signing key a warrant must name as `executor_key`, hex. */
|
|
108
|
+
executorKey: string;
|
|
109
|
+
}
|
|
110
|
+
/** What a relay says about its ability to run intents in one context. */
|
|
111
|
+
export interface RelayDescription extends RelayExecutor {
|
|
108
112
|
/** Whether the relay holds `CAN_AUTHOR_ON_BEHALF` on the owning group. */
|
|
109
113
|
canAuthorOnBehalf: boolean;
|
|
110
114
|
/** The group whose admin grants that capability, hex. */
|
|
111
115
|
groupId: string;
|
|
112
116
|
}
|
|
113
117
|
/** What a relay says about its ability to create contexts in one group. */
|
|
114
|
-
export interface CreationDescription {
|
|
115
|
-
/** The account a creation warrant must name as `executor`, hex. */
|
|
116
|
-
executorAccount: string;
|
|
118
|
+
export interface CreationDescription extends RelayExecutor {
|
|
117
119
|
/** The group, hex. */
|
|
118
120
|
groupId: string;
|
|
119
121
|
/**
|
|
@@ -156,9 +158,7 @@ export interface CreatedContext {
|
|
|
156
158
|
memberPublicKey: string;
|
|
157
159
|
}
|
|
158
160
|
/** What a relay says about publishing governance ops for members of one group. */
|
|
159
|
-
export interface GovernanceDescription {
|
|
160
|
-
/** The account a governance warrant must name as `executor`, hex. */
|
|
161
|
-
executorAccount: string;
|
|
161
|
+
export interface GovernanceDescription extends RelayExecutor {
|
|
162
162
|
/** The group asked about, hex. */
|
|
163
163
|
groupId: string;
|
|
164
164
|
/**
|
|
@@ -192,11 +192,10 @@ export interface FoundNamespaceInput {
|
|
|
192
192
|
*/
|
|
193
193
|
salt?: string;
|
|
194
194
|
/**
|
|
195
|
-
* The relay's account
|
|
196
|
-
*
|
|
197
|
-
* to the client's configured or already-learned `executorAccount`.
|
|
195
|
+
* The relay's account and node key (pinned, or from `resolveRelayNodeKey`). Never
|
|
196
|
+
* a discovery answer: that key is unauthenticated, and no namespace exists yet.
|
|
198
197
|
*/
|
|
199
|
-
|
|
198
|
+
executor: RelayExecutor;
|
|
200
199
|
/**
|
|
201
200
|
* The namespace root's default capability mask (`MemberCapabilities` bits),
|
|
202
201
|
* set right after founding. A namespace is founded with core's minimal
|
|
@@ -292,7 +291,7 @@ export declare class IntentRefusedError extends Error {
|
|
|
292
291
|
}
|
|
293
292
|
export declare class RelayClient {
|
|
294
293
|
private readonly baseUrl;
|
|
295
|
-
private
|
|
294
|
+
private executor;
|
|
296
295
|
private readonly config;
|
|
297
296
|
private readonly fetchImpl;
|
|
298
297
|
private readonly timeoutMs;
|
|
@@ -389,7 +388,7 @@ export declare class RelayClient {
|
|
|
389
388
|
createContext(input: CreateContextInput): Promise<CreatedContext>;
|
|
390
389
|
/**
|
|
391
390
|
* Ask the relay whether it may publish governance ops for members of
|
|
392
|
-
* `groupId`, and which account a warrant must name.
|
|
391
|
+
* `groupId`, and which account and key a warrant must name.
|
|
393
392
|
*/
|
|
394
393
|
describeGovernance(groupId: string): Promise<GovernanceDescription>;
|
|
395
394
|
/**
|
|
@@ -427,9 +426,7 @@ export declare class RelayClient {
|
|
|
427
426
|
*
|
|
428
427
|
* The executor cannot be learned from the relay first: the namespace does not
|
|
429
428
|
* exist, so `describeGovernance` has nothing to answer about. It comes from
|
|
430
|
-
* `input.
|
|
431
|
-
* (`describe`, `createContext`, `govern`), and without any of them this
|
|
432
|
-
* throws before taking a nonce.
|
|
429
|
+
* `input.executor` only, and without it this throws before taking a nonce.
|
|
433
430
|
* The nonce is spent in the new namespace's ledger.
|
|
434
431
|
*
|
|
435
432
|
* With `defaultCapabilities`, the founder then sets the namespace's default
|
|
@@ -440,11 +437,11 @@ export declare class RelayClient {
|
|
|
440
437
|
* Refusals (`400`/`403`) come back as {@link IntentRefusedError}; a salt
|
|
441
438
|
* that names an existing namespace is a `409`, an `HTTPError`.
|
|
442
439
|
*/
|
|
443
|
-
foundNamespace(input
|
|
440
|
+
foundNamespace(input: FoundNamespaceInput): Promise<FoundedNamespace>;
|
|
444
441
|
private postGovernance;
|
|
445
442
|
/**
|
|
446
|
-
* The
|
|
447
|
-
*
|
|
443
|
+
* The relay's answer, checked against a configured account and remembered.
|
|
444
|
+
* Decoded here so a relay naming no key is refused before a nonce is taken.
|
|
448
445
|
*/
|
|
449
446
|
private checkedExecutor;
|
|
450
447
|
/**
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* # The three preconditions, and why they are checked in this order
|
|
19
19
|
*
|
|
20
20
|
* `describe()` answers the first two in one call, before anything is signed:
|
|
21
|
-
* the relay's executor account (which the warrant must name)
|
|
22
|
-
* holds `CAN_AUTHOR_ON_BEHALF` on the owning group. The third
|
|
23
|
-
* author's account is a member
|
|
24
|
-
* refusal.
|
|
21
|
+
* the relay's executor account and signing key (which the warrant must name)
|
|
22
|
+
* and whether it holds `CAN_AUTHOR_ON_BEHALF` on the owning group. The third,
|
|
23
|
+
* that the author's account is a member, only the group knows, and it surfaces
|
|
24
|
+
* as a refusal.
|
|
25
25
|
*
|
|
26
26
|
* Checking before signing is not politeness. A warrant consumes a nonce from a
|
|
27
27
|
* monotonic per-device sequence, and one minted against the wrong executor is
|
|
@@ -85,7 +85,6 @@ export class RelayClient {
|
|
|
85
85
|
constructor(config) {
|
|
86
86
|
this.config = config;
|
|
87
87
|
this.baseUrl = config.relayUrl.replace(/\/+$/, '');
|
|
88
|
-
this.executorAccount = config.executorAccount;
|
|
89
88
|
const injected = config.fetch;
|
|
90
89
|
this.fetchImpl = injected
|
|
91
90
|
? (input, init) => injected(input, init)
|
|
@@ -139,8 +138,8 @@ export class RelayClient {
|
|
|
139
138
|
async describe(contextId) {
|
|
140
139
|
const body = await this.json('GET', `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`);
|
|
141
140
|
// Learned, not merely returned: a client that called `describe` should not
|
|
142
|
-
// then have to pass the
|
|
143
|
-
this.
|
|
141
|
+
// then have to pass the executor back in to `execute`.
|
|
142
|
+
this.checkedExecutor(body.data);
|
|
144
143
|
return body.data;
|
|
145
144
|
}
|
|
146
145
|
/**
|
|
@@ -151,7 +150,7 @@ export class RelayClient {
|
|
|
151
150
|
* apply. Nothing accumulates and nothing is reusable.
|
|
152
151
|
*/
|
|
153
152
|
async execute(contextId, method, argsJson = {}) {
|
|
154
|
-
const executor = this.
|
|
153
|
+
const executor = this.executor ?? this.checkedExecutor(await this.describe(contextId));
|
|
155
154
|
// Resolved before the nonce, so a misconfigured key burns none.
|
|
156
155
|
const signer = await this.authorSigner();
|
|
157
156
|
const nonce = await this.config.nonces.next();
|
|
@@ -159,14 +158,20 @@ export class RelayClient {
|
|
|
159
158
|
const warrant = await signWarrant({
|
|
160
159
|
context: contextId,
|
|
161
160
|
authorAccount: this.config.authorAccount,
|
|
162
|
-
executor,
|
|
161
|
+
executor: executor.executorAccount,
|
|
162
|
+
executorKey: executor.executorKey,
|
|
163
163
|
method,
|
|
164
164
|
argsJson,
|
|
165
165
|
nonce,
|
|
166
166
|
notAfter: BigInt(Math.floor(Date.now() / 1000)) + BigInt(ttl),
|
|
167
167
|
signer,
|
|
168
168
|
});
|
|
169
|
-
const body = await this.json('POST', `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`, { method, argsJson, warrant, authorProof: this.config.authorProof })
|
|
169
|
+
const body = await this.json('POST', `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`, { method, argsJson, warrant, authorProof: this.config.authorProof }).catch((err) => {
|
|
170
|
+
// A relay that re-keyed refuses every warrant naming its old key, so ask again next time.
|
|
171
|
+
if (err instanceof IntentRefusedError)
|
|
172
|
+
this.executor = undefined;
|
|
173
|
+
throw err;
|
|
174
|
+
});
|
|
170
175
|
return { rootHash: body.data.rootHash, returns: body.data.returns ?? null };
|
|
171
176
|
}
|
|
172
177
|
/**
|
|
@@ -224,7 +229,7 @@ export class RelayClient {
|
|
|
224
229
|
`${described.groupId}; it needs CAN_CREATE_CONTEXT or admin ` +
|
|
225
230
|
'(checked before signing — no nonce was spent)', false, 403);
|
|
226
231
|
}
|
|
227
|
-
const executor = this.checkedExecutor(described
|
|
232
|
+
const executor = this.checkedExecutor(described);
|
|
228
233
|
const initArgs = input.initArgs ?? {};
|
|
229
234
|
// Resolved before the nonce, so a misconfigured key burns none.
|
|
230
235
|
const signer = await this.authorSigner();
|
|
@@ -234,7 +239,8 @@ export class RelayClient {
|
|
|
234
239
|
group: input.groupId,
|
|
235
240
|
seed: input.seed,
|
|
236
241
|
authorAccount: this.config.authorAccount,
|
|
237
|
-
executor,
|
|
242
|
+
executor: executor.executorAccount,
|
|
243
|
+
executorKey: executor.executorKey,
|
|
238
244
|
applicationId: input.applicationId,
|
|
239
245
|
serviceName: input.serviceName,
|
|
240
246
|
name: input.name,
|
|
@@ -252,7 +258,7 @@ export class RelayClient {
|
|
|
252
258
|
}
|
|
253
259
|
/**
|
|
254
260
|
* Ask the relay whether it may publish governance ops for members of
|
|
255
|
-
* `groupId`, and which account a warrant must name.
|
|
261
|
+
* `groupId`, and which account and key a warrant must name.
|
|
256
262
|
*/
|
|
257
263
|
async describeGovernance(groupId) {
|
|
258
264
|
const body = await this.json('GET', `/admin-api/groups/${encodeURIComponent(groupId)}/governance-intents`);
|
|
@@ -281,7 +287,7 @@ export class RelayClient {
|
|
|
281
287
|
`${described.groupId}; an admin must admit it as a relay or grant it CAN_AUTHOR_ON_BEHALF ` +
|
|
282
288
|
'(checked before signing, no nonce was spent)', false, 403);
|
|
283
289
|
}
|
|
284
|
-
const executor = this.checkedExecutor(described
|
|
290
|
+
const executor = this.checkedExecutor(described);
|
|
285
291
|
// Resolved before the nonce, so a misconfigured key burns none.
|
|
286
292
|
const signer = await this.authorSigner();
|
|
287
293
|
const nonce = await this.config.nonces.next();
|
|
@@ -290,7 +296,8 @@ export class RelayClient {
|
|
|
290
296
|
scope: input.groupId,
|
|
291
297
|
op: input.op,
|
|
292
298
|
authorAccount: this.config.authorAccount,
|
|
293
|
-
executor,
|
|
299
|
+
executor: executor.executorAccount,
|
|
300
|
+
executorKey: executor.executorKey,
|
|
294
301
|
nonce,
|
|
295
302
|
notAfter: BigInt(Math.floor(Date.now() / 1000)) + BigInt(ttl),
|
|
296
303
|
signer,
|
|
@@ -316,9 +323,7 @@ export class RelayClient {
|
|
|
316
323
|
*
|
|
317
324
|
* The executor cannot be learned from the relay first: the namespace does not
|
|
318
325
|
* exist, so `describeGovernance` has nothing to answer about. It comes from
|
|
319
|
-
* `input.
|
|
320
|
-
* (`describe`, `createContext`, `govern`), and without any of them this
|
|
321
|
-
* throws before taking a nonce.
|
|
326
|
+
* `input.executor` only, and without it this throws before taking a nonce.
|
|
322
327
|
* The nonce is spent in the new namespace's ledger.
|
|
323
328
|
*
|
|
324
329
|
* With `defaultCapabilities`, the founder then sets the namespace's default
|
|
@@ -329,14 +334,12 @@ export class RelayClient {
|
|
|
329
334
|
* Refusals (`400`/`403`) come back as {@link IntentRefusedError}; a salt
|
|
330
335
|
* that names an existing namespace is a `409`, an `HTTPError`.
|
|
331
336
|
*/
|
|
332
|
-
async foundNamespace(input
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
: this.executorAccount;
|
|
336
|
-
if (!executor) {
|
|
337
|
-
throw new Error("foundNamespace needs the relay's account: pass executorAccount or configure it " +
|
|
337
|
+
async foundNamespace(input) {
|
|
338
|
+
if (!input?.executor) {
|
|
339
|
+
throw new Error("foundNamespace needs the relay's account and signing key: pass executor " +
|
|
338
340
|
'(checked before signing, no nonce was spent)');
|
|
339
341
|
}
|
|
342
|
+
const executor = this.checkedExecutor(input.executor);
|
|
340
343
|
const salt = input.salt
|
|
341
344
|
? hex(fromHex(input.salt, 'salt', 32))
|
|
342
345
|
: hex(crypto.getRandomValues(new Uint8Array(32)));
|
|
@@ -360,7 +363,8 @@ export class RelayClient {
|
|
|
360
363
|
scope: namespaceId,
|
|
361
364
|
op,
|
|
362
365
|
authorAccount: this.config.authorAccount,
|
|
363
|
-
executor,
|
|
366
|
+
executor: executor.executorAccount,
|
|
367
|
+
executorKey: executor.executorKey,
|
|
364
368
|
nonce,
|
|
365
369
|
notAfter: BigInt(Math.floor(Date.now() / 1000)) + BigInt(ttl),
|
|
366
370
|
signer,
|
|
@@ -408,17 +412,21 @@ export class RelayClient {
|
|
|
408
412
|
return body.data;
|
|
409
413
|
}
|
|
410
414
|
/**
|
|
411
|
-
* The
|
|
412
|
-
*
|
|
415
|
+
* The relay's answer, checked against a configured account and remembered.
|
|
416
|
+
* Decoded here so a relay naming no key is refused before a nonce is taken.
|
|
413
417
|
*/
|
|
414
418
|
checkedExecutor(answered) {
|
|
419
|
+
const executor = {
|
|
420
|
+
executorAccount: hex(fromHex(answered.executorAccount ?? '', "the relay's executorAccount", 32)),
|
|
421
|
+
executorKey: hex(fromHex(answered.executorKey ?? '', "the relay's executorKey", 32)),
|
|
422
|
+
};
|
|
415
423
|
const configured = this.config.executorAccount;
|
|
416
|
-
if (configured && configured.toLowerCase() !==
|
|
424
|
+
if (configured && configured.toLowerCase() !== executor.executorAccount) {
|
|
417
425
|
throw new Error(`configured executorAccount ${configured} is not the relay's account ` +
|
|
418
|
-
`${
|
|
426
|
+
`${executor.executorAccount}; a warrant naming it would be unspendable`);
|
|
419
427
|
}
|
|
420
|
-
this.
|
|
421
|
-
return
|
|
428
|
+
this.executor = executor;
|
|
429
|
+
return executor;
|
|
422
430
|
}
|
|
423
431
|
/**
|
|
424
432
|
* One request, waiting out a rate limit rather than failing the caller.
|
|
@@ -36,8 +36,10 @@ export interface CreationWarrantInput {
|
|
|
36
36
|
seed?: string;
|
|
37
37
|
/** The author's account, hex — whose consent this is, and who runs `init`. */
|
|
38
38
|
authorAccount: string;
|
|
39
|
-
/** The relay
|
|
39
|
+
/** The relay's account, hex: the subject its authorship grant is held by. */
|
|
40
40
|
executor: string;
|
|
41
|
+
/** The one signing key of `executor` that may carry it out, hex: discovery's `executorKey`. */
|
|
42
|
+
executorKey: string;
|
|
41
43
|
/** The application the context is created with, hex (32 bytes). */
|
|
42
44
|
applicationId: string;
|
|
43
45
|
/**
|
|
@@ -103,6 +105,7 @@ export interface CreationWarrantFields {
|
|
|
103
105
|
/** The author device key: the signer's public key. */
|
|
104
106
|
deviceKey: string;
|
|
105
107
|
executor: string;
|
|
108
|
+
executorKey: string;
|
|
106
109
|
applicationId: string;
|
|
107
110
|
/** `null` when absent — distinct from `""`. */
|
|
108
111
|
serviceName: string | null;
|
|
@@ -129,6 +132,7 @@ export interface CreationPreimageParts {
|
|
|
129
132
|
authorAccount: Uint8Array;
|
|
130
133
|
deviceKey: Uint8Array;
|
|
131
134
|
executor: Uint8Array;
|
|
135
|
+
executorKey: Uint8Array;
|
|
132
136
|
applicationId: Uint8Array;
|
|
133
137
|
serviceName: Uint8Array | null;
|
|
134
138
|
name: Uint8Array | null;
|
|
@@ -51,6 +51,7 @@ export async function creationWarrantPreimage(p) {
|
|
|
51
51
|
p.authorAccount,
|
|
52
52
|
p.deviceKey,
|
|
53
53
|
p.executor,
|
|
54
|
+
p.executorKey,
|
|
54
55
|
p.applicationId,
|
|
55
56
|
new Uint8Array([p.serviceName === null ? 0 : 1]),
|
|
56
57
|
p.serviceName ?? new Uint8Array(0),
|
|
@@ -78,6 +79,7 @@ export async function signCreationWarrant(input) {
|
|
|
78
79
|
: fromHex(input.seed, 'seed', 32);
|
|
79
80
|
const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
|
|
80
81
|
const executor = fromHex(input.executor, 'executor', 32);
|
|
82
|
+
const executorKey = fromHex(input.executorKey, 'executorKey', 32);
|
|
81
83
|
const applicationId = fromHex(input.applicationId, 'applicationId', 32);
|
|
82
84
|
const serviceName = label(input.serviceName, 'serviceName');
|
|
83
85
|
const name = label(input.name, 'name');
|
|
@@ -92,6 +94,7 @@ export async function signCreationWarrant(input) {
|
|
|
92
94
|
authorAccount,
|
|
93
95
|
deviceKey,
|
|
94
96
|
executor,
|
|
97
|
+
executorKey,
|
|
95
98
|
applicationId,
|
|
96
99
|
serviceName,
|
|
97
100
|
name,
|
|
@@ -102,7 +105,7 @@ export async function signCreationWarrant(input) {
|
|
|
102
105
|
notAfter: input.notAfter,
|
|
103
106
|
});
|
|
104
107
|
const signature = checkedSignature(await signer.sign(preimage));
|
|
105
|
-
const warrant = hex(concat(group, seed, authorAccount, deviceKey, executor, applicationId, borshOption(serviceName), borshOption(name), initHash, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, u64le(input.nonce), u64le(input.notAfter), signature));
|
|
108
|
+
const warrant = hex(concat(group, seed, authorAccount, deviceKey, executor, executorKey, applicationId, borshOption(serviceName), borshOption(name), initHash, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, u64le(input.nonce), u64le(input.notAfter), signature));
|
|
106
109
|
return { warrant, seed: hex(seed) };
|
|
107
110
|
}
|
|
108
111
|
/**
|
|
@@ -157,6 +160,7 @@ export function parseCreationWarrant(warrant) {
|
|
|
157
160
|
authorAccount: hex(take(32, 'authorAccount')),
|
|
158
161
|
deviceKey: hex(take(32, 'deviceKey')),
|
|
159
162
|
executor: hex(take(32, 'executor')),
|
|
163
|
+
executorKey: hex(take(32, 'executorKey')),
|
|
160
164
|
applicationId: hex(take(32, 'applicationId')),
|
|
161
165
|
serviceName: option('serviceName'),
|
|
162
166
|
name: option('name'),
|
|
@@ -39,8 +39,10 @@ export interface GovernanceWarrantInput {
|
|
|
39
39
|
op: GovernanceOp;
|
|
40
40
|
/** The author's account, hex: whose authority the op is applied under. */
|
|
41
41
|
authorAccount: string;
|
|
42
|
-
/** The relay
|
|
42
|
+
/** The relay's account, hex: the subject its authorship grant is held by. */
|
|
43
43
|
executor: string;
|
|
44
|
+
/** The one signing key of `executor` that may publish it, hex: discovery's `executorKey`. */
|
|
45
|
+
executorKey: string;
|
|
44
46
|
/**
|
|
45
47
|
* Monotonic per author **device**, spent in a per-group ledger. The same
|
|
46
48
|
* nonce source `RelayClient` uses for every other warrant is correct.
|
|
@@ -76,6 +78,7 @@ export interface GovernanceWarrantFields {
|
|
|
76
78
|
/** The author device key: the signer's public key. */
|
|
77
79
|
deviceKey: string;
|
|
78
80
|
executor: string;
|
|
81
|
+
executorKey: string;
|
|
79
82
|
opHash: string;
|
|
80
83
|
accountHeads: string[];
|
|
81
84
|
governanceFloor: string[];
|
|
@@ -95,6 +98,7 @@ export interface GovernancePreimageParts {
|
|
|
95
98
|
authorAccount: Uint8Array;
|
|
96
99
|
deviceKey: Uint8Array;
|
|
97
100
|
executor: Uint8Array;
|
|
101
|
+
executorKey: Uint8Array;
|
|
98
102
|
opHash: Uint8Array;
|
|
99
103
|
accountHeads: Uint8Array[];
|
|
100
104
|
governanceFloor: Uint8Array[];
|
|
@@ -53,6 +53,7 @@ export async function governanceWarrantPreimage(p) {
|
|
|
53
53
|
p.authorAccount,
|
|
54
54
|
p.deviceKey,
|
|
55
55
|
p.executor,
|
|
56
|
+
p.executorKey,
|
|
56
57
|
p.opHash,
|
|
57
58
|
u64le(p.accountHeads.length),
|
|
58
59
|
...p.accountHeads,
|
|
@@ -70,6 +71,7 @@ export async function signGovernanceWarrant(input) {
|
|
|
70
71
|
const scope = fromHex(input.scope, 'scope', 32);
|
|
71
72
|
const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
|
|
72
73
|
const executor = fromHex(input.executor, 'executor', 32);
|
|
74
|
+
const executorKey = fromHex(input.executorKey, 'executorKey', 32);
|
|
73
75
|
const accountHeads = citedHeads(input.accountHeads, 'accountHeads');
|
|
74
76
|
const governanceFloor = citedHeads(input.governanceFloor, 'governanceFloor');
|
|
75
77
|
const kind = kindByte(input.op.kind);
|
|
@@ -82,6 +84,7 @@ export async function signGovernanceWarrant(input) {
|
|
|
82
84
|
authorAccount,
|
|
83
85
|
deviceKey,
|
|
84
86
|
executor,
|
|
87
|
+
executorKey,
|
|
85
88
|
opHash,
|
|
86
89
|
accountHeads,
|
|
87
90
|
governanceFloor,
|
|
@@ -89,7 +92,7 @@ export async function signGovernanceWarrant(input) {
|
|
|
89
92
|
notAfter: input.notAfter,
|
|
90
93
|
});
|
|
91
94
|
const signature = checkedSignature(await signer.sign(preimage));
|
|
92
|
-
return hex(concat(scope, new Uint8Array([kind]), authorAccount, deviceKey, executor, opHash, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, u64le(input.nonce), u64le(input.notAfter), signature));
|
|
95
|
+
return hex(concat(scope, new Uint8Array([kind]), authorAccount, deviceKey, executor, executorKey, opHash, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, u64le(input.nonce), u64le(input.notAfter), signature));
|
|
93
96
|
}
|
|
94
97
|
/**
|
|
95
98
|
* Decode a hex governance warrant back into its fields.
|
|
@@ -139,6 +142,7 @@ export function parseGovernanceWarrant(warrant) {
|
|
|
139
142
|
authorAccount: hex(take(32, 'authorAccount')),
|
|
140
143
|
deviceKey: hex(take(32, 'deviceKey')),
|
|
141
144
|
executor: hex(take(32, 'executor')),
|
|
145
|
+
executorKey: hex(take(32, 'executorKey')),
|
|
142
146
|
opHash: hex(take(32, 'opHash')),
|
|
143
147
|
accountHeads: heads('accountHeads'),
|
|
144
148
|
governanceFloor: heads('governanceFloor'),
|
|
@@ -42,8 +42,13 @@ export interface WarrantInput {
|
|
|
42
42
|
context: string;
|
|
43
43
|
/** The author's account, hex — whose consent this is. */
|
|
44
44
|
authorAccount: string;
|
|
45
|
-
/** The relay
|
|
45
|
+
/** The relay's account, hex: the subject its authorship grant is held by. */
|
|
46
46
|
executor: string;
|
|
47
|
+
/**
|
|
48
|
+
* The one signing key of `executor` that may spend this warrant, hex: the
|
|
49
|
+
* relay's `executorKey` from discovery. Any other key of the account is refused.
|
|
50
|
+
*/
|
|
51
|
+
executorKey: string;
|
|
47
52
|
/**
|
|
48
53
|
* The application build this warrant is signed against, hex (32 bytes).
|
|
49
54
|
*
|
|
@@ -152,6 +157,7 @@ export interface WarrantFields {
|
|
|
152
157
|
/** The author device key `signWarrant` derived from the secret. */
|
|
153
158
|
deviceKey: string;
|
|
154
159
|
executor: string;
|
|
160
|
+
executorKey: string;
|
|
155
161
|
appVersion: string;
|
|
156
162
|
method: string;
|
|
157
163
|
intentHash: string;
|
package/dist/warrant/warrant.js
CHANGED
|
@@ -59,6 +59,7 @@ export async function signWarrant(input) {
|
|
|
59
59
|
const context = fromHex(input.context, 'context', 32);
|
|
60
60
|
const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
|
|
61
61
|
const executor = fromHex(input.executor, 'executor', 32);
|
|
62
|
+
const executorKey = fromHex(input.executorKey, 'executorKey', 32);
|
|
62
63
|
const appVersion = fromHex(input.appVersion ?? UNPINNED_APP, 'appVersion', 32);
|
|
63
64
|
const accountHeads = citedHeads(input.accountHeads, 'accountHeads');
|
|
64
65
|
const governanceFloor = citedHeads(input.governanceFloor, 'governanceFloor');
|
|
@@ -78,6 +79,7 @@ export async function signWarrant(input) {
|
|
|
78
79
|
authorAccount,
|
|
79
80
|
publicKey,
|
|
80
81
|
executor,
|
|
82
|
+
executorKey,
|
|
81
83
|
appVersion,
|
|
82
84
|
method,
|
|
83
85
|
commitment,
|
|
@@ -91,7 +93,7 @@ export async function signWarrant(input) {
|
|
|
91
93
|
const signature = checkedSignature(await signer.sign(preimage));
|
|
92
94
|
// Borsh: a `String` is a u32 length then its UTF-8 bytes; a `Vec<[u8; 32]>` is
|
|
93
95
|
// a u32 count then the elements. u32 HERE; u64 in the preimage above.
|
|
94
|
-
return hex(concat(context, authorAccount, publicKey, executor, appVersion, u32le(method.length), method, commitment, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, nonce, notAfter, signature));
|
|
96
|
+
return hex(concat(context, authorAccount, publicKey, executor, executorKey, appVersion, u32le(method.length), method, commitment, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, nonce, notAfter, signature));
|
|
95
97
|
}
|
|
96
98
|
/**
|
|
97
99
|
* Decode a cited-head list, refusing one longer than a node will accept.
|
|
@@ -146,6 +148,7 @@ export function parseWarrant(warrant) {
|
|
|
146
148
|
const authorAccount = take(32);
|
|
147
149
|
const deviceKey = take(32);
|
|
148
150
|
const executor = take(32);
|
|
151
|
+
const executorKey = take(32);
|
|
149
152
|
const appVersion = take(32);
|
|
150
153
|
const methodLen = u32At(o);
|
|
151
154
|
o += 4;
|
|
@@ -168,6 +171,7 @@ export function parseWarrant(warrant) {
|
|
|
168
171
|
authorAccount,
|
|
169
172
|
deviceKey,
|
|
170
173
|
executor,
|
|
174
|
+
executorKey,
|
|
171
175
|
appVersion,
|
|
172
176
|
method,
|
|
173
177
|
intentHash: intentHashField,
|