@tokamak-private-dapps/private-state-cli 0.1.3 → 0.1.5
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/package.json +2 -2
- package/private-state-bridge-cli.mjs +74 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.5 - 2026-04-28
|
|
4
|
+
|
|
5
|
+
- Switched channel balance proof generation to invoke `tokamak-groth16 --prove` instead of importing Groth16 proof internals directly.
|
|
6
|
+
- Read proof artifacts from the fixed Groth16 runtime workspace manifest.
|
|
7
|
+
|
|
8
|
+
## 0.1.4 - 2026-04-28
|
|
9
|
+
|
|
10
|
+
- Paced chunked log recovery queries at five requests per second to avoid RPC throughput bursts.
|
|
11
|
+
- Combined channel manager recovery log scans and filtered wallet note recovery scans to reduce RPC usage.
|
|
12
|
+
|
|
3
13
|
## 0.1.3 - 2026-04-28
|
|
4
14
|
|
|
5
15
|
- Installed the Groth16 runtime during `private-state-cli --install` and reported Groth16 readiness from `--doctor`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokamak-private-dapps/private-state-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@ethereumjs/util": "^10.1.1",
|
|
44
44
|
"@noble/curves": "^1.2.0",
|
|
45
45
|
"@tokamak-private-dapps/common-library": "^0.1.0",
|
|
46
|
-
"@tokamak-private-dapps/groth16": "^0.1.
|
|
46
|
+
"@tokamak-private-dapps/groth16": "^0.1.1",
|
|
47
47
|
"@tokamak-zk-evm/cli": "^2.0.8",
|
|
48
48
|
"ethers": "^6.14.1",
|
|
49
49
|
"tokamak-l2js": "^0.1.3"
|
|
@@ -67,7 +67,6 @@ import {
|
|
|
67
67
|
PUBLIC_GROTH16_MPC_DRIVE_FOLDER_ID,
|
|
68
68
|
downloadLatestPublicGroth16MpcArtifacts,
|
|
69
69
|
} from "@tokamak-private-dapps/groth16/public-drive-crs";
|
|
70
|
-
import { main as generateUpdateTreeProof } from "@tokamak-private-dapps/groth16/prover/updateTree/generateProof";
|
|
71
70
|
import {
|
|
72
71
|
CHANNEL_BOUND_L2_DERIVATION_MODE,
|
|
73
72
|
deriveChannelIdFromName,
|
|
@@ -152,6 +151,9 @@ const JUBJUB_D = jubjub.CURVE.d;
|
|
|
152
151
|
const BLS12_381_SCALAR_FIELD_MODULUS =
|
|
153
152
|
hexToBigInt(addHexPrefix("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"));
|
|
154
153
|
const DEFAULT_LOG_CHUNK_SIZE = 2000;
|
|
154
|
+
const DEFAULT_LOG_REQUESTS_PER_SECOND = 5;
|
|
155
|
+
const LOG_REQUEST_INTERVAL_MS = Math.ceil(1000 / DEFAULT_LOG_REQUESTS_PER_SECOND);
|
|
156
|
+
let lastLogRequestStartedAtMs = 0;
|
|
155
157
|
|
|
156
158
|
async function prepareDeploymentArtifacts(chainId) {
|
|
157
159
|
const normalizedChainId = Number(chainId);
|
|
@@ -2030,6 +2032,7 @@ async function recoverDeliveredNotesFromEventLogs({
|
|
|
2030
2032
|
const nullifierUsedSlot = ethers.toBigInt(findStorageSlot(storageLayoutManifest, "PrivateStateController", "nullifierUsed"));
|
|
2031
2033
|
const observedLogs = await fetchLogsChunked(provider, {
|
|
2032
2034
|
address: context.workspace.channelManager,
|
|
2035
|
+
topics: [NOTE_VALUE_ENCRYPTED_TOPIC],
|
|
2033
2036
|
fromBlock: scanStartBlock,
|
|
2034
2037
|
toBlock: latestBlock,
|
|
2035
2038
|
});
|
|
@@ -3453,10 +3456,7 @@ async function buildGrothTransition({ operationDir, workspace, stateManager, vau
|
|
|
3453
3456
|
|
|
3454
3457
|
const inputPath = path.join(operationDir, "input.json");
|
|
3455
3458
|
writeJson(inputPath, input);
|
|
3456
|
-
const proofManifest =
|
|
3457
|
-
"--input",
|
|
3458
|
-
inputPath,
|
|
3459
|
-
]);
|
|
3459
|
+
const proofManifest = runGroth16UpdateTreeProof(inputPath);
|
|
3460
3460
|
|
|
3461
3461
|
const proofJson = readJson(proofManifest.proofPath);
|
|
3462
3462
|
const publicSignals = readJson(proofManifest.publicPath);
|
|
@@ -3503,6 +3503,21 @@ function runCaptured(command, args, { cwd = defaultCommandCwd, env = process.env
|
|
|
3503
3503
|
};
|
|
3504
3504
|
}
|
|
3505
3505
|
|
|
3506
|
+
function runGroth16UpdateTreeProof(inputPath) {
|
|
3507
|
+
const packageRoot = resolveGroth16PackageRoot();
|
|
3508
|
+
const entryPath = resolveGroth16CliEntryPath(packageRoot);
|
|
3509
|
+
run(process.execPath, [entryPath, "--prove", inputPath], { cwd: packageRoot });
|
|
3510
|
+
const manifestPath = groth16ProofManifestPath();
|
|
3511
|
+
const manifest = readJson(manifestPath);
|
|
3512
|
+
expect(typeof manifest.proofPath === "string" && manifest.proofPath.length > 0, "Groth16 proof manifest is missing proofPath.");
|
|
3513
|
+
expect(typeof manifest.publicPath === "string" && manifest.publicPath.length > 0, "Groth16 proof manifest is missing publicPath.");
|
|
3514
|
+
return manifest;
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
function groth16ProofManifestPath() {
|
|
3518
|
+
return path.join(os.homedir(), "tokamak-private-channels", "groth16", "proof", "proof-manifest.json");
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3506
3521
|
function runTokamakProofPipeline({ operationDir, bundlePath }) {
|
|
3507
3522
|
runTokamakCliStage({
|
|
3508
3523
|
operationDir,
|
|
@@ -3854,30 +3869,39 @@ async function reconstructChannelSnapshot({
|
|
|
3854
3869
|
provider,
|
|
3855
3870
|
);
|
|
3856
3871
|
const latestBlock = await provider.getBlockNumber();
|
|
3857
|
-
const
|
|
3858
|
-
|
|
3859
|
-
|
|
3872
|
+
const currentRootVectorObservedTopic =
|
|
3873
|
+
normalizeBytes32Hex(channelManager.interface.getEvent("CurrentRootVectorObserved").topicHash);
|
|
3874
|
+
const channelManagerLogs = await fetchLogsChunked(provider, {
|
|
3875
|
+
address: channelInfo.manager,
|
|
3876
|
+
topics: [[
|
|
3877
|
+
currentRootVectorObservedTopic,
|
|
3878
|
+
CONTROLLER_STORAGE_KEY_OBSERVED_TOPIC,
|
|
3879
|
+
VAULT_STORAGE_WRITE_OBSERVED_TOPIC,
|
|
3880
|
+
]],
|
|
3860
3881
|
fromBlock: genesisBlockNumber,
|
|
3861
3882
|
toBlock: latestBlock,
|
|
3862
3883
|
});
|
|
3884
|
+
const channelManagerEvents = channelManagerLogs.map((log) => {
|
|
3885
|
+
const topic0 = log.topics[0] ? normalizeBytes32Hex(log.topics[0]) : null;
|
|
3886
|
+
if (topic0 !== null && ethers.toBigInt(topic0) === ethers.toBigInt(currentRootVectorObservedTopic)) {
|
|
3887
|
+
const parsed = channelManager.interface.parseLog(log);
|
|
3888
|
+
return {
|
|
3889
|
+
...log,
|
|
3890
|
+
args: parsed.args,
|
|
3891
|
+
fragment: parsed.fragment,
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
return log;
|
|
3895
|
+
});
|
|
3863
3896
|
const vaultStorageWriteEvents = await queryContractEventsChunked({
|
|
3864
3897
|
contract: bridgeTokenVault,
|
|
3865
3898
|
eventName: "StorageWriteObserved",
|
|
3866
3899
|
fromBlock: genesisBlockNumber,
|
|
3867
3900
|
toBlock: latestBlock,
|
|
3868
3901
|
});
|
|
3869
|
-
const observedStorageLogs = await fetchLogsChunked(provider, {
|
|
3870
|
-
address: channelInfo.manager,
|
|
3871
|
-
topics: [[
|
|
3872
|
-
CONTROLLER_STORAGE_KEY_OBSERVED_TOPIC,
|
|
3873
|
-
VAULT_STORAGE_WRITE_OBSERVED_TOPIC,
|
|
3874
|
-
]],
|
|
3875
|
-
fromBlock: genesisBlockNumber,
|
|
3876
|
-
toBlock: latestBlock,
|
|
3877
|
-
});
|
|
3878
3902
|
|
|
3879
3903
|
const groupedEvents = new Map();
|
|
3880
|
-
for (const event of [...
|
|
3904
|
+
for (const event of [...channelManagerEvents, ...vaultStorageWriteEvents]) {
|
|
3881
3905
|
const key = event.transactionHash;
|
|
3882
3906
|
const group = groupedEvents.get(key) ?? [];
|
|
3883
3907
|
group.push(event);
|
|
@@ -4000,6 +4024,7 @@ async function fetchLogsChunked(provider, {
|
|
|
4000
4024
|
while (cursor <= resolvedToBlock) {
|
|
4001
4025
|
const chunkToBlock = Math.min(resolvedToBlock, cursor + chunkSize - 1);
|
|
4002
4026
|
try {
|
|
4027
|
+
await throttleLogRequest();
|
|
4003
4028
|
const logs = await provider.getLogs({
|
|
4004
4029
|
address,
|
|
4005
4030
|
topics,
|
|
@@ -4009,6 +4034,12 @@ async function fetchLogsChunked(provider, {
|
|
|
4009
4034
|
aggregatedLogs.push(...logs);
|
|
4010
4035
|
cursor = chunkToBlock + 1;
|
|
4011
4036
|
} catch (error) {
|
|
4037
|
+
if (isRateLimitError(error)) {
|
|
4038
|
+
throw new Error(
|
|
4039
|
+
`RPC log query rate limit exceeded. Log chunk requests are paced at ${DEFAULT_LOG_REQUESTS_PER_SECOND} requests per second.`,
|
|
4040
|
+
{ cause: error },
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4012
4043
|
const suggestedChunkSize = deriveRecommendedLogChunkSize(error, chunkSize);
|
|
4013
4044
|
if (suggestedChunkSize >= chunkSize) {
|
|
4014
4045
|
throw error;
|
|
@@ -4020,6 +4051,31 @@ async function fetchLogsChunked(provider, {
|
|
|
4020
4051
|
return aggregatedLogs;
|
|
4021
4052
|
}
|
|
4022
4053
|
|
|
4054
|
+
async function throttleLogRequest() {
|
|
4055
|
+
const elapsedMs = Date.now() - lastLogRequestStartedAtMs;
|
|
4056
|
+
if (elapsedMs < LOG_REQUEST_INTERVAL_MS) {
|
|
4057
|
+
await sleep(LOG_REQUEST_INTERVAL_MS - elapsedMs);
|
|
4058
|
+
}
|
|
4059
|
+
lastLogRequestStartedAtMs = Date.now();
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
function sleep(ms) {
|
|
4063
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
function isRateLimitError(error) {
|
|
4067
|
+
const serializedError = [
|
|
4068
|
+
error?.code,
|
|
4069
|
+
error?.status,
|
|
4070
|
+
error?.message,
|
|
4071
|
+
error?.shortMessage,
|
|
4072
|
+
error?.info?.responseStatus,
|
|
4073
|
+
error?.info?.responseBody,
|
|
4074
|
+
].filter((value) => value !== undefined && value !== null).join("\n");
|
|
4075
|
+
|
|
4076
|
+
return /\b429\b|too many requests|rate limit|compute units/i.test(serializedError);
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4023
4079
|
function deriveRecommendedLogChunkSize(error, currentChunkSize) {
|
|
4024
4080
|
const serializedError = [
|
|
4025
4081
|
error?.message,
|