polkadot-cli 1.20.0 → 1.21.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/README.md +110 -29
- package/dist/cli.mjs +785 -243
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -73,7 +73,9 @@ var init_errors = __esm(() => {
|
|
|
73
73
|
/codec/i,
|
|
74
74
|
/decod(e|ing)/i,
|
|
75
75
|
/Lookup failed/i,
|
|
76
|
-
/metadata.*mismatch/i
|
|
76
|
+
/metadata.*mismatch/i,
|
|
77
|
+
/BadProof/,
|
|
78
|
+
/AncientBirthBlock/
|
|
77
79
|
];
|
|
78
80
|
BLOCK_UNAVAILABLE_PATTERNS = [
|
|
79
81
|
/is not pinned/i,
|
|
@@ -231,31 +233,84 @@ var init_types = __esm(() => {
|
|
|
231
233
|
BUILTIN_CHAIN_NAMES = new Set(Object.keys(DEFAULT_CONFIG.chains));
|
|
232
234
|
});
|
|
233
235
|
|
|
236
|
+
// src/config/workspace.ts
|
|
237
|
+
import { realpathSync, statSync } from "node:fs";
|
|
238
|
+
import { homedir } from "node:os";
|
|
239
|
+
import { dirname, join, resolve } from "node:path";
|
|
240
|
+
function isDirectory(path) {
|
|
241
|
+
try {
|
|
242
|
+
return statSync(path).isDirectory();
|
|
243
|
+
} catch {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function canonicalPath(path) {
|
|
248
|
+
try {
|
|
249
|
+
return realpathSync(path);
|
|
250
|
+
} catch {
|
|
251
|
+
return resolve(path);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function findWorkspace(startDir, home = homedir()) {
|
|
255
|
+
const homePath = canonicalPath(home);
|
|
256
|
+
let dir = canonicalPath(startDir);
|
|
257
|
+
while (dir !== homePath) {
|
|
258
|
+
const candidate = join(dir, WORKSPACE_DIR_NAME);
|
|
259
|
+
if (isDirectory(candidate))
|
|
260
|
+
return candidate;
|
|
261
|
+
const parent = dirname(dir);
|
|
262
|
+
if (parent === dir)
|
|
263
|
+
return null;
|
|
264
|
+
dir = parent;
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
var WORKSPACE_DIR_NAME = ".polkadot";
|
|
269
|
+
var init_workspace = () => {};
|
|
270
|
+
|
|
234
271
|
// src/config/store.ts
|
|
235
272
|
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
236
|
-
import { homedir } from "node:os";
|
|
237
|
-
import { join } from "node:path";
|
|
238
|
-
function
|
|
273
|
+
import { homedir as homedir2 } from "node:os";
|
|
274
|
+
import { join as join2 } from "node:path";
|
|
275
|
+
function resolveConfigDir(cwd = process.cwd()) {
|
|
239
276
|
const override = process.env.DOT_HOME;
|
|
240
|
-
|
|
277
|
+
if (override && override.length > 0)
|
|
278
|
+
return { path: override, source: "env" };
|
|
279
|
+
const workspace = findWorkspace(cwd);
|
|
280
|
+
if (workspace)
|
|
281
|
+
return { path: workspace, source: "workspace" };
|
|
282
|
+
return { path: join2(homedir2(), ".polkadot"), source: "global" };
|
|
283
|
+
}
|
|
284
|
+
function describeConfigDir(resolved = resolveConfigDir()) {
|
|
285
|
+
switch (resolved.source) {
|
|
286
|
+
case "env":
|
|
287
|
+
return `DOT_HOME ${resolved.path}`;
|
|
288
|
+
case "workspace":
|
|
289
|
+
return `workspace ${resolved.path}`;
|
|
290
|
+
case "global":
|
|
291
|
+
return `global config ${resolved.path}`;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function getConfigDir() {
|
|
295
|
+
return resolveConfigDir().path;
|
|
241
296
|
}
|
|
242
297
|
function getChainsDir() {
|
|
243
|
-
return
|
|
298
|
+
return join2(getConfigDir(), "chains");
|
|
244
299
|
}
|
|
245
300
|
function getChainDir(chainName) {
|
|
246
|
-
return
|
|
301
|
+
return join2(getChainsDir(), chainName);
|
|
247
302
|
}
|
|
248
303
|
function getMetadataPath(chainName) {
|
|
249
|
-
return
|
|
304
|
+
return join2(getChainDir(chainName), "metadata.bin");
|
|
250
305
|
}
|
|
251
306
|
function getMetadataFingerprintPath(chainName) {
|
|
252
|
-
return
|
|
307
|
+
return join2(getChainDir(chainName), "metadata.fingerprint.json");
|
|
253
308
|
}
|
|
254
309
|
function getRpcMethodsPath(chainName) {
|
|
255
|
-
return
|
|
310
|
+
return join2(getChainDir(chainName), "rpc-methods.json");
|
|
256
311
|
}
|
|
257
312
|
function getConfigPath() {
|
|
258
|
-
return
|
|
313
|
+
return join2(getConfigDir(), "config.json");
|
|
259
314
|
}
|
|
260
315
|
async function ensureDir(dir) {
|
|
261
316
|
await mkdir(dir, { recursive: true });
|
|
@@ -360,20 +415,21 @@ function resolveChain(config, chainFlag) {
|
|
|
360
415
|
}
|
|
361
416
|
const name = findChainName(config, chainFlag);
|
|
362
417
|
if (!name) {
|
|
363
|
-
throw new CliError(`Unknown chain "${chainFlag}". Available chains: ${available}`);
|
|
418
|
+
throw new CliError(`Unknown chain "${chainFlag}" in ${describeConfigDir()}. Available chains: ${available}`);
|
|
364
419
|
}
|
|
365
420
|
return { name, chain: config.chains[name] };
|
|
366
421
|
}
|
|
367
422
|
var init_store = __esm(() => {
|
|
368
423
|
init_errors();
|
|
369
424
|
init_types();
|
|
425
|
+
init_workspace();
|
|
370
426
|
});
|
|
371
427
|
|
|
372
428
|
// src/config/accounts-store.ts
|
|
373
429
|
import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
374
|
-
import { join as
|
|
430
|
+
import { join as join3 } from "node:path";
|
|
375
431
|
function getAccountsPath() {
|
|
376
|
-
return
|
|
432
|
+
return join3(getConfigDir(), "accounts.json");
|
|
377
433
|
}
|
|
378
434
|
async function ensureDir2(dir) {
|
|
379
435
|
await mkdir2(dir, { recursive: true });
|
|
@@ -645,6 +701,16 @@ function tryDerivePublicKey(envVarName, path = "") {
|
|
|
645
701
|
return null;
|
|
646
702
|
}
|
|
647
703
|
}
|
|
704
|
+
function unknownAccountError(name, accountsFile) {
|
|
705
|
+
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
706
|
+
const suggestions = findClosest(name, available);
|
|
707
|
+
const hint = suggestions.length > 0 ? `
|
|
708
|
+
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
709
|
+
const list = available.map((a) => `
|
|
710
|
+
- ${a}`).join("");
|
|
711
|
+
return new Error(`Unknown account "${name}" in ${describeConfigDir()}.${hint}
|
|
712
|
+
Available accounts:${list}`);
|
|
713
|
+
}
|
|
648
714
|
async function resolveAccountKeypair(name) {
|
|
649
715
|
if (isDevAccount(name)) {
|
|
650
716
|
return getDevKeypair(name);
|
|
@@ -652,14 +718,7 @@ async function resolveAccountKeypair(name) {
|
|
|
652
718
|
const accountsFile = await loadAccounts();
|
|
653
719
|
const account = findAccount(accountsFile, name);
|
|
654
720
|
if (!account) {
|
|
655
|
-
|
|
656
|
-
const suggestions = findClosest(name, available);
|
|
657
|
-
const hint = suggestions.length > 0 ? `
|
|
658
|
-
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
659
|
-
const list = available.map((a) => `
|
|
660
|
-
- ${a}`).join("");
|
|
661
|
-
throw new Error(`Unknown account "${name}".${hint}
|
|
662
|
-
Available accounts:${list}`);
|
|
721
|
+
throw unknownAccountError(name, accountsFile);
|
|
663
722
|
}
|
|
664
723
|
if (account.secret === undefined) {
|
|
665
724
|
throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
|
|
@@ -678,14 +737,7 @@ async function resolveAccountExpandedSecret(name) {
|
|
|
678
737
|
const accountsFile = await loadAccounts();
|
|
679
738
|
const account = findAccount(accountsFile, name);
|
|
680
739
|
if (!account) {
|
|
681
|
-
|
|
682
|
-
const suggestions = findClosest(name, available);
|
|
683
|
-
const hint = suggestions.length > 0 ? `
|
|
684
|
-
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
685
|
-
const list = available.map((a) => `
|
|
686
|
-
- ${a}`).join("");
|
|
687
|
-
throw new Error(`Unknown account "${name}".${hint}
|
|
688
|
-
Available accounts:${list}`);
|
|
740
|
+
throw unknownAccountError(name, accountsFile);
|
|
689
741
|
}
|
|
690
742
|
if (account.secret === undefined) {
|
|
691
743
|
throw new Error(`Account "${name}" is watch-only (no secret). Cannot derive private key. Import with --secret or --env.`);
|
|
@@ -698,6 +750,7 @@ function bytesToHex(bytes) {
|
|
|
698
750
|
var DEV_NAMES, EXPANDED_SECRET_RE, HEX_SEED_RE, DERIVATION_RE;
|
|
699
751
|
var init_accounts = __esm(() => {
|
|
700
752
|
init_accounts_store();
|
|
753
|
+
init_store();
|
|
701
754
|
DEV_NAMES = ["alice", "bob", "charlie", "dave", "eve", "ferdie"];
|
|
702
755
|
EXPANDED_SECRET_RE = /^0x[0-9a-fA-F]{128}$/;
|
|
703
756
|
HEX_SEED_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
@@ -759,8 +812,8 @@ function isJsonOutput(opts) {
|
|
|
759
812
|
return opts.json === true || opts.output === "json";
|
|
760
813
|
}
|
|
761
814
|
function writeStdout(text) {
|
|
762
|
-
return new Promise((
|
|
763
|
-
process.stdout.write(text, () =>
|
|
815
|
+
return new Promise((resolve2) => {
|
|
816
|
+
process.stdout.write(text, () => resolve2());
|
|
764
817
|
});
|
|
765
818
|
}
|
|
766
819
|
function printJsonLine(data) {
|
|
@@ -864,24 +917,130 @@ var init_output = __esm(() => {
|
|
|
864
917
|
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
865
918
|
});
|
|
866
919
|
|
|
867
|
-
// src/
|
|
868
|
-
var
|
|
869
|
-
__export(
|
|
870
|
-
|
|
920
|
+
// src/features/verifiable/lib.ts
|
|
921
|
+
var exports_lib = {};
|
|
922
|
+
__export(exports_lib, {
|
|
923
|
+
verifyRingProof: () => verifyRingProof,
|
|
924
|
+
verifyBandersnatchSig: () => verifyBandersnatchSig,
|
|
925
|
+
ringRoot: () => ringRoot,
|
|
926
|
+
ringProve: () => ringProve,
|
|
927
|
+
resolveEntropyKey: () => resolveEntropyKey,
|
|
928
|
+
isRingExponent: () => isRingExponent,
|
|
929
|
+
encodeMembers: () => encodeMembers,
|
|
930
|
+
encodeContext: () => encodeContext,
|
|
931
|
+
deriveMemberKey: () => deriveMemberKey,
|
|
932
|
+
deriveMemberEntropy: () => deriveMemberEntropy,
|
|
933
|
+
deriveBandersnatchMember: () => deriveBandersnatchMember,
|
|
934
|
+
deriveAlias: () => deriveAlias,
|
|
935
|
+
compactEncode: () => compactEncode,
|
|
936
|
+
bandersnatchSign: () => bandersnatchSign,
|
|
937
|
+
DEFAULT_RING_EXPONENT: () => DEFAULT_RING_EXPONENT
|
|
871
938
|
});
|
|
872
939
|
import { blake2b } from "@noble/hashes/blake2.js";
|
|
940
|
+
import { hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
|
|
941
|
+
import { compact } from "@polkadot-api/substrate-bindings";
|
|
873
942
|
import { mnemonicToEntropy as mnemonicToEntropy2 } from "@polkadot-labs/hdkd-helpers";
|
|
874
|
-
import {
|
|
875
|
-
|
|
943
|
+
import {
|
|
944
|
+
alias_in_context,
|
|
945
|
+
member_from_entropy,
|
|
946
|
+
members_root,
|
|
947
|
+
one_shot,
|
|
948
|
+
sign as sign2,
|
|
949
|
+
validate,
|
|
950
|
+
validate_with_commitment,
|
|
951
|
+
verify_signature
|
|
952
|
+
} from "verifiablejs/nodejs";
|
|
953
|
+
function isRingExponent(n) {
|
|
954
|
+
return RING_EXPONENTS.includes(n);
|
|
955
|
+
}
|
|
956
|
+
function textOrHexBytes(value, label) {
|
|
957
|
+
if (value.startsWith("0x")) {
|
|
958
|
+
const hex = value.slice(2);
|
|
959
|
+
if (hex.length % 2 !== 0) {
|
|
960
|
+
throw new Error(`Invalid hex ${label}: odd number of characters`);
|
|
961
|
+
}
|
|
962
|
+
return hexToBytes3(hex);
|
|
963
|
+
}
|
|
964
|
+
return new TextEncoder().encode(value);
|
|
965
|
+
}
|
|
966
|
+
function resolveEntropyKey(value) {
|
|
967
|
+
if (value === undefined || value === "")
|
|
968
|
+
return;
|
|
969
|
+
return textOrHexBytes(value, "entropy-key");
|
|
970
|
+
}
|
|
971
|
+
function deriveMemberEntropy(mnemonic, entropyKey) {
|
|
876
972
|
const entropy = mnemonicToEntropy2(mnemonic);
|
|
877
973
|
const opts = { dkLen: 32 };
|
|
878
|
-
if (
|
|
879
|
-
opts.key =
|
|
974
|
+
if (entropyKey !== undefined && entropyKey.length > 0) {
|
|
975
|
+
opts.key = entropyKey;
|
|
976
|
+
}
|
|
977
|
+
return blake2b(entropy, opts);
|
|
978
|
+
}
|
|
979
|
+
function deriveMemberKey(entropy) {
|
|
980
|
+
return member_from_entropy(entropy);
|
|
981
|
+
}
|
|
982
|
+
function deriveBandersnatchMember(mnemonic, entropyKey) {
|
|
983
|
+
return deriveMemberKey(deriveMemberEntropy(mnemonic, resolveEntropyKey(entropyKey)));
|
|
984
|
+
}
|
|
985
|
+
function deriveAlias(entropy, context) {
|
|
986
|
+
return alias_in_context(entropy, context);
|
|
987
|
+
}
|
|
988
|
+
function bandersnatchSign(entropy, message) {
|
|
989
|
+
return sign2(entropy, message);
|
|
990
|
+
}
|
|
991
|
+
function verifyBandersnatchSig(signature, message, member) {
|
|
992
|
+
return verify_signature(signature, message, member);
|
|
993
|
+
}
|
|
994
|
+
function ringProve(ringExp, entropy, members, context, message) {
|
|
995
|
+
const result = one_shot(ringExp, entropy, members, context, message);
|
|
996
|
+
return { proof: result.proof, alias: result.alias };
|
|
997
|
+
}
|
|
998
|
+
function verifyRingProof(ringExp, proof, source, context, message) {
|
|
999
|
+
if (source.commitment !== undefined) {
|
|
1000
|
+
return validate_with_commitment(ringExp, proof, source.commitment, context, message);
|
|
880
1001
|
}
|
|
881
|
-
|
|
882
|
-
|
|
1002
|
+
if (source.members !== undefined) {
|
|
1003
|
+
return validate(ringExp, proof, source.members, context, message);
|
|
1004
|
+
}
|
|
1005
|
+
throw new Error("verifyRingProof requires either `members` or `commitment`");
|
|
1006
|
+
}
|
|
1007
|
+
function ringRoot(ringExp, members) {
|
|
1008
|
+
return members_root(ringExp, members);
|
|
1009
|
+
}
|
|
1010
|
+
function compactEncode(n) {
|
|
1011
|
+
if (!Number.isInteger(n) || n < 0)
|
|
1012
|
+
throw new Error("compactEncode: non-negative integer required");
|
|
1013
|
+
return compact.enc(n);
|
|
1014
|
+
}
|
|
1015
|
+
function encodeMembers(members) {
|
|
1016
|
+
for (const m of members) {
|
|
1017
|
+
if (m.length !== 32) {
|
|
1018
|
+
throw new Error(`member key must be 32 bytes (got ${m.length})`);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
const prefix = compactEncode(members.length);
|
|
1022
|
+
const out = new Uint8Array(prefix.length + members.length * 32);
|
|
1023
|
+
out.set(prefix, 0);
|
|
1024
|
+
let offset = prefix.length;
|
|
1025
|
+
for (const m of members) {
|
|
1026
|
+
out.set(m, offset);
|
|
1027
|
+
offset += 32;
|
|
1028
|
+
}
|
|
1029
|
+
return out;
|
|
1030
|
+
}
|
|
1031
|
+
function encodeContext(input) {
|
|
1032
|
+
const bytes = textOrHexBytes(input, "context");
|
|
1033
|
+
if (bytes.length > 32) {
|
|
1034
|
+
throw new Error(`Context must be at most 32 bytes (got ${bytes.length})`);
|
|
1035
|
+
}
|
|
1036
|
+
const out = new Uint8Array(32);
|
|
1037
|
+
out.set(bytes, 0);
|
|
1038
|
+
return out;
|
|
883
1039
|
}
|
|
884
|
-
var
|
|
1040
|
+
var RING_EXPONENTS, DEFAULT_RING_EXPONENT = 9;
|
|
1041
|
+
var init_lib = __esm(() => {
|
|
1042
|
+
RING_EXPONENTS = [9, 10, 14];
|
|
1043
|
+
});
|
|
885
1044
|
|
|
886
1045
|
// src/core/client.ts
|
|
887
1046
|
import { createClient } from "polkadot-api";
|
|
@@ -1007,9 +1166,9 @@ function compactEntry(entry, color) {
|
|
|
1007
1166
|
}
|
|
1008
1167
|
}
|
|
1009
1168
|
function expandEntry(entry, indent, width, color, prefix = 0) {
|
|
1010
|
-
const
|
|
1011
|
-
if (visualWidth(
|
|
1012
|
-
return
|
|
1169
|
+
const compact2 = compactEntry(entry, color);
|
|
1170
|
+
if (visualWidth(compact2) + indent + prefix <= width)
|
|
1171
|
+
return compact2;
|
|
1013
1172
|
switch (entry.type) {
|
|
1014
1173
|
case "struct":
|
|
1015
1174
|
return expandStruct(entry.value, indent, width, color);
|
|
@@ -1043,7 +1202,7 @@ ${closePadding}>`;
|
|
|
1043
1202
|
case "lookupEntry":
|
|
1044
1203
|
return expandEntry(entry.value, indent, width, color);
|
|
1045
1204
|
default:
|
|
1046
|
-
return
|
|
1205
|
+
return compact2;
|
|
1047
1206
|
}
|
|
1048
1207
|
}
|
|
1049
1208
|
function expandStruct(fields, indent, width, color) {
|
|
@@ -1102,9 +1261,9 @@ ${closePadding}${close}`;
|
|
|
1102
1261
|
}
|
|
1103
1262
|
function prettyType(entry, opts = {}) {
|
|
1104
1263
|
const { indent, prefix, width, color } = resolveOpts(opts);
|
|
1105
|
-
const
|
|
1106
|
-
if (visualWidth(
|
|
1107
|
-
return
|
|
1264
|
+
const compact2 = compactEntry(entry, color);
|
|
1265
|
+
if (visualWidth(compact2) + indent + prefix <= width)
|
|
1266
|
+
return compact2;
|
|
1108
1267
|
return expandEntry(entry, indent, width, color);
|
|
1109
1268
|
}
|
|
1110
1269
|
function prettyTypeById(lookup, typeId, opts = {}) {
|
|
@@ -1208,15 +1367,15 @@ function renderArgsFromFields(fields, opts) {
|
|
|
1208
1367
|
case "void":
|
|
1209
1368
|
return "()";
|
|
1210
1369
|
case "named": {
|
|
1211
|
-
const
|
|
1212
|
-
if (visualWidth(
|
|
1213
|
-
return
|
|
1370
|
+
const compact2 = `(${fields.fields.map(([k, v]) => `${paint(color, CYAN2, k)}: ${compactEntry(v, color)}`).join(", ")})`;
|
|
1371
|
+
if (visualWidth(compact2) + lead <= width)
|
|
1372
|
+
return compact2;
|
|
1214
1373
|
return renderFieldList(fields.fields, "(", ")", indent, width, color);
|
|
1215
1374
|
}
|
|
1216
1375
|
case "positional": {
|
|
1217
|
-
const
|
|
1218
|
-
if (visualWidth(
|
|
1219
|
-
return
|
|
1376
|
+
const compact2 = `(${fields.types.map((t) => compactEntry(t, color)).join(", ")})`;
|
|
1377
|
+
if (visualWidth(compact2) + lead <= width)
|
|
1378
|
+
return compact2;
|
|
1220
1379
|
const innerIndent = indent + 2;
|
|
1221
1380
|
const padding = " ".repeat(innerIndent);
|
|
1222
1381
|
const closePadding = " ".repeat(indent);
|
|
@@ -1227,9 +1386,9 @@ ${lines.join(`,
|
|
|
1227
1386
|
${closePadding})`;
|
|
1228
1387
|
}
|
|
1229
1388
|
case "single": {
|
|
1230
|
-
const
|
|
1231
|
-
if (visualWidth(
|
|
1232
|
-
return
|
|
1389
|
+
const compact2 = `(${compactEntry(fields.type, color)})`;
|
|
1390
|
+
if (visualWidth(compact2) + lead <= width)
|
|
1391
|
+
return compact2;
|
|
1233
1392
|
const inner = expandEntry(fields.type, indent + 2, width, color);
|
|
1234
1393
|
return `(
|
|
1235
1394
|
${" ".repeat(indent + 2)}${inner},
|
|
@@ -1300,7 +1459,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
|
|
|
1300
1459
|
let bytes;
|
|
1301
1460
|
try {
|
|
1302
1461
|
const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
|
|
1303
|
-
const raw =
|
|
1462
|
+
const raw = hexToBytes4(hex);
|
|
1304
1463
|
const decoded = optionalOpaqueBytes.dec(raw);
|
|
1305
1464
|
if (decoded !== undefined) {
|
|
1306
1465
|
bytes = new Uint8Array(decoded);
|
|
@@ -1309,7 +1468,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
|
|
|
1309
1468
|
if (!bytes) {
|
|
1310
1469
|
try {
|
|
1311
1470
|
const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
|
|
1312
|
-
bytes =
|
|
1471
|
+
bytes = hexToBytes4(hex);
|
|
1313
1472
|
} catch (err) {
|
|
1314
1473
|
if (err instanceof ConnectionError)
|
|
1315
1474
|
throw err;
|
|
@@ -1505,7 +1664,7 @@ function describeCallArgs(meta, palletName, callName) {
|
|
|
1505
1664
|
function describeEventFields(meta, palletName, eventName) {
|
|
1506
1665
|
return compactArgsString(getEventFields(meta, palletName, eventName));
|
|
1507
1666
|
}
|
|
1508
|
-
function
|
|
1667
|
+
function hexToBytes4(hex) {
|
|
1509
1668
|
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1510
1669
|
const bytes = new Uint8Array(clean.length / 2);
|
|
1511
1670
|
for (let i = 0;i < clean.length; i += 2) {
|
|
@@ -3234,7 +3393,7 @@ var init_xxh64 = __esm(() => {
|
|
|
3234
3393
|
import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
|
|
3235
3394
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
3236
3395
|
import { keccak_256 as keccak_2562 } from "@noble/hashes/sha3.js";
|
|
3237
|
-
import { bytesToHex as bytesToHex3, hexToBytes as
|
|
3396
|
+
import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils.js";
|
|
3238
3397
|
function computeHash(algorithm, data) {
|
|
3239
3398
|
const algo = ALGORITHMS[algorithm];
|
|
3240
3399
|
if (!algo) {
|
|
@@ -3248,7 +3407,7 @@ function parseInputData(input) {
|
|
|
3248
3407
|
if (hex.length % 2 !== 0) {
|
|
3249
3408
|
throw new Error(`Invalid hex input: odd number of characters`);
|
|
3250
3409
|
}
|
|
3251
|
-
return
|
|
3410
|
+
return hexToBytes5(hex);
|
|
3252
3411
|
}
|
|
3253
3412
|
return new TextEncoder().encode(input);
|
|
3254
3413
|
}
|
|
@@ -3303,6 +3462,349 @@ var init_hash = __esm(() => {
|
|
|
3303
3462
|
};
|
|
3304
3463
|
});
|
|
3305
3464
|
|
|
3465
|
+
// src/core/input.ts
|
|
3466
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
3467
|
+
async function resolveDataInput(inline, opts, messages) {
|
|
3468
|
+
const sources = [inline !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
3469
|
+
if (sources > 1) {
|
|
3470
|
+
throw new CliError(messages?.conflict ?? "Provide only one of: inline data, --file, or --stdin");
|
|
3471
|
+
}
|
|
3472
|
+
if (sources === 0) {
|
|
3473
|
+
throw new CliError(messages?.missing ?? "No input provided. Pass data as argument, or use --file or --stdin");
|
|
3474
|
+
}
|
|
3475
|
+
if (opts.file) {
|
|
3476
|
+
const buf = await readFile5(opts.file);
|
|
3477
|
+
return new Uint8Array(buf);
|
|
3478
|
+
}
|
|
3479
|
+
if (opts.stdin) {
|
|
3480
|
+
const chunks = [];
|
|
3481
|
+
for await (const chunk of process.stdin) {
|
|
3482
|
+
chunks.push(chunk);
|
|
3483
|
+
}
|
|
3484
|
+
return new Uint8Array(Buffer.concat(chunks));
|
|
3485
|
+
}
|
|
3486
|
+
return parseInputData(inline);
|
|
3487
|
+
}
|
|
3488
|
+
var init_input = __esm(() => {
|
|
3489
|
+
init_errors();
|
|
3490
|
+
init_hash();
|
|
3491
|
+
});
|
|
3492
|
+
|
|
3493
|
+
// src/platform/cli.ts
|
|
3494
|
+
function readRawOptionValue(name, argv = process.argv) {
|
|
3495
|
+
const flag = `--${name}`;
|
|
3496
|
+
const prefix = `${flag}=`;
|
|
3497
|
+
let value;
|
|
3498
|
+
for (let i = 0;i < argv.length; i++) {
|
|
3499
|
+
const arg = argv[i];
|
|
3500
|
+
if (arg === "--")
|
|
3501
|
+
break;
|
|
3502
|
+
if (arg === flag && i + 1 < argv.length)
|
|
3503
|
+
value = argv[i + 1];
|
|
3504
|
+
else if (arg.startsWith(prefix))
|
|
3505
|
+
value = arg.slice(prefix.length);
|
|
3506
|
+
}
|
|
3507
|
+
return value;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
// src/platform/index.ts
|
|
3511
|
+
var init_platform = __esm(() => {
|
|
3512
|
+
init_accounts_store();
|
|
3513
|
+
init_accounts();
|
|
3514
|
+
init_hash();
|
|
3515
|
+
init_input();
|
|
3516
|
+
init_output();
|
|
3517
|
+
init_errors();
|
|
3518
|
+
});
|
|
3519
|
+
|
|
3520
|
+
// src/features/verifiable/commands.ts
|
|
3521
|
+
var exports_commands = {};
|
|
3522
|
+
__export(exports_commands, {
|
|
3523
|
+
runVerifiable: () => runVerifiable
|
|
3524
|
+
});
|
|
3525
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
3526
|
+
import { DEV_PHRASE as DEV_PHRASE2 } from "@polkadot-labs/hdkd-helpers";
|
|
3527
|
+
async function runVerifiable(action, rest, opts) {
|
|
3528
|
+
switch (action) {
|
|
3529
|
+
case "member":
|
|
3530
|
+
return deriveMember(rest[0], opts);
|
|
3531
|
+
case "alias":
|
|
3532
|
+
return deriveAliasCmd(rest[0], opts);
|
|
3533
|
+
case "sign":
|
|
3534
|
+
return signCmd(rest[0], opts);
|
|
3535
|
+
case "prove":
|
|
3536
|
+
return proveCmd(rest[0], opts);
|
|
3537
|
+
case "verify":
|
|
3538
|
+
return verifyCmd(opts);
|
|
3539
|
+
case "verify-sig":
|
|
3540
|
+
return verifySigCmd(opts);
|
|
3541
|
+
case "members":
|
|
3542
|
+
return membersCmd(rest, opts);
|
|
3543
|
+
default:
|
|
3544
|
+
return deriveMember(action, opts);
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
function mnemonicFromStored(stored, account, accountsFile) {
|
|
3548
|
+
if (!stored) {
|
|
3549
|
+
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
3550
|
+
const suggestions = findClosest(account, available);
|
|
3551
|
+
const hint = suggestions.length > 0 ? `
|
|
3552
|
+
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
3553
|
+
const list = available.map((a) => `
|
|
3554
|
+
- ${a}`).join("");
|
|
3555
|
+
throw new Error(`Unknown account "${account}".${hint}
|
|
3556
|
+
Available accounts:${list}`);
|
|
3557
|
+
}
|
|
3558
|
+
if (isWatchOnly(stored)) {
|
|
3559
|
+
throw new Error(`Account "${account}" is watch-only (no secret). Cannot derive Bandersnatch key.`);
|
|
3560
|
+
}
|
|
3561
|
+
const secret = resolveSecret(stored.secret);
|
|
3562
|
+
if (isHexPublicKey(`0x${secret.replace(/^0x/, "")}`)) {
|
|
3563
|
+
throw new Error(`Account "${account}" uses a hex seed. Bandersnatch derivation requires a BIP39 mnemonic.`);
|
|
3564
|
+
}
|
|
3565
|
+
return secret;
|
|
3566
|
+
}
|
|
3567
|
+
async function resolveMnemonic(account) {
|
|
3568
|
+
if (isDevAccount(account)) {
|
|
3569
|
+
return DEV_PHRASE2;
|
|
3570
|
+
}
|
|
3571
|
+
const accountsFile = await loadAccounts();
|
|
3572
|
+
return mnemonicFromStored(findAccount(accountsFile, account), account, accountsFile);
|
|
3573
|
+
}
|
|
3574
|
+
async function resolveEntropy(account, entropyKey) {
|
|
3575
|
+
const mnemonic = await resolveMnemonic(account);
|
|
3576
|
+
return deriveMemberEntropy(mnemonic, resolveEntropyKey(entropyKey));
|
|
3577
|
+
}
|
|
3578
|
+
function requireAccount(account, action) {
|
|
3579
|
+
if (!account) {
|
|
3580
|
+
throw new CliError(`dot verifiable ${action} requires an account. See "dot verifiable".`);
|
|
3581
|
+
}
|
|
3582
|
+
return account;
|
|
3583
|
+
}
|
|
3584
|
+
function resolveMessage(opts) {
|
|
3585
|
+
return resolveDataInput(opts.message, opts, {
|
|
3586
|
+
conflict: "Provide only one of: --message, --file, or --stdin",
|
|
3587
|
+
missing: "No message provided. Use --message, --file, or --stdin"
|
|
3588
|
+
});
|
|
3589
|
+
}
|
|
3590
|
+
async function resolveBytesArg(value, name, allowFile = false) {
|
|
3591
|
+
if (value.startsWith("0x")) {
|
|
3592
|
+
return parseInputData(value);
|
|
3593
|
+
}
|
|
3594
|
+
if (allowFile) {
|
|
3595
|
+
const buf = await readFile8(value);
|
|
3596
|
+
const text = buf.toString("utf8").trim();
|
|
3597
|
+
return text.startsWith("0x") ? parseInputData(text) : new Uint8Array(buf);
|
|
3598
|
+
}
|
|
3599
|
+
throw new CliError(`${name} must be 0x-prefixed hex`);
|
|
3600
|
+
}
|
|
3601
|
+
function resolveRingExponent(opts) {
|
|
3602
|
+
if (opts.ringExponent === undefined)
|
|
3603
|
+
return DEFAULT_RING_EXPONENT;
|
|
3604
|
+
const n = Number(opts.ringExponent);
|
|
3605
|
+
if (!isRingExponent(n)) {
|
|
3606
|
+
throw new CliError(`Invalid --ring-exponent "${opts.ringExponent}". Supported: 9, 10, 14.`);
|
|
3607
|
+
}
|
|
3608
|
+
return n;
|
|
3609
|
+
}
|
|
3610
|
+
function requireOption(value, flag, action) {
|
|
3611
|
+
if (value === undefined) {
|
|
3612
|
+
throw new CliError(`dot verifiable ${action} requires ${flag}.`);
|
|
3613
|
+
}
|
|
3614
|
+
return value;
|
|
3615
|
+
}
|
|
3616
|
+
async function deriveMember(accountArg, opts) {
|
|
3617
|
+
const account = requireAccount(accountArg, "member");
|
|
3618
|
+
const usedDeprecatedContext = opts.entropyKey === undefined && opts.context !== undefined;
|
|
3619
|
+
if (usedDeprecatedContext) {
|
|
3620
|
+
if (opts.context.startsWith("0x")) {
|
|
3621
|
+
throw new CliError(`"--context" on "dot verifiable" now means the 32-byte ring context, and hex ` + `entropy keys changed meaning in this release. Pass "--entropy-key ${opts.context}" explicitly.`);
|
|
3622
|
+
}
|
|
3623
|
+
process.stderr.write(`Warning: "--context" on "dot verifiable" now refers to the 32-byte ring context. ` + `For member-key derivation use "--entropy-key". Treating "--context ${opts.context}" ` + `as the entropy key for now.
|
|
3624
|
+
`);
|
|
3625
|
+
}
|
|
3626
|
+
const entropyKeyStr = opts.entropyKey ?? opts.context;
|
|
3627
|
+
let mnemonic;
|
|
3628
|
+
let accountsFile;
|
|
3629
|
+
let stored;
|
|
3630
|
+
if (isDevAccount(account)) {
|
|
3631
|
+
mnemonic = DEV_PHRASE2;
|
|
3632
|
+
} else {
|
|
3633
|
+
accountsFile = await loadAccounts();
|
|
3634
|
+
stored = findAccount(accountsFile, account);
|
|
3635
|
+
mnemonic = mnemonicFromStored(stored, account, accountsFile);
|
|
3636
|
+
}
|
|
3637
|
+
const memberKeyHex = publicKeyToHex(deriveBandersnatchMember(mnemonic, entropyKeyStr));
|
|
3638
|
+
if (stored && accountsFile) {
|
|
3639
|
+
if (!stored.bandersnatch)
|
|
3640
|
+
stored.bandersnatch = {};
|
|
3641
|
+
const entryKey = entropyKeyStr ?? "";
|
|
3642
|
+
if (stored.bandersnatch[entryKey] !== memberKeyHex) {
|
|
3643
|
+
stored.bandersnatch[entryKey] = memberKeyHex;
|
|
3644
|
+
await saveAccounts(accountsFile);
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
const fieldKey = usedDeprecatedContext ? "context" : "entropyKey";
|
|
3648
|
+
if (isJsonOutput(opts)) {
|
|
3649
|
+
const result = { account, memberKey: memberKeyHex };
|
|
3650
|
+
if (entropyKeyStr)
|
|
3651
|
+
result[fieldKey] = entropyKeyStr;
|
|
3652
|
+
console.log(formatJson(result));
|
|
3653
|
+
} else {
|
|
3654
|
+
printHeading("Bandersnatch Member Key");
|
|
3655
|
+
console.log(` ${BOLD}Account:${RESET} ${account}`);
|
|
3656
|
+
if (entropyKeyStr) {
|
|
3657
|
+
const line = usedDeprecatedContext ? ` ${BOLD}Context:${RESET} ${entropyKeyStr}` : ` ${BOLD}Entropy Key:${RESET} ${entropyKeyStr}`;
|
|
3658
|
+
console.log(line);
|
|
3659
|
+
}
|
|
3660
|
+
console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
|
|
3661
|
+
console.log();
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
async function deriveAliasCmd(accountArg, opts) {
|
|
3665
|
+
const account = requireAccount(accountArg, "alias");
|
|
3666
|
+
const contextStr = requireOption(opts.context, "--context", "alias");
|
|
3667
|
+
const entropy = await resolveEntropy(account, opts.entropyKey);
|
|
3668
|
+
const context = encodeContext(contextStr);
|
|
3669
|
+
const aliasHex = toHex2(deriveAlias(entropy, context));
|
|
3670
|
+
if (isJsonOutput(opts)) {
|
|
3671
|
+
console.log(formatJson({ account, context: contextStr, alias: aliasHex }));
|
|
3672
|
+
} else {
|
|
3673
|
+
printHeading("Verifiable Alias");
|
|
3674
|
+
console.log(` ${BOLD}Account:${RESET} ${account}`);
|
|
3675
|
+
console.log(` ${BOLD}Context:${RESET} ${contextStr}`);
|
|
3676
|
+
console.log(` ${BOLD}Alias:${RESET} ${aliasHex}`);
|
|
3677
|
+
console.log();
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
async function signCmd(accountArg, opts) {
|
|
3681
|
+
const account = requireAccount(accountArg, "sign");
|
|
3682
|
+
const message = await resolveMessage(opts);
|
|
3683
|
+
const entropy = await resolveEntropy(account, opts.entropyKey);
|
|
3684
|
+
const signature = bandersnatchSign(entropy, message);
|
|
3685
|
+
const member = deriveMemberKey(entropy);
|
|
3686
|
+
const sigHex = toHex2(signature);
|
|
3687
|
+
const result = {
|
|
3688
|
+
type: "Bandersnatch",
|
|
3689
|
+
account,
|
|
3690
|
+
message: toHex2(message),
|
|
3691
|
+
signature: sigHex,
|
|
3692
|
+
member: toHex2(member),
|
|
3693
|
+
enum: `Bandersnatch(${sigHex})`
|
|
3694
|
+
};
|
|
3695
|
+
if (isJsonOutput(opts)) {
|
|
3696
|
+
console.log(formatJson(result));
|
|
3697
|
+
} else {
|
|
3698
|
+
console.log(` ${BOLD}Type:${RESET} ${result.type}`);
|
|
3699
|
+
console.log(` ${BOLD}Message:${RESET} ${result.message}`);
|
|
3700
|
+
console.log(` ${BOLD}Signature:${RESET} ${result.signature}`);
|
|
3701
|
+
console.log(` ${BOLD}Member:${RESET} ${result.member}`);
|
|
3702
|
+
console.log(` ${BOLD}Enum:${RESET} ${result.enum}`);
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
async function proveCmd(accountArg, opts) {
|
|
3706
|
+
const account = requireAccount(accountArg, "prove");
|
|
3707
|
+
const contextStr = requireOption(opts.context, "--context", "prove");
|
|
3708
|
+
const membersArg = requireOption(opts.members, "--members", "prove");
|
|
3709
|
+
const message = await resolveMessage(opts);
|
|
3710
|
+
const ringExponent = resolveRingExponent(opts);
|
|
3711
|
+
const entropy = await resolveEntropy(account, opts.entropyKey);
|
|
3712
|
+
const context = encodeContext(contextStr);
|
|
3713
|
+
const members = await resolveBytesArg(membersArg, "--members", true);
|
|
3714
|
+
const { proof, alias } = ringProve(ringExponent, entropy, members, context, message);
|
|
3715
|
+
const result = {
|
|
3716
|
+
account,
|
|
3717
|
+
context: contextStr,
|
|
3718
|
+
ringExponent,
|
|
3719
|
+
alias: toHex2(alias),
|
|
3720
|
+
proof: toHex2(proof)
|
|
3721
|
+
};
|
|
3722
|
+
if (isJsonOutput(opts)) {
|
|
3723
|
+
console.log(formatJson(result));
|
|
3724
|
+
} else {
|
|
3725
|
+
printHeading("Ring-VRF Proof");
|
|
3726
|
+
console.log(` ${BOLD}Account:${RESET} ${account}`);
|
|
3727
|
+
console.log(` ${BOLD}Context:${RESET} ${contextStr}`);
|
|
3728
|
+
console.log(` ${BOLD}Ring exponent:${RESET} ${ringExponent}`);
|
|
3729
|
+
console.log(` ${BOLD}Alias:${RESET} ${result.alias}`);
|
|
3730
|
+
console.log(` ${BOLD}Proof:${RESET} ${result.proof}`);
|
|
3731
|
+
console.log();
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
async function verifyCmd(opts) {
|
|
3735
|
+
const proofArg = requireOption(opts.proof, "--proof", "verify");
|
|
3736
|
+
const contextStr = requireOption(opts.context, "--context", "verify");
|
|
3737
|
+
if (!opts.members && !opts.root) {
|
|
3738
|
+
throw new CliError("dot verifiable verify requires --members or --root.");
|
|
3739
|
+
}
|
|
3740
|
+
if (opts.members && opts.root) {
|
|
3741
|
+
throw new CliError("Provide either --members or --root, not both.");
|
|
3742
|
+
}
|
|
3743
|
+
const message = await resolveMessage(opts);
|
|
3744
|
+
const ringExponent = resolveRingExponent(opts);
|
|
3745
|
+
const proof = await resolveBytesArg(proofArg, "--proof", true);
|
|
3746
|
+
const context = encodeContext(contextStr);
|
|
3747
|
+
const source = opts.root ? { commitment: await resolveBytesArg(opts.root, "--root", true) } : { members: await resolveBytesArg(opts.members, "--members", true) };
|
|
3748
|
+
let aliasHex;
|
|
3749
|
+
try {
|
|
3750
|
+
aliasHex = toHex2(verifyRingProof(ringExponent, proof, source, context, message));
|
|
3751
|
+
} catch (err) {
|
|
3752
|
+
const exponentHint = opts.ringExponent === undefined ? ` (assumed ring exponent ${ringExponent} — pass --ring-exponent if the ring uses 10 or 14)` : "";
|
|
3753
|
+
throw new CliError(`Ring-VRF proof is invalid: ${err instanceof Error ? err.message : String(err)}${exponentHint}`);
|
|
3754
|
+
}
|
|
3755
|
+
if (isJsonOutput(opts)) {
|
|
3756
|
+
console.log(formatJson({ valid: true, alias: aliasHex, ringExponent }));
|
|
3757
|
+
} else {
|
|
3758
|
+
printHeading("Ring-VRF Verification");
|
|
3759
|
+
console.log(` ${BOLD}Valid:${RESET} yes`);
|
|
3760
|
+
console.log(` ${BOLD}Alias:${RESET} ${aliasHex}`);
|
|
3761
|
+
console.log();
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
async function verifySigCmd(opts) {
|
|
3765
|
+
const sigArg = requireOption(opts.signature, "--signature", "verify-sig");
|
|
3766
|
+
const memberArg = requireOption(opts.member, "--member", "verify-sig");
|
|
3767
|
+
const message = await resolveMessage(opts);
|
|
3768
|
+
const signature = await resolveBytesArg(sigArg, "--signature", true);
|
|
3769
|
+
const member = await resolveBytesArg(memberArg, "--member");
|
|
3770
|
+
const valid = verifyBandersnatchSig(signature, message, member);
|
|
3771
|
+
if (!valid) {
|
|
3772
|
+
throw new CliError("Bandersnatch signature is invalid.");
|
|
3773
|
+
}
|
|
3774
|
+
if (isJsonOutput(opts)) {
|
|
3775
|
+
console.log(formatJson({ valid: true }));
|
|
3776
|
+
} else {
|
|
3777
|
+
printHeading("Bandersnatch Signature Verification");
|
|
3778
|
+
console.log(` ${BOLD}Valid:${RESET} yes`);
|
|
3779
|
+
console.log();
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
async function membersCmd(rest, opts) {
|
|
3783
|
+
if (rest.length === 0) {
|
|
3784
|
+
throw new CliError("dot verifiable members requires one or more 0x-hex member keys.");
|
|
3785
|
+
}
|
|
3786
|
+
const memberBytes = rest.map((k) => {
|
|
3787
|
+
const bytes = parseInputData(k);
|
|
3788
|
+
if (bytes.length !== 32) {
|
|
3789
|
+
throw new CliError(`member key must be 32 bytes (got ${bytes.length}): ${k}`);
|
|
3790
|
+
}
|
|
3791
|
+
return bytes;
|
|
3792
|
+
});
|
|
3793
|
+
const encoded = toHex2(encodeMembers(memberBytes));
|
|
3794
|
+
if (isJsonOutput(opts)) {
|
|
3795
|
+
console.log(formatJson({ count: rest.length, members: encoded }));
|
|
3796
|
+
} else {
|
|
3797
|
+
printHeading("Encoded Members");
|
|
3798
|
+
console.log(` ${BOLD}Count:${RESET} ${rest.length}`);
|
|
3799
|
+
console.log(` ${BOLD}Members:${RESET} ${encoded}`);
|
|
3800
|
+
console.log();
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
var init_commands = __esm(() => {
|
|
3804
|
+
init_platform();
|
|
3805
|
+
init_lib();
|
|
3806
|
+
});
|
|
3807
|
+
|
|
3306
3808
|
// src/completions/complete.ts
|
|
3307
3809
|
var exports_complete = {};
|
|
3308
3810
|
__export(exports_complete, {
|
|
@@ -3664,7 +4166,16 @@ var init_complete = __esm(() => {
|
|
|
3664
4166
|
ext: "extensions",
|
|
3665
4167
|
rpc: "rpc"
|
|
3666
4168
|
};
|
|
3667
|
-
NAMED_COMMANDS = [
|
|
4169
|
+
NAMED_COMMANDS = [
|
|
4170
|
+
"chain",
|
|
4171
|
+
"account",
|
|
4172
|
+
"inspect",
|
|
4173
|
+
"hash",
|
|
4174
|
+
"sign",
|
|
4175
|
+
"completions",
|
|
4176
|
+
"init",
|
|
4177
|
+
"which"
|
|
4178
|
+
];
|
|
3668
4179
|
CHAIN_SUBCOMMANDS = ["add", "info", "list", "remove", "update"];
|
|
3669
4180
|
ACCOUNT_SUBCOMMANDS = [
|
|
3670
4181
|
"add",
|
|
@@ -3696,7 +4207,7 @@ var init_complete = __esm(() => {
|
|
|
3696
4207
|
// src/cli.ts
|
|
3697
4208
|
import cac from "cac";
|
|
3698
4209
|
// package.json
|
|
3699
|
-
var version = "1.
|
|
4210
|
+
var version = "1.21.0";
|
|
3700
4211
|
|
|
3701
4212
|
// src/commands/account.ts
|
|
3702
4213
|
init_accounts_store();
|
|
@@ -3943,7 +4454,7 @@ async function accountCreate(name, opts) {
|
|
|
3943
4454
|
const { mnemonic, publicKey } = createNewAccount(path);
|
|
3944
4455
|
const hexPub = publicKeyToHex(publicKey);
|
|
3945
4456
|
const address = toSs58(publicKey);
|
|
3946
|
-
const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (
|
|
4457
|
+
const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_lib(), exports_lib));
|
|
3947
4458
|
const bandersnatch = {};
|
|
3948
4459
|
bandersnatch[""] = publicKeyToHex(deriveBandersnatchMember2(mnemonic));
|
|
3949
4460
|
bandersnatch.candidate = publicKeyToHex(deriveBandersnatchMember2(mnemonic, "candidate"));
|
|
@@ -5600,7 +6111,7 @@ function registerCompletionsCommand(cli) {
|
|
|
5600
6111
|
process.exit(1);
|
|
5601
6112
|
}
|
|
5602
6113
|
const instructions = SETUP_INSTRUCTIONS[shell];
|
|
5603
|
-
if (instructions) {
|
|
6114
|
+
if (instructions && process.stdout.isTTY) {
|
|
5604
6115
|
process.stderr.write(`${instructions}
|
|
5605
6116
|
`);
|
|
5606
6117
|
}
|
|
@@ -6381,30 +6892,9 @@ async function handleExtensions(target, opts) {
|
|
|
6381
6892
|
|
|
6382
6893
|
// src/commands/hash.ts
|
|
6383
6894
|
init_hash();
|
|
6895
|
+
init_input();
|
|
6384
6896
|
init_output();
|
|
6385
6897
|
init_errors();
|
|
6386
|
-
import { readFile as readFile5 } from "node:fs/promises";
|
|
6387
|
-
async function resolveInput(data, opts) {
|
|
6388
|
-
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
6389
|
-
if (sources > 1) {
|
|
6390
|
-
throw new CliError("Provide only one of: inline data, --file, or --stdin");
|
|
6391
|
-
}
|
|
6392
|
-
if (sources === 0) {
|
|
6393
|
-
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
6394
|
-
}
|
|
6395
|
-
if (opts.file) {
|
|
6396
|
-
const buf = await readFile5(opts.file);
|
|
6397
|
-
return new Uint8Array(buf);
|
|
6398
|
-
}
|
|
6399
|
-
if (opts.stdin) {
|
|
6400
|
-
const chunks = [];
|
|
6401
|
-
for await (const chunk of process.stdin) {
|
|
6402
|
-
chunks.push(chunk);
|
|
6403
|
-
}
|
|
6404
|
-
return new Uint8Array(Buffer.concat(chunks));
|
|
6405
|
-
}
|
|
6406
|
-
return parseInputData(data);
|
|
6407
|
-
}
|
|
6408
6898
|
function printAlgorithmHelp() {
|
|
6409
6899
|
console.log(`${BOLD}Usage:${RESET} dot hash <algorithm> <data> [options]
|
|
6410
6900
|
`);
|
|
@@ -6433,7 +6923,7 @@ function registerHashCommand(cli) {
|
|
|
6433
6923
|
if (!isValidAlgorithm(algorithm)) {
|
|
6434
6924
|
throw new CliError(suggestMessage("algorithm", algorithm, getAlgorithmNames()));
|
|
6435
6925
|
}
|
|
6436
|
-
const input = await
|
|
6926
|
+
const input = await resolveDataInput(data, opts);
|
|
6437
6927
|
const hash = computeHash(algorithm, input);
|
|
6438
6928
|
const hexHash = toHex2(hash);
|
|
6439
6929
|
if (isJsonOutput(opts)) {
|
|
@@ -7339,9 +7829,9 @@ async function handleRpc(method, args, opts) {
|
|
|
7339
7829
|
// src/commands/sign.ts
|
|
7340
7830
|
init_accounts();
|
|
7341
7831
|
init_hash();
|
|
7832
|
+
init_input();
|
|
7342
7833
|
init_output();
|
|
7343
7834
|
init_errors();
|
|
7344
|
-
import { readFile as readFile6 } from "node:fs/promises";
|
|
7345
7835
|
var SUPPORTED_TYPES = ["sr25519"];
|
|
7346
7836
|
function isSupportedType(type) {
|
|
7347
7837
|
return SUPPORTED_TYPES.includes(type.toLowerCase());
|
|
@@ -7352,27 +7842,6 @@ function variantName(type) {
|
|
|
7352
7842
|
return "Sr25519";
|
|
7353
7843
|
}
|
|
7354
7844
|
}
|
|
7355
|
-
async function resolveInput2(data, opts) {
|
|
7356
|
-
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
7357
|
-
if (sources > 1) {
|
|
7358
|
-
throw new CliError("Provide only one of: inline data, --file, or --stdin");
|
|
7359
|
-
}
|
|
7360
|
-
if (sources === 0) {
|
|
7361
|
-
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
7362
|
-
}
|
|
7363
|
-
if (opts.file) {
|
|
7364
|
-
const buf = await readFile6(opts.file);
|
|
7365
|
-
return new Uint8Array(buf);
|
|
7366
|
-
}
|
|
7367
|
-
if (opts.stdin) {
|
|
7368
|
-
const chunks = [];
|
|
7369
|
-
for await (const chunk of process.stdin) {
|
|
7370
|
-
chunks.push(chunk);
|
|
7371
|
-
}
|
|
7372
|
-
return new Uint8Array(Buffer.concat(chunks));
|
|
7373
|
-
}
|
|
7374
|
-
return parseInputData(data);
|
|
7375
|
-
}
|
|
7376
7845
|
function printSignHelp() {
|
|
7377
7846
|
console.log(`${BOLD}Usage:${RESET} dot sign <message> --from <account> [options]
|
|
7378
7847
|
`);
|
|
@@ -7406,7 +7875,7 @@ function registerSignCommand(cli) {
|
|
|
7406
7875
|
if (!isSupportedType(type)) {
|
|
7407
7876
|
throw new CliError(`Unsupported signature type "${opts.type}". Supported: ${SUPPORTED_TYPES.join(", ")}`);
|
|
7408
7877
|
}
|
|
7409
|
-
const input = await
|
|
7878
|
+
const input = await resolveDataInput(message, opts);
|
|
7410
7879
|
const keypair = await resolveAccountKeypair(opts.from);
|
|
7411
7880
|
const signature = keypair.sign(input);
|
|
7412
7881
|
const hexMessage = toHex2(input);
|
|
@@ -8778,7 +9247,7 @@ function buildGeneralTx(meta, callData, userExtOverrides) {
|
|
|
8778
9247
|
}
|
|
8779
9248
|
function watchTransaction(observable, level, options) {
|
|
8780
9249
|
const spinner = new Spinner;
|
|
8781
|
-
return new Promise((
|
|
9250
|
+
return new Promise((resolve2, reject) => {
|
|
8782
9251
|
let settled = false;
|
|
8783
9252
|
spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
|
|
8784
9253
|
const subscription = observable.subscribe({
|
|
@@ -8798,7 +9267,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8798
9267
|
spinner.succeed("Broadcasted");
|
|
8799
9268
|
settled = true;
|
|
8800
9269
|
subscription.unsubscribe();
|
|
8801
|
-
|
|
9270
|
+
resolve2(event);
|
|
8802
9271
|
} else {
|
|
8803
9272
|
spinner.succeed("Broadcasted");
|
|
8804
9273
|
spinner.start("In best block...");
|
|
@@ -8810,7 +9279,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8810
9279
|
spinner.succeed(`In best block #${event.block.number}`);
|
|
8811
9280
|
settled = true;
|
|
8812
9281
|
subscription.unsubscribe();
|
|
8813
|
-
|
|
9282
|
+
resolve2(event);
|
|
8814
9283
|
} else {
|
|
8815
9284
|
spinner.succeed(`In best block #${event.block.number}`);
|
|
8816
9285
|
spinner.start("Finalizing...");
|
|
@@ -8822,7 +9291,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8822
9291
|
case "finalized":
|
|
8823
9292
|
spinner.succeed(`Finalized in block #${event.block.number}`);
|
|
8824
9293
|
settled = true;
|
|
8825
|
-
|
|
9294
|
+
resolve2(event);
|
|
8826
9295
|
break;
|
|
8827
9296
|
}
|
|
8828
9297
|
},
|
|
@@ -8836,7 +9305,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8836
9305
|
});
|
|
8837
9306
|
}
|
|
8838
9307
|
function watchTransactionJson(observable, level, options) {
|
|
8839
|
-
return new Promise((
|
|
9308
|
+
return new Promise((resolve2, reject) => {
|
|
8840
9309
|
let settled = false;
|
|
8841
9310
|
const subscription = observable.subscribe({
|
|
8842
9311
|
next(event) {
|
|
@@ -8853,7 +9322,7 @@ function watchTransactionJson(observable, level, options) {
|
|
|
8853
9322
|
if (level === "broadcast") {
|
|
8854
9323
|
settled = true;
|
|
8855
9324
|
subscription.unsubscribe();
|
|
8856
|
-
|
|
9325
|
+
resolve2(event);
|
|
8857
9326
|
}
|
|
8858
9327
|
break;
|
|
8859
9328
|
case "txBestBlocksState":
|
|
@@ -8862,13 +9331,13 @@ function watchTransactionJson(observable, level, options) {
|
|
|
8862
9331
|
if (level === "best-block") {
|
|
8863
9332
|
settled = true;
|
|
8864
9333
|
subscription.unsubscribe();
|
|
8865
|
-
|
|
9334
|
+
resolve2(event);
|
|
8866
9335
|
}
|
|
8867
9336
|
}
|
|
8868
9337
|
break;
|
|
8869
9338
|
case "finalized":
|
|
8870
9339
|
settled = true;
|
|
8871
|
-
|
|
9340
|
+
resolve2(event);
|
|
8872
9341
|
break;
|
|
8873
9342
|
}
|
|
8874
9343
|
},
|
|
@@ -8881,119 +9350,91 @@ function watchTransactionJson(observable, level, options) {
|
|
|
8881
9350
|
});
|
|
8882
9351
|
}
|
|
8883
9352
|
|
|
8884
|
-
// src/commands/
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
init_bandersnatch();
|
|
9353
|
+
// src/commands/workspace.ts
|
|
9354
|
+
init_store();
|
|
9355
|
+
init_workspace();
|
|
8888
9356
|
init_output();
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
${BOLD}Options:${RESET}
|
|
8898
|
-
--context <value> Blake2b context for keyed derivation (e.g. "candidate")
|
|
8899
|
-
|
|
8900
|
-
${BOLD}Examples:${RESET}
|
|
8901
|
-
$ dot verifiable alice Unkeyed derivation (lite person)
|
|
8902
|
-
$ dot verifiable alice --context candidate Keyed with "candidate" (full person)
|
|
8903
|
-
$ dot verifiable my-account --context candidate
|
|
8904
|
-
|
|
8905
|
-
${BOLD}How it works:${RESET}
|
|
8906
|
-
|
|
8907
|
-
Mnemonic (12/24 words)
|
|
8908
|
-
│ mnemonicToEntropy() (raw BIP39 entropy, NOT miniSecret)
|
|
8909
|
-
▼
|
|
8910
|
-
blake2b256(entropy, context?) keyed or unkeyed
|
|
8911
|
-
▼
|
|
8912
|
-
member_from_entropy() verifiablejs WASM (Bandersnatch curve)
|
|
8913
|
-
▼
|
|
8914
|
-
32-byte member key for on-chain member set registration
|
|
8915
|
-
`.trimStart();
|
|
8916
|
-
function registerVerifiableCommands(cli) {
|
|
8917
|
-
cli.command("verifiable [account]", "Derive Bandersnatch member key from account mnemonic").option("--context <value>", "Blake2b context for keyed derivation (e.g. candidate)").action(async (account, opts) => {
|
|
8918
|
-
if (!account) {
|
|
8919
|
-
console.log(VERIFIABLE_HELP);
|
|
8920
|
-
return;
|
|
8921
|
-
}
|
|
8922
|
-
return deriveVerifiable(account, opts);
|
|
8923
|
-
});
|
|
8924
|
-
}
|
|
8925
|
-
async function deriveVerifiable(account, opts) {
|
|
8926
|
-
const mnemonic = await resolveMnemonic(account);
|
|
8927
|
-
const memberKey = deriveBandersnatchMember(mnemonic, opts.context);
|
|
8928
|
-
const memberKeyHex = publicKeyToHex(memberKey);
|
|
8929
|
-
if (!isDevAccount(account)) {
|
|
8930
|
-
const accountsFile = await loadAccounts();
|
|
8931
|
-
const stored = findAccount(accountsFile, account);
|
|
8932
|
-
if (stored) {
|
|
8933
|
-
if (!stored.bandersnatch)
|
|
8934
|
-
stored.bandersnatch = {};
|
|
8935
|
-
stored.bandersnatch[opts.context ?? ""] = memberKeyHex;
|
|
8936
|
-
await saveAccounts(accountsFile);
|
|
8937
|
-
}
|
|
9357
|
+
init_errors();
|
|
9358
|
+
import { mkdir as mkdir3, stat } from "node:fs/promises";
|
|
9359
|
+
import { homedir as homedir3 } from "node:os";
|
|
9360
|
+
import { join as join4 } from "node:path";
|
|
9361
|
+
async function initWorkspace(cwd, home = homedir3()) {
|
|
9362
|
+
const dir = canonicalPath(cwd);
|
|
9363
|
+
if (dir === canonicalPath(home)) {
|
|
9364
|
+
throw new CliError(`Cannot initialize a workspace in your home directory — ${join4(dir, WORKSPACE_DIR_NAME)} is the global config root.`);
|
|
8938
9365
|
}
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
};
|
|
8944
|
-
if (opts.context)
|
|
8945
|
-
result.context = opts.context;
|
|
8946
|
-
console.log(formatJson(result));
|
|
8947
|
-
} else {
|
|
8948
|
-
printHeading("Bandersnatch Member Key");
|
|
8949
|
-
console.log(` ${BOLD}Account:${RESET} ${account}`);
|
|
8950
|
-
if (opts.context)
|
|
8951
|
-
console.log(` ${BOLD}Context:${RESET} ${opts.context}`);
|
|
8952
|
-
console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
|
|
8953
|
-
console.log();
|
|
9366
|
+
const workspacePath = join4(dir, WORKSPACE_DIR_NAME);
|
|
9367
|
+
const exists = await stat(workspacePath).then(() => true).catch(() => false);
|
|
9368
|
+
if (exists) {
|
|
9369
|
+
throw new CliError(`A workspace already exists at ${workspacePath}.`);
|
|
8954
9370
|
}
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
if (
|
|
8958
|
-
|
|
9371
|
+
const warnings = [];
|
|
9372
|
+
const parentWorkspace = findWorkspace(dir, home);
|
|
9373
|
+
if (parentWorkspace) {
|
|
9374
|
+
warnings.push(`This workspace shadows ${parentWorkspace} for commands run below ${dir}.`);
|
|
8959
9375
|
}
|
|
8960
|
-
const
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
8964
|
-
const suggestions = findClosest(account, available);
|
|
8965
|
-
const hint = suggestions.length > 0 ? `
|
|
8966
|
-
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
8967
|
-
const list = available.map((a) => `
|
|
8968
|
-
- ${a}`).join("");
|
|
8969
|
-
throw new Error(`Unknown account "${account}".${hint}
|
|
8970
|
-
Available accounts:${list}`);
|
|
9376
|
+
const dotHome = process.env.DOT_HOME;
|
|
9377
|
+
if (dotHome && dotHome.length > 0) {
|
|
9378
|
+
warnings.push(`DOT_HOME is set (${dotHome}) and takes precedence — this workspace will not be picked up until you unset it.`);
|
|
8971
9379
|
}
|
|
8972
|
-
|
|
8973
|
-
|
|
9380
|
+
await mkdir3(workspacePath, { recursive: true });
|
|
9381
|
+
return { workspacePath, warnings };
|
|
9382
|
+
}
|
|
9383
|
+
var SOURCE_LABELS = {
|
|
9384
|
+
env: "DOT_HOME environment variable",
|
|
9385
|
+
workspace: "local workspace (discovered from current directory)",
|
|
9386
|
+
global: "global config"
|
|
9387
|
+
};
|
|
9388
|
+
async function handleInit(cwd = process.cwd()) {
|
|
9389
|
+
const result = await initWorkspace(cwd);
|
|
9390
|
+
for (const warning of result.warnings) {
|
|
9391
|
+
process.stderr.write(`Warning: ${warning}
|
|
9392
|
+
`);
|
|
8974
9393
|
}
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
9394
|
+
await writeStdout(`Initialized empty dot workspace at ${result.workspacePath}
|
|
9395
|
+
` + `Check which workspace is active with: dot which
|
|
9396
|
+
`);
|
|
9397
|
+
}
|
|
9398
|
+
async function handleWhich(opts, cwd = process.cwd()) {
|
|
9399
|
+
const resolved = resolveConfigDir(cwd);
|
|
9400
|
+
if (isJsonOutput(opts)) {
|
|
9401
|
+
await writeStdout(`${JSON.stringify({ path: resolved.path, source: resolved.source })}
|
|
9402
|
+
`);
|
|
9403
|
+
return;
|
|
8978
9404
|
}
|
|
8979
|
-
|
|
9405
|
+
await writeStdout(`${resolved.path}
|
|
9406
|
+
Source: ${SOURCE_LABELS[resolved.source]}
|
|
9407
|
+
`);
|
|
9408
|
+
}
|
|
9409
|
+
function registerWorkspaceCommands(cli) {
|
|
9410
|
+
cli.command("init", "Initialize a local .polkadot workspace in the current directory").action(() => handleInit());
|
|
9411
|
+
cli.command("which", "Show the active config root (workspace, DOT_HOME, or global)").action((opts) => handleWhich(opts));
|
|
8980
9412
|
}
|
|
8981
9413
|
|
|
8982
9414
|
// src/config/store.ts
|
|
8983
9415
|
init_errors();
|
|
8984
9416
|
init_types();
|
|
8985
|
-
|
|
8986
|
-
import {
|
|
8987
|
-
import {
|
|
8988
|
-
|
|
9417
|
+
init_workspace();
|
|
9418
|
+
import { access as access3, mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
9419
|
+
import { homedir as homedir4 } from "node:os";
|
|
9420
|
+
import { join as join5 } from "node:path";
|
|
9421
|
+
function resolveConfigDir2(cwd = process.cwd()) {
|
|
8989
9422
|
const override = process.env.DOT_HOME;
|
|
8990
|
-
|
|
9423
|
+
if (override && override.length > 0)
|
|
9424
|
+
return { path: override, source: "env" };
|
|
9425
|
+
const workspace = findWorkspace(cwd);
|
|
9426
|
+
if (workspace)
|
|
9427
|
+
return { path: workspace, source: "workspace" };
|
|
9428
|
+
return { path: join5(homedir4(), ".polkadot"), source: "global" };
|
|
9429
|
+
}
|
|
9430
|
+
function getConfigDir2() {
|
|
9431
|
+
return resolveConfigDir2().path;
|
|
8991
9432
|
}
|
|
8992
9433
|
function getConfigPath2() {
|
|
8993
|
-
return
|
|
9434
|
+
return join5(getConfigDir2(), "config.json");
|
|
8994
9435
|
}
|
|
8995
9436
|
async function ensureDir3(dir) {
|
|
8996
|
-
await
|
|
9437
|
+
await mkdir4(dir, { recursive: true });
|
|
8997
9438
|
}
|
|
8998
9439
|
async function fileExists3(path) {
|
|
8999
9440
|
try {
|
|
@@ -9007,7 +9448,7 @@ async function loadConfig2() {
|
|
|
9007
9448
|
await ensureDir3(getConfigDir2());
|
|
9008
9449
|
const configPath = getConfigPath2();
|
|
9009
9450
|
if (await fileExists3(configPath)) {
|
|
9010
|
-
const saved = JSON.parse(await
|
|
9451
|
+
const saved = JSON.parse(await readFile6(configPath, "utf-8"));
|
|
9011
9452
|
const chains = {};
|
|
9012
9453
|
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
9013
9454
|
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
@@ -9030,7 +9471,7 @@ async function saveConfig2(config) {
|
|
|
9030
9471
|
|
|
9031
9472
|
// src/core/file-loader.ts
|
|
9032
9473
|
init_errors();
|
|
9033
|
-
import { access as access4, readFile as
|
|
9474
|
+
import { access as access4, readFile as readFile7 } from "node:fs/promises";
|
|
9034
9475
|
import { parse as parseYaml } from "yaml";
|
|
9035
9476
|
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
9036
9477
|
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
@@ -9095,7 +9536,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
9095
9536
|
} catch {
|
|
9096
9537
|
throw new CliError(`File not found: ${filePath}`);
|
|
9097
9538
|
}
|
|
9098
|
-
const rawText = await
|
|
9539
|
+
const rawText = await readFile7(filePath, "utf-8");
|
|
9099
9540
|
if (!rawText.trim()) {
|
|
9100
9541
|
throw new CliError(`File is empty: ${filePath}`);
|
|
9101
9542
|
}
|
|
@@ -9167,8 +9608,8 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
9167
9608
|
// src/core/update-notifier.ts
|
|
9168
9609
|
init_store();
|
|
9169
9610
|
import { readFileSync } from "node:fs";
|
|
9170
|
-
import { mkdir as
|
|
9171
|
-
import { join as
|
|
9611
|
+
import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
|
|
9612
|
+
import { join as join6 } from "node:path";
|
|
9172
9613
|
var CACHE_FILE = "update-check.json";
|
|
9173
9614
|
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
9174
9615
|
var FETCH_TIMEOUT_MS = 5000;
|
|
@@ -9224,7 +9665,7 @@ function buildNotificationBox(current, latest) {
|
|
|
9224
9665
|
`);
|
|
9225
9666
|
}
|
|
9226
9667
|
function getCachePath() {
|
|
9227
|
-
return
|
|
9668
|
+
return join6(getConfigDir(), CACHE_FILE);
|
|
9228
9669
|
}
|
|
9229
9670
|
function readCache() {
|
|
9230
9671
|
try {
|
|
@@ -9236,7 +9677,7 @@ function readCache() {
|
|
|
9236
9677
|
}
|
|
9237
9678
|
async function writeCache(cache) {
|
|
9238
9679
|
try {
|
|
9239
|
-
await
|
|
9680
|
+
await mkdir5(getConfigDir(), { recursive: true });
|
|
9240
9681
|
await writeFile6(getCachePath(), `${JSON.stringify(cache)}
|
|
9241
9682
|
`);
|
|
9242
9683
|
} catch {}
|
|
@@ -9264,7 +9705,7 @@ function startBackgroundCheck(currentVersion) {
|
|
|
9264
9705
|
async function waitForPendingCheck() {
|
|
9265
9706
|
if (!pendingCheck)
|
|
9266
9707
|
return;
|
|
9267
|
-
const timeout = new Promise((
|
|
9708
|
+
const timeout = new Promise((resolve2) => setTimeout(resolve2, EXIT_WAIT_TIMEOUT_MS));
|
|
9268
9709
|
await Promise.race([pendingCheck.catch(() => {}), timeout]);
|
|
9269
9710
|
pendingCheck = null;
|
|
9270
9711
|
}
|
|
@@ -9284,6 +9725,117 @@ function getUpdateNotification(currentVersion) {
|
|
|
9284
9725
|
return null;
|
|
9285
9726
|
}
|
|
9286
9727
|
|
|
9728
|
+
// src/features/verifiable/register.ts
|
|
9729
|
+
init_platform();
|
|
9730
|
+
var VERIFIABLE_HELP = `
|
|
9731
|
+
${BOLD}Usage:${RESET}
|
|
9732
|
+
$ dot verifiable [account] [--entropy-key <key>] Derive the member key (default action)
|
|
9733
|
+
$ dot verifiable <action> [account] [options]
|
|
9734
|
+
|
|
9735
|
+
${BOLD}Actions:${RESET}
|
|
9736
|
+
member <account> Derive the Bandersnatch member key (default if omitted)
|
|
9737
|
+
alias <account> Derive the alias for a 32-byte ring context
|
|
9738
|
+
sign <account> Standalone Bandersnatch signature (64 bytes)
|
|
9739
|
+
prove <account> Ring-VRF proof (one_shot) over a members set
|
|
9740
|
+
verify Locally verify a ring-VRF proof against members/root
|
|
9741
|
+
verify-sig Verify a standalone Bandersnatch signature
|
|
9742
|
+
members <key…> SCALE-encode member keys as Vec<[u8;32]>
|
|
9743
|
+
|
|
9744
|
+
verify / verify-sig exit non-zero on failure (the verdict is the exit code);
|
|
9745
|
+
on success they print the recovered alias / {"valid":true}.
|
|
9746
|
+
|
|
9747
|
+
${BOLD}Key concepts (do not conflate these):${RESET}
|
|
9748
|
+
--entropy-key <text|0xhex>
|
|
9749
|
+
Key mixed into the keyed-blake2b that turns your mnemonic into the
|
|
9750
|
+
Bandersnatch member entropy. Omit for a ${BOLD}lite${RESET} person (unkeyed);
|
|
9751
|
+
use "candidate" for a ${BOLD}full${RESET} person. Must match the key used when the
|
|
9752
|
+
member was recognised on-chain, or you derive a different (unrecognised)
|
|
9753
|
+
member key. It is NOT a derivation path and NOT the ring --context.
|
|
9754
|
+
--context <text|0xhex>
|
|
9755
|
+
The 32-byte ring/proof namespace (e.g. "dotns"), zero-padded right to 32
|
|
9756
|
+
bytes like Solidity bytes32(). Determines the alias. Used by alias/prove/verify.
|
|
9757
|
+
|
|
9758
|
+
${BOLD}Options:${RESET}
|
|
9759
|
+
--entropy-key <key> Entropy-derivation key (see above)
|
|
9760
|
+
--context <value> 32-byte ring context (alias/prove/verify)
|
|
9761
|
+
--message <data> Message to sign / bind / verify (text or 0x hex)
|
|
9762
|
+
--file <path> Read the message from a file (raw bytes)
|
|
9763
|
+
--stdin Read the message from stdin
|
|
9764
|
+
--members <hex|file> SCALE-encoded Vec<[u8;32]> ring (prove/verify)
|
|
9765
|
+
--root <hex> 768-byte ring root / commitment (verify)
|
|
9766
|
+
--proof <hex> Ring-VRF proof bytes (verify)
|
|
9767
|
+
--signature <hex> Bandersnatch signature (verify-sig)
|
|
9768
|
+
--member <hex> 32-byte member public key (verify-sig)
|
|
9769
|
+
--ring-exponent <n> Ring exponent: 9 (default), 10, or 14
|
|
9770
|
+
--output json Output as JSON
|
|
9771
|
+
|
|
9772
|
+
${BOLD}Examples:${RESET}
|
|
9773
|
+
$ dot verifiable alice Lite member key
|
|
9774
|
+
$ dot verifiable alice --entropy-key candidate Full member key
|
|
9775
|
+
$ dot verifiable alias alice --entropy-key candidate --context dotns
|
|
9776
|
+
$ dot verifiable sign alice --message "hello" --entropy-key candidate
|
|
9777
|
+
$ dot verifiable prove alice --entropy-key candidate --context dotns \\
|
|
9778
|
+
--message 0x… --members 0x…
|
|
9779
|
+
$ dot verifiable verify --proof 0x… --context dotns --message 0x… --members 0x…
|
|
9780
|
+
$ dot verifiable members 0x… 0x…
|
|
9781
|
+
|
|
9782
|
+
${BOLD}Derivation flow:${RESET}
|
|
9783
|
+
|
|
9784
|
+
Mnemonic ─BIP39─▶ entropy ─keyed blake2b─▶ member entropy ─▶ member key / secret
|
|
9785
|
+
(key = --entropy-key) │
|
|
9786
|
+
ring proof: one_shot(…, --context, --message)
|
|
9787
|
+
`.trimStart();
|
|
9788
|
+
var RAW_STRING_FLAGS = [
|
|
9789
|
+
["entropy-key", "entropyKey"],
|
|
9790
|
+
["context", "context"],
|
|
9791
|
+
["message", "message"],
|
|
9792
|
+
["members", "members"],
|
|
9793
|
+
["root", "root"],
|
|
9794
|
+
["proof", "proof"],
|
|
9795
|
+
["signature", "signature"],
|
|
9796
|
+
["member", "member"]
|
|
9797
|
+
];
|
|
9798
|
+
function registerVerifiableCommands(cli) {
|
|
9799
|
+
cli.command("verifiable [action] [...rest]", "Bandersnatch member keys, ring-VRF proofs, signing and verification").option("--entropy-key <key>", "Entropy-derivation key (omit = lite, 'candidate' = full)").option("--context <value>", "32-byte ring/proof context (alias/prove/verify)").option("--message <data>", "Message to sign/bind/verify (text or 0x hex)").option("--file <path>", "Read message from a file (raw bytes)").option("--stdin", "Read message from stdin").option("--members <hex|file>", "SCALE-encoded Vec<[u8;32]> ring (prove/verify)").option("--root <hex>", "768-byte ring root/commitment (verify)").option("--proof <hex>", "Ring-VRF proof bytes (verify)").option("--signature <hex>", "Bandersnatch signature (verify-sig)").option("--member <hex>", "32-byte member public key (verify-sig)").option("--ring-exponent <n>", "Ring exponent: 9 (default), 10, or 14").action(async (action, rest, opts) => {
|
|
9800
|
+
if (!action) {
|
|
9801
|
+
console.log(VERIFIABLE_HELP);
|
|
9802
|
+
return;
|
|
9803
|
+
}
|
|
9804
|
+
for (const [flag, key] of RAW_STRING_FLAGS) {
|
|
9805
|
+
const raw = readRawOptionValue(flag);
|
|
9806
|
+
if (raw !== undefined)
|
|
9807
|
+
opts[key] = raw;
|
|
9808
|
+
}
|
|
9809
|
+
const { runVerifiable: runVerifiable2 } = await Promise.resolve().then(() => (init_commands(), exports_commands));
|
|
9810
|
+
return runVerifiable2(action, rest, opts);
|
|
9811
|
+
});
|
|
9812
|
+
}
|
|
9813
|
+
|
|
9814
|
+
// src/platform/cli.ts
|
|
9815
|
+
function registerGlobalOptions(cli) {
|
|
9816
|
+
cli.option("--chain <name>", "Target chain (required)");
|
|
9817
|
+
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
9818
|
+
cli.option("--output <format>", "Output format: pretty or json", {
|
|
9819
|
+
default: "pretty"
|
|
9820
|
+
});
|
|
9821
|
+
cli.option("--json", "Output as JSON (shorthand for --output json)");
|
|
9822
|
+
}
|
|
9823
|
+
function readRawOptionValue2(name, argv = process.argv) {
|
|
9824
|
+
const flag = `--${name}`;
|
|
9825
|
+
const prefix = `${flag}=`;
|
|
9826
|
+
let value;
|
|
9827
|
+
for (let i = 0;i < argv.length; i++) {
|
|
9828
|
+
const arg = argv[i];
|
|
9829
|
+
if (arg === "--")
|
|
9830
|
+
break;
|
|
9831
|
+
if (arg === flag && i + 1 < argv.length)
|
|
9832
|
+
value = argv[i + 1];
|
|
9833
|
+
else if (arg.startsWith(prefix))
|
|
9834
|
+
value = arg.slice(prefix.length);
|
|
9835
|
+
}
|
|
9836
|
+
return value;
|
|
9837
|
+
}
|
|
9838
|
+
|
|
9287
9839
|
// src/utils/errors.ts
|
|
9288
9840
|
class CliError2 extends Error {
|
|
9289
9841
|
constructor(message) {
|
|
@@ -9406,15 +9958,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9406
9958
|
process.exit(0);
|
|
9407
9959
|
})();
|
|
9408
9960
|
} else {
|
|
9409
|
-
let
|
|
9410
|
-
for (let i = 0;i < argv.length; i++) {
|
|
9411
|
-
if (argv[i] === "--at" && i + 1 < argv.length)
|
|
9412
|
-
return argv[i + 1];
|
|
9413
|
-
if (argv[i].startsWith("--at="))
|
|
9414
|
-
return argv[i].slice(5);
|
|
9415
|
-
}
|
|
9416
|
-
return;
|
|
9417
|
-
}, collectVarFlags = function(argv) {
|
|
9961
|
+
let collectVarFlags = function(argv) {
|
|
9418
9962
|
const vars = [];
|
|
9419
9963
|
for (let i = 0;i < argv.length; i++) {
|
|
9420
9964
|
if (argv[i] === "--var" && i + 1 < argv.length) {
|
|
@@ -9470,8 +10014,10 @@ if (process.argv[2] === "__complete") {
|
|
|
9470
10014
|
console.log(" hash Hash utilities");
|
|
9471
10015
|
console.log(" sign Sign a message with an account keypair");
|
|
9472
10016
|
console.log(" parachain Derive parachain sovereign accounts (deprecated \u2014 use `account inspect --parachain`)");
|
|
9473
|
-
console.log(" verifiable
|
|
10017
|
+
console.log(" verifiable Bandersnatch member keys, ring-VRF proofs, sign/verify");
|
|
9474
10018
|
console.log(" completions <sh> Generate shell completions (zsh, bash, fish)");
|
|
10019
|
+
console.log(" init Initialize a local .polkadot workspace in this directory");
|
|
10020
|
+
console.log(" which Show the active config root (workspace, DOT_HOME, or global)");
|
|
9475
10021
|
console.log();
|
|
9476
10022
|
console.log("Global options:");
|
|
9477
10023
|
console.log(" --chain <name> Target chain (required)");
|
|
@@ -9483,12 +10029,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9483
10029
|
};
|
|
9484
10030
|
startBackgroundCheck(version);
|
|
9485
10031
|
const cli = cac("dot");
|
|
9486
|
-
cli
|
|
9487
|
-
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
9488
|
-
cli.option("--output <format>", "Output format: pretty or json", {
|
|
9489
|
-
default: "pretty"
|
|
9490
|
-
});
|
|
9491
|
-
cli.option("--json", "Output as JSON (shorthand for --output json)");
|
|
10032
|
+
registerGlobalOptions(cli);
|
|
9492
10033
|
registerChainCommands(cli);
|
|
9493
10034
|
registerInspectCommand(cli);
|
|
9494
10035
|
registerMetadataCommand(cli);
|
|
@@ -9498,6 +10039,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9498
10039
|
registerParachainCommand(cli);
|
|
9499
10040
|
registerCompletionsCommand(cli);
|
|
9500
10041
|
registerVerifiableCommands(cli);
|
|
10042
|
+
registerWorkspaceCommands(cli);
|
|
9501
10043
|
cli.command("[dotpath] [...args]").option("--from <name>", "Account to sign with (for tx)").option("--dry-run", "Estimate fees without submitting (for tx)").option("--encode", "Encode call to hex without signing (for tx)").option("--to-yaml", "Decode call to YAML file format (for tx)").option("--to-json", "Decode call to JSON file format (for tx)").option("--ext <json>", "Custom signed extension values as JSON (for tx)").option("--asset <json>", "Pay fees in an alternative asset (XCM location JSON, for tx)").option("-w, --wait <level>", "Resolve at: broadcast, best-block (or best), finalized (for tx)", {
|
|
9502
10044
|
default: "finalized"
|
|
9503
10045
|
}).option("--dump", "Dump all entries of a storage map (without specifying a key)").option("--var <kv>", "Template variable for file input (KEY=VALUE, repeatable)").option("--nonce <n>", "Custom nonce for manual tx sequencing (for tx)").option("--tip <amount>", "Tip to prioritize transaction (for tx)").option("--mortality <spec>", '"immortal" or period number (for tx)').option("--at <block>", 'Block hash, "best", or "finalized" to read/validate against (tx, query, apis)').option("--unsigned", "Submit as unsigned/bare transaction (no signer required, for tx)").option("--refresh", "Refresh the cached RPC method list from the node (for rpc)").action(async (dotpath, args, opts) => {
|
|
@@ -9505,7 +10047,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9505
10047
|
printHelp();
|
|
9506
10048
|
return;
|
|
9507
10049
|
}
|
|
9508
|
-
const atRaw =
|
|
10050
|
+
const atRaw = readRawOptionValue2("at");
|
|
9509
10051
|
if (isFilePath(dotpath)) {
|
|
9510
10052
|
const cliVars = collectVarFlags(process.argv);
|
|
9511
10053
|
const cmd = await loadCommandFile(dotpath, cliVars);
|