@tokamak-private-dapps/private-state-cli 1.2.0 → 1.2.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 +5 -2
- package/README.md +4 -4
- package/package.json +1 -1
- package/private-state-bridge-cli.mjs +28 -47
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.1 - 2026-05-11
|
|
4
|
+
|
|
5
|
+
- Changed pre-command automatic recovery from an RPC log request time estimate to a fixed
|
|
6
|
+
7,200-block recovery delta budget.
|
|
7
|
+
|
|
3
8
|
## 1.2.0 - 2026-05-08
|
|
4
9
|
|
|
5
10
|
- Added optional channel workspace mirror recovery. `channel recover-workspace` now accepts
|
|
@@ -20,8 +25,6 @@
|
|
|
20
25
|
when a genesis rebuild is required.
|
|
21
26
|
- Restored received-note event-log refresh for `wallet get-notes`, `wallet transfer-notes`, and
|
|
22
27
|
`wallet redeem-notes`, limited to the saved wallet note recovery index.
|
|
23
|
-
- Limited pre-command automatic recovery to a 10 second RPC log scan budget based on the CLI's
|
|
24
|
-
paced log query rate.
|
|
25
28
|
- Reworked workspace mirror recovery around leader-signed checkpoint manifests and
|
|
26
29
|
delta bundles. When a local recovery index exists, the CLI prechecks the mirror checkpoint and
|
|
27
30
|
downloads only the matching delta bundle instead of a full workspace bundle.
|
package/README.md
CHANGED
|
@@ -118,9 +118,9 @@ saved index instead of silently replaying from genesis.
|
|
|
118
118
|
Wallet getter commands that need channel state, including `wallet get-meta`, `wallet get-channel-fund`, and
|
|
119
119
|
`wallet get-notes`, refresh stale local workspaces through saved recovery indexes before reading state. `wallet get-notes`
|
|
120
120
|
also refreshes received-note logs through the saved wallet note recovery index. Automatic refresh never replays from
|
|
121
|
-
channel genesis and only runs when the
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
channel genesis and only runs when the recovery delta fits within the 7,200-block pre-command budget. If a saved index
|
|
122
|
+
is missing, unusable, or too far behind, the command stops and asks the user to run the appropriate recovery command
|
|
123
|
+
with `--from-genesis` explicitly when needed.
|
|
124
124
|
|
|
125
125
|
Channel leaders can optionally register a workspace mirror server so users can bootstrap recovery
|
|
126
126
|
from a signed checkpoint and download only the local-to-checkpoint delta when a local recovery index
|
|
@@ -341,7 +341,7 @@ Suggested interaction flow:
|
|
|
341
341
|
`wallet mint-notes`.
|
|
342
342
|
7. For a private transfer, select available note IDs from `wallet get-notes`, find the recipient L2 address from
|
|
343
343
|
`wallet get-meta`, then build `wallet transfer-notes`.
|
|
344
|
-
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
|
|
344
|
+
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 --from-genesis`.
|
|
345
345
|
|
|
346
346
|
Example onboarding explanation for `channel join`:
|
|
347
347
|
|
package/package.json
CHANGED
|
@@ -207,8 +207,7 @@ const ZERO_TOPIC = normalizeBytes32Hex(ethers.ZeroHash);
|
|
|
207
207
|
const DEFAULT_LOG_CHUNK_SIZE = 2000;
|
|
208
208
|
const DEFAULT_LOG_REQUESTS_PER_SECOND = 5;
|
|
209
209
|
const LOG_REQUEST_INTERVAL_MS = Math.ceil(1000 / DEFAULT_LOG_REQUESTS_PER_SECOND);
|
|
210
|
-
const
|
|
211
|
-
const AUTO_RECOVERY_LOG_REQUEST_BUDGET = DEFAULT_LOG_REQUESTS_PER_SECOND * AUTO_RECOVERY_TIME_BUDGET_SECONDS;
|
|
210
|
+
const AUTO_RECOVERY_BLOCK_BUDGET = 7200;
|
|
212
211
|
let lastLogRequestStartedAtMs = 0;
|
|
213
212
|
|
|
214
213
|
function printImmutableChannelPolicyWarning({
|
|
@@ -4456,7 +4455,7 @@ async function handleRedeemNotes({ args, provider }) {
|
|
|
4456
4455
|
context: preparedContextResult.context,
|
|
4457
4456
|
provider,
|
|
4458
4457
|
progressAction: "wallet redeem-notes",
|
|
4459
|
-
|
|
4458
|
+
preConsumedBlockDelta: preparedContextResult.autoRecoveryBlockDelta,
|
|
4460
4459
|
});
|
|
4461
4460
|
const inputNotes = loadWalletUnusedInputNotes(wallet, noteIds);
|
|
4462
4461
|
const templatePayload = buildRedeemNotesTemplatePayload({
|
|
@@ -4519,7 +4518,7 @@ async function handleWalletGetNotes({ args, provider }) {
|
|
|
4519
4518
|
context,
|
|
4520
4519
|
provider,
|
|
4521
4520
|
progressAction: "wallet get-notes",
|
|
4522
|
-
|
|
4521
|
+
preConsumedBlockDelta: contextResult.autoRecoveryBlockDelta,
|
|
4523
4522
|
});
|
|
4524
4523
|
|
|
4525
4524
|
const unusedTrackedNotes = wallet.wallet.notes.unusedOrder
|
|
@@ -4580,7 +4579,7 @@ async function handleTransferNotes({ args, provider }) {
|
|
|
4580
4579
|
provider,
|
|
4581
4580
|
signer,
|
|
4582
4581
|
progressAction: "wallet transfer-notes",
|
|
4583
|
-
|
|
4582
|
+
preConsumedBlockDelta: preparedContextResult.autoRecoveryBlockDelta,
|
|
4584
4583
|
});
|
|
4585
4584
|
const canonicalAssetDecimals = Number(wallet.wallet.canonicalAssetDecimals);
|
|
4586
4585
|
const noteIds = parseNoteIdVector(requireArg(args.noteIds, "--note-ids"));
|
|
@@ -5142,7 +5141,7 @@ async function ensureWalletNoteReceiveStateCurrent({
|
|
|
5142
5141
|
provider,
|
|
5143
5142
|
signer = null,
|
|
5144
5143
|
progressAction = null,
|
|
5145
|
-
|
|
5144
|
+
preConsumedBlockDelta = 0,
|
|
5146
5145
|
}) {
|
|
5147
5146
|
const latestBlock = await provider.getBlockNumber();
|
|
5148
5147
|
let nextBlock;
|
|
@@ -5167,17 +5166,16 @@ async function ensureWalletNoteReceiveStateCurrent({
|
|
|
5167
5166
|
nextBlock,
|
|
5168
5167
|
recoveredWalletWorkspace: false,
|
|
5169
5168
|
recoveredDeliveryState: null,
|
|
5170
|
-
|
|
5169
|
+
autoRecoveryBlockDelta: 0,
|
|
5171
5170
|
};
|
|
5172
5171
|
}
|
|
5173
|
-
const
|
|
5174
|
-
const
|
|
5172
|
+
const remainingBlockBudget = AUTO_RECOVERY_BLOCK_BUDGET - Math.max(0, Number(preConsumedBlockDelta));
|
|
5173
|
+
const autoRecoveryBlockDelta = assertAutoRecoveryBlockBudget({
|
|
5175
5174
|
label: `wallet note workspace ${walletContext.walletName}`,
|
|
5176
5175
|
fromBlock: nextBlock,
|
|
5177
5176
|
toBlock: latestBlock,
|
|
5178
|
-
logScanCount: 1,
|
|
5179
5177
|
recoveryCommand: `wallet recover-workspace --channel-name ${context.workspace.channelName} --network ${context.workspace.network} --account <ACCOUNT>`,
|
|
5180
|
-
|
|
5178
|
+
blockBudget: remainingBlockBudget,
|
|
5181
5179
|
});
|
|
5182
5180
|
|
|
5183
5181
|
const resolvedSigner = signer ?? restoreWalletParticipant(walletContext, provider).signer;
|
|
@@ -5205,7 +5203,7 @@ async function ensureWalletNoteReceiveStateCurrent({
|
|
|
5205
5203
|
...freshness,
|
|
5206
5204
|
recoveredWalletWorkspace: true,
|
|
5207
5205
|
recoveredDeliveryState,
|
|
5208
|
-
|
|
5206
|
+
autoRecoveryBlockDelta,
|
|
5209
5207
|
};
|
|
5210
5208
|
} catch (postRecoveryError) {
|
|
5211
5209
|
throw new Error([
|
|
@@ -5721,7 +5719,7 @@ async function loadFreshWalletChannelContext({
|
|
|
5721
5719
|
network: resolveCliNetwork(contextResult.context.workspace.network),
|
|
5722
5720
|
usingWorkspaceCache: !contextResult.recoveredWorkspace,
|
|
5723
5721
|
recoveredWorkspace: contextResult.recoveredWorkspace,
|
|
5724
|
-
|
|
5722
|
+
autoRecoveryBlockDelta: contextResult.autoRecoveryBlockDelta,
|
|
5725
5723
|
};
|
|
5726
5724
|
}
|
|
5727
5725
|
|
|
@@ -5753,7 +5751,7 @@ async function loadFreshChannelWorkspaceContextResult({
|
|
|
5753
5751
|
return {
|
|
5754
5752
|
context,
|
|
5755
5753
|
recoveredWorkspace: false,
|
|
5756
|
-
|
|
5754
|
+
autoRecoveryBlockDelta: 0,
|
|
5757
5755
|
};
|
|
5758
5756
|
} catch (error) {
|
|
5759
5757
|
const recovery = await recoverChannelWorkspaceFromIndexOnly({
|
|
@@ -5766,7 +5764,7 @@ async function loadFreshChannelWorkspaceContextResult({
|
|
|
5766
5764
|
return {
|
|
5767
5765
|
context: recovery.context,
|
|
5768
5766
|
recoveredWorkspace: true,
|
|
5769
|
-
|
|
5767
|
+
autoRecoveryBlockDelta: recovery.autoRecoveryBlockDelta,
|
|
5770
5768
|
};
|
|
5771
5769
|
}
|
|
5772
5770
|
}
|
|
@@ -5790,7 +5788,7 @@ async function recoverChannelWorkspaceFromIndexOnly({
|
|
|
5790
5788
|
if (readiness.alreadyCurrent) {
|
|
5791
5789
|
return {
|
|
5792
5790
|
context: await loadWorkspaceContext(channelName, networkName, provider),
|
|
5793
|
-
|
|
5791
|
+
autoRecoveryBlockDelta: 0,
|
|
5794
5792
|
};
|
|
5795
5793
|
}
|
|
5796
5794
|
try {
|
|
@@ -5830,7 +5828,7 @@ async function recoverChannelWorkspaceFromIndexOnly({
|
|
|
5830
5828
|
}
|
|
5831
5829
|
return {
|
|
5832
5830
|
context,
|
|
5833
|
-
|
|
5831
|
+
autoRecoveryBlockDelta: readiness.autoRecoveryBlockDelta,
|
|
5834
5832
|
};
|
|
5835
5833
|
}
|
|
5836
5834
|
|
|
@@ -5886,14 +5884,13 @@ async function requireChannelWorkspaceRecoveryIndexForAutoRefresh({
|
|
|
5886
5884
|
if (Number(recoveryIndex.nextBlock) > Number(latestBlock)) {
|
|
5887
5885
|
fail(`Channel workspace recovery index has already scanned through block ${recoveryIndex.nextBlock - 1}, but the local snapshot is not current.`);
|
|
5888
5886
|
}
|
|
5889
|
-
const
|
|
5887
|
+
const autoRecoveryBlockDelta = assertAutoRecoveryBlockBudget({
|
|
5890
5888
|
label: `channel workspace ${channelName} on ${networkName}`,
|
|
5891
5889
|
fromBlock: recoveryIndex.nextBlock,
|
|
5892
5890
|
toBlock: latestBlock,
|
|
5893
|
-
logScanCount: 2,
|
|
5894
5891
|
recoveryCommand: `channel recover-workspace --channel-name ${channelName} --network ${networkName}`,
|
|
5895
5892
|
});
|
|
5896
|
-
return { alreadyCurrent: false,
|
|
5893
|
+
return { alreadyCurrent: false, autoRecoveryBlockDelta };
|
|
5897
5894
|
}
|
|
5898
5895
|
|
|
5899
5896
|
async function refreshPersistedWorkspaceAfterLocalTransaction({
|
|
@@ -7416,12 +7413,7 @@ async function fetchLogsChunked(provider, {
|
|
|
7416
7413
|
return aggregatedLogs;
|
|
7417
7414
|
}
|
|
7418
7415
|
|
|
7419
|
-
function
|
|
7420
|
-
fromBlock,
|
|
7421
|
-
toBlock,
|
|
7422
|
-
logScanCount = 1,
|
|
7423
|
-
chunkSize = DEFAULT_LOG_CHUNK_SIZE,
|
|
7424
|
-
}) {
|
|
7416
|
+
function recoveryBlockDelta({ fromBlock, toBlock }) {
|
|
7425
7417
|
const normalizedFromBlock = Number(fromBlock);
|
|
7426
7418
|
const normalizedToBlock = Number(toBlock);
|
|
7427
7419
|
if (!Number.isInteger(normalizedFromBlock) || !Number.isInteger(normalizedToBlock)) {
|
|
@@ -7430,38 +7422,27 @@ function estimateLogScanRequestCount({
|
|
|
7430
7422
|
if (normalizedFromBlock > normalizedToBlock) {
|
|
7431
7423
|
return 0;
|
|
7432
7424
|
}
|
|
7433
|
-
|
|
7434
|
-
return Math.ceil(totalBlocks / Math.max(1, Number(chunkSize))) * Math.max(1, Number(logScanCount));
|
|
7425
|
+
return normalizedToBlock - normalizedFromBlock + 1;
|
|
7435
7426
|
}
|
|
7436
7427
|
|
|
7437
|
-
function
|
|
7428
|
+
function assertAutoRecoveryBlockBudget({
|
|
7438
7429
|
label,
|
|
7439
7430
|
fromBlock,
|
|
7440
7431
|
toBlock,
|
|
7441
|
-
logScanCount,
|
|
7442
7432
|
recoveryCommand,
|
|
7443
|
-
|
|
7433
|
+
blockBudget = AUTO_RECOVERY_BLOCK_BUDGET,
|
|
7444
7434
|
}) {
|
|
7445
|
-
const
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
});
|
|
7450
|
-
const normalizedBudget = Math.max(0, Number(logRequestBudget));
|
|
7451
|
-
if (estimatedRequests <= normalizedBudget) {
|
|
7452
|
-
return estimatedRequests;
|
|
7435
|
+
const blockDelta = recoveryBlockDelta({ fromBlock, toBlock });
|
|
7436
|
+
const normalizedBudget = Math.max(0, Number(blockBudget));
|
|
7437
|
+
if (blockDelta <= normalizedBudget) {
|
|
7438
|
+
return blockDelta;
|
|
7453
7439
|
}
|
|
7454
7440
|
const normalizedFromBlock = Number(fromBlock);
|
|
7455
7441
|
const normalizedToBlock = Number(toBlock);
|
|
7456
|
-
const totalBlocks = normalizedFromBlock <= normalizedToBlock
|
|
7457
|
-
? normalizedToBlock - normalizedFromBlock + 1
|
|
7458
|
-
: 0;
|
|
7459
|
-
const estimatedSeconds = estimatedRequests / DEFAULT_LOG_REQUESTS_PER_SECOND;
|
|
7460
7442
|
throw new Error([
|
|
7461
|
-
`Automatic recovery for ${label} would exceed the ${
|
|
7462
|
-
`Recovery delta is ${
|
|
7463
|
-
`
|
|
7464
|
-
`Estimated minimum scan time: ${estimatedSeconds.toFixed(1)}s.`,
|
|
7443
|
+
`Automatic recovery for ${label} would exceed the ${AUTO_RECOVERY_BLOCK_BUDGET}-block pre-command budget.`,
|
|
7444
|
+
`Recovery delta is ${blockDelta} blocks from ${normalizedFromBlock} to ${normalizedToBlock}.`,
|
|
7445
|
+
`Remaining automatic recovery budget is ${normalizedBudget} blocks.`,
|
|
7465
7446
|
`Run ${recoveryCommand} first; add --from-genesis only if the saved recovery index is unusable.`,
|
|
7466
7447
|
].join(" "));
|
|
7467
7448
|
}
|