polkadot-cli 1.19.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 +148 -40
- package/dist/cli.mjs +879 -277
- package/package.json +7 -7
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 });
|
|
@@ -461,6 +517,7 @@ function suggestMessage(kind, input, candidates) {
|
|
|
461
517
|
}
|
|
462
518
|
|
|
463
519
|
// src/core/accounts.ts
|
|
520
|
+
import { hexToBytes as nobleHexToBytes } from "@noble/hashes/utils.js";
|
|
464
521
|
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
465
522
|
import {
|
|
466
523
|
DEV_PHRASE,
|
|
@@ -471,7 +528,7 @@ import {
|
|
|
471
528
|
ss58Decode,
|
|
472
529
|
validateMnemonic
|
|
473
530
|
} from "@polkadot-labs/hdkd-helpers";
|
|
474
|
-
import { HDKD, secretFromSeed } from "@scure/sr25519";
|
|
531
|
+
import { getPublicKey, HDKD, secretFromSeed, sign } from "@scure/sr25519";
|
|
475
532
|
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
476
533
|
function isDevAccount(name) {
|
|
477
534
|
return DEV_NAMES.includes(name.toLowerCase());
|
|
@@ -486,18 +543,54 @@ function deriveFromMnemonic(mnemonic, path) {
|
|
|
486
543
|
return derive(path);
|
|
487
544
|
}
|
|
488
545
|
function deriveFromHexSeed(hexSeed, path) {
|
|
489
|
-
const
|
|
490
|
-
const seed = new Uint8Array(clean.length / 2);
|
|
491
|
-
for (let i = 0;i < clean.length; i += 2) {
|
|
492
|
-
seed[i / 2] = parseInt(clean.substring(i, i + 2), 16);
|
|
493
|
-
}
|
|
494
|
-
const derive = sr25519CreateDerive(seed);
|
|
546
|
+
const derive = sr25519CreateDerive(hexToBytes(hexSeed));
|
|
495
547
|
return derive(path);
|
|
496
548
|
}
|
|
497
549
|
function getDevKeypair(name) {
|
|
498
550
|
const path = devDerivationPath(name);
|
|
499
551
|
return deriveFromMnemonic(DEV_PHRASE, path);
|
|
500
552
|
}
|
|
553
|
+
function hexToBytes(hex) {
|
|
554
|
+
return nobleHexToBytes(hex.startsWith("0x") ? hex.slice(2) : hex);
|
|
555
|
+
}
|
|
556
|
+
function isExpandedSecret(secret) {
|
|
557
|
+
return EXPANDED_SECRET_RE.test(secret);
|
|
558
|
+
}
|
|
559
|
+
function isHexSeed(secret) {
|
|
560
|
+
return HEX_SEED_RE.test(secret);
|
|
561
|
+
}
|
|
562
|
+
function secretKind(secret) {
|
|
563
|
+
if (isExpandedSecret(secret))
|
|
564
|
+
return "expanded";
|
|
565
|
+
if (isHexSeed(secret))
|
|
566
|
+
return "seed";
|
|
567
|
+
return "mnemonic";
|
|
568
|
+
}
|
|
569
|
+
function assertNoPathForExpandedSecret(derivationPath) {
|
|
570
|
+
if (derivationPath) {
|
|
571
|
+
throw new Error("Stored account has a 64-byte expanded secret with a derivation path, which cannot be applied. An expanded secret cannot be HD-derived; remove the derivationPath from accounts.json.");
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function keypairFromExpandedSecret(secret) {
|
|
575
|
+
return {
|
|
576
|
+
publicKey: getPublicKey(secret),
|
|
577
|
+
sign: (msg) => sign(secret, msg)
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function keypairFromSecret(secret, derivationPath = "") {
|
|
581
|
+
if (isExpandedSecret(secret)) {
|
|
582
|
+
assertNoPathForExpandedSecret(derivationPath);
|
|
583
|
+
return keypairFromExpandedSecret(hexToBytes(secret));
|
|
584
|
+
}
|
|
585
|
+
return isHexSeed(secret) ? deriveFromHexSeed(secret, derivationPath) : deriveFromMnemonic(secret, derivationPath);
|
|
586
|
+
}
|
|
587
|
+
function expandedSecretFromStored(secret, derivationPath = "") {
|
|
588
|
+
if (isExpandedSecret(secret)) {
|
|
589
|
+
assertNoPathForExpandedSecret(derivationPath);
|
|
590
|
+
return hexToBytes(secret);
|
|
591
|
+
}
|
|
592
|
+
return deriveExpandedSecret(miniSecretFromSecret(secret), derivationPath);
|
|
593
|
+
}
|
|
501
594
|
function parseDerivations(path) {
|
|
502
595
|
const out = [];
|
|
503
596
|
for (const [, type, code] of path.matchAll(DERIVATION_RE)) {
|
|
@@ -528,17 +621,11 @@ function deriveExpandedSecret(miniSecret, path) {
|
|
|
528
621
|
return parseDerivations(path).reduce((sk, [type, code]) => type === "hard" ? HDKD.secretHard(sk, createChainCode(code)) : HDKD.secretSoft(sk, createChainCode(code)), secretFromSeed(miniSecret));
|
|
529
622
|
}
|
|
530
623
|
function miniSecretFromSecret(secret) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const clean = secret.slice(2);
|
|
534
|
-
const bytes = new Uint8Array(32);
|
|
535
|
-
for (let i = 0;i < clean.length; i += 2) {
|
|
536
|
-
bytes[i / 2] = parseInt(clean.substring(i, i + 2), 16);
|
|
537
|
-
}
|
|
538
|
-
return bytes;
|
|
624
|
+
if (isHexSeed(secret)) {
|
|
625
|
+
return hexToBytes(secret);
|
|
539
626
|
}
|
|
540
627
|
if (!validateMnemonic(secret)) {
|
|
541
|
-
throw new Error("Invalid secret. Expected a 0x-prefixed 32-byte hex seed
|
|
628
|
+
throw new Error("Invalid secret. Expected a BIP39 mnemonic or a 0x-prefixed 32-byte hex seed.");
|
|
542
629
|
}
|
|
543
630
|
return entropyToMiniSecret(mnemonicToEntropy(secret));
|
|
544
631
|
}
|
|
@@ -555,13 +642,19 @@ function createNewAccount(path = "") {
|
|
|
555
642
|
return { mnemonic, publicKey: keypair.publicKey };
|
|
556
643
|
}
|
|
557
644
|
function importAccount(secret, path = "") {
|
|
558
|
-
|
|
559
|
-
|
|
645
|
+
if (isExpandedSecret(secret)) {
|
|
646
|
+
if (path) {
|
|
647
|
+
throw new Error("Derivation paths are not supported for raw private key (64-byte expanded secret) import. An expanded secret cannot be HD-derived; omit --path.");
|
|
648
|
+
}
|
|
649
|
+
const keypair2 = keypairFromExpandedSecret(hexToBytes(secret));
|
|
650
|
+
return { publicKey: keypair2.publicKey };
|
|
651
|
+
}
|
|
652
|
+
if (isHexSeed(secret)) {
|
|
560
653
|
const keypair2 = deriveFromHexSeed(secret, path);
|
|
561
654
|
return { publicKey: keypair2.publicKey };
|
|
562
655
|
}
|
|
563
656
|
if (!validateMnemonic(secret)) {
|
|
564
|
-
throw new Error("Invalid secret. Expected a 0x-prefixed 32-byte hex seed or a
|
|
657
|
+
throw new Error("Invalid secret. Expected a BIP39 mnemonic, a 0x-prefixed 32-byte hex seed, or a 0x-prefixed 64-byte sr25519 expanded secret.");
|
|
565
658
|
}
|
|
566
659
|
const keypair = deriveFromMnemonic(secret, path);
|
|
567
660
|
return { publicKey: keypair.publicKey };
|
|
@@ -608,6 +701,16 @@ function tryDerivePublicKey(envVarName, path = "") {
|
|
|
608
701
|
return null;
|
|
609
702
|
}
|
|
610
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
|
+
}
|
|
611
714
|
async function resolveAccountKeypair(name) {
|
|
612
715
|
if (isDevAccount(name)) {
|
|
613
716
|
return getDevKeypair(name);
|
|
@@ -615,21 +718,12 @@ async function resolveAccountKeypair(name) {
|
|
|
615
718
|
const accountsFile = await loadAccounts();
|
|
616
719
|
const account = findAccount(accountsFile, name);
|
|
617
720
|
if (!account) {
|
|
618
|
-
|
|
619
|
-
const suggestions = findClosest(name, available);
|
|
620
|
-
const hint = suggestions.length > 0 ? `
|
|
621
|
-
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
622
|
-
const list = available.map((a) => `
|
|
623
|
-
- ${a}`).join("");
|
|
624
|
-
throw new Error(`Unknown account "${name}".${hint}
|
|
625
|
-
Available accounts:${list}`);
|
|
721
|
+
throw unknownAccountError(name, accountsFile);
|
|
626
722
|
}
|
|
627
723
|
if (account.secret === undefined) {
|
|
628
724
|
throw new Error(`Account "${name}" is watch-only (no secret). Cannot sign. Import with --secret or --env.`);
|
|
629
725
|
}
|
|
630
|
-
|
|
631
|
-
const isHexSeed = /^0x[0-9a-fA-F]{64}$/.test(secret);
|
|
632
|
-
return isHexSeed ? deriveFromHexSeed(secret, account.derivationPath) : deriveFromMnemonic(secret, account.derivationPath);
|
|
726
|
+
return keypairFromSecret(resolveSecret(account.secret), account.derivationPath);
|
|
633
727
|
}
|
|
634
728
|
async function resolveAccountSigner(name) {
|
|
635
729
|
const keypair = await resolveAccountKeypair(name);
|
|
@@ -637,34 +731,29 @@ async function resolveAccountSigner(name) {
|
|
|
637
731
|
}
|
|
638
732
|
async function resolveAccountExpandedSecret(name) {
|
|
639
733
|
if (isDevAccount(name)) {
|
|
640
|
-
const
|
|
641
|
-
return deriveExpandedSecret(
|
|
734
|
+
const miniSecret = entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE));
|
|
735
|
+
return deriveExpandedSecret(miniSecret, devDerivationPath(name));
|
|
642
736
|
}
|
|
643
737
|
const accountsFile = await loadAccounts();
|
|
644
738
|
const account = findAccount(accountsFile, name);
|
|
645
739
|
if (!account) {
|
|
646
|
-
|
|
647
|
-
const suggestions = findClosest(name, available);
|
|
648
|
-
const hint = suggestions.length > 0 ? `
|
|
649
|
-
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
650
|
-
const list = available.map((a) => `
|
|
651
|
-
- ${a}`).join("");
|
|
652
|
-
throw new Error(`Unknown account "${name}".${hint}
|
|
653
|
-
Available accounts:${list}`);
|
|
740
|
+
throw unknownAccountError(name, accountsFile);
|
|
654
741
|
}
|
|
655
742
|
if (account.secret === undefined) {
|
|
656
743
|
throw new Error(`Account "${name}" is watch-only (no secret). Cannot derive private key. Import with --secret or --env.`);
|
|
657
744
|
}
|
|
658
|
-
|
|
659
|
-
return deriveExpandedSecret(miniSecret, account.derivationPath);
|
|
745
|
+
return expandedSecretFromStored(resolveSecret(account.secret), account.derivationPath);
|
|
660
746
|
}
|
|
661
747
|
function bytesToHex(bytes) {
|
|
662
748
|
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
663
749
|
}
|
|
664
|
-
var DEV_NAMES, DERIVATION_RE;
|
|
750
|
+
var DEV_NAMES, EXPANDED_SECRET_RE, HEX_SEED_RE, DERIVATION_RE;
|
|
665
751
|
var init_accounts = __esm(() => {
|
|
666
752
|
init_accounts_store();
|
|
753
|
+
init_store();
|
|
667
754
|
DEV_NAMES = ["alice", "bob", "charlie", "dave", "eve", "ferdie"];
|
|
755
|
+
EXPANDED_SECRET_RE = /^0x[0-9a-fA-F]{128}$/;
|
|
756
|
+
HEX_SEED_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
668
757
|
DERIVATION_RE = /(\/{1,2})([^/]+)/g;
|
|
669
758
|
});
|
|
670
759
|
|
|
@@ -723,8 +812,8 @@ function isJsonOutput(opts) {
|
|
|
723
812
|
return opts.json === true || opts.output === "json";
|
|
724
813
|
}
|
|
725
814
|
function writeStdout(text) {
|
|
726
|
-
return new Promise((
|
|
727
|
-
process.stdout.write(text, () =>
|
|
815
|
+
return new Promise((resolve2) => {
|
|
816
|
+
process.stdout.write(text, () => resolve2());
|
|
728
817
|
});
|
|
729
818
|
}
|
|
730
819
|
function printJsonLine(data) {
|
|
@@ -828,24 +917,130 @@ var init_output = __esm(() => {
|
|
|
828
917
|
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
829
918
|
});
|
|
830
919
|
|
|
831
|
-
// src/
|
|
832
|
-
var
|
|
833
|
-
__export(
|
|
834
|
-
|
|
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
|
|
835
938
|
});
|
|
836
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";
|
|
837
942
|
import { mnemonicToEntropy as mnemonicToEntropy2 } from "@polkadot-labs/hdkd-helpers";
|
|
838
|
-
import {
|
|
839
|
-
|
|
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) {
|
|
840
972
|
const entropy = mnemonicToEntropy2(mnemonic);
|
|
841
973
|
const opts = { dkLen: 32 };
|
|
842
|
-
if (
|
|
843
|
-
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);
|
|
1001
|
+
}
|
|
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})`);
|
|
844
1035
|
}
|
|
845
|
-
const
|
|
846
|
-
|
|
1036
|
+
const out = new Uint8Array(32);
|
|
1037
|
+
out.set(bytes, 0);
|
|
1038
|
+
return out;
|
|
847
1039
|
}
|
|
848
|
-
var
|
|
1040
|
+
var RING_EXPONENTS, DEFAULT_RING_EXPONENT = 9;
|
|
1041
|
+
var init_lib = __esm(() => {
|
|
1042
|
+
RING_EXPONENTS = [9, 10, 14];
|
|
1043
|
+
});
|
|
849
1044
|
|
|
850
1045
|
// src/core/client.ts
|
|
851
1046
|
import { createClient } from "polkadot-api";
|
|
@@ -971,9 +1166,9 @@ function compactEntry(entry, color) {
|
|
|
971
1166
|
}
|
|
972
1167
|
}
|
|
973
1168
|
function expandEntry(entry, indent, width, color, prefix = 0) {
|
|
974
|
-
const
|
|
975
|
-
if (visualWidth(
|
|
976
|
-
return
|
|
1169
|
+
const compact2 = compactEntry(entry, color);
|
|
1170
|
+
if (visualWidth(compact2) + indent + prefix <= width)
|
|
1171
|
+
return compact2;
|
|
977
1172
|
switch (entry.type) {
|
|
978
1173
|
case "struct":
|
|
979
1174
|
return expandStruct(entry.value, indent, width, color);
|
|
@@ -1007,7 +1202,7 @@ ${closePadding}>`;
|
|
|
1007
1202
|
case "lookupEntry":
|
|
1008
1203
|
return expandEntry(entry.value, indent, width, color);
|
|
1009
1204
|
default:
|
|
1010
|
-
return
|
|
1205
|
+
return compact2;
|
|
1011
1206
|
}
|
|
1012
1207
|
}
|
|
1013
1208
|
function expandStruct(fields, indent, width, color) {
|
|
@@ -1066,9 +1261,9 @@ ${closePadding}${close}`;
|
|
|
1066
1261
|
}
|
|
1067
1262
|
function prettyType(entry, opts = {}) {
|
|
1068
1263
|
const { indent, prefix, width, color } = resolveOpts(opts);
|
|
1069
|
-
const
|
|
1070
|
-
if (visualWidth(
|
|
1071
|
-
return
|
|
1264
|
+
const compact2 = compactEntry(entry, color);
|
|
1265
|
+
if (visualWidth(compact2) + indent + prefix <= width)
|
|
1266
|
+
return compact2;
|
|
1072
1267
|
return expandEntry(entry, indent, width, color);
|
|
1073
1268
|
}
|
|
1074
1269
|
function prettyTypeById(lookup, typeId, opts = {}) {
|
|
@@ -1172,15 +1367,15 @@ function renderArgsFromFields(fields, opts) {
|
|
|
1172
1367
|
case "void":
|
|
1173
1368
|
return "()";
|
|
1174
1369
|
case "named": {
|
|
1175
|
-
const
|
|
1176
|
-
if (visualWidth(
|
|
1177
|
-
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;
|
|
1178
1373
|
return renderFieldList(fields.fields, "(", ")", indent, width, color);
|
|
1179
1374
|
}
|
|
1180
1375
|
case "positional": {
|
|
1181
|
-
const
|
|
1182
|
-
if (visualWidth(
|
|
1183
|
-
return
|
|
1376
|
+
const compact2 = `(${fields.types.map((t) => compactEntry(t, color)).join(", ")})`;
|
|
1377
|
+
if (visualWidth(compact2) + lead <= width)
|
|
1378
|
+
return compact2;
|
|
1184
1379
|
const innerIndent = indent + 2;
|
|
1185
1380
|
const padding = " ".repeat(innerIndent);
|
|
1186
1381
|
const closePadding = " ".repeat(indent);
|
|
@@ -1191,9 +1386,9 @@ ${lines.join(`,
|
|
|
1191
1386
|
${closePadding})`;
|
|
1192
1387
|
}
|
|
1193
1388
|
case "single": {
|
|
1194
|
-
const
|
|
1195
|
-
if (visualWidth(
|
|
1196
|
-
return
|
|
1389
|
+
const compact2 = `(${compactEntry(fields.type, color)})`;
|
|
1390
|
+
if (visualWidth(compact2) + lead <= width)
|
|
1391
|
+
return compact2;
|
|
1197
1392
|
const inner = expandEntry(fields.type, indent + 2, width, color);
|
|
1198
1393
|
return `(
|
|
1199
1394
|
${" ".repeat(indent + 2)}${inner},
|
|
@@ -1264,7 +1459,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
|
|
|
1264
1459
|
let bytes;
|
|
1265
1460
|
try {
|
|
1266
1461
|
const hex = await withTimeout(client._request("state_call", ["Metadata_metadata_at_version", v15Arg]), chainName);
|
|
1267
|
-
const raw =
|
|
1462
|
+
const raw = hexToBytes4(hex);
|
|
1268
1463
|
const decoded = optionalOpaqueBytes.dec(raw);
|
|
1269
1464
|
if (decoded !== undefined) {
|
|
1270
1465
|
bytes = new Uint8Array(decoded);
|
|
@@ -1273,7 +1468,7 @@ async function fetchMetadataFromChain(clientHandle, chainName) {
|
|
|
1273
1468
|
if (!bytes) {
|
|
1274
1469
|
try {
|
|
1275
1470
|
const hex = await withTimeout(client._request("state_getMetadata", []), chainName);
|
|
1276
|
-
bytes =
|
|
1471
|
+
bytes = hexToBytes4(hex);
|
|
1277
1472
|
} catch (err) {
|
|
1278
1473
|
if (err instanceof ConnectionError)
|
|
1279
1474
|
throw err;
|
|
@@ -1469,7 +1664,7 @@ function describeCallArgs(meta, palletName, callName) {
|
|
|
1469
1664
|
function describeEventFields(meta, palletName, eventName) {
|
|
1470
1665
|
return compactArgsString(getEventFields(meta, palletName, eventName));
|
|
1471
1666
|
}
|
|
1472
|
-
function
|
|
1667
|
+
function hexToBytes4(hex) {
|
|
1473
1668
|
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1474
1669
|
const bytes = new Uint8Array(clean.length / 2);
|
|
1475
1670
|
for (let i = 0;i < clean.length; i += 2) {
|
|
@@ -3198,7 +3393,7 @@ var init_xxh64 = __esm(() => {
|
|
|
3198
3393
|
import { blake2b as blake2b2 } from "@noble/hashes/blake2.js";
|
|
3199
3394
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
3200
3395
|
import { keccak_256 as keccak_2562 } from "@noble/hashes/sha3.js";
|
|
3201
|
-
import { bytesToHex as bytesToHex3, hexToBytes as
|
|
3396
|
+
import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes5 } from "@noble/hashes/utils.js";
|
|
3202
3397
|
function computeHash(algorithm, data) {
|
|
3203
3398
|
const algo = ALGORITHMS[algorithm];
|
|
3204
3399
|
if (!algo) {
|
|
@@ -3212,7 +3407,7 @@ function parseInputData(input) {
|
|
|
3212
3407
|
if (hex.length % 2 !== 0) {
|
|
3213
3408
|
throw new Error(`Invalid hex input: odd number of characters`);
|
|
3214
3409
|
}
|
|
3215
|
-
return
|
|
3410
|
+
return hexToBytes5(hex);
|
|
3216
3411
|
}
|
|
3217
3412
|
return new TextEncoder().encode(input);
|
|
3218
3413
|
}
|
|
@@ -3267,6 +3462,349 @@ var init_hash = __esm(() => {
|
|
|
3267
3462
|
};
|
|
3268
3463
|
});
|
|
3269
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
|
+
|
|
3270
3808
|
// src/completions/complete.ts
|
|
3271
3809
|
var exports_complete = {};
|
|
3272
3810
|
__export(exports_complete, {
|
|
@@ -3628,7 +4166,16 @@ var init_complete = __esm(() => {
|
|
|
3628
4166
|
ext: "extensions",
|
|
3629
4167
|
rpc: "rpc"
|
|
3630
4168
|
};
|
|
3631
|
-
NAMED_COMMANDS = [
|
|
4169
|
+
NAMED_COMMANDS = [
|
|
4170
|
+
"chain",
|
|
4171
|
+
"account",
|
|
4172
|
+
"inspect",
|
|
4173
|
+
"hash",
|
|
4174
|
+
"sign",
|
|
4175
|
+
"completions",
|
|
4176
|
+
"init",
|
|
4177
|
+
"which"
|
|
4178
|
+
];
|
|
3632
4179
|
CHAIN_SUBCOMMANDS = ["add", "info", "list", "remove", "update"];
|
|
3633
4180
|
ACCOUNT_SUBCOMMANDS = [
|
|
3634
4181
|
"add",
|
|
@@ -3660,17 +4207,17 @@ var init_complete = __esm(() => {
|
|
|
3660
4207
|
// src/cli.ts
|
|
3661
4208
|
import cac from "cac";
|
|
3662
4209
|
// package.json
|
|
3663
|
-
var version = "1.
|
|
4210
|
+
var version = "1.21.0";
|
|
3664
4211
|
|
|
3665
4212
|
// src/commands/account.ts
|
|
3666
4213
|
init_accounts_store();
|
|
3667
4214
|
init_accounts();
|
|
3668
4215
|
import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
3669
|
-
import { hexToBytes as
|
|
4216
|
+
import { hexToBytes as nobleHexToBytes2 } from "@noble/hashes/utils.js";
|
|
3670
4217
|
|
|
3671
4218
|
// src/core/h160.ts
|
|
3672
4219
|
import { keccak_256 } from "@noble/hashes/sha3.js";
|
|
3673
|
-
import { bytesToHex as bytesToHex2, hexToBytes } from "@noble/hashes/utils.js";
|
|
4220
|
+
import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
|
|
3674
4221
|
var ETH_DERIVED_SUFFIX_BYTE = 238;
|
|
3675
4222
|
var H160_LEN = 20;
|
|
3676
4223
|
var ACCOUNT_ID_LEN = 32;
|
|
@@ -3722,7 +4269,7 @@ function h160FromHex(input) {
|
|
|
3722
4269
|
if (!isH160Hex(input)) {
|
|
3723
4270
|
throw new Error(`Not a valid 0x-prefixed 20-byte hex string: ${input}`);
|
|
3724
4271
|
}
|
|
3725
|
-
return
|
|
4272
|
+
return hexToBytes2(input.slice(2));
|
|
3726
4273
|
}
|
|
3727
4274
|
|
|
3728
4275
|
// src/commands/account.ts
|
|
@@ -3797,7 +4344,8 @@ function isValidParaId(value) {
|
|
|
3797
4344
|
var ACCOUNT_HELP = `
|
|
3798
4345
|
${BOLD}Usage:${RESET}
|
|
3799
4346
|
$ dot account add <name> <ss58|hex> Add a watch-only address (no secret)
|
|
3800
|
-
$ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic
|
|
4347
|
+
$ dot account add <name> --secret <s> [--path <derivation>] Import from BIP39 mnemonic or 32-byte hex seed
|
|
4348
|
+
$ dot account add <name> --secret 0x<128 hex> Import a raw 64-byte sr25519 private key (no --path)
|
|
3801
4349
|
$ dot account add <name> --env <VAR> [--path <derivation>] Import account backed by env variable
|
|
3802
4350
|
$ dot account add <name> --parachain <id> --parachain-type <t> Derive a parachain sovereign (t = child|sibling)
|
|
3803
4351
|
$ dot account add <name> --pallet-id <8 chars or 0x hex> Derive a pallet sovereign (e.g. py/trsry)
|
|
@@ -3814,6 +4362,7 @@ ${BOLD}Usage:${RESET}
|
|
|
3814
4362
|
${BOLD}Examples:${RESET}
|
|
3815
4363
|
$ dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|
|
3816
4364
|
$ dot account add treasury --secret "word1 word2 ... word12"
|
|
4365
|
+
$ dot account add raw-key --secret 0x<128-hex-char sr25519 expanded secret>
|
|
3817
4366
|
$ dot account add ci-signer --env MY_SECRET --path //ci
|
|
3818
4367
|
$ dot account add Treasury --pallet-id py/trsry
|
|
3819
4368
|
$ dot account add Bounties --pallet-id 0x70792f626f756e74
|
|
@@ -3841,16 +4390,21 @@ ${BOLD}Examples:${RESET}
|
|
|
3841
4390
|
|
|
3842
4391
|
${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
3843
4392
|
Use --env to keep secrets off disk entirely.
|
|
3844
|
-
|
|
4393
|
+
--secret accepts a BIP39 mnemonic, a 0x 32-byte hex seed, or a
|
|
4394
|
+
0x 64-byte raw sr25519 private key (the value --show-secret prints).
|
|
4395
|
+
Raw private keys cannot be HD-derived, so --path is rejected for them.${RESET}
|
|
3845
4396
|
`.trimStart();
|
|
3846
4397
|
function registerAccountCommands(cli) {
|
|
3847
|
-
cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").alias("accounts").option("--secret <value>", "Secret
|
|
4398
|
+
cli.command("account [action] [...names]", "Manage local accounts (create, import, list, remove, export)").alias("accounts").option("--secret <value>", "Secret for import: BIP39 mnemonic, 0x 32-byte hex seed, or 0x 64-byte raw private key").option("--env <varName>", "Environment variable name holding the secret").option("--path <derivation>", "Derivation path (e.g. //staking, //polkadot//0/wallet)").option("--parachain <id>", "Derive a parachain sovereign account (requires --parachain-type)").option("--parachain-type <type>", "Parachain sovereign type: child or sibling").option("--pallet-id <id>", "Derive a pallet sovereign account from an 8-byte PalletId").option("--prefix <number>", "SS58 prefix for address encoding (default: 42)").option("--file <path>", "Input/output file for batch import/export").option("--overwrite", "Overwrite existing accounts on batch import").option("--dry-run", "Preview batch import without applying changes").option("--include-secrets", "Include secrets in export (redacted by default)").option("--watch-only", "Export only watch-only accounts").option("--show-secret", "Reveal the 64-byte sr25519 expanded private key (inspect only)").action(async (action, names, opts) => {
|
|
3848
4399
|
if (!action) {
|
|
3849
4400
|
if (process.argv[2] === "accounts")
|
|
3850
4401
|
return accountList(opts);
|
|
3851
4402
|
console.log(ACCOUNT_HELP);
|
|
3852
4403
|
return;
|
|
3853
4404
|
}
|
|
4405
|
+
const rawSecret = rawArgValue("--secret");
|
|
4406
|
+
if (rawSecret != null)
|
|
4407
|
+
opts.secret = rawSecret;
|
|
3854
4408
|
switch (action) {
|
|
3855
4409
|
case "new":
|
|
3856
4410
|
case "create":
|
|
@@ -3900,7 +4454,7 @@ async function accountCreate(name, opts) {
|
|
|
3900
4454
|
const { mnemonic, publicKey } = createNewAccount(path);
|
|
3901
4455
|
const hexPub = publicKeyToHex(publicKey);
|
|
3902
4456
|
const address = toSs58(publicKey);
|
|
3903
|
-
const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (
|
|
4457
|
+
const { deriveBandersnatchMember: deriveBandersnatchMember2 } = await Promise.resolve().then(() => (init_lib(), exports_lib));
|
|
3904
4458
|
const bandersnatch = {};
|
|
3905
4459
|
bandersnatch[""] = publicKeyToHex(deriveBandersnatchMember2(mnemonic));
|
|
3906
4460
|
bandersnatch.candidate = publicKeyToHex(deriveBandersnatchMember2(mnemonic, "candidate"));
|
|
@@ -4492,8 +5046,9 @@ async function accountInspect(input, opts) {
|
|
|
4492
5046
|
}
|
|
4493
5047
|
}
|
|
4494
5048
|
const ss58 = toSs58(publicKeyHex, prefix);
|
|
4495
|
-
const h160Hex = toEip55(accountIdToH160(
|
|
5049
|
+
const h160Hex = toEip55(accountIdToH160(nobleHexToBytes2(publicKeyHex.slice(2))));
|
|
4496
5050
|
let privateKeyHex;
|
|
5051
|
+
let revealedSecret;
|
|
4497
5052
|
if (opts.showSecret) {
|
|
4498
5053
|
if (!name) {
|
|
4499
5054
|
console.error("--show-secret requires an account name; raw addresses and hex keys have no secret to reveal.");
|
|
@@ -4509,6 +5064,14 @@ async function accountInspect(input, opts) {
|
|
|
4509
5064
|
console.error(err.message);
|
|
4510
5065
|
process.exit(1);
|
|
4511
5066
|
}
|
|
5067
|
+
if (storedAccount?.secret !== undefined && !isEnvSecret(storedAccount.secret)) {
|
|
5068
|
+
const kind = secretKind(storedAccount.secret);
|
|
5069
|
+
if (kind === "mnemonic") {
|
|
5070
|
+
revealedSecret = { label: "Mnemonic", field: "mnemonic", value: storedAccount.secret };
|
|
5071
|
+
} else if (kind === "seed") {
|
|
5072
|
+
revealedSecret = { label: "Seed", field: "seed", value: storedAccount.secret };
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
4512
5075
|
}
|
|
4513
5076
|
let kindLabel;
|
|
4514
5077
|
let sourceLine;
|
|
@@ -4587,6 +5150,8 @@ async function accountInspect(input, opts) {
|
|
|
4587
5150
|
result.env = envLine.replace(/^\$/, "");
|
|
4588
5151
|
if (bandersnatch && Object.keys(bandersnatch).length > 0)
|
|
4589
5152
|
result.bandersnatch = bandersnatch;
|
|
5153
|
+
if (revealedSecret)
|
|
5154
|
+
result[revealedSecret.field] = revealedSecret.value;
|
|
4590
5155
|
if (privateKeyHex)
|
|
4591
5156
|
result.privateKey = privateKeyHex;
|
|
4592
5157
|
console.log(formatJson(result));
|
|
@@ -4618,6 +5183,12 @@ async function accountInspect(input, opts) {
|
|
|
4618
5183
|
}
|
|
4619
5184
|
}
|
|
4620
5185
|
console.log(` ${BOLD}Prefix:${RESET} ${prefix}`);
|
|
5186
|
+
if (revealedSecret) {
|
|
5187
|
+
console.log(` ${BOLD}${`${revealedSecret.label}:`.padEnd(13)}${RESET}${revealedSecret.value}`);
|
|
5188
|
+
if (derivationLine) {
|
|
5189
|
+
console.log(` ${YELLOW}(derived with ${derivationLine} — re-import needs --path ${derivationLine})${RESET}`);
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
4621
5192
|
if (privateKeyHex) {
|
|
4622
5193
|
console.log(` ${BOLD}Private Key:${RESET} ${privateKeyHex}`);
|
|
4623
5194
|
console.log(` ${YELLOW}(sr25519 expanded, 64 bytes — never share)${RESET}`);
|
|
@@ -5540,7 +6111,7 @@ function registerCompletionsCommand(cli) {
|
|
|
5540
6111
|
process.exit(1);
|
|
5541
6112
|
}
|
|
5542
6113
|
const instructions = SETUP_INSTRUCTIONS[shell];
|
|
5543
|
-
if (instructions) {
|
|
6114
|
+
if (instructions && process.stdout.isTTY) {
|
|
5544
6115
|
process.stderr.write(`${instructions}
|
|
5545
6116
|
`);
|
|
5546
6117
|
}
|
|
@@ -6321,30 +6892,9 @@ async function handleExtensions(target, opts) {
|
|
|
6321
6892
|
|
|
6322
6893
|
// src/commands/hash.ts
|
|
6323
6894
|
init_hash();
|
|
6895
|
+
init_input();
|
|
6324
6896
|
init_output();
|
|
6325
6897
|
init_errors();
|
|
6326
|
-
import { readFile as readFile5 } from "node:fs/promises";
|
|
6327
|
-
async function resolveInput(data, opts) {
|
|
6328
|
-
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
6329
|
-
if (sources > 1) {
|
|
6330
|
-
throw new CliError("Provide only one of: inline data, --file, or --stdin");
|
|
6331
|
-
}
|
|
6332
|
-
if (sources === 0) {
|
|
6333
|
-
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
6334
|
-
}
|
|
6335
|
-
if (opts.file) {
|
|
6336
|
-
const buf = await readFile5(opts.file);
|
|
6337
|
-
return new Uint8Array(buf);
|
|
6338
|
-
}
|
|
6339
|
-
if (opts.stdin) {
|
|
6340
|
-
const chunks = [];
|
|
6341
|
-
for await (const chunk of process.stdin) {
|
|
6342
|
-
chunks.push(chunk);
|
|
6343
|
-
}
|
|
6344
|
-
return new Uint8Array(Buffer.concat(chunks));
|
|
6345
|
-
}
|
|
6346
|
-
return parseInputData(data);
|
|
6347
|
-
}
|
|
6348
6898
|
function printAlgorithmHelp() {
|
|
6349
6899
|
console.log(`${BOLD}Usage:${RESET} dot hash <algorithm> <data> [options]
|
|
6350
6900
|
`);
|
|
@@ -6373,7 +6923,7 @@ function registerHashCommand(cli) {
|
|
|
6373
6923
|
if (!isValidAlgorithm(algorithm)) {
|
|
6374
6924
|
throw new CliError(suggestMessage("algorithm", algorithm, getAlgorithmNames()));
|
|
6375
6925
|
}
|
|
6376
|
-
const input = await
|
|
6926
|
+
const input = await resolveDataInput(data, opts);
|
|
6377
6927
|
const hash = computeHash(algorithm, input);
|
|
6378
6928
|
const hexHash = toHex2(hash);
|
|
6379
6929
|
if (isJsonOutput(opts)) {
|
|
@@ -7279,9 +7829,9 @@ async function handleRpc(method, args, opts) {
|
|
|
7279
7829
|
// src/commands/sign.ts
|
|
7280
7830
|
init_accounts();
|
|
7281
7831
|
init_hash();
|
|
7832
|
+
init_input();
|
|
7282
7833
|
init_output();
|
|
7283
7834
|
init_errors();
|
|
7284
|
-
import { readFile as readFile6 } from "node:fs/promises";
|
|
7285
7835
|
var SUPPORTED_TYPES = ["sr25519"];
|
|
7286
7836
|
function isSupportedType(type) {
|
|
7287
7837
|
return SUPPORTED_TYPES.includes(type.toLowerCase());
|
|
@@ -7292,27 +7842,6 @@ function variantName(type) {
|
|
|
7292
7842
|
return "Sr25519";
|
|
7293
7843
|
}
|
|
7294
7844
|
}
|
|
7295
|
-
async function resolveInput2(data, opts) {
|
|
7296
|
-
const sources = [data !== undefined, !!opts.file, !!opts.stdin].filter(Boolean).length;
|
|
7297
|
-
if (sources > 1) {
|
|
7298
|
-
throw new CliError("Provide only one of: inline data, --file, or --stdin");
|
|
7299
|
-
}
|
|
7300
|
-
if (sources === 0) {
|
|
7301
|
-
throw new CliError("No input provided. Pass data as argument, or use --file or --stdin");
|
|
7302
|
-
}
|
|
7303
|
-
if (opts.file) {
|
|
7304
|
-
const buf = await readFile6(opts.file);
|
|
7305
|
-
return new Uint8Array(buf);
|
|
7306
|
-
}
|
|
7307
|
-
if (opts.stdin) {
|
|
7308
|
-
const chunks = [];
|
|
7309
|
-
for await (const chunk of process.stdin) {
|
|
7310
|
-
chunks.push(chunk);
|
|
7311
|
-
}
|
|
7312
|
-
return new Uint8Array(Buffer.concat(chunks));
|
|
7313
|
-
}
|
|
7314
|
-
return parseInputData(data);
|
|
7315
|
-
}
|
|
7316
7845
|
function printSignHelp() {
|
|
7317
7846
|
console.log(`${BOLD}Usage:${RESET} dot sign <message> --from <account> [options]
|
|
7318
7847
|
`);
|
|
@@ -7346,7 +7875,7 @@ function registerSignCommand(cli) {
|
|
|
7346
7875
|
if (!isSupportedType(type)) {
|
|
7347
7876
|
throw new CliError(`Unsupported signature type "${opts.type}". Supported: ${SUPPORTED_TYPES.join(", ")}`);
|
|
7348
7877
|
}
|
|
7349
|
-
const input = await
|
|
7878
|
+
const input = await resolveDataInput(message, opts);
|
|
7350
7879
|
const keypair = await resolveAccountKeypair(opts.from);
|
|
7351
7880
|
const signature = keypair.sign(input);
|
|
7352
7881
|
const hexMessage = toHex2(input);
|
|
@@ -8718,7 +9247,7 @@ function buildGeneralTx(meta, callData, userExtOverrides) {
|
|
|
8718
9247
|
}
|
|
8719
9248
|
function watchTransaction(observable, level, options) {
|
|
8720
9249
|
const spinner = new Spinner;
|
|
8721
|
-
return new Promise((
|
|
9250
|
+
return new Promise((resolve2, reject) => {
|
|
8722
9251
|
let settled = false;
|
|
8723
9252
|
spinner.start(options?.unsigned ? "Submitting..." : "Signing...");
|
|
8724
9253
|
const subscription = observable.subscribe({
|
|
@@ -8738,7 +9267,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8738
9267
|
spinner.succeed("Broadcasted");
|
|
8739
9268
|
settled = true;
|
|
8740
9269
|
subscription.unsubscribe();
|
|
8741
|
-
|
|
9270
|
+
resolve2(event);
|
|
8742
9271
|
} else {
|
|
8743
9272
|
spinner.succeed("Broadcasted");
|
|
8744
9273
|
spinner.start("In best block...");
|
|
@@ -8750,7 +9279,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8750
9279
|
spinner.succeed(`In best block #${event.block.number}`);
|
|
8751
9280
|
settled = true;
|
|
8752
9281
|
subscription.unsubscribe();
|
|
8753
|
-
|
|
9282
|
+
resolve2(event);
|
|
8754
9283
|
} else {
|
|
8755
9284
|
spinner.succeed(`In best block #${event.block.number}`);
|
|
8756
9285
|
spinner.start("Finalizing...");
|
|
@@ -8762,7 +9291,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8762
9291
|
case "finalized":
|
|
8763
9292
|
spinner.succeed(`Finalized in block #${event.block.number}`);
|
|
8764
9293
|
settled = true;
|
|
8765
|
-
|
|
9294
|
+
resolve2(event);
|
|
8766
9295
|
break;
|
|
8767
9296
|
}
|
|
8768
9297
|
},
|
|
@@ -8776,7 +9305,7 @@ function watchTransaction(observable, level, options) {
|
|
|
8776
9305
|
});
|
|
8777
9306
|
}
|
|
8778
9307
|
function watchTransactionJson(observable, level, options) {
|
|
8779
|
-
return new Promise((
|
|
9308
|
+
return new Promise((resolve2, reject) => {
|
|
8780
9309
|
let settled = false;
|
|
8781
9310
|
const subscription = observable.subscribe({
|
|
8782
9311
|
next(event) {
|
|
@@ -8793,7 +9322,7 @@ function watchTransactionJson(observable, level, options) {
|
|
|
8793
9322
|
if (level === "broadcast") {
|
|
8794
9323
|
settled = true;
|
|
8795
9324
|
subscription.unsubscribe();
|
|
8796
|
-
|
|
9325
|
+
resolve2(event);
|
|
8797
9326
|
}
|
|
8798
9327
|
break;
|
|
8799
9328
|
case "txBestBlocksState":
|
|
@@ -8802,13 +9331,13 @@ function watchTransactionJson(observable, level, options) {
|
|
|
8802
9331
|
if (level === "best-block") {
|
|
8803
9332
|
settled = true;
|
|
8804
9333
|
subscription.unsubscribe();
|
|
8805
|
-
|
|
9334
|
+
resolve2(event);
|
|
8806
9335
|
}
|
|
8807
9336
|
}
|
|
8808
9337
|
break;
|
|
8809
9338
|
case "finalized":
|
|
8810
9339
|
settled = true;
|
|
8811
|
-
|
|
9340
|
+
resolve2(event);
|
|
8812
9341
|
break;
|
|
8813
9342
|
}
|
|
8814
9343
|
},
|
|
@@ -8821,119 +9350,91 @@ function watchTransactionJson(observable, level, options) {
|
|
|
8821
9350
|
});
|
|
8822
9351
|
}
|
|
8823
9352
|
|
|
8824
|
-
// src/commands/
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
init_bandersnatch();
|
|
9353
|
+
// src/commands/workspace.ts
|
|
9354
|
+
init_store();
|
|
9355
|
+
init_workspace();
|
|
8828
9356
|
init_output();
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
${BOLD}Options:${RESET}
|
|
8838
|
-
--context <value> Blake2b context for keyed derivation (e.g. "candidate")
|
|
8839
|
-
|
|
8840
|
-
${BOLD}Examples:${RESET}
|
|
8841
|
-
$ dot verifiable alice Unkeyed derivation (lite person)
|
|
8842
|
-
$ dot verifiable alice --context candidate Keyed with "candidate" (full person)
|
|
8843
|
-
$ dot verifiable my-account --context candidate
|
|
8844
|
-
|
|
8845
|
-
${BOLD}How it works:${RESET}
|
|
8846
|
-
|
|
8847
|
-
Mnemonic (12/24 words)
|
|
8848
|
-
│ mnemonicToEntropy() (raw BIP39 entropy, NOT miniSecret)
|
|
8849
|
-
▼
|
|
8850
|
-
blake2b256(entropy, context?) keyed or unkeyed
|
|
8851
|
-
▼
|
|
8852
|
-
member_from_entropy() verifiablejs WASM (Bandersnatch curve)
|
|
8853
|
-
▼
|
|
8854
|
-
32-byte member key for on-chain member set registration
|
|
8855
|
-
`.trimStart();
|
|
8856
|
-
function registerVerifiableCommands(cli) {
|
|
8857
|
-
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) => {
|
|
8858
|
-
if (!account) {
|
|
8859
|
-
console.log(VERIFIABLE_HELP);
|
|
8860
|
-
return;
|
|
8861
|
-
}
|
|
8862
|
-
return deriveVerifiable(account, opts);
|
|
8863
|
-
});
|
|
8864
|
-
}
|
|
8865
|
-
async function deriveVerifiable(account, opts) {
|
|
8866
|
-
const mnemonic = await resolveMnemonic(account);
|
|
8867
|
-
const memberKey = deriveBandersnatchMember(mnemonic, opts.context);
|
|
8868
|
-
const memberKeyHex = publicKeyToHex(memberKey);
|
|
8869
|
-
if (!isDevAccount(account)) {
|
|
8870
|
-
const accountsFile = await loadAccounts();
|
|
8871
|
-
const stored = findAccount(accountsFile, account);
|
|
8872
|
-
if (stored) {
|
|
8873
|
-
if (!stored.bandersnatch)
|
|
8874
|
-
stored.bandersnatch = {};
|
|
8875
|
-
stored.bandersnatch[opts.context ?? ""] = memberKeyHex;
|
|
8876
|
-
await saveAccounts(accountsFile);
|
|
8877
|
-
}
|
|
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.`);
|
|
8878
9365
|
}
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
};
|
|
8884
|
-
if (opts.context)
|
|
8885
|
-
result.context = opts.context;
|
|
8886
|
-
console.log(formatJson(result));
|
|
8887
|
-
} else {
|
|
8888
|
-
printHeading("Bandersnatch Member Key");
|
|
8889
|
-
console.log(` ${BOLD}Account:${RESET} ${account}`);
|
|
8890
|
-
if (opts.context)
|
|
8891
|
-
console.log(` ${BOLD}Context:${RESET} ${opts.context}`);
|
|
8892
|
-
console.log(` ${BOLD}Member Key:${RESET} ${memberKeyHex}`);
|
|
8893
|
-
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}.`);
|
|
8894
9370
|
}
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
if (
|
|
8898
|
-
|
|
9371
|
+
const warnings = [];
|
|
9372
|
+
const parentWorkspace = findWorkspace(dir, home);
|
|
9373
|
+
if (parentWorkspace) {
|
|
9374
|
+
warnings.push(`This workspace shadows ${parentWorkspace} for commands run below ${dir}.`);
|
|
8899
9375
|
}
|
|
8900
|
-
const
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
const available = [...DEV_NAMES, ...accountsFile.accounts.map((a) => a.name)].sort((a, b) => a.localeCompare(b));
|
|
8904
|
-
const suggestions = findClosest(account, available);
|
|
8905
|
-
const hint = suggestions.length > 0 ? `
|
|
8906
|
-
Did you mean: ${suggestions.join(", ")}?` : "";
|
|
8907
|
-
const list = available.map((a) => `
|
|
8908
|
-
- ${a}`).join("");
|
|
8909
|
-
throw new Error(`Unknown account "${account}".${hint}
|
|
8910
|
-
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.`);
|
|
8911
9379
|
}
|
|
8912
|
-
|
|
8913
|
-
|
|
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
|
+
`);
|
|
8914
9393
|
}
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
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;
|
|
8918
9404
|
}
|
|
8919
|
-
|
|
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));
|
|
8920
9412
|
}
|
|
8921
9413
|
|
|
8922
9414
|
// src/config/store.ts
|
|
8923
9415
|
init_errors();
|
|
8924
9416
|
init_types();
|
|
8925
|
-
|
|
8926
|
-
import {
|
|
8927
|
-
import {
|
|
8928
|
-
|
|
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()) {
|
|
8929
9422
|
const override = process.env.DOT_HOME;
|
|
8930
|
-
|
|
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;
|
|
8931
9432
|
}
|
|
8932
9433
|
function getConfigPath2() {
|
|
8933
|
-
return
|
|
9434
|
+
return join5(getConfigDir2(), "config.json");
|
|
8934
9435
|
}
|
|
8935
9436
|
async function ensureDir3(dir) {
|
|
8936
|
-
await
|
|
9437
|
+
await mkdir4(dir, { recursive: true });
|
|
8937
9438
|
}
|
|
8938
9439
|
async function fileExists3(path) {
|
|
8939
9440
|
try {
|
|
@@ -8947,7 +9448,7 @@ async function loadConfig2() {
|
|
|
8947
9448
|
await ensureDir3(getConfigDir2());
|
|
8948
9449
|
const configPath = getConfigPath2();
|
|
8949
9450
|
if (await fileExists3(configPath)) {
|
|
8950
|
-
const saved = JSON.parse(await
|
|
9451
|
+
const saved = JSON.parse(await readFile6(configPath, "utf-8"));
|
|
8951
9452
|
const chains = {};
|
|
8952
9453
|
for (const [name, defaultConfig] of Object.entries(DEFAULT_CONFIG.chains)) {
|
|
8953
9454
|
chains[name] = saved.chains[name] ? { ...defaultConfig, ...saved.chains[name] } : defaultConfig;
|
|
@@ -8970,7 +9471,7 @@ async function saveConfig2(config) {
|
|
|
8970
9471
|
|
|
8971
9472
|
// src/core/file-loader.ts
|
|
8972
9473
|
init_errors();
|
|
8973
|
-
import { access as access4, readFile as
|
|
9474
|
+
import { access as access4, readFile as readFile7 } from "node:fs/promises";
|
|
8974
9475
|
import { parse as parseYaml } from "yaml";
|
|
8975
9476
|
var CATEGORIES = ["tx", "query", "const", "apis"];
|
|
8976
9477
|
var FILE_EXTENSIONS = [".json", ".yaml", ".yml"];
|
|
@@ -9035,7 +9536,7 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
9035
9536
|
} catch {
|
|
9036
9537
|
throw new CliError(`File not found: ${filePath}`);
|
|
9037
9538
|
}
|
|
9038
|
-
const rawText = await
|
|
9539
|
+
const rawText = await readFile7(filePath, "utf-8");
|
|
9039
9540
|
if (!rawText.trim()) {
|
|
9040
9541
|
throw new CliError(`File is empty: ${filePath}`);
|
|
9041
9542
|
}
|
|
@@ -9107,8 +9608,8 @@ async function loadCommandFile(filePath, cliVars) {
|
|
|
9107
9608
|
// src/core/update-notifier.ts
|
|
9108
9609
|
init_store();
|
|
9109
9610
|
import { readFileSync } from "node:fs";
|
|
9110
|
-
import { mkdir as
|
|
9111
|
-
import { join as
|
|
9611
|
+
import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
|
|
9612
|
+
import { join as join6 } from "node:path";
|
|
9112
9613
|
var CACHE_FILE = "update-check.json";
|
|
9113
9614
|
var STALE_MS = 24 * 60 * 60 * 1000;
|
|
9114
9615
|
var FETCH_TIMEOUT_MS = 5000;
|
|
@@ -9164,7 +9665,7 @@ function buildNotificationBox(current, latest) {
|
|
|
9164
9665
|
`);
|
|
9165
9666
|
}
|
|
9166
9667
|
function getCachePath() {
|
|
9167
|
-
return
|
|
9668
|
+
return join6(getConfigDir(), CACHE_FILE);
|
|
9168
9669
|
}
|
|
9169
9670
|
function readCache() {
|
|
9170
9671
|
try {
|
|
@@ -9176,7 +9677,7 @@ function readCache() {
|
|
|
9176
9677
|
}
|
|
9177
9678
|
async function writeCache(cache) {
|
|
9178
9679
|
try {
|
|
9179
|
-
await
|
|
9680
|
+
await mkdir5(getConfigDir(), { recursive: true });
|
|
9180
9681
|
await writeFile6(getCachePath(), `${JSON.stringify(cache)}
|
|
9181
9682
|
`);
|
|
9182
9683
|
} catch {}
|
|
@@ -9204,7 +9705,7 @@ function startBackgroundCheck(currentVersion) {
|
|
|
9204
9705
|
async function waitForPendingCheck() {
|
|
9205
9706
|
if (!pendingCheck)
|
|
9206
9707
|
return;
|
|
9207
|
-
const timeout = new Promise((
|
|
9708
|
+
const timeout = new Promise((resolve2) => setTimeout(resolve2, EXIT_WAIT_TIMEOUT_MS));
|
|
9208
9709
|
await Promise.race([pendingCheck.catch(() => {}), timeout]);
|
|
9209
9710
|
pendingCheck = null;
|
|
9210
9711
|
}
|
|
@@ -9224,6 +9725,117 @@ function getUpdateNotification(currentVersion) {
|
|
|
9224
9725
|
return null;
|
|
9225
9726
|
}
|
|
9226
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
|
+
|
|
9227
9839
|
// src/utils/errors.ts
|
|
9228
9840
|
class CliError2 extends Error {
|
|
9229
9841
|
constructor(message) {
|
|
@@ -9346,15 +9958,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9346
9958
|
process.exit(0);
|
|
9347
9959
|
})();
|
|
9348
9960
|
} else {
|
|
9349
|
-
let
|
|
9350
|
-
for (let i = 0;i < argv.length; i++) {
|
|
9351
|
-
if (argv[i] === "--at" && i + 1 < argv.length)
|
|
9352
|
-
return argv[i + 1];
|
|
9353
|
-
if (argv[i].startsWith("--at="))
|
|
9354
|
-
return argv[i].slice(5);
|
|
9355
|
-
}
|
|
9356
|
-
return;
|
|
9357
|
-
}, collectVarFlags = function(argv) {
|
|
9961
|
+
let collectVarFlags = function(argv) {
|
|
9358
9962
|
const vars = [];
|
|
9359
9963
|
for (let i = 0;i < argv.length; i++) {
|
|
9360
9964
|
if (argv[i] === "--var" && i + 1 < argv.length) {
|
|
@@ -9410,8 +10014,10 @@ if (process.argv[2] === "__complete") {
|
|
|
9410
10014
|
console.log(" hash Hash utilities");
|
|
9411
10015
|
console.log(" sign Sign a message with an account keypair");
|
|
9412
10016
|
console.log(" parachain Derive parachain sovereign accounts (deprecated \u2014 use `account inspect --parachain`)");
|
|
9413
|
-
console.log(" verifiable
|
|
10017
|
+
console.log(" verifiable Bandersnatch member keys, ring-VRF proofs, sign/verify");
|
|
9414
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)");
|
|
9415
10021
|
console.log();
|
|
9416
10022
|
console.log("Global options:");
|
|
9417
10023
|
console.log(" --chain <name> Target chain (required)");
|
|
@@ -9423,12 +10029,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9423
10029
|
};
|
|
9424
10030
|
startBackgroundCheck(version);
|
|
9425
10031
|
const cli = cac("dot");
|
|
9426
|
-
cli
|
|
9427
|
-
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
9428
|
-
cli.option("--output <format>", "Output format: pretty or json", {
|
|
9429
|
-
default: "pretty"
|
|
9430
|
-
});
|
|
9431
|
-
cli.option("--json", "Output as JSON (shorthand for --output json)");
|
|
10032
|
+
registerGlobalOptions(cli);
|
|
9432
10033
|
registerChainCommands(cli);
|
|
9433
10034
|
registerInspectCommand(cli);
|
|
9434
10035
|
registerMetadataCommand(cli);
|
|
@@ -9438,6 +10039,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9438
10039
|
registerParachainCommand(cli);
|
|
9439
10040
|
registerCompletionsCommand(cli);
|
|
9440
10041
|
registerVerifiableCommands(cli);
|
|
10042
|
+
registerWorkspaceCommands(cli);
|
|
9441
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)", {
|
|
9442
10044
|
default: "finalized"
|
|
9443
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) => {
|
|
@@ -9445,7 +10047,7 @@ if (process.argv[2] === "__complete") {
|
|
|
9445
10047
|
printHelp();
|
|
9446
10048
|
return;
|
|
9447
10049
|
}
|
|
9448
|
-
const atRaw =
|
|
10050
|
+
const atRaw = readRawOptionValue2("at");
|
|
9449
10051
|
if (isFilePath(dotpath)) {
|
|
9450
10052
|
const cliVars = collectVarFlags(process.argv);
|
|
9451
10053
|
const cmd = await loadCommandFile(dotpath, cliVars);
|