@tokamak-private-dapps/private-state-cli 2.4.0 → 2.4.1
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 +10 -0
- package/README.md +25 -134
- package/agents.md +146 -0
- package/lib/runtime.mjs +81 -10
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2.4.1 - 2026-05-30
|
|
6
|
+
|
|
7
|
+
- Classified `UnexpectedCurrentRootVector()` submit reverts as stale channel-root failures with recovery hints that
|
|
8
|
+
tell agents to refresh workspace state, re-check affected wallet state, and regenerate the original intended proof
|
|
9
|
+
without changing command semantics.
|
|
10
|
+
- Moved LLM-agent operating guidance from the CLI README into package-shipped `agents.md`.
|
|
11
|
+
- Generalized the CLI README's LLM-agent summary to refer to error-response policy instead of naming one revert.
|
|
12
|
+
- Added private-state CLI install prerequisites to the README while delegating Tokamak zk-EVM CLI prerequisites to that
|
|
13
|
+
package's README.
|
|
14
|
+
|
|
5
15
|
## 2.4.0 - 2026-05-29
|
|
6
16
|
|
|
7
17
|
- Removed the standalone `channel publish-workspace-mirror` command. Channel leaders now publish
|
package/README.md
CHANGED
|
@@ -47,6 +47,28 @@ UTC.
|
|
|
47
47
|
|
|
48
48
|
## Install
|
|
49
49
|
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
Before installing this package, prepare the private-state CLI prerequisites:
|
|
53
|
+
|
|
54
|
+
- Node.js 18 or newer and npm for installing and running `private-state-cli`.
|
|
55
|
+
- Outbound HTTPS access to the npm registry, the public private-state deployment artifact index, and the public
|
|
56
|
+
Groth16 CRS archive source.
|
|
57
|
+
- A writable home-directory workspace under `~/tokamak-private-channels/` for private-state artifacts, Groth16
|
|
58
|
+
workspace files, account secrets, wallet key files, channel workspaces, and proof outputs.
|
|
59
|
+
- For `private-state-cli install --read-only`, no proof runtime prerequisites are needed because read-only mode installs
|
|
60
|
+
only public bridge and private-state DApp artifacts.
|
|
61
|
+
- For `private-state-cli install --include-local-artifacts`, run the command from a repository or deployment workspace
|
|
62
|
+
that contains the local `deployment/` artifacts you intentionally want to install.
|
|
63
|
+
- For `private-state-cli install --docker`, the private-state Groth16 Docker path requires Docker to be installed and
|
|
64
|
+
running. The Groth16 Docker path is supported on Linux hosts and Windows hosts with Docker Desktop; macOS hosts should
|
|
65
|
+
use the native Groth16 path.
|
|
66
|
+
|
|
67
|
+
Full `private-state-cli install` also installs and invokes `@tokamak-zk-evm/cli`. The operating-system, native build
|
|
68
|
+
toolchain, Docker, CUDA, and network prerequisites for the Tokamak zk-EVM CLI are intentionally not duplicated here.
|
|
69
|
+
Read the [`@tokamak-zk-evm/cli` README](https://github.com/tokamak-network/Tokamak-zk-EVM/tree/main/packages/cli#readme)
|
|
70
|
+
before running full install, especially when using `--docker` or a GPU-enabled host.
|
|
71
|
+
|
|
50
72
|
```bash
|
|
51
73
|
npm install -g @tokamak-private-dapps/private-state-cli
|
|
52
74
|
```
|
|
@@ -491,140 +513,9 @@ possible.
|
|
|
491
513
|
|
|
492
514
|
## LLM Agent Guidance
|
|
493
515
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
Primary goal: help the user safely use private-state note workflows: self-custody L1 funding, channel-local note
|
|
499
|
-
creation, note transfer, note recovery, and user-controlled disclosure where supported. Present this
|
|
500
|
-
as privacy-preserving note semantics for the current `private-state` DApp, not as invisible
|
|
501
|
-
activity or as a bridge-wide disclosure rule for every DApp.
|
|
502
|
-
|
|
503
|
-
Operating rules:
|
|
504
|
-
|
|
505
|
-
- Do not ask the user to reveal raw private keys or wallet secrets in chat. Use `account import --private-key-file`
|
|
506
|
-
once, then use `--account` for L1 signing commands. Wallet viewing and spending authority is imported separately
|
|
507
|
-
with `wallet import viewing-key` and `wallet import spending-key`.
|
|
508
|
-
- Treat `private key file`, `account`, `wallet secret`, `wallet`, `network RPC URL`, and `channel policy` as
|
|
509
|
-
new concepts unless the user has already demonstrated that they understand them. Define each term before using it
|
|
510
|
-
in an instruction.
|
|
511
|
-
- Explain local-secret handling in plain language:
|
|
512
|
-
- A private key file is a local file that contains the user's L1 wallet private key. The CLI reads it once during
|
|
513
|
-
`account import` and stores a protected local account secret.
|
|
514
|
-
- An account is the local nickname created by `account import`. After import, signing commands should use
|
|
515
|
-
`--account <NAME>` instead of asking for the raw key again.
|
|
516
|
-
- A wallet secret source file is a separate high-entropy local secret chosen by the user for this private-state
|
|
517
|
-
wallet. It is not the L1 private key. `channel join` reads it once for channel-bound spending-key derivation and
|
|
518
|
-
does not persist it in the wallet workspace.
|
|
519
|
-
- A wallet is the local private-state metadata set created during `channel join`. Its deterministic name is
|
|
520
|
-
`<channelName>-<l1Address>`. The wallet backup tracks encrypted note state, while viewing and spending authority
|
|
521
|
-
are stored in separate protected key files.
|
|
522
|
-
- A viewing key decrypts encrypted note-delivery events for the registered note-receive public key. A spending key is
|
|
523
|
-
the channel-bound L2 private key used to authorize note use. Do not describe either key as interchangeable with the
|
|
524
|
-
other.
|
|
525
|
-
- The network RPC URL is the endpoint used to read and write chain state. It must be configured once with
|
|
526
|
-
`private-state-cli set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or with explicit
|
|
527
|
-
`--log-requests-per-second` and `--block-range-cap` values when the provider is not built in.
|
|
528
|
-
- A workspace recovery index is the saved block pointer and state-root hash that lets the CLI resume log scanning
|
|
529
|
-
without replaying the channel from its creation block. If it is missing, explain `--from-genesis` before using it
|
|
530
|
-
because genesis replay can take much longer.
|
|
531
|
-
- Before guiding a user to run `channel recover-workspace --source rpc --from-genesis`, explain that RPC genesis
|
|
532
|
-
recovery can be very slow because it scans channel logs from the creation block. If a channel workspace mirror is
|
|
533
|
-
available, try mirror-based recovery first, and use RPC genesis replay only when mirror recovery is unavailable or
|
|
534
|
-
unsuitable.
|
|
535
|
-
- When the user asks about gas use, transaction fees, transaction cost, or USD cost for private-state CLI commands, run
|
|
536
|
-
`private-state-cli help transaction-fees --network <NETWORK> --json` and answer from the returned `rows`. If the
|
|
537
|
-
network is unclear, ask which network to use. Do not tell the user to ask the developer unless the command fails after
|
|
538
|
-
following the CLI's printed corrective guidance.
|
|
539
|
-
- When `channel recover-workspace` or `wallet recover-workspace` is unexpectedly slow, first inspect the RPC provider
|
|
540
|
-
configured by `set rpc`. Explain that recovery speed is dominated by `eth_getLogs` block range cap and log request
|
|
541
|
-
rate. Suggest re-running `set rpc` with a provider that supports a larger block range cap, such as Ankr or Chainnodes
|
|
542
|
-
when appropriate, or with explicit `--log-requests-per-second` and `--block-range-cap` values from the provider's
|
|
543
|
-
documentation.
|
|
544
|
-
- When a channel leader needs to refresh workspace mirror files, guide them to run
|
|
545
|
-
`channel recover-workspace --publish-workspace-mirror --leader-account <ACCOUNT> --output <PATH>`. The standalone
|
|
546
|
-
`channel publish-workspace-mirror` command is no longer available.
|
|
547
|
-
- When a CLI command fails, read the error message and any printed `Try:` hints first. Prefer the corrective action
|
|
548
|
-
suggested by the CLI before inventing a different recovery sequence.
|
|
549
|
-
- When the user does not have a network RPC URL yet, explain that they need an Ethereum JSON-RPC endpoint for the
|
|
550
|
-
selected network. They can obtain one from an infrastructure provider such as Alchemy, Ankr, Chainstack, Chainnodes,
|
|
551
|
-
QuickNode, or from their own node. Ask the user to create or select the endpoint in that provider's UI, then paste only
|
|
552
|
-
the endpoint URL into `private-state-cli set rpc`; do not ask for provider account passwords, API dashboards, seed
|
|
553
|
-
phrases, private keys, or wallet secrets.
|
|
554
|
-
- When a user wants to join a channel, do not jump straight to `channel join`. Walk them through:
|
|
555
|
-
1. choose the network and channel name
|
|
556
|
-
2. run `private-state-cli install`
|
|
557
|
-
3. run `private-state-cli help doctor`
|
|
558
|
-
4. obtain or confirm a network RPC URL for the selected network
|
|
559
|
-
5. run `set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or use explicit scan limits for an
|
|
560
|
-
unlisted provider
|
|
561
|
-
6. prepare a private key source file locally, without pasting the key into chat
|
|
562
|
-
7. run `account import --account <NAME> --network <NETWORK> --private-key-file <PATH>`
|
|
563
|
-
8. prepare a wallet secret source file locally, for example with `openssl rand -hex 32 > ./wallet-secret.txt`
|
|
564
|
-
9. inspect the channel with `channel get-meta` if it already exists, or create it with `channel create` if the user is
|
|
565
|
-
the channel creator
|
|
566
|
-
10. explain the immutable policy warning and that the join toll is paid directly from the L1 wallet, not bridge-deposited balance
|
|
567
|
-
11. run `channel join --channel-name <CHANNEL> --network <NETWORK> --account <ACCOUNT> --wallet-secret-path <PATH> --acknowledge-action-impact`
|
|
568
|
-
- Before executing any command for a user that requires an `--acknowledge-*` option, strongly warn the user in plain
|
|
569
|
-
language about what that acknowledgement means and ask for explicit confirmation. Do not add
|
|
570
|
-
`--acknowledge-action-impact` or `--acknowledge-full-note-plaintext-export` on the user's behalf until they confirm.
|
|
571
|
-
For `--acknowledge-action-impact`, explain the command's public/private action-impact summary. For
|
|
572
|
-
`--acknowledge-full-note-plaintext-export`, explain that all locally known note plaintext will be written into the
|
|
573
|
-
exported ZIP.
|
|
574
|
-
- Before asking the user to create a file, explain what will be inside that file, who should be able to read it, and
|
|
575
|
-
whether losing it prevents wallet recovery.
|
|
576
|
-
- Prefer testnet examples unless the user explicitly asks for mainnet.
|
|
577
|
-
- Before any proof-backed or bridge-facing workflow, ask the user to run `private-state-cli help doctor` and inspect
|
|
578
|
-
whether the runtime, Docker mode, CUDA/GPU probes, Groth16 runtime, and deployment artifacts are healthy.
|
|
579
|
-
- Use `private-state-cli wallet list` to discover local wallet names instead of asking the user to inspect
|
|
580
|
-
filesystem paths manually.
|
|
581
|
-
- Use `private-state-cli account get-l1-address --account <ACCOUNT> --network <NETWORK>` to derive the L1 address
|
|
582
|
-
for a local account when wallet ownership needs to be identified.
|
|
583
|
-
- Use `private-state-cli wallet get-meta --wallet <WALLET> --network <NETWORK>` to inspect
|
|
584
|
-
local wallet metadata and on-chain channel registration state.
|
|
585
|
-
- Use `private-state-cli account get-bridge-fund` and `private-state-cli wallet get-channel-fund` to check balances before
|
|
586
|
-
telling the user to move funds.
|
|
587
|
-
- Explain that wallet names are local CLI identifiers, while confidential note transfers use notes owned by L2 addresses
|
|
588
|
-
registered in the channel.
|
|
589
|
-
- Explain `--tx-submitter <ACCOUNT>` when the user wants a separate L1 transaction submitter for `wallet mint-notes`,
|
|
590
|
-
`wallet transfer-notes`, or `wallet redeem-notes`: the wallet owner still proves note ownership, but another imported
|
|
591
|
-
local L1 account can submit the on-chain `executeChannelTransaction` and pay gas.
|
|
592
|
-
- Before guiding a user through `channel create` or `channel join`, explain that channel policy is immutable after
|
|
593
|
-
creation and that joining a channel means accepting its current verifier, DApp metadata, function layout, managed
|
|
594
|
-
storage vector, and refund policy.
|
|
595
|
-
- Do not present one fixed command sequence as universally correct. Some flows start from an existing channel or wallet,
|
|
596
|
-
while others require creating or joining a channel first.
|
|
597
|
-
- When the user asks for a transfer, first determine whether the sender has minted notes available. If not, guide them
|
|
598
|
-
through joining or recovering the channel wallet, funding the bridge for channel liquidity, depositing into the channel, and minting notes.
|
|
599
|
-
- When generating commands, use placeholders for secrets and explicit values for public fields. Show one command at a
|
|
600
|
-
time unless the user asks for a batch.
|
|
601
|
-
|
|
602
|
-
Suggested interaction flow:
|
|
603
|
-
|
|
604
|
-
1. Identify the target network, usually `sepolia` for testing.
|
|
605
|
-
2. Identify whether a channel already exists.
|
|
606
|
-
3. Identify the sender and recipient wallets or local account names.
|
|
607
|
-
4. Run `help doctor`.
|
|
608
|
-
5. Run `wallet list` and relevant metadata or balance checks.
|
|
609
|
-
6. If needed, guide the user through `channel create`, `channel join`, `account deposit-bridge`, `wallet deposit-channel`, and
|
|
610
|
-
`wallet mint-notes`.
|
|
611
|
-
7. For a confidential note transfer, select available note IDs from `wallet get-notes`, find the recipient L2 address from
|
|
612
|
-
`wallet get-meta`, then build `wallet transfer-notes` with JSON arrays for `--note-ids`, `--recipients`, and `--amounts`.
|
|
613
|
-
8. After transfer, guide the recipient to run `wallet get-notes`; it refreshes received notes from the saved recovery index when the delta fits the 7,200-block pre-command budget. If the index is missing or too far behind, explain `wallet recover-workspace`.
|
|
614
|
-
|
|
615
|
-
Example onboarding explanation for `channel join`:
|
|
616
|
-
|
|
617
|
-
> First we need two different local secrets. Your L1 private key proves which Ethereum account pays gas and signs
|
|
618
|
-
> bridge transactions. We import it once into a local account nickname, so later commands can say `--account alice`
|
|
619
|
-
> instead of handling the raw key again. Separately, the wallet secret source derives the channel-bound spending key
|
|
620
|
-
> during `channel join`. It is not sent on-chain, it is not the same as your L1 private key, and the CLI does not store
|
|
621
|
-
> it in the wallet workspace. A wallet backup restores encrypted tracking state; the viewing key restores note
|
|
622
|
-
> readability; the spending key restores note spendability.
|
|
623
|
-
|
|
624
|
-
Example style: if the user says, "ADDR6 sends 10 tokens privately to ADDR8", do not assume the required note exists.
|
|
625
|
-
First ask or check which channel and network to use, whether ADDR6 and ADDR8 are already joined, what the local wallet
|
|
626
|
-
names are, and whether ADDR6 has an unused note worth exactly 10 or notes that sum to 10. Then provide the next concrete
|
|
627
|
-
command.
|
|
516
|
+
LLM agents that guide users through this CLI should read [`agents.md`](agents.md) before suggesting or running
|
|
517
|
+
commands. That file contains the agent-specific operating rules, including secret-handling boundaries, onboarding
|
|
518
|
+
sequence, acknowledgement handling, recovery behavior, and error-response policy.
|
|
628
519
|
|
|
629
520
|
## Artifacts
|
|
630
521
|
|
package/agents.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# private-state CLI Agent Instructions
|
|
2
|
+
|
|
3
|
+
These instructions are for LLM agents that guide users through the `private-state-cli` package.
|
|
4
|
+
|
|
5
|
+
You may act as an interactive guide for users who do not understand this CLI or the private-state DApp. Assume the
|
|
6
|
+
user wants to use confidential channel-local notes while keeping L1 bridge deposits and withdrawals transparent.
|
|
7
|
+
Translate the user's intent into safe, step-by-step CLI actions.
|
|
8
|
+
|
|
9
|
+
Primary goal: help the user safely use private-state note workflows: self-custody L1 funding, channel-local note
|
|
10
|
+
creation, note transfer, note recovery, and user-controlled disclosure where supported. Present this
|
|
11
|
+
as privacy-preserving note semantics for the current `private-state` DApp, not as invisible
|
|
12
|
+
activity or as a bridge-wide disclosure rule for every DApp.
|
|
13
|
+
|
|
14
|
+
## Operating Rules
|
|
15
|
+
|
|
16
|
+
- Do not ask the user to reveal raw private keys or wallet secrets in chat. Use `account import --private-key-file`
|
|
17
|
+
once, then use `--account` for L1 signing commands. Wallet viewing and spending authority is imported separately
|
|
18
|
+
with `wallet import viewing-key` and `wallet import spending-key`.
|
|
19
|
+
- Treat `private key file`, `account`, `wallet secret`, `wallet`, `network RPC URL`, and `channel policy` as
|
|
20
|
+
new concepts unless the user has already demonstrated that they understand them. Define each term before using it
|
|
21
|
+
in an instruction.
|
|
22
|
+
- Explain local-secret handling in plain language:
|
|
23
|
+
- A private key file is a local file that contains the user's L1 wallet private key. The CLI reads it once during
|
|
24
|
+
`account import` and stores a protected local account secret.
|
|
25
|
+
- An account is the local nickname created by `account import`. After import, signing commands should use
|
|
26
|
+
`--account <NAME>` instead of asking for the raw key again.
|
|
27
|
+
- A wallet secret source file is a separate high-entropy local secret chosen by the user for this private-state
|
|
28
|
+
wallet. It is not the L1 private key. `channel join` reads it once for channel-bound spending-key derivation and
|
|
29
|
+
does not persist it in the wallet workspace.
|
|
30
|
+
- A wallet is the local private-state metadata set created during `channel join`. Its deterministic name is
|
|
31
|
+
`<channelName>-<l1Address>`. The wallet backup tracks encrypted note state, while viewing and spending authority
|
|
32
|
+
are stored in separate protected key files.
|
|
33
|
+
- A viewing key decrypts encrypted note-delivery events for the registered note-receive public key. A spending key is
|
|
34
|
+
the channel-bound L2 private key used to authorize note use. Do not describe either key as interchangeable with the
|
|
35
|
+
other.
|
|
36
|
+
- The network RPC URL is the endpoint used to read and write chain state. It must be configured once with
|
|
37
|
+
`private-state-cli set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or with explicit
|
|
38
|
+
`--log-requests-per-second` and `--block-range-cap` values when the provider is not built in.
|
|
39
|
+
- A workspace recovery index is the saved block pointer and state-root hash that lets the CLI resume log scanning
|
|
40
|
+
without replaying the channel from its creation block. If it is missing, explain `--from-genesis` before using it
|
|
41
|
+
because genesis replay can take much longer.
|
|
42
|
+
- Before guiding a user to run `channel recover-workspace --source rpc --from-genesis`, explain that RPC genesis
|
|
43
|
+
recovery can be very slow because it scans channel logs from the creation block. If a channel workspace mirror is
|
|
44
|
+
available, try mirror-based recovery first, and use RPC genesis replay only when mirror recovery is unavailable or
|
|
45
|
+
unsuitable.
|
|
46
|
+
- When the user asks about gas use, transaction fees, transaction cost, or USD cost for private-state CLI commands, run
|
|
47
|
+
`private-state-cli help transaction-fees --network <NETWORK> --json` and answer from the returned `rows`. If the
|
|
48
|
+
network is unclear, ask which network to use. Do not tell the user to ask the developer unless the command fails after
|
|
49
|
+
following the CLI's printed corrective guidance.
|
|
50
|
+
- When `channel recover-workspace` or `wallet recover-workspace` is unexpectedly slow, first inspect the RPC provider
|
|
51
|
+
configured by `set rpc`. Explain that recovery speed is dominated by `eth_getLogs` block range cap and log request
|
|
52
|
+
rate. Suggest re-running `set rpc` with a provider that supports a larger block range cap, such as Ankr or Chainnodes
|
|
53
|
+
when appropriate, or with explicit `--log-requests-per-second` and `--block-range-cap` values from the provider's
|
|
54
|
+
documentation.
|
|
55
|
+
- When a channel leader needs to refresh workspace mirror files, guide them to run
|
|
56
|
+
`channel recover-workspace --publish-workspace-mirror --leader-account <ACCOUNT> --output <PATH>`. The standalone
|
|
57
|
+
`channel publish-workspace-mirror` command is no longer available.
|
|
58
|
+
- When a CLI command fails, read the error message and any printed `Try:` hints first. Prefer the corrective action
|
|
59
|
+
suggested by the CLI before inventing a different recovery sequence.
|
|
60
|
+
- Treat `UnexpectedCurrentRootVector()` as a stale channel-root or stale-proof failure, not as evidence that the
|
|
61
|
+
command shape is wrong. Do not recover by changing recipients, changing amounts, changing note counts, changing
|
|
62
|
+
function arity, or splitting one intended transfer into multiple transfers. Refresh the channel workspace, re-check
|
|
63
|
+
affected wallet state such as notes and balances, then rerun the user's original intended command so the CLI
|
|
64
|
+
regenerates a proof from the fresh snapshot. If the original notes or balances are no longer usable after refresh,
|
|
65
|
+
ask the user to choose a new plan instead of silently substituting one.
|
|
66
|
+
- When the user does not have a network RPC URL yet, explain that they need an Ethereum JSON-RPC endpoint for the
|
|
67
|
+
selected network. They can obtain one from an infrastructure provider such as Alchemy, Ankr, Chainstack, Chainnodes,
|
|
68
|
+
QuickNode, or from their own node. Ask the user to create or select the endpoint in that provider's UI, then paste only
|
|
69
|
+
the endpoint URL into `private-state-cli set rpc`; do not ask for provider account passwords, API dashboards, seed
|
|
70
|
+
phrases, private keys, or wallet secrets.
|
|
71
|
+
- When a user wants to join a channel, do not jump straight to `channel join`. Walk them through:
|
|
72
|
+
1. choose the network and channel name
|
|
73
|
+
2. run `private-state-cli install`
|
|
74
|
+
3. run `private-state-cli help doctor`
|
|
75
|
+
4. obtain or confirm a network RPC URL for the selected network
|
|
76
|
+
5. run `set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or use explicit scan limits for an
|
|
77
|
+
unlisted provider
|
|
78
|
+
6. prepare a private key source file locally, without pasting the key into chat
|
|
79
|
+
7. run `account import --account <NAME> --network <NETWORK> --private-key-file <PATH>`
|
|
80
|
+
8. prepare a wallet secret source file locally, for example with `openssl rand -hex 32 > ./wallet-secret.txt`
|
|
81
|
+
9. inspect the channel with `channel get-meta` if it already exists, or create it with `channel create` if the user is
|
|
82
|
+
the channel creator
|
|
83
|
+
10. explain the immutable policy warning and that the join toll is paid directly from the L1 wallet, not bridge-deposited balance
|
|
84
|
+
11. run `channel join --channel-name <CHANNEL> --network <NETWORK> --account <ACCOUNT> --wallet-secret-path <PATH> --acknowledge-action-impact`
|
|
85
|
+
- Before executing any command for a user that requires an `--acknowledge-*` option, strongly warn the user in plain
|
|
86
|
+
language about what that acknowledgement means and ask for explicit confirmation. Do not add
|
|
87
|
+
`--acknowledge-action-impact` or `--acknowledge-full-note-plaintext-export` on the user's behalf until they confirm.
|
|
88
|
+
For `--acknowledge-action-impact`, explain the command's public/private action-impact summary. For
|
|
89
|
+
`--acknowledge-full-note-plaintext-export`, explain that all locally known note plaintext will be written into the
|
|
90
|
+
exported ZIP.
|
|
91
|
+
- Before asking the user to create a file, explain what will be inside that file, who should be able to read it, and
|
|
92
|
+
whether losing it prevents wallet recovery.
|
|
93
|
+
- Prefer testnet examples unless the user explicitly asks for mainnet.
|
|
94
|
+
- Before any proof-backed or bridge-facing workflow, ask the user to run `private-state-cli help doctor` and inspect
|
|
95
|
+
whether the runtime, Docker mode, CUDA/GPU probes, Groth16 runtime, and deployment artifacts are healthy.
|
|
96
|
+
- Use `private-state-cli wallet list` to discover local wallet names instead of asking the user to inspect
|
|
97
|
+
filesystem paths manually.
|
|
98
|
+
- Use `private-state-cli account get-l1-address --account <ACCOUNT> --network <NETWORK>` to derive the L1 address
|
|
99
|
+
for a local account when wallet ownership needs to be identified.
|
|
100
|
+
- Use `private-state-cli wallet get-meta --wallet <WALLET> --network <NETWORK>` to inspect
|
|
101
|
+
local wallet metadata and on-chain channel registration state.
|
|
102
|
+
- Use `private-state-cli account get-bridge-fund` and `private-state-cli wallet get-channel-fund` to check balances before
|
|
103
|
+
telling the user to move funds.
|
|
104
|
+
- Explain that wallet names are local CLI identifiers, while confidential note transfers use notes owned by L2 addresses
|
|
105
|
+
registered in the channel.
|
|
106
|
+
- Explain `--tx-submitter <ACCOUNT>` when the user wants a separate L1 transaction submitter for `wallet mint-notes`,
|
|
107
|
+
`wallet transfer-notes`, or `wallet redeem-notes`: the wallet owner still proves note ownership, but another imported
|
|
108
|
+
local L1 account can submit the on-chain `executeChannelTransaction` and pay gas.
|
|
109
|
+
- Before guiding a user through `channel create` or `channel join`, explain that channel policy is immutable after
|
|
110
|
+
creation and that joining a channel means accepting its current verifier, DApp metadata, function layout, managed
|
|
111
|
+
storage vector, and refund policy.
|
|
112
|
+
- Do not present one fixed command sequence as universally correct. Some flows start from an existing channel or wallet,
|
|
113
|
+
while others require creating or joining a channel first.
|
|
114
|
+
- When the user asks for a transfer, first determine whether the sender has minted notes available. If not, guide them
|
|
115
|
+
through joining or recovering the channel wallet, funding the bridge for channel liquidity, depositing into the channel, and minting notes.
|
|
116
|
+
- When generating commands, use placeholders for secrets and explicit values for public fields. Show one command at a
|
|
117
|
+
time unless the user asks for a batch.
|
|
118
|
+
|
|
119
|
+
## Suggested Interaction Flow
|
|
120
|
+
|
|
121
|
+
1. Identify the target network, usually `sepolia` for testing.
|
|
122
|
+
2. Identify whether a channel already exists.
|
|
123
|
+
3. Identify the sender and recipient wallets or local account names.
|
|
124
|
+
4. Run `help doctor`.
|
|
125
|
+
5. Run `wallet list` and relevant metadata or balance checks.
|
|
126
|
+
6. If needed, guide the user through `channel create`, `channel join`, `account deposit-bridge`, `wallet deposit-channel`, and
|
|
127
|
+
`wallet mint-notes`.
|
|
128
|
+
7. For a confidential note transfer, select available note IDs from `wallet get-notes`, find the recipient L2 address from
|
|
129
|
+
`wallet get-meta`, then build `wallet transfer-notes` with JSON arrays for `--note-ids`, `--recipients`, and `--amounts`.
|
|
130
|
+
8. After transfer, guide the recipient to run `wallet get-notes`; it refreshes received notes from the saved recovery index when the delta fits the 7,200-block pre-command budget. If the index is missing or too far behind, explain `wallet recover-workspace`.
|
|
131
|
+
|
|
132
|
+
## Example Onboarding Explanation For `channel join`
|
|
133
|
+
|
|
134
|
+
> First we need two different local secrets. Your L1 private key proves which Ethereum account pays gas and signs
|
|
135
|
+
> bridge transactions. We import it once into a local account nickname, so later commands can say `--account alice`
|
|
136
|
+
> instead of handling the raw key again. Separately, the wallet secret source derives the channel-bound spending key
|
|
137
|
+
> during `channel join`. It is not sent on-chain, it is not the same as your L1 private key, and the CLI does not store
|
|
138
|
+
> it in the wallet workspace. A wallet backup restores encrypted tracking state; the viewing key restores note
|
|
139
|
+
> readability; the spending key restores note spendability.
|
|
140
|
+
|
|
141
|
+
## Example Style
|
|
142
|
+
|
|
143
|
+
If the user says, "ADDR6 sends 10 tokens privately to ADDR8", do not assume the required note exists.
|
|
144
|
+
First ask or check which channel and network to use, whether ADDR6 and ADDR8 are already joined, what the local wallet
|
|
145
|
+
names are, and whether ADDR6 has an unused note worth exactly 10 or notes that sum to 10. Then provide the next concrete
|
|
146
|
+
command.
|
package/lib/runtime.mjs
CHANGED
|
@@ -165,6 +165,7 @@ const CLI_ERROR_CODES = Object.freeze({
|
|
|
165
165
|
MISSING_DEPLOYMENT_ARTIFACTS: "MISSING_DEPLOYMENT_ARTIFACTS",
|
|
166
166
|
MISSING_CHANNEL_REGISTRATION: "MISSING_CHANNEL_REGISTRATION",
|
|
167
167
|
STALE_WORKSPACE: "STALE_WORKSPACE",
|
|
168
|
+
STALE_CHANNEL_ROOT: "STALE_CHANNEL_ROOT",
|
|
168
169
|
});
|
|
169
170
|
|
|
170
171
|
class PrivateStateCliError extends Error {
|
|
@@ -4855,9 +4856,16 @@ async function handleGrothVaultMove({ args, provider, direction }) {
|
|
|
4855
4856
|
const methodName = direction === "deposit" ? "depositToChannelVault" : "withdrawFromChannelVault";
|
|
4856
4857
|
await assertWorkspaceAlignedWithChain(context);
|
|
4857
4858
|
emitProgress(operationName, "submitting");
|
|
4858
|
-
const receipt = await
|
|
4859
|
-
|
|
4860
|
-
|
|
4859
|
+
const receipt = await submitProofBackedRootUpdate({
|
|
4860
|
+
context,
|
|
4861
|
+
walletName: walletContext.walletName,
|
|
4862
|
+
operationName,
|
|
4863
|
+
submit: () => bridgeTokenVault[methodName](
|
|
4864
|
+
ethers.toBigInt(context.workspace.channelId),
|
|
4865
|
+
transition.proof,
|
|
4866
|
+
transition.update,
|
|
4867
|
+
),
|
|
4868
|
+
});
|
|
4861
4869
|
const onchainRootVectorHash = normalizeBytes32Hex(await context.channelManager.currentRootVectorHash());
|
|
4862
4870
|
expect(
|
|
4863
4871
|
onchainRootVectorHash === normalizeBytes32Hex(hashRootVector(transition.nextSnapshot.stateRoots)),
|
|
@@ -7699,10 +7707,12 @@ async function executeWalletTemplateSend({
|
|
|
7699
7707
|
|
|
7700
7708
|
await assertWorkspaceAlignedWithChain(context);
|
|
7701
7709
|
emitProgress(operationName, "submitting");
|
|
7702
|
-
const receipt =
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7710
|
+
const receipt = await submitProofBackedRootUpdate({
|
|
7711
|
+
context,
|
|
7712
|
+
walletName: wallet.walletName,
|
|
7713
|
+
operationName,
|
|
7714
|
+
submit: () => context.channelManager.connect(txSubmitter).executeChannelTransaction(payload, functionProof),
|
|
7715
|
+
});
|
|
7706
7716
|
await waitForProviderBlockAtLeast(provider, receipt.blockNumber, { action: operationName });
|
|
7707
7717
|
|
|
7708
7718
|
const onchainRootVectorHash = normalizeBytes32Hex(await context.channelManager.currentRootVectorHash());
|
|
@@ -8369,6 +8379,56 @@ function isContractError(error, contractInterface, errorName) {
|
|
|
8369
8379
|
return false;
|
|
8370
8380
|
}
|
|
8371
8381
|
|
|
8382
|
+
function isUnexpectedCurrentRootVectorError(error, context) {
|
|
8383
|
+
if (isContractError(error, context.channelManager.interface, "UnexpectedCurrentRootVector")) {
|
|
8384
|
+
return true;
|
|
8385
|
+
}
|
|
8386
|
+
return String(error?.message ?? error).includes("UnexpectedCurrentRootVector");
|
|
8387
|
+
}
|
|
8388
|
+
|
|
8389
|
+
async function submitProofBackedRootUpdate({
|
|
8390
|
+
context,
|
|
8391
|
+
walletName,
|
|
8392
|
+
operationName,
|
|
8393
|
+
submit,
|
|
8394
|
+
}) {
|
|
8395
|
+
try {
|
|
8396
|
+
return await waitForReceipt(await submit());
|
|
8397
|
+
} catch (error) {
|
|
8398
|
+
if (isUnexpectedCurrentRootVectorError(error, context)) {
|
|
8399
|
+
throw staleChannelRootError({
|
|
8400
|
+
cause: error,
|
|
8401
|
+
context,
|
|
8402
|
+
walletName,
|
|
8403
|
+
operationName,
|
|
8404
|
+
});
|
|
8405
|
+
}
|
|
8406
|
+
throw error;
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8409
|
+
|
|
8410
|
+
function staleChannelRootError({
|
|
8411
|
+
cause,
|
|
8412
|
+
context,
|
|
8413
|
+
walletName,
|
|
8414
|
+
operationName,
|
|
8415
|
+
}) {
|
|
8416
|
+
const message = [
|
|
8417
|
+
`${operationName} failed because the submitted proof was generated for an older channel root.`,
|
|
8418
|
+
"The rejected proof cannot be reused.",
|
|
8419
|
+
"Do not change recipients, amounts, note counts, function arity, or split the command as recovery.",
|
|
8420
|
+
"Refresh the channel workspace, re-check affected wallet state when the command uses notes, then rerun the original intended command so the CLI regenerates a proof from a fresh snapshot.",
|
|
8421
|
+
].join(" ");
|
|
8422
|
+
const error = cliError(CLI_ERROR_CODES.STALE_CHANNEL_ROOT, message, { cause });
|
|
8423
|
+
error.channelName = context.workspace.channelName;
|
|
8424
|
+
error.networkName = context.workspace.network;
|
|
8425
|
+
error.walletName = walletName;
|
|
8426
|
+
error.retryPolicy = "recover_workspace_then_regenerate_proof";
|
|
8427
|
+
error.semanticMutationAllowed = false;
|
|
8428
|
+
error.reuseProofAllowed = false;
|
|
8429
|
+
return error;
|
|
8430
|
+
}
|
|
8431
|
+
|
|
8372
8432
|
function extractContractErrorDataCandidates(error) {
|
|
8373
8433
|
return [
|
|
8374
8434
|
error?.data,
|
|
@@ -11690,16 +11750,16 @@ function buildRecoveryHints(error, args = {}) {
|
|
|
11690
11750
|
const hints = [];
|
|
11691
11751
|
const networkName = typeof args.network === "string" && args.network.length > 0
|
|
11692
11752
|
? args.network
|
|
11693
|
-
: "<NETWORK>";
|
|
11753
|
+
: error?.networkName ?? "<NETWORK>";
|
|
11694
11754
|
const channelName = typeof args.channelName === "string" && args.channelName.length > 0
|
|
11695
11755
|
? args.channelName
|
|
11696
|
-
: "<CHANNEL>";
|
|
11756
|
+
: error?.channelName ?? "<CHANNEL>";
|
|
11697
11757
|
const accountName = typeof args.account === "string" && args.account.length > 0
|
|
11698
11758
|
? args.account
|
|
11699
11759
|
: "<ACCOUNT>";
|
|
11700
11760
|
const walletName = typeof args.wallet === "string" && args.wallet.length > 0
|
|
11701
11761
|
? args.wallet
|
|
11702
|
-
: extractUnknownWalletName(message) ?? "<WALLET>";
|
|
11762
|
+
: error?.walletName ?? extractUnknownWalletName(message) ?? "<WALLET>";
|
|
11703
11763
|
|
|
11704
11764
|
if (
|
|
11705
11765
|
error?.code === CLI_ERROR_CODES.MISSING_RPC_URL
|
|
@@ -11746,6 +11806,17 @@ function buildRecoveryHints(error, args = {}) {
|
|
|
11746
11806
|
hints.push(`private-state-cli help guide --network ${networkName} --channel-name ${channelName}`);
|
|
11747
11807
|
}
|
|
11748
11808
|
|
|
11809
|
+
if (
|
|
11810
|
+
error?.code === CLI_ERROR_CODES.STALE_CHANNEL_ROOT
|
|
11811
|
+
|| message.includes("UnexpectedCurrentRootVector")
|
|
11812
|
+
) {
|
|
11813
|
+
hints.push(`private-state-cli channel recover-workspace --channel-name ${channelName} --network ${networkName}`);
|
|
11814
|
+
if (walletName !== "<WALLET>") {
|
|
11815
|
+
hints.push(`private-state-cli wallet get-notes --wallet ${walletName} --network ${networkName}`);
|
|
11816
|
+
}
|
|
11817
|
+
hints.push("rerun the original proof-backed command unchanged so the CLI regenerates a fresh proof");
|
|
11818
|
+
}
|
|
11819
|
+
|
|
11749
11820
|
if (message.includes("Workspace recovery index is missing or unusable")) {
|
|
11750
11821
|
hints.push(`private-state-cli channel recover-workspace --channel-name ${channelName} --network ${networkName}`);
|
|
11751
11822
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokamak-private-dapps/private-state-cli",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Command-line client for the Tokamak private-state DApp.",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"author": "Tokamak Network",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"README.md",
|
|
29
|
+
"agents.md",
|
|
29
30
|
"CHANGELOG.md",
|
|
30
31
|
"LICENSE",
|
|
31
32
|
"private-state-bridge-cli.mjs",
|