Skip to content

Commit 9cab5b6

Browse files
committed
update docs/shadow-testing/README.md
1 parent 129d6e5 commit 9cab5b6

2 files changed

Lines changed: 164 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ target
3333
zkvm-prover/*.json
3434
.work/
3535
rollup/tests.test
36+
local-secrets.md
37+
tmp/

docs/shadow-testing/README.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,60 @@ Proving status values:
305305
- If you have system PostgreSQL on 5432, use 5433 for shadow DB (already configured).
306306
- Ensure all configs use the correct port.
307307

308+
### Multi-GPU prover cache conflicts
309+
When running multiple prover instances on the same machine, the shared `.work/galileo` cache directory can cause `File exists (os error 17)` conflicts if two provers write the same temp file simultaneously.
310+
311+
**Mitigation**: Ensure each prover has its own work directory, or symlink `.work/galileo` to a shared read-only cache while giving each instance a distinct write directory. Example launch script:
312+
```bash
313+
for i in 0 1 2 3; do
314+
mkdir -p /tmp/prover-gpu${i}/work
315+
ln -s /shared/cache/galileo /tmp/prover-gpu${i}/work/galileo
316+
CUDA_VISIBLE_DEVICES=$i ./prover --config /tmp/prover-gpu${i}/config.json &
317+
done
318+
```
319+
320+
### Bundle proving never starts
321+
If coordinator is actively assigning chunk/batch tasks but never assigns bundle tasks, the most likely cause is **orphan bundles** — bundle records whose corresponding batch data no longer exists in the shadow DB.
322+
323+
**Diagnosis**:
324+
```sql
325+
-- Count bundles with no linked batches
326+
SELECT COUNT(*) FROM bundle b
327+
WHERE NOT EXISTS (
328+
SELECT 1 FROM batch bat
329+
WHERE bat.index BETWEEN b.start_batch_index AND b.end_batch_index
330+
);
331+
```
332+
333+
**Root cause**: The bundle table often retains historical records from production (e.g., batch 308516+) while the batch table only holds recently imported batches (e.g., 517760+). Coordinator's `GetUnassignedBundle` picks the lowest-index bundle with `batch_proofs_status = 2`, finds it has no batches, and fails silently in a loop.
334+
335+
**Fix**:
336+
```sql
337+
UPDATE bundle
338+
SET batch_proofs_status = 1
339+
WHERE index NOT IN (
340+
SELECT DISTINCT b.index
341+
FROM bundle b
342+
JOIN batch bat ON bat.index BETWEEN b.start_batch_index AND b.end_batch_index
343+
);
344+
```
345+
346+
### DB data inconsistency after import
347+
If imported chunks have `proving_status = 2` (assigned) but `proof = NULL`, coordinator may incorrectly set `batch.chunk_proofs_status = 2` and then fail when formatting batch tasks.
348+
349+
**Fix**:
350+
```sql
351+
UPDATE chunk SET proving_status = 1, total_attempts = 0, active_attempts = 0
352+
WHERE proving_status = 2 AND proof IS NULL;
353+
354+
UPDATE batch SET chunk_proofs_status = 0
355+
WHERE chunk_proofs_status != 0
356+
AND EXISTS (
357+
SELECT 1 FROM chunk c
358+
WHERE c.batch_hash = batch.hash AND c.proving_status != 4
359+
);
360+
```
361+
308362
## Configuration Reference
309363

310364
### Shadow Coordinator Config
@@ -362,6 +416,77 @@ cd rollup && go build -o rollup_relayer ./cmd/rollup_relayer/app
362416

363417
For **full end-to-end** validation (including signature + receipt), use **Anvil** with `evm_snapshot`/`evm_revert` instead.
364418

419+
### Anvil + Mock ScrollChain Setup (Recommended for Dry-Run)
420+
421+
For the most realistic dry-run testing, deploy a minimal mock ScrollChain contract on a local Anvil node:
422+
423+
```bash
424+
# 1. Start Anvil forked from mainnet (or standalone)
425+
anvil --fork-url https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY --fork-block-number 33878313
426+
427+
# 2. Deploy mock contract (minimal Solidity with no-op commitBatches / finalizeBundle)
428+
cat > MockScrollChain.sol << 'EOF'
429+
// SPDX-License-Identifier: MIT
430+
pragma solidity ^0.8.0;
431+
contract MockScrollChain {
432+
mapping(address => bool) public isProver;
433+
address public owner;
434+
constructor() { owner = msg.sender; }
435+
function addProver(address _prover) external {
436+
require(msg.sender == owner, "Not owner");
437+
isProver[_prover] = true;
438+
}
439+
function commitBatches(uint8 version, bytes32 parentBatchHash, bytes32 batchHash) external {}
440+
function finalizeBundlePostEuclidV2NoProof(bytes calldata, uint256, bytes32, bytes32) external {}
441+
function finalizeBundlePostEuclidV2(bytes calldata, uint256, bytes32, bytes32, bytes calldata) external {}
442+
}
443+
EOF
444+
445+
# Compile and deploy
446+
solc --bin MockScrollChain.sol -o /tmp/mock
447+
BYTECODE=$(cat /tmp/mock/MockScrollChain.bin)
448+
cast send --rpc-url http://localhost:18545 \
449+
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
450+
--create "0x$BYTECODE"
451+
# → contractAddress: 0x1fA02b2d6A771842690194Cf62D91bdd92BfE28d
452+
453+
# 3. Fund sender accounts and add prover
454+
COMMIT_ADDR="0x1e32ABcfE6db15c1570709E3fC02725335f50A47"
455+
FINALIZE_ADDR="0x33e0F539E31B35170FAaA062af703b76a8282bf7"
456+
cast rpc anvil_setBalance "$COMMIT_ADDR" "0x3635c9adc5dea00000" --rpc-url http://localhost:18545
457+
cast rpc anvil_setBalance "$FINALIZE_ADDR" "0x3635c9adc5dea00000" --rpc-url http://localhost:18545
458+
cast send <MOCK_ADDR> "addProver(address)" "$FINALIZE_ADDR" --rpc-url http://localhost:18545 \
459+
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
460+
```
461+
462+
**Key sender config changes**:
463+
```json
464+
{
465+
"sender_config": {
466+
"endpoint": "http://localhost:18545",
467+
"dry_run": true
468+
}
469+
}
470+
```
471+
472+
**Dry-run gas estimation skip**: Anvil may fail `EstimateGas` on blob transactions or missing functions. A small patch to `rollup/internal/controller/sender/estimategas.go` skips gas estimation in dry-run mode:
473+
```go
474+
func (s *Sender) estimateGasLimit(...) (uint64, *types.AccessList, error) {
475+
if s.config.DryRun {
476+
return 10000000, nil, nil // skip estimation
477+
}
478+
// ... original logic
479+
}
480+
```
481+
482+
### What We Verified in Practice
483+
484+
| Transaction | Status | Notes |
485+
|-------------|--------|-------|
486+
| `commitBatches` |`eth_call` succeeded | Selector `0x9bbaa2ba` via mock `commitBatches(uint8,bytes32,bytes32)` |
487+
| `finalizeBundlePostEuclidV2NoProof` |`eth_call` succeeded | Selector `0xbd6f916b` via mock no-op |
488+
| `finalizeBundlePostEuclidV2` (with proof) |`eth_call` succeeded | Bundle 17301 with valid `OpenVMBundleProof` |
489+
365490
## Known Limitations
366491

367492
1. **L1 messages**: If chunks contain L1 messages, the prover needs `scroll_getL1MessagesInBlock` RPC support. Most public RPCs don't expose this. Workaround: select chunks/blocks with no L1 messages, or use an internal RPC. In non-validium mode, the prover does not call this RPC at all.
@@ -372,6 +497,43 @@ For **full end-to-end** validation (including signature + receipt), use **Anvil*
372497

373498
4. **Circuit download**: First prover run downloads ~5-10GB of circuit assets. Ensure good internet.
374499

500+
5. **Bundle vs batch count mismatch**: The shadow DB's `bundle` table may contain 10,000+ historical records while `batch` only holds ~500 recent ones. This is expected when importing production data — the bundle table retains full history but batches are truncated. **Crucially**, orphan bundles (those with no matching batches) must have `batch_proofs_status = 1` or coordinator will deadlock trying to prove them. See "Bundle proving never starts" in Troubleshooting.
501+
502+
## Common DB Fixes
503+
504+
After importing production data or running for extended periods, these SQL fixes resolve common coordinator deadlocks:
505+
506+
### 1. Reset proving status after import
507+
```sql
508+
UPDATE chunk SET proving_status = 1, total_attempts = 0, active_attempts = 0;
509+
UPDATE batch SET proving_status = 1, total_attempts = 0, active_attempts = 0, chunk_proofs_status = 0;
510+
UPDATE bundle SET proving_status = 1, total_attempts = 0, active_attempts = 0;
511+
```
512+
513+
### 2. Mark orphan bundles (no linked batches)
514+
```sql
515+
UPDATE bundle
516+
SET batch_proofs_status = 1
517+
WHERE index NOT IN (
518+
SELECT DISTINCT b.index
519+
FROM bundle b
520+
JOIN batch bat ON bat.index BETWEEN b.start_batch_index AND b.end_batch_index
521+
);
522+
```
523+
524+
### 3. Fix stale assigned chunks without proofs
525+
```sql
526+
UPDATE chunk SET proving_status = 1, total_attempts = 0, active_attempts = 0
527+
WHERE proving_status = 2 AND proof IS NULL;
528+
529+
UPDATE batch SET chunk_proofs_status = 0
530+
WHERE chunk_proofs_status != 0
531+
AND EXISTS (
532+
SELECT 1 FROM chunk c
533+
WHERE c.batch_hash = batch.hash AND c.proving_status != 4
534+
);
535+
```
536+
375537
## Scripts Reference
376538

377539
| Script | Purpose |

0 commit comments

Comments
 (0)