-
Notifications
You must be signed in to change notification settings - Fork 47
feat: add F3 proofs cache #1457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 34 commits
95ee4d3
fbe0db6
4c6bf4e
5f3eb8a
fbee757
8a5533e
59ab3bc
e69da3f
d0c1e99
0cb4a42
784b599
2ed6931
d25d646
9df2716
6cfde38
bc8076d
8495902
e2a7de6
d59aece
3b84809
279aacd
e174661
ac527de
25a029a
221baa3
d77f568
495d7f0
2fe7b60
a4e27bd
1747f78
be9c28e
0e9612e
ab2ba27
d40e5f4
7192140
7aef2f2
93f317d
3cc0376
6914245
9be81e8
4e168f0
b0213ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Copyright 2022-2025 Protocol Labs | ||
| // SPDX-License-Identifier: Apache-2.0, MIT | ||
|
|
||
| use clap::{Args, Subcommand}; | ||
| use std::path::PathBuf; | ||
|
|
||
| #[derive(Debug, Args)] | ||
| #[command(name = "proof-cache", about = "Inspect and debug F3 proof cache")] | ||
| pub struct ProofCacheArgs { | ||
| #[command(subcommand)] | ||
| pub command: ProofCacheCommands, | ||
| } | ||
|
|
||
| #[derive(Debug, Subcommand)] | ||
| pub enum ProofCacheCommands { | ||
| /// Inspect cache contents | ||
| Inspect { | ||
| /// Database path | ||
| #[arg(long, env = "FM_PROOF_CACHE_DB")] | ||
| db_path: PathBuf, | ||
| }, | ||
| /// Show cache statistics | ||
| Stats { | ||
| /// Database path | ||
| #[arg(long, env = "FM_PROOF_CACHE_DB")] | ||
| db_path: PathBuf, | ||
| }, | ||
| /// Get specific proof by instance ID | ||
| Get { | ||
| /// Database path | ||
| #[arg(long, env = "FM_PROOF_CACHE_DB")] | ||
| db_path: PathBuf, | ||
|
|
||
| /// Instance ID to fetch | ||
| #[arg(long)] | ||
| instance_id: u64, | ||
| }, | ||
| /// Clear the cache | ||
| Clear { | ||
| /// Database path | ||
| #[arg(long, env = "FM_PROOF_CACHE_DB")] | ||
| db_path: PathBuf, | ||
| }, | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| // Copyright 2022-2025 Protocol Labs | ||
| // SPDX-License-Identifier: Apache-2.0, MIT | ||
|
|
||
| use crate::cmd; | ||
| use crate::options::proof_cache::{ProofCacheArgs, ProofCacheCommands}; | ||
| use fendermint_vm_topdown_proof_service::persistence::ProofCachePersistence; | ||
| use std::path::Path; | ||
|
|
||
| cmd! { | ||
| ProofCacheArgs(self) { | ||
| handle_proof_cache_command(self) | ||
| } | ||
| } | ||
|
|
||
| fn handle_proof_cache_command(args: &ProofCacheArgs) -> anyhow::Result<()> { | ||
| match &args.command { | ||
| ProofCacheCommands::Inspect { db_path } => inspect_cache(db_path), | ||
| ProofCacheCommands::Stats { db_path } => show_stats(db_path), | ||
| ProofCacheCommands::Get { | ||
| db_path, | ||
| instance_id, | ||
| } => get_proof(db_path, *instance_id), | ||
| ProofCacheCommands::Clear { db_path } => clear_cache(db_path), | ||
| } | ||
| } | ||
|
|
||
| fn inspect_cache(db_path: &Path) -> anyhow::Result<()> { | ||
| println!("=== Proof Cache Inspection ==="); | ||
| println!("Database: {}", db_path.display()); | ||
| println!(); | ||
|
|
||
| let persistence = ProofCachePersistence::open(db_path)?; | ||
| let entries = persistence.load_all_entries()?; | ||
|
|
||
| if entries.is_empty() { | ||
| println!("\nCache is empty."); | ||
| return Ok(()); | ||
| } | ||
|
|
||
| println!("\nEntries:"); | ||
| println!( | ||
| "{:<12} {:<20} {:<15} {:<15}", | ||
| "Instance ID", "Epochs", "Proof Size", "Signers" | ||
| ); | ||
| println!("{}", "-".repeat(70)); | ||
|
|
||
| for entry in &entries { | ||
| let proof_size = fvm_ipld_encoding::to_vec(&entry.proof_bundle) | ||
| .map(|v| v.len()) | ||
| .unwrap_or(0); | ||
|
|
||
| println!( | ||
| "{:<12} {:<20?} {:<15} {:<15}", | ||
| entry.certificate.gpbft_instance, | ||
| entry.certificate.ec_chain.suffix(), | ||
| format!("{} bytes", proof_size), | ||
| format!("{} signers", entry.certificate.signers.len()) | ||
| ); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn show_stats(db_path: &Path) -> anyhow::Result<()> { | ||
| println!("=== Proof Cache Statistics ==="); | ||
| println!("Database: {}", db_path.display()); | ||
| println!(); | ||
|
|
||
| let persistence = ProofCachePersistence::open(db_path)?; | ||
| let entries = persistence.load_all_entries()?; | ||
|
|
||
| if entries.is_empty() { | ||
| println!("Cache is empty."); | ||
| return Ok(()); | ||
| } | ||
|
|
||
| println!("Count: {}", entries.len()); | ||
| println!( | ||
| "Instances: {} - {}", | ||
| entries | ||
| .first() | ||
| .map(|e| e.certificate.gpbft_instance) | ||
| .unwrap_or(0), | ||
| entries | ||
| .last() | ||
| .map(|e| e.certificate.gpbft_instance) | ||
| .unwrap_or(0) | ||
| ); | ||
| println!(); | ||
|
|
||
| // Proof size statistics | ||
| let total_proof_size: usize = entries | ||
| .iter() | ||
| .map(|e| { | ||
| fvm_ipld_encoding::to_vec(&e.proof_bundle) | ||
| .map(|v| v.len()) | ||
| .unwrap_or(0) | ||
| }) | ||
| .sum(); | ||
| let avg_proof_size = total_proof_size / entries.len(); | ||
|
|
||
| println!("Proof Bundle Statistics:"); | ||
| println!( | ||
| " Total Size: {} bytes ({:.2} MB)", | ||
| total_proof_size, | ||
| total_proof_size as f64 / 1024.0 / 1024.0 | ||
| ); | ||
| println!( | ||
| " Average Size: {} bytes ({:.2} KB)", | ||
| avg_proof_size, | ||
| avg_proof_size as f64 / 1024.0 | ||
| ); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn get_proof(db_path: &Path, instance_id: u64) -> anyhow::Result<()> { | ||
| println!("=== Get Proof for Instance {} ===", instance_id); | ||
| println!("Database: {}", db_path.display()); | ||
| println!(); | ||
|
|
||
| let persistence = ProofCachePersistence::open(db_path)?; | ||
| let entries = persistence.load_all_entries()?; | ||
|
|
||
| if entries.is_empty() { | ||
| println!("Cache is empty."); | ||
| return Ok(()); | ||
| } | ||
|
|
||
| let entry = entries | ||
| .iter() | ||
| .find(|e| e.certificate.gpbft_instance == instance_id); | ||
|
|
||
| if let Some(entry) = entry { | ||
| println!("Found proof for instance {}", instance_id); | ||
| println!(); | ||
|
|
||
| // Certificate Details | ||
| println!("F3 Certificate:"); | ||
| println!(" Instance ID: {}", entry.certificate.gpbft_instance); | ||
| println!( | ||
| " Finalized Epochs: {:?}", | ||
| &entry.certificate.ec_chain.suffix() | ||
| ); | ||
| println!( | ||
| " BLS Signature: {} bytes", | ||
| entry.certificate.signature.len() | ||
| ); | ||
| println!(" Signers: {} validators", entry.certificate.signers.len()); | ||
| println!(); | ||
|
|
||
| // Proof Bundle Summary | ||
| let proof_bundle_size = fvm_ipld_encoding::to_vec(&entry.proof_bundle) | ||
| .map(|v| v.len()) | ||
| .unwrap_or(0); | ||
| println!("Proof Bundle:"); | ||
| println!( | ||
| " Total Size: {} bytes ({:.2} KB)", | ||
| proof_bundle_size, | ||
| proof_bundle_size as f64 / 1024.0 | ||
| ); | ||
|
|
||
| if let Some(proof_bundle) = &entry.proof_bundle { | ||
| println!(" Storage Proofs: {}", proof_bundle.storage_proofs.len()); | ||
| println!(" Event Proofs: {}", proof_bundle.event_proofs.len()); | ||
| println!(" Witness Blocks: {}", proof_bundle.blocks.len()); | ||
| println!(); | ||
| } else { | ||
| println!(" No proof bundle found"); | ||
| } | ||
|
|
||
| // Metadata | ||
| println!("Metadata:"); | ||
| println!(" Generated At: {:?}", entry.generated_at); | ||
| println!(" Source RPC: {}", entry.source_rpc); | ||
| } else { | ||
| println!("No proof found for instance {}", instance_id); | ||
| println!(); | ||
| println!("Available instances: {:?}", entries.len()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Misleading CLI output shows count instead of instance IDsIn the |
||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn clear_cache(db_path: &Path) -> anyhow::Result<()> { | ||
| println!("=== Clear Cache ==="); | ||
| println!("Database: {}", db_path.display()); | ||
| println!(); | ||
|
|
||
| let persistence = ProofCachePersistence::open(db_path)?; | ||
| persistence.clear_all_entries()?; | ||
|
|
||
| Ok(()) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.