@calimero-network/mero-js 24.1.2 → 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/index.cjs CHANGED
@@ -1767,12 +1767,12 @@ var AdminApiClient = class {
1767
1767
  * anything.
1768
1768
  *
1769
1769
  * The two facts {@link performIntent} needs and a client cannot derive: whose
1770
- * account goes in the warrant's `executor`, and whether this node holds
1771
- * `CAN_AUTHOR_ON_BEHALF` on the owning group. `canAuthorOnBehalf: false` comes
1772
- * back as an answer rather than an error — it is the default state of every
1773
- * context — so a caller can say "an admin of `groupId` must grant it to
1774
- * `executorAccount`" instead of presenting a warrant that will be refused
1775
- * after the author has spent a nonce on it.
1770
+ * account and key go in the warrant's `executor` and `executor_key`, and
1771
+ * whether this node holds `CAN_AUTHOR_ON_BEHALF` on the owning group.
1772
+ * `canAuthorOnBehalf: false` comes back as an answer rather than an error, as
1773
+ * it is the default state of every context, so a caller can say "an admin of
1774
+ * `groupId` must grant it to `executorAccount`" instead of presenting a
1775
+ * warrant that will be refused after the author has spent a nonce on it.
1776
1776
  *
1777
1777
  * `404` means the context belongs to no group, or this node holds no account
1778
1778
  * yet; either way it can be named as no warrant's executor.
@@ -7481,6 +7481,7 @@ async function signWarrant(input) {
7481
7481
  const context = fromHex(input.context, "context", 32);
7482
7482
  const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
7483
7483
  const executor = fromHex(input.executor, "executor", 32);
7484
+ const executorKey = fromHex(input.executorKey, "executorKey", 32);
7484
7485
  const appVersion = fromHex(
7485
7486
  input.appVersion ?? UNPINNED_APP,
7486
7487
  "appVersion",
@@ -7499,6 +7500,7 @@ async function signWarrant(input) {
7499
7500
  authorAccount,
7500
7501
  publicKey,
7501
7502
  executor,
7503
+ executorKey,
7502
7504
  appVersion,
7503
7505
  method,
7504
7506
  commitment,
@@ -7516,6 +7518,7 @@ async function signWarrant(input) {
7516
7518
  authorAccount,
7517
7519
  publicKey,
7518
7520
  executor,
7521
+ executorKey,
7519
7522
  appVersion,
7520
7523
  u32le(method.length),
7521
7524
  method,
@@ -7562,6 +7565,7 @@ async function creationWarrantPreimage(p) {
7562
7565
  p.authorAccount,
7563
7566
  p.deviceKey,
7564
7567
  p.executor,
7568
+ p.executorKey,
7565
7569
  p.applicationId,
7566
7570
  new Uint8Array([p.serviceName === null ? 0 : 1]),
7567
7571
  p.serviceName ?? new Uint8Array(0),
@@ -7581,6 +7585,7 @@ async function signCreationWarrant(input) {
7581
7585
  const seed = input.seed === void 0 ? crypto.getRandomValues(new Uint8Array(32)) : fromHex(input.seed, "seed", 32);
7582
7586
  const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
7583
7587
  const executor = fromHex(input.executor, "executor", 32);
7588
+ const executorKey = fromHex(input.executorKey, "executorKey", 32);
7584
7589
  const applicationId = fromHex(input.applicationId, "applicationId", 32);
7585
7590
  const serviceName = label(input.serviceName, "serviceName");
7586
7591
  const name = label(input.name, "name");
@@ -7595,6 +7600,7 @@ async function signCreationWarrant(input) {
7595
7600
  authorAccount,
7596
7601
  deviceKey,
7597
7602
  executor,
7603
+ executorKey,
7598
7604
  applicationId,
7599
7605
  serviceName,
7600
7606
  name,
@@ -7612,6 +7618,7 @@ async function signCreationWarrant(input) {
7612
7618
  authorAccount,
7613
7619
  deviceKey,
7614
7620
  executor,
7621
+ executorKey,
7615
7622
  applicationId,
7616
7623
  borshOption(serviceName),
7617
7624
  borshOption(name),
@@ -7671,6 +7678,7 @@ function parseCreationWarrant(warrant) {
7671
7678
  authorAccount: hex(take(32, "authorAccount")),
7672
7679
  deviceKey: hex(take(32, "deviceKey")),
7673
7680
  executor: hex(take(32, "executor")),
7681
+ executorKey: hex(take(32, "executorKey")),
7674
7682
  applicationId: hex(take(32, "applicationId")),
7675
7683
  serviceName: option("serviceName"),
7676
7684
  name: option("name"),
@@ -7717,6 +7725,7 @@ async function governanceWarrantPreimage(p) {
7717
7725
  p.authorAccount,
7718
7726
  p.deviceKey,
7719
7727
  p.executor,
7728
+ p.executorKey,
7720
7729
  p.opHash,
7721
7730
  u64le(p.accountHeads.length),
7722
7731
  ...p.accountHeads,
@@ -7730,6 +7739,7 @@ async function signGovernanceWarrant(input) {
7730
7739
  const scope = fromHex(input.scope, "scope", 32);
7731
7740
  const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
7732
7741
  const executor = fromHex(input.executor, "executor", 32);
7742
+ const executorKey = fromHex(input.executorKey, "executorKey", 32);
7733
7743
  const accountHeads = citedHeads(input.accountHeads, "accountHeads");
7734
7744
  const governanceFloor = citedHeads(input.governanceFloor, "governanceFloor");
7735
7745
  const kind = kindByte(input.op.kind);
@@ -7742,6 +7752,7 @@ async function signGovernanceWarrant(input) {
7742
7752
  authorAccount,
7743
7753
  deviceKey,
7744
7754
  executor,
7755
+ executorKey,
7745
7756
  opHash,
7746
7757
  accountHeads,
7747
7758
  governanceFloor,
@@ -7756,6 +7767,7 @@ async function signGovernanceWarrant(input) {
7756
7767
  authorAccount,
7757
7768
  deviceKey,
7758
7769
  executor,
7770
+ executorKey,
7759
7771
  opHash,
7760
7772
  u32le(accountHeads.length),
7761
7773
  ...accountHeads,
@@ -7807,6 +7819,7 @@ function parseGovernanceWarrant(warrant) {
7807
7819
  authorAccount: hex(take(32, "authorAccount")),
7808
7820
  deviceKey: hex(take(32, "deviceKey")),
7809
7821
  executor: hex(take(32, "executor")),
7822
+ executorKey: hex(take(32, "executorKey")),
7810
7823
  opHash: hex(take(32, "opHash")),
7811
7824
  accountHeads: heads("accountHeads"),
7812
7825
  governanceFloor: heads("governanceFloor"),
@@ -8111,7 +8124,6 @@ var RelayClient = class {
8111
8124
  constructor(config) {
8112
8125
  this.config = config;
8113
8126
  this.baseUrl = config.relayUrl.replace(/\/+$/, "");
8114
- this.executorAccount = config.executorAccount;
8115
8127
  const injected = config.fetch;
8116
8128
  this.fetchImpl = injected ? (input, init) => injected(input, init) : (input, init) => globalThis.fetch(input, init);
8117
8129
  this.timeoutMs = config.timeoutMs ?? 1e4;
@@ -8161,8 +8173,11 @@ var RelayClient = class {
8161
8173
  * as diagnostics after a failure.
8162
8174
  */
8163
8175
  async describe(contextId) {
8164
- const body = await this.json("GET", `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`);
8165
- this.executorAccount = body.data.executorAccount;
8176
+ const body = await this.json(
8177
+ "GET",
8178
+ `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`
8179
+ );
8180
+ this.checkedExecutor(body.data);
8166
8181
  return body.data;
8167
8182
  }
8168
8183
  /**
@@ -8173,14 +8188,15 @@ var RelayClient = class {
8173
8188
  * apply. Nothing accumulates and nothing is reusable.
8174
8189
  */
8175
8190
  async execute(contextId, method, argsJson = {}) {
8176
- const executor = this.executorAccount ?? (await this.describe(contextId)).executorAccount;
8191
+ const executor = this.executor ?? this.checkedExecutor(await this.describe(contextId));
8177
8192
  const signer = await this.authorSigner();
8178
8193
  const nonce2 = await this.config.nonces.next();
8179
8194
  const ttl = this.config.ttlSeconds ?? DEFAULT_TTL_SECONDS;
8180
8195
  const warrant = await signWarrant({
8181
8196
  context: contextId,
8182
8197
  authorAccount: this.config.authorAccount,
8183
- executor,
8198
+ executor: executor.executorAccount,
8199
+ executorKey: executor.executorKey,
8184
8200
  method,
8185
8201
  argsJson,
8186
8202
  nonce: nonce2,
@@ -8191,7 +8207,10 @@ var RelayClient = class {
8191
8207
  "POST",
8192
8208
  `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`,
8193
8209
  { method, argsJson, warrant, authorProof: this.config.authorProof }
8194
- );
8210
+ ).catch((err) => {
8211
+ if (err instanceof IntentRefusedError) this.executor = void 0;
8212
+ throw err;
8213
+ });
8195
8214
  return { rootHash: body.data.rootHash, returns: body.data.returns ?? null };
8196
8215
  }
8197
8216
  /**
@@ -8260,7 +8279,7 @@ var RelayClient = class {
8260
8279
  403
8261
8280
  );
8262
8281
  }
8263
- const executor = this.checkedExecutor(described.executorAccount);
8282
+ const executor = this.checkedExecutor(described);
8264
8283
  const initArgs = input.initArgs ?? {};
8265
8284
  const signer = await this.authorSigner();
8266
8285
  const nonce2 = await this.config.nonces.next();
@@ -8269,7 +8288,8 @@ var RelayClient = class {
8269
8288
  group: input.groupId,
8270
8289
  seed: input.seed,
8271
8290
  authorAccount: this.config.authorAccount,
8272
- executor,
8291
+ executor: executor.executorAccount,
8292
+ executorKey: executor.executorKey,
8273
8293
  applicationId: input.applicationId,
8274
8294
  serviceName: input.serviceName,
8275
8295
  name: input.name,
@@ -8291,7 +8311,7 @@ var RelayClient = class {
8291
8311
  }
8292
8312
  /**
8293
8313
  * Ask the relay whether it may publish governance ops for members of
8294
- * `groupId`, and which account a warrant must name.
8314
+ * `groupId`, and which account and key a warrant must name.
8295
8315
  */
8296
8316
  async describeGovernance(groupId) {
8297
8317
  const body = await this.json(
@@ -8325,7 +8345,7 @@ var RelayClient = class {
8325
8345
  403
8326
8346
  );
8327
8347
  }
8328
- const executor = this.checkedExecutor(described.executorAccount);
8348
+ const executor = this.checkedExecutor(described);
8329
8349
  const signer = await this.authorSigner();
8330
8350
  const nonce2 = await this.config.nonces.next();
8331
8351
  const ttl = this.config.ttlSeconds ?? DEFAULT_TTL_SECONDS;
@@ -8333,7 +8353,8 @@ var RelayClient = class {
8333
8353
  scope: input.groupId,
8334
8354
  op: input.op,
8335
8355
  authorAccount: this.config.authorAccount,
8336
- executor,
8356
+ executor: executor.executorAccount,
8357
+ executorKey: executor.executorKey,
8337
8358
  nonce: nonce2,
8338
8359
  notAfter: BigInt(Math.floor(Date.now() / 1e3)) + BigInt(ttl),
8339
8360
  signer
@@ -8359,9 +8380,7 @@ var RelayClient = class {
8359
8380
  *
8360
8381
  * The executor cannot be learned from the relay first: the namespace does not
8361
8382
  * exist, so `describeGovernance` has nothing to answer about. It comes from
8362
- * `input.executorAccount`, the configured one, or one an earlier call learned
8363
- * (`describe`, `createContext`, `govern`), and without any of them this
8364
- * throws before taking a nonce.
8383
+ * `input.executor` only, and without it this throws before taking a nonce.
8365
8384
  * The nonce is spent in the new namespace's ledger.
8366
8385
  *
8367
8386
  * With `defaultCapabilities`, the founder then sets the namespace's default
@@ -8372,13 +8391,13 @@ var RelayClient = class {
8372
8391
  * Refusals (`400`/`403`) come back as {@link IntentRefusedError}; a salt
8373
8392
  * that names an existing namespace is a `409`, an `HTTPError`.
8374
8393
  */
8375
- async foundNamespace(input = {}) {
8376
- const executor = input.executorAccount ? this.checkedExecutor(input.executorAccount) : this.executorAccount;
8377
- if (!executor) {
8394
+ async foundNamespace(input) {
8395
+ if (!input?.executor) {
8378
8396
  throw new Error(
8379
- "foundNamespace needs the relay's account: pass executorAccount or configure it (checked before signing, no nonce was spent)"
8397
+ "foundNamespace needs the relay's account and signing key: pass executor (checked before signing, no nonce was spent)"
8380
8398
  );
8381
8399
  }
8400
+ const executor = this.checkedExecutor(input.executor);
8382
8401
  const salt = input.salt ? hex(fromHex(input.salt, "salt", 32)) : hex(crypto.getRandomValues(new Uint8Array(32)));
8383
8402
  const namespaceId = await foundedNamespaceId(this.config.authorAccount, salt);
8384
8403
  const op = namespaceCreatedOp({
@@ -8395,7 +8414,8 @@ var RelayClient = class {
8395
8414
  scope: namespaceId,
8396
8415
  op,
8397
8416
  authorAccount: this.config.authorAccount,
8398
- executor,
8417
+ executor: executor.executorAccount,
8418
+ executorKey: executor.executorKey,
8399
8419
  nonce: nonce2,
8400
8420
  notAfter: BigInt(Math.floor(Date.now() / 1e3)) + BigInt(ttl),
8401
8421
  signer
@@ -8436,18 +8456,22 @@ var RelayClient = class {
8436
8456
  return body.data;
8437
8457
  }
8438
8458
  /**
8439
- * The executor a warrant must name: the one the relay answered with, which a
8440
- * configured account must agree with. Remembered for later calls.
8459
+ * The relay's answer, checked against a configured account and remembered.
8460
+ * Decoded here so a relay naming no key is refused before a nonce is taken.
8441
8461
  */
8442
8462
  checkedExecutor(answered) {
8463
+ const executor = {
8464
+ executorAccount: hex(fromHex(answered.executorAccount ?? "", "the relay's executorAccount", 32)),
8465
+ executorKey: hex(fromHex(answered.executorKey ?? "", "the relay's executorKey", 32))
8466
+ };
8443
8467
  const configured = this.config.executorAccount;
8444
- if (configured && configured.toLowerCase() !== answered.toLowerCase()) {
8468
+ if (configured && configured.toLowerCase() !== executor.executorAccount) {
8445
8469
  throw new Error(
8446
- `configured executorAccount ${configured} is not the relay's account ${answered}; a warrant naming it would be unspendable`
8470
+ `configured executorAccount ${configured} is not the relay's account ${executor.executorAccount}; a warrant naming it would be unspendable`
8447
8471
  );
8448
8472
  }
8449
- this.executorAccount = answered;
8450
- return answered;
8473
+ this.executor = executor;
8474
+ return executor;
8451
8475
  }
8452
8476
  /**
8453
8477
  * One request, waiting out a rate limit rather than failing the caller.
@@ -10434,7 +10458,7 @@ function bytes32(value, label2) {
10434
10458
  }
10435
10459
 
10436
10460
  // src/namespace-op/namespace-op.ts
10437
- var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 22;
10461
+ var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 23;
10438
10462
  var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");
10439
10463
  var NAMESPACE_OP_ROOT = 0;
10440
10464
  var ROOT_OP2 = {
@@ -10789,19 +10813,10 @@ async function foundDelegatedNamespace(s, req = {}, deps = {}) {
10789
10813
  const relays = knownRelays(s).map((u) => u.replace(/\/+$/, ""));
10790
10814
  const relay = relays[0];
10791
10815
  if (!relay) throw new Error("no relay is known for this account, so there is nowhere to found a namespace");
10792
- const known = Object.entries(readRelayMap(s.account).namespaces).find(
10793
- ([, url]) => url.replace(/\/+$/, "") === relay
10794
- )?.[0];
10795
- const named = s.executorAccount && s.relayUrl && s.relayUrl.replace(/\/+$/, "") === relay ? s.executorAccount : null;
10796
- if (!named && !known) {
10797
- throw new Error(
10798
- "the executor account of this relay is not known: join a namespace on it first, or connect with the relay's executor account (the cloud shows it beside the relay)"
10799
- );
10800
- }
10801
- const executorAccount = named ?? (await client(s, relay, governanceNonce(relay, known), deps.fetch).describeGovernance(known)).executorAccount;
10816
+ const executor = await foundingExecutor(s, relay, deps.fetch);
10802
10817
  let next = BigInt(Date.now());
10803
10818
  const founded = await client(s, relay, { next: async () => next++ }, deps.fetch).foundNamespace({
10804
- executorAccount,
10819
+ executor,
10805
10820
  ...req.defaultCapabilities !== void 0 ? { defaultCapabilities: req.defaultCapabilities } : {},
10806
10821
  ...req.application ? { application: req.application } : {}
10807
10822
  });
@@ -10821,6 +10836,33 @@ async function foundDelegatedNamespace(s, req = {}, deps = {}) {
10821
10836
  };
10822
10837
  return { namespaceId: founded.namespaceId, teeEnabled: founded.teeEnabled, ...ha };
10823
10838
  }
10839
+ async function foundingExecutor(s, relay, fetch2) {
10840
+ const named = s.executorAccount && s.relayUrl?.replace(/\/+$/, "") === relay ? s.executorAccount : null;
10841
+ if (named) return { executorAccount: named, executorKey: await attestedNodeKey(relay) };
10842
+ const known = Object.entries(readRelayMap(s.account).namespaces).find(
10843
+ ([, url]) => url.replace(/\/+$/, "") === relay
10844
+ )?.[0];
10845
+ if (!known) {
10846
+ throw new Error(
10847
+ "the executor account of this relay is not known: join a namespace on it first, or connect with the relay's executor account (the cloud shows it beside the relay)"
10848
+ );
10849
+ }
10850
+ const executorKey = await attestedNodeKey(relay);
10851
+ const described = await client(s, relay, governanceNonce(relay, known), fetch2).describeGovernance(known);
10852
+ if (described.executorKey?.toLowerCase() !== executorKey) {
10853
+ throw new Error(
10854
+ `the relay's discovery names signing key ${described.executorKey}, not the relay's node key ${executorKey}: refusing to found through it`
10855
+ );
10856
+ }
10857
+ return { executorAccount: described.executorAccount, executorKey };
10858
+ }
10859
+ async function attestedNodeKey(relay) {
10860
+ const key = await attemptRelayNodeKey(relay);
10861
+ if (key.kind === "learned") return key.nodeKey;
10862
+ throw new Error(
10863
+ key.kind === "unavailable" ? "the relay's signing key could not be learned: its attestation did not answer, try again" : "the relay's attestation was refused, so founding through it is not possible (only a key from its operator, pinned with pinRelayNodeKey, can stand in)"
10864
+ );
10865
+ }
10824
10866
  var HA_ACCOUNT_NOT_LINKED_MESSAGE = "link this account to your cloud user in the wallet so invitees can find this namespace";
10825
10867
  var HA_REFUSAL_MESSAGES = {
10826
10868
  account_not_linked: HA_ACCOUNT_NOT_LINKED_MESSAGE,