@calimero-network/mero-js 22.3.0 → 22.5.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.
@@ -17,11 +17,15 @@ import type { SignedGroupOpenInvitation } from '../admin-api/admin-types.js';
17
17
  /**
18
18
  * Core's `SIGNED_NAMESPACE_OP_SCHEMA_VERSION`. A node refuses any other value.
19
19
  *
20
- * 12: core gained the `RelayTee` role and the TEE admission policy's
21
- * replica/relay mode. No layout this module signs changed; the version moves
22
- * with core's so a v11 node and a v12 node never share a namespace.
20
+ * 15: core went to 13 (TEE rows locked to the TEE roles, a TEE's relay role
21
+ * read at its namespace root row, inherited read-only roles read-only in Open
22
+ * subgroups), then 14 (`GroupOp::ContextRegisteredOnBehalf`) and 15
23
+ * (`GroupOp::OnBehalf` / `RootOp::OnBehalf`) for delegated context creation and
24
+ * governance through a relay. None of these changes a layout this module
25
+ * signs; the version moves with core's so nodes on different versions never
26
+ * share a namespace.
23
27
  */
24
- export declare const SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 12;
28
+ export declare const SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 15;
25
29
  /**
26
30
  * Borsh-encode a `SignedGroupOpenInvitation`.
27
31
  *
@@ -2,11 +2,15 @@ import { concat, derivePublicKey, fromHex, hex, importSigningKey, u32le, u64le,
2
2
  /**
3
3
  * Core's `SIGNED_NAMESPACE_OP_SCHEMA_VERSION`. A node refuses any other value.
4
4
  *
5
- * 12: core gained the `RelayTee` role and the TEE admission policy's
6
- * replica/relay mode. No layout this module signs changed; the version moves
7
- * with core's so a v11 node and a v12 node never share a namespace.
5
+ * 15: core went to 13 (TEE rows locked to the TEE roles, a TEE's relay role
6
+ * read at its namespace root row, inherited read-only roles read-only in Open
7
+ * subgroups), then 14 (`GroupOp::ContextRegisteredOnBehalf`) and 15
8
+ * (`GroupOp::OnBehalf` / `RootOp::OnBehalf`) for delegated context creation and
9
+ * governance through a relay. None of these changes a layout this module
10
+ * signs; the version moves with core's so nodes on different versions never
11
+ * share a namespace.
8
12
  */
9
- export const SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 12;
13
+ export const SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 15;
10
14
  /** Domain prefixed to the signable bytes; core's `NAMESPACE_GOVERNANCE_SIGN_DOMAIN`. */
11
15
  const NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode('calimero.namespace.v1');
12
16
  /** `NamespaceOp::Root` is the first variant. */
@@ -1,4 +1,4 @@
1
1
  export { RelayClient, IntentRefusedError } from './relay-client.js';
2
- export type { RelayClientConfig, RelayDescription, IntentResult } from './relay-client.js';
2
+ export type { RelayClientConfig, RelayDescription, IntentResult, CreationDescription, CreateContextInput, CreatedContext, GovernanceDescription, GovernInput, GovernResult, } from './relay-client.js';
3
3
  export { createMemoryNonceSource, createLocalStorageNonceSource } from './nonce-source.js';
4
4
  export type { NonceSource } from './nonce-source.js';
@@ -27,6 +27,7 @@
27
27
  * monotonic per-device sequence, and one minted against the wrong executor is
28
28
  * unspendable: the number is gone and the write never happened.
29
29
  */
30
+ import type { GovernanceOp } from '../warrant/governance-op.js';
30
31
  import type { NonceSource } from './nonce-source.js';
31
32
  /** Where and as whom to write, plus the key that consents. */
32
33
  export interface RelayClientConfig {
@@ -84,6 +85,79 @@ export interface RelayDescription {
84
85
  /** The group whose admin grants that capability, hex. */
85
86
  groupId: string;
86
87
  }
88
+ /** What a relay says about its ability to create contexts in one group. */
89
+ export interface CreationDescription {
90
+ /** The account a creation warrant must name as `executor`, hex. */
91
+ executorAccount: string;
92
+ /** The group, hex. */
93
+ groupId: string;
94
+ /**
95
+ * Whether the relay has standing to act for members of this group (a
96
+ * `RelayTee` role or `CAN_AUTHOR_ON_BEHALF`). It needs no create rights of
97
+ * its own — those are checked on the author.
98
+ */
99
+ canCreateOnBehalf: boolean;
100
+ /**
101
+ * Whether the author holds `CAN_CREATE_CONTEXT` (or admin) here. Present only
102
+ * when `describeCreation` was given an `author`.
103
+ */
104
+ authorMayCreate?: boolean;
105
+ }
106
+ /** What {@link RelayClient.createContext} creates. */
107
+ export interface CreateContextInput {
108
+ /** The group to create the context in, hex. */
109
+ groupId: string;
110
+ /** The application to create it with, hex (32 bytes). */
111
+ applicationId: string;
112
+ /** The JSON the app's `init()` receives — run as the author's account. */
113
+ initArgs?: unknown;
114
+ /** Which service of a multi-service bundle; absent for the default. */
115
+ serviceName?: string;
116
+ /** A display name for the context. */
117
+ name?: string;
118
+ /**
119
+ * The seed the context id is derived from, hex (32 bytes). Random when
120
+ * absent, which is what almost every caller wants.
121
+ */
122
+ seed?: string;
123
+ }
124
+ /** Where a delegated creation landed. */
125
+ export interface CreatedContext {
126
+ /** The new context's id, hex. */
127
+ contextId: string;
128
+ /** The group it was created in, hex. */
129
+ groupId: string;
130
+ /** The relay's identity in the new context, hex. */
131
+ memberPublicKey: string;
132
+ }
133
+ /** What a relay says about publishing governance ops for members of one group. */
134
+ export interface GovernanceDescription {
135
+ /** The account a governance warrant must name as `executor`, hex. */
136
+ executorAccount: string;
137
+ /** The group asked about, hex. */
138
+ groupId: string;
139
+ /**
140
+ * Whether the relay may act for members here (a `RelayTee` role or
141
+ * `CAN_AUTHOR_ON_BEHALF`). It needs no right to the op itself: that is
142
+ * checked on the author, as the op is applied as the author.
143
+ */
144
+ canActOnBehalf: boolean;
145
+ }
146
+ /** What {@link RelayClient.govern} publishes. */
147
+ export interface GovernInput {
148
+ /**
149
+ * Where the op is published, hex: the group itself for a group op, the
150
+ * **namespace** for a root op (creating, moving or deleting a subgroup).
151
+ */
152
+ groupId: string;
153
+ /** The op, from one of the encoders (`memberAddedOp`, `groupCreatedOp`, ...). */
154
+ op: GovernanceOp;
155
+ }
156
+ /** Where a delegated governance op landed. */
157
+ export interface GovernResult {
158
+ /** The group the op acted on, hex: the new subgroup, for a creation. */
159
+ groupId: string;
160
+ }
87
161
  /** Where an accepted intent landed. */
88
162
  export interface IntentResult<T = unknown> {
89
163
  /** The context's scope root after the run — did this change anything? */
@@ -140,5 +214,63 @@ export declare class RelayClient {
140
214
  * apply. Nothing accumulates and nothing is reusable.
141
215
  */
142
216
  execute<T = unknown>(contextId: string, method: string, argsJson?: unknown): Promise<IntentResult<T>>;
217
+ /**
218
+ * Ask the relay what it can do about creating contexts in `groupId`.
219
+ *
220
+ * Pass `author` (an account, hex) to also learn whether that account may
221
+ * create here — `authorMayCreate` is present only then.
222
+ */
223
+ describeCreation(groupId: string, opts?: {
224
+ author?: string;
225
+ }): Promise<CreationDescription>;
226
+ /**
227
+ * Create a context in a group through the relay, as the author.
228
+ *
229
+ * Always asks {@link describeCreation} first — with the author, so both
230
+ * standing checks happen before anything is signed. A relay without standing
231
+ * in the group, or an author without `CAN_CREATE_CONTEXT`, is refused here as
232
+ * an {@link IntentRefusedError} *before* a nonce is taken: the nonce source
233
+ * is monotonic, and a warrant nobody will spend burns one for nothing.
234
+ *
235
+ * The creation warrant's nonce is spent in the new context's per-device
236
+ * ledger — the same one later `execute` calls in that context draw from — so
237
+ * it comes from the configured {@link NonceSource} like any other warrant.
238
+ *
239
+ * Sealing: nothing here is plaintext-specific. The request goes through the
240
+ * injected `fetch`, so a `createAttestedSealedFetch` seals it exactly as it
241
+ * seals `execute`. Whether the relay *accepts* it sealed is the node's call:
242
+ * a proxy-auth relay only admits sealed requests for routes `merod` serves
243
+ * without a credential, so `/admin-api/groups/:id/context-intents` must be on
244
+ * that list on the core side or the request comes back
245
+ * `403 sealed_route_unguarded`.
246
+ */
247
+ createContext(input: CreateContextInput): Promise<CreatedContext>;
248
+ /**
249
+ * Ask the relay whether it may publish governance ops for members of
250
+ * `groupId`, and which account a warrant must name.
251
+ */
252
+ describeGovernance(groupId: string): Promise<GovernanceDescription>;
253
+ /**
254
+ * Publish one governance op through the relay, as the author.
255
+ *
256
+ * The warrant commits to the op's exact bytes and is scoped to `groupId`, so
257
+ * the relay can neither change what the op does nor spend it elsewhere. Every
258
+ * peer applies the op as the author, so the author's own rights decide
259
+ * (`MANAGE_MEMBERS` to add a member, `CAN_CREATE_SUBGROUP` to create a
260
+ * subgroup, and so on); a lack of them comes back as an
261
+ * {@link IntentRefusedError} from the relay.
262
+ *
263
+ * Always asks {@link describeGovernance} first and refuses *before* taking a
264
+ * nonce when the relay has no standing, for the reason `createContext` does.
265
+ * The nonce comes from the configured {@link NonceSource}; it is spent in a
266
+ * per-group ledger, and a monotonic source is correct for it as for every
267
+ * other warrant.
268
+ */
269
+ govern(input: GovernInput): Promise<GovernResult>;
270
+ /**
271
+ * The executor a warrant must name: the one the relay answered with, which a
272
+ * configured account must agree with. Remembered for later calls.
273
+ */
274
+ private checkedExecutor;
143
275
  private json;
144
276
  }
@@ -28,7 +28,10 @@
28
28
  * unspendable: the number is gone and the write never happened.
29
29
  */
30
30
  import { signWarrant } from '../warrant/warrant.js';
31
+ import { signCreationWarrant } from '../warrant/creation-warrant.js';
32
+ import { signGovernanceWarrant } from '../warrant/governance-warrant.js';
31
33
  import { HTTPError } from '../http-client/web-client.js';
34
+ import { hex } from '../crypto/internal.js';
32
35
  /** How long a freshly minted warrant stays presentable, in seconds. */
33
36
  const DEFAULT_TTL_SECONDS = 300;
34
37
  /**
@@ -106,6 +109,135 @@ export class RelayClient {
106
109
  const body = await this.json('POST', `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`, { method, argsJson, warrant, authorProof: this.config.authorProof });
107
110
  return { rootHash: body.data.rootHash, returns: body.data.returns ?? null };
108
111
  }
112
+ /**
113
+ * Ask the relay what it can do about creating contexts in `groupId`.
114
+ *
115
+ * Pass `author` (an account, hex) to also learn whether that account may
116
+ * create here — `authorMayCreate` is present only then.
117
+ */
118
+ async describeCreation(groupId, opts = {}) {
119
+ const query = opts.author ? `?author=${encodeURIComponent(opts.author)}` : '';
120
+ const body = await this.json('GET', `/admin-api/groups/${encodeURIComponent(groupId)}/context-intents${query}`);
121
+ return body.data;
122
+ }
123
+ /**
124
+ * Create a context in a group through the relay, as the author.
125
+ *
126
+ * Always asks {@link describeCreation} first — with the author, so both
127
+ * standing checks happen before anything is signed. A relay without standing
128
+ * in the group, or an author without `CAN_CREATE_CONTEXT`, is refused here as
129
+ * an {@link IntentRefusedError} *before* a nonce is taken: the nonce source
130
+ * is monotonic, and a warrant nobody will spend burns one for nothing.
131
+ *
132
+ * The creation warrant's nonce is spent in the new context's per-device
133
+ * ledger — the same one later `execute` calls in that context draw from — so
134
+ * it comes from the configured {@link NonceSource} like any other warrant.
135
+ *
136
+ * Sealing: nothing here is plaintext-specific. The request goes through the
137
+ * injected `fetch`, so a `createAttestedSealedFetch` seals it exactly as it
138
+ * seals `execute`. Whether the relay *accepts* it sealed is the node's call:
139
+ * a proxy-auth relay only admits sealed requests for routes `merod` serves
140
+ * without a credential, so `/admin-api/groups/:id/context-intents` must be on
141
+ * that list on the core side or the request comes back
142
+ * `403 sealed_route_unguarded`.
143
+ */
144
+ async createContext(input) {
145
+ const described = await this.describeCreation(input.groupId, {
146
+ author: this.config.authorAccount,
147
+ });
148
+ if (!described.canCreateOnBehalf) {
149
+ throw new IntentRefusedError(`the relay (${described.executorAccount}) has no standing to act for members of group ` +
150
+ `${described.groupId}; an admin must admit it as a relay or grant it CAN_AUTHOR_ON_BEHALF ` +
151
+ '(checked before signing — no nonce was spent)', false, 403);
152
+ }
153
+ if (described.authorMayCreate === false) {
154
+ throw new IntentRefusedError(`the author (${this.config.authorAccount}) may not create contexts in group ` +
155
+ `${described.groupId}; it needs CAN_CREATE_CONTEXT or admin ` +
156
+ '(checked before signing — no nonce was spent)', false, 403);
157
+ }
158
+ const executor = this.checkedExecutor(described.executorAccount);
159
+ const initArgs = input.initArgs ?? {};
160
+ const nonce = await this.config.nonces.next();
161
+ const ttl = this.config.ttlSeconds ?? DEFAULT_TTL_SECONDS;
162
+ const { warrant } = await signCreationWarrant({
163
+ group: input.groupId,
164
+ seed: input.seed,
165
+ authorAccount: this.config.authorAccount,
166
+ executor,
167
+ applicationId: input.applicationId,
168
+ serviceName: input.serviceName,
169
+ name: input.name,
170
+ initArgs,
171
+ nonce,
172
+ notAfter: BigInt(Math.floor(Date.now() / 1000)) + BigInt(ttl),
173
+ deviceSecret: this.config.deviceSecret,
174
+ });
175
+ const body = await this.json('POST', `/admin-api/groups/${encodeURIComponent(input.groupId)}/context-intents`, { warrant, authorProof: this.config.authorProof, initArgs });
176
+ return {
177
+ contextId: body.data.contextId,
178
+ groupId: body.data.groupId,
179
+ memberPublicKey: body.data.memberPublicKey,
180
+ };
181
+ }
182
+ /**
183
+ * Ask the relay whether it may publish governance ops for members of
184
+ * `groupId`, and which account a warrant must name.
185
+ */
186
+ async describeGovernance(groupId) {
187
+ const body = await this.json('GET', `/admin-api/groups/${encodeURIComponent(groupId)}/governance-intents`);
188
+ return body.data;
189
+ }
190
+ /**
191
+ * Publish one governance op through the relay, as the author.
192
+ *
193
+ * The warrant commits to the op's exact bytes and is scoped to `groupId`, so
194
+ * the relay can neither change what the op does nor spend it elsewhere. Every
195
+ * peer applies the op as the author, so the author's own rights decide
196
+ * (`MANAGE_MEMBERS` to add a member, `CAN_CREATE_SUBGROUP` to create a
197
+ * subgroup, and so on); a lack of them comes back as an
198
+ * {@link IntentRefusedError} from the relay.
199
+ *
200
+ * Always asks {@link describeGovernance} first and refuses *before* taking a
201
+ * nonce when the relay has no standing, for the reason `createContext` does.
202
+ * The nonce comes from the configured {@link NonceSource}; it is spent in a
203
+ * per-group ledger, and a monotonic source is correct for it as for every
204
+ * other warrant.
205
+ */
206
+ async govern(input) {
207
+ const described = await this.describeGovernance(input.groupId);
208
+ if (!described.canActOnBehalf) {
209
+ throw new IntentRefusedError(`the relay (${described.executorAccount}) has no standing to act for members of group ` +
210
+ `${described.groupId}; an admin must admit it as a relay or grant it CAN_AUTHOR_ON_BEHALF ` +
211
+ '(checked before signing, no nonce was spent)', false, 403);
212
+ }
213
+ const executor = this.checkedExecutor(described.executorAccount);
214
+ const nonce = await this.config.nonces.next();
215
+ const ttl = this.config.ttlSeconds ?? DEFAULT_TTL_SECONDS;
216
+ const warrant = await signGovernanceWarrant({
217
+ scope: input.groupId,
218
+ op: input.op,
219
+ authorAccount: this.config.authorAccount,
220
+ executor,
221
+ nonce,
222
+ notAfter: BigInt(Math.floor(Date.now() / 1000)) + BigInt(ttl),
223
+ deviceSecret: this.config.deviceSecret,
224
+ });
225
+ const body = await this.json('POST', `/admin-api/groups/${encodeURIComponent(input.groupId)}/governance-intents`, { warrant, authorProof: this.config.authorProof, op: hex(input.op.bytes) });
226
+ return { groupId: body.data.groupId };
227
+ }
228
+ /**
229
+ * The executor a warrant must name: the one the relay answered with, which a
230
+ * configured account must agree with. Remembered for later calls.
231
+ */
232
+ checkedExecutor(answered) {
233
+ const configured = this.config.executorAccount;
234
+ if (configured && configured.toLowerCase() !== answered.toLowerCase()) {
235
+ throw new Error(`configured executorAccount ${configured} is not the relay's account ` +
236
+ `${answered}; a warrant naming it would be unspendable`);
237
+ }
238
+ this.executorAccount = answered;
239
+ return answered;
240
+ }
109
241
  async json(method, path, body) {
110
242
  const headers = { Accept: 'application/json' };
111
243
  if (body !== undefined)
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Mint a creation warrant: the author's consent for one relay to create one
3
+ * context in one group, once.
4
+ *
5
+ * The intent warrant in `warrant.ts` lets an account with no node *write* to a
6
+ * context; this lets it *create* one. The relay executes the warrant and
7
+ * publishes `GroupOp::ContextRegisteredOnBehalf` carrying it, and every peer
8
+ * checks the **author's** `CAN_CREATE_CONTEXT` (or admin) — the relay needs no
9
+ * create rights of its own, only standing to act for members. The app's `init`
10
+ * runs as the author's account.
11
+ *
12
+ * Same primitives and the same two-widths trap as `warrant.ts`: counts **inside
13
+ * the encoding** are borsh `u32`, counts the **signing preimage** hashes are
14
+ * `u64`. The two `Option<String>` labels add a third shape: on the wire an
15
+ * option is a one-byte tag followed (when present) by a `u32`-prefixed string,
16
+ * while the preimage hashes the tag and the bytes as two separate parts, the
17
+ * bytes empty when absent. That is what keeps `None` and `Some("")` distinct in
18
+ * both.
19
+ *
20
+ * **The byte contract is pinned in core**, at
21
+ * `crates/account/src/tests/creation_wire_fixture.rs`, and this module's test
22
+ * asserts the same vectors. A drift would surface at a relay as a 403.
23
+ */
24
+ /** What a creation warrant authorises. */
25
+ export interface CreationWarrantInput {
26
+ /** The group the context is created in, hex (32 bytes). */
27
+ group: string;
28
+ /**
29
+ * The seed the node derives the new context's id from, hex (32 bytes).
30
+ *
31
+ * Optional: when absent, 32 random bytes are drawn and returned alongside the
32
+ * warrant. Pass one only to make the id predictable — reusing a seed asks for
33
+ * a context that already exists.
34
+ */
35
+ seed?: string;
36
+ /** The author's account, hex — whose consent this is, and who runs `init`. */
37
+ authorAccount: string;
38
+ /** The relay authorised to act, hex. An account, not a key. */
39
+ executor: string;
40
+ /** The application the context is created with, hex (32 bytes). */
41
+ applicationId: string;
42
+ /**
43
+ * Which service of a multi-service bundle, or absent for the default.
44
+ *
45
+ * Absent and `""` are different values and sign differently. At most 256
46
+ * UTF-8 bytes.
47
+ */
48
+ serviceName?: string;
49
+ /** A display name for the context, or absent. At most 256 UTF-8 bytes. */
50
+ name?: string;
51
+ /**
52
+ * The JSON the app's `init()` will receive.
53
+ *
54
+ * Committed to rather than carried: the relay is sent the same value
55
+ * separately and the node checks it hashes to what was signed, so pass the
56
+ * exact value you will send. See {@link creationInitHash}.
57
+ */
58
+ initArgs: unknown;
59
+ /**
60
+ * Monotonic per author **device**. Spent in the *new* context's warrant
61
+ * ledger, so the same per-device nonce source `RelayClient` uses is correct.
62
+ */
63
+ nonce: number | bigint;
64
+ /** Unix seconds after which the relay must refuse it. */
65
+ notAfter: number | bigint;
66
+ /** Account-log heads the author saw, each hex (32 bytes). At most 64. */
67
+ accountHeads?: string[];
68
+ /**
69
+ * Governance heads the author's view descended from, each hex (32 bytes). At
70
+ * most 64. The same caveat as the intent warrant's floor applies: one the
71
+ * relay supplied is one the relay chose.
72
+ */
73
+ governanceFloor?: string[];
74
+ /** The author device's ed25519 signing secret, hex (32 bytes). Never sent. */
75
+ deviceSecret: string;
76
+ }
77
+ /** A signed creation warrant and the seed it commits to. */
78
+ export interface SignedCreationWarrant {
79
+ /** Hex borsh `ContextCreationWarrant`, ready for the relay. */
80
+ warrant: string;
81
+ /** The seed, hex — the one passed in, or the one drawn when none was. */
82
+ seed: string;
83
+ }
84
+ /** A creation warrant's fields, decoded from the wire encoding. */
85
+ export interface CreationWarrantFields {
86
+ group: string;
87
+ seed: string;
88
+ authorAccount: string;
89
+ /** The author device key `signCreationWarrant` derived from the secret. */
90
+ deviceKey: string;
91
+ executor: string;
92
+ applicationId: string;
93
+ /** `null` when absent — distinct from `""`. */
94
+ serviceName: string | null;
95
+ /** `null` when absent — distinct from `""`. */
96
+ name: string | null;
97
+ initHash: string;
98
+ accountHeads: string[];
99
+ governanceFloor: string[];
100
+ nonce: bigint;
101
+ notAfter: bigint;
102
+ signature: string;
103
+ }
104
+ /**
105
+ * The commitment a creation warrant carries in place of the init arguments.
106
+ *
107
+ * Hashes `JSON.stringify(initArgs)`, the same bytes `RelayClient.createContext`
108
+ * sends, under its own domain.
109
+ */
110
+ export declare function creationInitHash(initArgs: unknown): Promise<Uint8Array>;
111
+ /** The already-decoded fields the signature covers. Internal. */
112
+ export interface CreationPreimageParts {
113
+ group: Uint8Array;
114
+ seed: Uint8Array;
115
+ authorAccount: Uint8Array;
116
+ deviceKey: Uint8Array;
117
+ executor: Uint8Array;
118
+ applicationId: Uint8Array;
119
+ serviceName: Uint8Array | null;
120
+ name: Uint8Array | null;
121
+ initHash: Uint8Array;
122
+ accountHeads: Uint8Array[];
123
+ governanceFloor: Uint8Array[];
124
+ nonce: number | bigint;
125
+ notAfter: number | bigint;
126
+ }
127
+ /**
128
+ * The bytes the device key signs. Exported for the conformance test only — not
129
+ * from the package root.
130
+ */
131
+ export declare function creationWarrantPreimage(p: CreationPreimageParts): Promise<Uint8Array>;
132
+ /**
133
+ * Sign a creation warrant and return it hex-encoded, with its seed.
134
+ *
135
+ * Returns the encoding rather than an object for the reason `signWarrant`
136
+ * does: the signature covers exactly these bytes.
137
+ */
138
+ export declare function signCreationWarrant(input: CreationWarrantInput): Promise<SignedCreationWarrant>;
139
+ /**
140
+ * Decode a hex creation warrant back into its fields.
141
+ *
142
+ * Refuses truncated or trailing bytes rather than reading a neighbouring field.
143
+ * Does not verify the signature.
144
+ */
145
+ export declare function parseCreationWarrant(warrant: string): CreationWarrantFields;