@tokamak-private-dapps/private-state-cli 2.1.0 → 2.1.2
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/CHANGELOG.md +51 -0
- package/README.md +96 -34
- package/cli-assistant.html +11 -5
- package/commands/account.mjs +43 -0
- package/commands/channel.mjs +66 -0
- package/commands/index.mjs +62 -0
- package/commands/investigator.mjs +11 -0
- package/commands/notes.mjs +39 -0
- package/commands/system.mjs +49 -0
- package/commands/wallet.mjs +86 -0
- package/investigator/README.md +16 -6
- package/investigator/app.js +612 -17
- package/investigator/index.html +153 -90
- package/investigator/styles.css +277 -28
- package/lib/private-state-cli-command-registry.mjs +71 -28
- package/lib/private-state-note-delivery.mjs +7 -14
- package/lib/private-state-runtime-management.mjs +0 -1
- package/lib/runtime.mjs +11531 -0
- package/package.json +3 -2
- package/private-state-bridge-cli.mjs +2 -11053
- package/lib/private-state-tokamak-helpers.mjs +0 -184
|
@@ -18,8 +18,34 @@ export const PRIVATE_STATE_CLI_FIELD_CATALOG = Object.freeze({
|
|
|
18
18
|
type: "password",
|
|
19
19
|
placeholder: "https://example-rpc",
|
|
20
20
|
valueLabel: "<URL>",
|
|
21
|
-
hint: "
|
|
21
|
+
hint: "Required by set rpc. Saved to ~/tokamak-private-channels/workspace/<network>/rpc-config.env.",
|
|
22
22
|
option: "--rpc-url",
|
|
23
|
+
},
|
|
24
|
+
provider: {
|
|
25
|
+
label: "RPC Provider",
|
|
26
|
+
type: "select",
|
|
27
|
+
options: ["alchemy", "ankr", "chainstack", "chainnodes", "quicknode"],
|
|
28
|
+
valueLabel: "<PROVIDER>",
|
|
29
|
+
hint: "Optional for set rpc. Uses the built-in provider table for eth_getLogs request rate and block range cap.",
|
|
30
|
+
option: "--provider",
|
|
31
|
+
optional: true,
|
|
32
|
+
},
|
|
33
|
+
logRequestsPerSecond: {
|
|
34
|
+
label: "Log Requests Per Second",
|
|
35
|
+
type: "text",
|
|
36
|
+
placeholder: "7.497",
|
|
37
|
+
valueLabel: "<N>",
|
|
38
|
+
hint: "Optional for set rpc. Required with --block-range-cap when --provider is not used.",
|
|
39
|
+
option: "--log-requests-per-second",
|
|
40
|
+
optional: true,
|
|
41
|
+
},
|
|
42
|
+
blockRangeCap: {
|
|
43
|
+
label: "Block Range Cap",
|
|
44
|
+
type: "text",
|
|
45
|
+
placeholder: "10",
|
|
46
|
+
valueLabel: "<N>",
|
|
47
|
+
hint: "Optional for set rpc. Required with --log-requests-per-second when --provider is not used.",
|
|
48
|
+
option: "--block-range-cap",
|
|
23
49
|
optional: true,
|
|
24
50
|
},
|
|
25
51
|
account: {
|
|
@@ -236,6 +262,18 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
236
262
|
fields: [],
|
|
237
263
|
usage: "no options",
|
|
238
264
|
},
|
|
265
|
+
{
|
|
266
|
+
id: "set-rpc",
|
|
267
|
+
display: "set rpc",
|
|
268
|
+
description: "Configure the network RPC URL and fixed eth_getLogs scan limits.",
|
|
269
|
+
fields: ["network", "rpcUrl", "provider", "logRequestsPerSecond", "blockRangeCap"],
|
|
270
|
+
usage: "--network, --rpc-url, and either --provider or both --log-requests-per-second and --block-range-cap",
|
|
271
|
+
help: [
|
|
272
|
+
"Writes ~/tokamak-private-channels/workspace/<network>/rpc-config.env",
|
|
273
|
+
"Built-in provider limits: ankr=27 calls/s and 3000 blocks, chainstack=22.5 calls/s and 100 blocks, chainnodes=22.5 calls/s and 20000 blocks, quicknode=13.5 calls/s and 5 blocks, alchemy=7.497 calls/s and 10 blocks",
|
|
274
|
+
"All bridge-facing and wallet commands read RPC settings from this file and do not accept --rpc-url",
|
|
275
|
+
],
|
|
276
|
+
},
|
|
239
277
|
{
|
|
240
278
|
id: "help-commands",
|
|
241
279
|
display: "help commands",
|
|
@@ -278,8 +316,8 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
278
316
|
id: "help-transaction-fees",
|
|
279
317
|
display: "help transaction-fees",
|
|
280
318
|
description: "Estimate ETH and USD fees for transaction-sending commands from packaged measured gas data and live network fee data.",
|
|
281
|
-
fields: ["network", "
|
|
282
|
-
usage: "--network
|
|
319
|
+
fields: ["network", "json"],
|
|
320
|
+
usage: "--network and optional --json",
|
|
283
321
|
help: [
|
|
284
322
|
"Uses packages/apps/private-state/cli/assets/tx-fees.json as the measured gas source packaged with the CLI",
|
|
285
323
|
"Reads live fee data from the selected network RPC and live ETH/USD from CoinGecko",
|
|
@@ -314,15 +352,15 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
314
352
|
id: "account-get-bridge-fund",
|
|
315
353
|
display: "account get-bridge-fund",
|
|
316
354
|
description: "Read the local account's current shared bridge vault balance.",
|
|
317
|
-
fields: ["network", "account"
|
|
318
|
-
usage: "--network, --account
|
|
355
|
+
fields: ["network", "account"],
|
|
356
|
+
usage: "--network, --account",
|
|
319
357
|
},
|
|
320
358
|
{
|
|
321
359
|
id: "channel-create",
|
|
322
360
|
display: "channel create",
|
|
323
361
|
description: "Create a bridge channel and initialize its workspace.",
|
|
324
|
-
fields: ["channelName", "joinToll", "network", "account"
|
|
325
|
-
usage: "--channel-name, --join-toll, --network, --account
|
|
362
|
+
fields: ["channelName", "joinToll", "network", "account"],
|
|
363
|
+
usage: "--channel-name, --join-toll, --network, --account",
|
|
326
364
|
help: [
|
|
327
365
|
"Prints the immutable policy snapshot before sending the transaction",
|
|
328
366
|
"Initializes the local channel workspace by replaying channel logs from channel genesis",
|
|
@@ -332,13 +370,16 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
332
370
|
id: "channel-recover-workspace",
|
|
333
371
|
display: "channel recover-workspace",
|
|
334
372
|
description: "Rebuild the local channel workspace from bridge state.",
|
|
335
|
-
fields: ["channelName", "network", "source", "fromGenesis"
|
|
336
|
-
usage: "--channel-name, --network, optional --source, optional --from-genesis
|
|
373
|
+
fields: ["channelName", "network", "source", "fromGenesis"],
|
|
374
|
+
usage: "--channel-name, --network, optional --source, optional --from-genesis",
|
|
337
375
|
help: [
|
|
338
376
|
"By default, --source rpc resumes RPC log scanning from the workspace recovery index when available",
|
|
339
377
|
"--source mirror validates the channel leader's registered checkpoint manifest, downloads only the needed checkpoint or delta bundle, and then replays RPC logs to latest",
|
|
378
|
+
"RPC recovery writes a usable channel workspace checkpoint after each RPC log chunk, so interrupted runs can resume from the last completed chunk",
|
|
379
|
+
"Mirror recovery uses a matching delta bundle when available; otherwise a newer verified full checkpoint replaces the local checkpoint before RPC catch-up",
|
|
340
380
|
"Fails instead of falling back to genesis when no usable recovery index exists",
|
|
341
381
|
"Use --source rpc --from-genesis to ignore the recovery index and replay logs from channel genesis",
|
|
382
|
+
"--from-genesis moves the existing local channel workspace to workspace-rebuild-backups before writing the current-format workspace; local secrets are preserved",
|
|
342
383
|
"Prints RPC log scan progress while rebuilding the workspace",
|
|
343
384
|
],
|
|
344
385
|
},
|
|
@@ -346,8 +387,8 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
346
387
|
id: "channel-set-workspace-mirror",
|
|
347
388
|
display: "channel set-workspace-mirror",
|
|
348
389
|
description: "Register or update the channel leader's workspace mirror base URL.",
|
|
349
|
-
fields: ["channelName", "network", "account", "url"
|
|
350
|
-
usage: "--channel-name, --network, --account, --url
|
|
390
|
+
fields: ["channelName", "network", "account", "url"],
|
|
391
|
+
usage: "--channel-name, --network, --account, --url",
|
|
351
392
|
help: [
|
|
352
393
|
"Only the on-chain channel leader can update the registered mirror URL",
|
|
353
394
|
"The URL points to a server implementing the private-state channel workspace mirror protocol",
|
|
@@ -357,8 +398,8 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
357
398
|
id: "channel-publish-workspace-mirror",
|
|
358
399
|
display: "channel publish-workspace-mirror",
|
|
359
400
|
description: "Build static workspace mirror files for the registered mirror URL.",
|
|
360
|
-
fields: ["channelName", "network", "account", "output", "force"
|
|
361
|
-
usage: "--channel-name, --network, --account, --output, optional --force
|
|
401
|
+
fields: ["channelName", "network", "account", "output", "force"],
|
|
402
|
+
usage: "--channel-name, --network, --account, --output, optional --force",
|
|
362
403
|
help: [
|
|
363
404
|
"Requires the local channel workspace to be current and ahead of the registered mirror checkpoint",
|
|
364
405
|
"--force ignores an unreadable or invalid existing mirror manifest and publishes a full checkpoint without a delta",
|
|
@@ -370,15 +411,15 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
370
411
|
id: "channel-get-meta",
|
|
371
412
|
display: "channel get-meta",
|
|
372
413
|
description: "Read channel existence, manager, vault, toll, refund schedule, and immutable policy snapshot.",
|
|
373
|
-
fields: ["channelName", "network"
|
|
374
|
-
usage: "--channel-name, --network
|
|
414
|
+
fields: ["channelName", "network"],
|
|
415
|
+
usage: "--channel-name, --network",
|
|
375
416
|
},
|
|
376
417
|
{
|
|
377
418
|
id: "account-deposit-bridge",
|
|
378
419
|
display: "account deposit-bridge",
|
|
379
420
|
description: "Deposit canonical tokens into the shared bridge vault.",
|
|
380
|
-
fields: ["amount", "network", "account", "acknowledgeActionImpact"
|
|
381
|
-
usage: "--amount, --network, --account, --acknowledge-action-impact
|
|
421
|
+
fields: ["amount", "network", "account", "acknowledgeActionImpact"],
|
|
422
|
+
usage: "--amount, --network, --account, --acknowledge-action-impact",
|
|
382
423
|
help: [
|
|
383
424
|
"Action impact: emits public L1 approval and bridge funding events that expose the local L1 account, bridge vault, amount, and transaction hashes.",
|
|
384
425
|
"Private note state is not changed by this command.",
|
|
@@ -391,8 +432,8 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
391
432
|
id: "account-withdraw-bridge",
|
|
392
433
|
display: "account withdraw-bridge",
|
|
393
434
|
description: "Withdraw tokens from the shared bridge vault back to the wallet.",
|
|
394
|
-
fields: ["amount", "network", "account", "acknowledgeActionImpact"
|
|
395
|
-
usage: "--amount, --network, --account, --acknowledge-action-impact
|
|
435
|
+
fields: ["amount", "network", "account", "acknowledgeActionImpact"],
|
|
436
|
+
usage: "--amount, --network, --account, --acknowledge-action-impact",
|
|
396
437
|
help: [
|
|
397
438
|
"Action impact: emits a public L1 bridge withdrawal event that exposes the local L1 recipient, bridge vault, amount, and transaction hash.",
|
|
398
439
|
"Private note state is not changed by this command; prior note provenance is not public by default.",
|
|
@@ -405,23 +446,24 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
405
446
|
id: "wallet-recover-workspace",
|
|
406
447
|
display: "wallet recover-workspace",
|
|
407
448
|
description: "Rebuild a recoverable local wallet from on-chain channel state.",
|
|
408
|
-
fields: ["channelName", "network", "account", "fromGenesis"
|
|
409
|
-
usage: "--channel-name, --network, --account, optional --from-genesis
|
|
449
|
+
fields: ["channelName", "network", "account", "fromGenesis"],
|
|
450
|
+
usage: "--channel-name, --network, --account, optional --from-genesis",
|
|
410
451
|
help: [
|
|
411
452
|
"Rebuilds backup metadata from channel state without recreating the spending key",
|
|
412
453
|
"Derives and stores the viewing key when the local account signer can reproduce the registered viewing public key",
|
|
413
|
-
"
|
|
414
|
-
"Fails
|
|
415
|
-
"Use --from-genesis to
|
|
416
|
-
"
|
|
454
|
+
"Before wallet recovery, refreshes stale channel workspace state only when the saved recovery index delta fits the pre-command budget",
|
|
455
|
+
"Fails and asks for channel recover-workspace first when the channel workspace is missing, unusable, or too stale for automatic recovery",
|
|
456
|
+
"Use --from-genesis to restart received-note scanning from channel genesis; it does not rebuild the channel workspace from genesis",
|
|
457
|
+
"Received-note recovery checkpoints after each RPC log chunk during ordinary recovery; --from-genesis still starts received-note scanning from channel genesis",
|
|
458
|
+
"Prints RPC log scan progress while refreshing channel state and rebuilding received-note state",
|
|
417
459
|
],
|
|
418
460
|
},
|
|
419
461
|
{
|
|
420
462
|
id: "channel-join",
|
|
421
463
|
display: "channel join",
|
|
422
464
|
description: "Pay the channel join toll and bind a wallet to a channel-specific L2 identity.",
|
|
423
|
-
fields: ["channelName", "network", "account", "walletSecretPath", "acknowledgeActionImpact"
|
|
424
|
-
usage: "--channel-name, --network, --account, --wallet-secret-path, --acknowledge-action-impact
|
|
465
|
+
fields: ["channelName", "network", "account", "walletSecretPath", "acknowledgeActionImpact"],
|
|
466
|
+
usage: "--channel-name, --network, --account, --wallet-secret-path, --acknowledge-action-impact",
|
|
425
467
|
help: [
|
|
426
468
|
"Refreshes the local channel workspace through the saved recovery index before joining when the scan fits the 10 second pre-command budget",
|
|
427
469
|
"Fails instead of replaying from genesis; run channel recover-workspace --source rpc --from-genesis when a genesis rebuild is required",
|
|
@@ -564,6 +606,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
564
606
|
usage: "--wallet, --network, --amounts, --acknowledge-action-impact, and optional --tx-submitter",
|
|
565
607
|
help: [
|
|
566
608
|
"Refreshes the local channel workspace through the saved recovery index before proving the mint when the scan fits the 10 second pre-command budget",
|
|
609
|
+
"Requires both viewing and spending key capability so the accepted mint can be recovered through the normal note event path",
|
|
567
610
|
"Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy",
|
|
568
611
|
"Action impact: emits public accepted-transition, commitment, encrypted note-delivery, root update, and transaction events.",
|
|
569
612
|
"Private note state changes by creating local note plaintext and public commitments; note owner/value/salt are not public by default.",
|
|
@@ -619,7 +662,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
|
|
|
619
662
|
help: [
|
|
620
663
|
"Refreshes the local channel workspace through the saved recovery index before reading notes when the scan fits the 10 second pre-command budget",
|
|
621
664
|
"Refreshes received-note logs through the saved wallet note recovery index when the scan fits the 10 second pre-command budget",
|
|
622
|
-
"Fails instead of replaying from genesis; run wallet recover-workspace
|
|
665
|
+
"Fails instead of replaying from genesis; run wallet recover-workspace first when explicit wallet recovery is required",
|
|
623
666
|
"Use --export-evidence <PATH> with --acknowledge-full-note-plaintext-export to write a local full-note evidence ZIP for private-state-cli investigator",
|
|
624
667
|
"Evidence export includes all local epochs for the selected wallet, including exited epochs retained for dispute evidence",
|
|
625
668
|
],
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { AbiCoder, ethers } from "ethers";
|
|
3
|
-
import { deriveL2KeysFromSignature
|
|
3
|
+
import { deriveL2KeysFromSignature } from "tokamak-l2js";
|
|
4
4
|
import { jubjub } from "@noble/curves/jubjub";
|
|
5
|
+
import {
|
|
6
|
+
normalizeBytesHex,
|
|
7
|
+
normalizeBytes32Hex,
|
|
8
|
+
poseidonHexFromBytes,
|
|
9
|
+
} from "@tokamak-private-dapps/common-library/tokamak-l2-helpers";
|
|
5
10
|
|
|
6
11
|
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
7
12
|
|
|
@@ -42,18 +47,6 @@ function expect(condition, message) {
|
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
function normalizeBytes32Hex(value) {
|
|
46
|
-
return ethers.hexlify(ethers.zeroPadValue(ethers.hexlify(value), 32)).toLowerCase();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function normalizeBytes16Hex(value) {
|
|
50
|
-
return ethers.hexlify(ethers.zeroPadValue(ethers.hexlify(value), 16)).toLowerCase();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function poseidonHexFromBytes(bytesLike) {
|
|
54
|
-
return ethers.hexlify(poseidon(ethers.getBytes(bytesLike))).toLowerCase();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
50
|
function noteReceivePubKeyFromPoint(point) {
|
|
58
51
|
const affine = point.toAffine();
|
|
59
52
|
return {
|
|
@@ -371,7 +364,7 @@ function decryptFieldEncryptedNoteValue({
|
|
|
371
364
|
encryptionInfo,
|
|
372
365
|
});
|
|
373
366
|
expect(
|
|
374
|
-
|
|
367
|
+
normalizeBytesHex(expectedTag, 16) === normalizeBytesHex(normalized.tag, 16),
|
|
375
368
|
"Encrypted note value integrity tag mismatch.",
|
|
376
369
|
);
|
|
377
370
|
const fieldMask = deriveFieldMask({
|
|
@@ -9,7 +9,6 @@ import { fetchNpmPackageMetadata } from "@tokamak-private-dapps/common-library/n
|
|
|
9
9
|
import {
|
|
10
10
|
normalizePackageVersionToCompatibleBackendVersion,
|
|
11
11
|
readTokamakZkEvmCompatibleBackendVersionFromPackageJson,
|
|
12
|
-
requireCanonicalCompatibleBackendVersion,
|
|
13
12
|
requireExactSemverVersion,
|
|
14
13
|
} from "@tokamak-private-dapps/common-library/proof-backend-versioning";
|
|
15
14
|
import {
|