@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.mjs CHANGED
@@ -1485,12 +1485,12 @@ var AdminApiClient = class {
1485
1485
  * anything.
1486
1486
  *
1487
1487
  * The two facts {@link performIntent} needs and a client cannot derive: whose
1488
- * account goes in the warrant's `executor`, and whether this node holds
1489
- * `CAN_AUTHOR_ON_BEHALF` on the owning group. `canAuthorOnBehalf: false` comes
1490
- * back as an answer rather than an error — it is the default state of every
1491
- * context — so a caller can say "an admin of `groupId` must grant it to
1492
- * `executorAccount`" instead of presenting a warrant that will be refused
1493
- * after the author has spent a nonce on it.
1488
+ * account and key go in the warrant's `executor` and `executor_key`, and
1489
+ * whether this node holds `CAN_AUTHOR_ON_BEHALF` on the owning group.
1490
+ * `canAuthorOnBehalf: false` comes back as an answer rather than an error, as
1491
+ * it is the default state of every context, so a caller can say "an admin of
1492
+ * `groupId` must grant it to `executorAccount`" instead of presenting a
1493
+ * warrant that will be refused after the author has spent a nonce on it.
1494
1494
  *
1495
1495
  * `404` means the context belongs to no group, or this node holds no account
1496
1496
  * yet; either way it can be named as no warrant's executor.
@@ -7199,6 +7199,7 @@ async function signWarrant(input) {
7199
7199
  const context = fromHex(input.context, "context", 32);
7200
7200
  const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
7201
7201
  const executor = fromHex(input.executor, "executor", 32);
7202
+ const executorKey = fromHex(input.executorKey, "executorKey", 32);
7202
7203
  const appVersion = fromHex(
7203
7204
  input.appVersion ?? UNPINNED_APP,
7204
7205
  "appVersion",
@@ -7217,6 +7218,7 @@ async function signWarrant(input) {
7217
7218
  authorAccount,
7218
7219
  publicKey,
7219
7220
  executor,
7221
+ executorKey,
7220
7222
  appVersion,
7221
7223
  method,
7222
7224
  commitment,
@@ -7234,6 +7236,7 @@ async function signWarrant(input) {
7234
7236
  authorAccount,
7235
7237
  publicKey,
7236
7238
  executor,
7239
+ executorKey,
7237
7240
  appVersion,
7238
7241
  u32le(method.length),
7239
7242
  method,
@@ -7280,6 +7283,7 @@ async function creationWarrantPreimage(p) {
7280
7283
  p.authorAccount,
7281
7284
  p.deviceKey,
7282
7285
  p.executor,
7286
+ p.executorKey,
7283
7287
  p.applicationId,
7284
7288
  new Uint8Array([p.serviceName === null ? 0 : 1]),
7285
7289
  p.serviceName ?? new Uint8Array(0),
@@ -7299,6 +7303,7 @@ async function signCreationWarrant(input) {
7299
7303
  const seed = input.seed === void 0 ? crypto.getRandomValues(new Uint8Array(32)) : fromHex(input.seed, "seed", 32);
7300
7304
  const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
7301
7305
  const executor = fromHex(input.executor, "executor", 32);
7306
+ const executorKey = fromHex(input.executorKey, "executorKey", 32);
7302
7307
  const applicationId = fromHex(input.applicationId, "applicationId", 32);
7303
7308
  const serviceName = label(input.serviceName, "serviceName");
7304
7309
  const name = label(input.name, "name");
@@ -7313,6 +7318,7 @@ async function signCreationWarrant(input) {
7313
7318
  authorAccount,
7314
7319
  deviceKey,
7315
7320
  executor,
7321
+ executorKey,
7316
7322
  applicationId,
7317
7323
  serviceName,
7318
7324
  name,
@@ -7330,6 +7336,7 @@ async function signCreationWarrant(input) {
7330
7336
  authorAccount,
7331
7337
  deviceKey,
7332
7338
  executor,
7339
+ executorKey,
7333
7340
  applicationId,
7334
7341
  borshOption(serviceName),
7335
7342
  borshOption(name),
@@ -7389,6 +7396,7 @@ function parseCreationWarrant(warrant) {
7389
7396
  authorAccount: hex(take(32, "authorAccount")),
7390
7397
  deviceKey: hex(take(32, "deviceKey")),
7391
7398
  executor: hex(take(32, "executor")),
7399
+ executorKey: hex(take(32, "executorKey")),
7392
7400
  applicationId: hex(take(32, "applicationId")),
7393
7401
  serviceName: option("serviceName"),
7394
7402
  name: option("name"),
@@ -7435,6 +7443,7 @@ async function governanceWarrantPreimage(p) {
7435
7443
  p.authorAccount,
7436
7444
  p.deviceKey,
7437
7445
  p.executor,
7446
+ p.executorKey,
7438
7447
  p.opHash,
7439
7448
  u64le(p.accountHeads.length),
7440
7449
  ...p.accountHeads,
@@ -7448,6 +7457,7 @@ async function signGovernanceWarrant(input) {
7448
7457
  const scope = fromHex(input.scope, "scope", 32);
7449
7458
  const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
7450
7459
  const executor = fromHex(input.executor, "executor", 32);
7460
+ const executorKey = fromHex(input.executorKey, "executorKey", 32);
7451
7461
  const accountHeads = citedHeads(input.accountHeads, "accountHeads");
7452
7462
  const governanceFloor = citedHeads(input.governanceFloor, "governanceFloor");
7453
7463
  const kind = kindByte(input.op.kind);
@@ -7460,6 +7470,7 @@ async function signGovernanceWarrant(input) {
7460
7470
  authorAccount,
7461
7471
  deviceKey,
7462
7472
  executor,
7473
+ executorKey,
7463
7474
  opHash,
7464
7475
  accountHeads,
7465
7476
  governanceFloor,
@@ -7474,6 +7485,7 @@ async function signGovernanceWarrant(input) {
7474
7485
  authorAccount,
7475
7486
  deviceKey,
7476
7487
  executor,
7488
+ executorKey,
7477
7489
  opHash,
7478
7490
  u32le(accountHeads.length),
7479
7491
  ...accountHeads,
@@ -7525,6 +7537,7 @@ function parseGovernanceWarrant(warrant) {
7525
7537
  authorAccount: hex(take(32, "authorAccount")),
7526
7538
  deviceKey: hex(take(32, "deviceKey")),
7527
7539
  executor: hex(take(32, "executor")),
7540
+ executorKey: hex(take(32, "executorKey")),
7528
7541
  opHash: hex(take(32, "opHash")),
7529
7542
  accountHeads: heads("accountHeads"),
7530
7543
  governanceFloor: heads("governanceFloor"),
@@ -7829,7 +7842,6 @@ var RelayClient = class {
7829
7842
  constructor(config) {
7830
7843
  this.config = config;
7831
7844
  this.baseUrl = config.relayUrl.replace(/\/+$/, "");
7832
- this.executorAccount = config.executorAccount;
7833
7845
  const injected = config.fetch;
7834
7846
  this.fetchImpl = injected ? (input, init) => injected(input, init) : (input, init) => globalThis.fetch(input, init);
7835
7847
  this.timeoutMs = config.timeoutMs ?? 1e4;
@@ -7879,8 +7891,11 @@ var RelayClient = class {
7879
7891
  * as diagnostics after a failure.
7880
7892
  */
7881
7893
  async describe(contextId) {
7882
- const body = await this.json("GET", `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`);
7883
- this.executorAccount = body.data.executorAccount;
7894
+ const body = await this.json(
7895
+ "GET",
7896
+ `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`
7897
+ );
7898
+ this.checkedExecutor(body.data);
7884
7899
  return body.data;
7885
7900
  }
7886
7901
  /**
@@ -7891,14 +7906,15 @@ var RelayClient = class {
7891
7906
  * apply. Nothing accumulates and nothing is reusable.
7892
7907
  */
7893
7908
  async execute(contextId, method, argsJson = {}) {
7894
- const executor = this.executorAccount ?? (await this.describe(contextId)).executorAccount;
7909
+ const executor = this.executor ?? this.checkedExecutor(await this.describe(contextId));
7895
7910
  const signer = await this.authorSigner();
7896
7911
  const nonce2 = await this.config.nonces.next();
7897
7912
  const ttl = this.config.ttlSeconds ?? DEFAULT_TTL_SECONDS;
7898
7913
  const warrant = await signWarrant({
7899
7914
  context: contextId,
7900
7915
  authorAccount: this.config.authorAccount,
7901
- executor,
7916
+ executor: executor.executorAccount,
7917
+ executorKey: executor.executorKey,
7902
7918
  method,
7903
7919
  argsJson,
7904
7920
  nonce: nonce2,
@@ -7909,7 +7925,10 @@ var RelayClient = class {
7909
7925
  "POST",
7910
7926
  `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`,
7911
7927
  { method, argsJson, warrant, authorProof: this.config.authorProof }
7912
- );
7928
+ ).catch((err) => {
7929
+ if (err instanceof IntentRefusedError) this.executor = void 0;
7930
+ throw err;
7931
+ });
7913
7932
  return { rootHash: body.data.rootHash, returns: body.data.returns ?? null };
7914
7933
  }
7915
7934
  /**
@@ -7978,7 +7997,7 @@ var RelayClient = class {
7978
7997
  403
7979
7998
  );
7980
7999
  }
7981
- const executor = this.checkedExecutor(described.executorAccount);
8000
+ const executor = this.checkedExecutor(described);
7982
8001
  const initArgs = input.initArgs ?? {};
7983
8002
  const signer = await this.authorSigner();
7984
8003
  const nonce2 = await this.config.nonces.next();
@@ -7987,7 +8006,8 @@ var RelayClient = class {
7987
8006
  group: input.groupId,
7988
8007
  seed: input.seed,
7989
8008
  authorAccount: this.config.authorAccount,
7990
- executor,
8009
+ executor: executor.executorAccount,
8010
+ executorKey: executor.executorKey,
7991
8011
  applicationId: input.applicationId,
7992
8012
  serviceName: input.serviceName,
7993
8013
  name: input.name,
@@ -8009,7 +8029,7 @@ var RelayClient = class {
8009
8029
  }
8010
8030
  /**
8011
8031
  * Ask the relay whether it may publish governance ops for members of
8012
- * `groupId`, and which account a warrant must name.
8032
+ * `groupId`, and which account and key a warrant must name.
8013
8033
  */
8014
8034
  async describeGovernance(groupId) {
8015
8035
  const body = await this.json(
@@ -8043,7 +8063,7 @@ var RelayClient = class {
8043
8063
  403
8044
8064
  );
8045
8065
  }
8046
- const executor = this.checkedExecutor(described.executorAccount);
8066
+ const executor = this.checkedExecutor(described);
8047
8067
  const signer = await this.authorSigner();
8048
8068
  const nonce2 = await this.config.nonces.next();
8049
8069
  const ttl = this.config.ttlSeconds ?? DEFAULT_TTL_SECONDS;
@@ -8051,7 +8071,8 @@ var RelayClient = class {
8051
8071
  scope: input.groupId,
8052
8072
  op: input.op,
8053
8073
  authorAccount: this.config.authorAccount,
8054
- executor,
8074
+ executor: executor.executorAccount,
8075
+ executorKey: executor.executorKey,
8055
8076
  nonce: nonce2,
8056
8077
  notAfter: BigInt(Math.floor(Date.now() / 1e3)) + BigInt(ttl),
8057
8078
  signer
@@ -8077,9 +8098,7 @@ var RelayClient = class {
8077
8098
  *
8078
8099
  * The executor cannot be learned from the relay first: the namespace does not
8079
8100
  * exist, so `describeGovernance` has nothing to answer about. It comes from
8080
- * `input.executorAccount`, the configured one, or one an earlier call learned
8081
- * (`describe`, `createContext`, `govern`), and without any of them this
8082
- * throws before taking a nonce.
8101
+ * `input.executor` only, and without it this throws before taking a nonce.
8083
8102
  * The nonce is spent in the new namespace's ledger.
8084
8103
  *
8085
8104
  * With `defaultCapabilities`, the founder then sets the namespace's default
@@ -8090,13 +8109,13 @@ var RelayClient = class {
8090
8109
  * Refusals (`400`/`403`) come back as {@link IntentRefusedError}; a salt
8091
8110
  * that names an existing namespace is a `409`, an `HTTPError`.
8092
8111
  */
8093
- async foundNamespace(input = {}) {
8094
- const executor = input.executorAccount ? this.checkedExecutor(input.executorAccount) : this.executorAccount;
8095
- if (!executor) {
8112
+ async foundNamespace(input) {
8113
+ if (!input?.executor) {
8096
8114
  throw new Error(
8097
- "foundNamespace needs the relay's account: pass executorAccount or configure it (checked before signing, no nonce was spent)"
8115
+ "foundNamespace needs the relay's account and signing key: pass executor (checked before signing, no nonce was spent)"
8098
8116
  );
8099
8117
  }
8118
+ const executor = this.checkedExecutor(input.executor);
8100
8119
  const salt = input.salt ? hex(fromHex(input.salt, "salt", 32)) : hex(crypto.getRandomValues(new Uint8Array(32)));
8101
8120
  const namespaceId = await foundedNamespaceId(this.config.authorAccount, salt);
8102
8121
  const op = namespaceCreatedOp({
@@ -8113,7 +8132,8 @@ var RelayClient = class {
8113
8132
  scope: namespaceId,
8114
8133
  op,
8115
8134
  authorAccount: this.config.authorAccount,
8116
- executor,
8135
+ executor: executor.executorAccount,
8136
+ executorKey: executor.executorKey,
8117
8137
  nonce: nonce2,
8118
8138
  notAfter: BigInt(Math.floor(Date.now() / 1e3)) + BigInt(ttl),
8119
8139
  signer
@@ -8154,18 +8174,22 @@ var RelayClient = class {
8154
8174
  return body.data;
8155
8175
  }
8156
8176
  /**
8157
- * The executor a warrant must name: the one the relay answered with, which a
8158
- * configured account must agree with. Remembered for later calls.
8177
+ * The relay's answer, checked against a configured account and remembered.
8178
+ * Decoded here so a relay naming no key is refused before a nonce is taken.
8159
8179
  */
8160
8180
  checkedExecutor(answered) {
8181
+ const executor = {
8182
+ executorAccount: hex(fromHex(answered.executorAccount ?? "", "the relay's executorAccount", 32)),
8183
+ executorKey: hex(fromHex(answered.executorKey ?? "", "the relay's executorKey", 32))
8184
+ };
8161
8185
  const configured = this.config.executorAccount;
8162
- if (configured && configured.toLowerCase() !== answered.toLowerCase()) {
8186
+ if (configured && configured.toLowerCase() !== executor.executorAccount) {
8163
8187
  throw new Error(
8164
- `configured executorAccount ${configured} is not the relay's account ${answered}; a warrant naming it would be unspendable`
8188
+ `configured executorAccount ${configured} is not the relay's account ${executor.executorAccount}; a warrant naming it would be unspendable`
8165
8189
  );
8166
8190
  }
8167
- this.executorAccount = answered;
8168
- return answered;
8191
+ this.executor = executor;
8192
+ return executor;
8169
8193
  }
8170
8194
  /**
8171
8195
  * One request, waiting out a rate limit rather than failing the caller.
@@ -10152,7 +10176,7 @@ function bytes32(value, label2) {
10152
10176
  }
10153
10177
 
10154
10178
  // src/namespace-op/namespace-op.ts
10155
- var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 22;
10179
+ var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 23;
10156
10180
  var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");
10157
10181
  var NAMESPACE_OP_ROOT = 0;
10158
10182
  var ROOT_OP2 = {
@@ -10507,19 +10531,10 @@ async function foundDelegatedNamespace(s, req = {}, deps = {}) {
10507
10531
  const relays = knownRelays(s).map((u) => u.replace(/\/+$/, ""));
10508
10532
  const relay = relays[0];
10509
10533
  if (!relay) throw new Error("no relay is known for this account, so there is nowhere to found a namespace");
10510
- const known = Object.entries(readRelayMap(s.account).namespaces).find(
10511
- ([, url]) => url.replace(/\/+$/, "") === relay
10512
- )?.[0];
10513
- const named = s.executorAccount && s.relayUrl && s.relayUrl.replace(/\/+$/, "") === relay ? s.executorAccount : null;
10514
- if (!named && !known) {
10515
- throw new Error(
10516
- "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)"
10517
- );
10518
- }
10519
- const executorAccount = named ?? (await client(s, relay, governanceNonce(relay, known), deps.fetch).describeGovernance(known)).executorAccount;
10534
+ const executor = await foundingExecutor(s, relay, deps.fetch);
10520
10535
  let next = BigInt(Date.now());
10521
10536
  const founded = await client(s, relay, { next: async () => next++ }, deps.fetch).foundNamespace({
10522
- executorAccount,
10537
+ executor,
10523
10538
  ...req.defaultCapabilities !== void 0 ? { defaultCapabilities: req.defaultCapabilities } : {},
10524
10539
  ...req.application ? { application: req.application } : {}
10525
10540
  });
@@ -10539,6 +10554,33 @@ async function foundDelegatedNamespace(s, req = {}, deps = {}) {
10539
10554
  };
10540
10555
  return { namespaceId: founded.namespaceId, teeEnabled: founded.teeEnabled, ...ha };
10541
10556
  }
10557
+ async function foundingExecutor(s, relay, fetch2) {
10558
+ const named = s.executorAccount && s.relayUrl?.replace(/\/+$/, "") === relay ? s.executorAccount : null;
10559
+ if (named) return { executorAccount: named, executorKey: await attestedNodeKey(relay) };
10560
+ const known = Object.entries(readRelayMap(s.account).namespaces).find(
10561
+ ([, url]) => url.replace(/\/+$/, "") === relay
10562
+ )?.[0];
10563
+ if (!known) {
10564
+ throw new Error(
10565
+ "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)"
10566
+ );
10567
+ }
10568
+ const executorKey = await attestedNodeKey(relay);
10569
+ const described = await client(s, relay, governanceNonce(relay, known), fetch2).describeGovernance(known);
10570
+ if (described.executorKey?.toLowerCase() !== executorKey) {
10571
+ throw new Error(
10572
+ `the relay's discovery names signing key ${described.executorKey}, not the relay's node key ${executorKey}: refusing to found through it`
10573
+ );
10574
+ }
10575
+ return { executorAccount: described.executorAccount, executorKey };
10576
+ }
10577
+ async function attestedNodeKey(relay) {
10578
+ const key = await attemptRelayNodeKey(relay);
10579
+ if (key.kind === "learned") return key.nodeKey;
10580
+ throw new Error(
10581
+ 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)"
10582
+ );
10583
+ }
10542
10584
  var HA_ACCOUNT_NOT_LINKED_MESSAGE = "link this account to your cloud user in the wallet so invitees can find this namespace";
10543
10585
  var HA_REFUSAL_MESSAGES = {
10544
10586
  account_not_linked: HA_ACCOUNT_NOT_LINKED_MESSAGE,