|
1 | 1 | use std::error::Error; |
| 2 | +use std::path::PathBuf; |
2 | 3 |
|
3 | 4 | use starknet_api::transaction::fields::Proof; |
4 | 5 | use starknet_types_core::felt::Felt; |
| 6 | +use thiserror::Error; |
5 | 7 |
|
6 | 8 | pub trait ProofStorage: Send + Sync { |
7 | 9 | type Error: Error; |
8 | 10 | fn set_proof(&self, facts_hash: Felt, proof: Proof) -> Result<(), Self::Error>; |
9 | 11 | fn get_proof(&self, facts_hash: Felt) -> Result<Option<Proof>, Self::Error>; |
10 | 12 | fn contains_proof(&self, facts_hash: Felt) -> Result<bool, Self::Error>; |
11 | 13 | } |
| 14 | + |
| 15 | +#[derive(Debug, Error)] |
| 16 | +pub enum FsProofStorageError { |
| 17 | + #[error(transparent)] |
| 18 | + IoError(#[from] std::io::Error), |
| 19 | + #[error("Proof for facts_hash {facts_hash} not found.")] |
| 20 | + ProofNotFound { facts_hash: Felt }, |
| 21 | +} |
| 22 | + |
| 23 | +type FsProofStorageResult<T> = Result<T, FsProofStorageError>; |
| 24 | + |
| 25 | +pub struct FsProofStorage { |
| 26 | + persistent_root: PathBuf, |
| 27 | +} |
| 28 | + |
| 29 | +impl FsProofStorage { |
| 30 | + // TODO(Einat): consider code sharing with class storage. |
| 31 | + pub fn new(persistent_root: PathBuf) -> Result<Self, std::io::Error> { |
| 32 | + std::fs::create_dir_all(&persistent_root)?; |
| 33 | + Ok(Self { persistent_root }) |
| 34 | + } |
| 35 | + |
| 36 | + /// Returns the directory that will hold the proof of a certain proof facts hash. |
| 37 | + /// For a proof facts hash: 0xa1b2c3d4... (rest of hash), the structure is: |
| 38 | + /// a1/ |
| 39 | + /// └── b2/ |
| 40 | + /// └── a1b2c3d4.../ |
| 41 | + #[allow(dead_code)] |
| 42 | + fn get_proof_dir(&self, facts_hash: Felt) -> PathBuf { |
| 43 | + let facts_hash = hex::encode(facts_hash.to_bytes_be()); |
| 44 | + let (first_msb_byte, second_msb_byte, _rest_of_bytes) = |
| 45 | + (&facts_hash[..2], &facts_hash[2..4], &facts_hash[4..]); |
| 46 | + PathBuf::from(first_msb_byte).join(second_msb_byte).join(facts_hash) |
| 47 | + } |
| 48 | + |
| 49 | + #[allow(dead_code)] |
| 50 | + fn get_persistent_dir(&self, facts_hash: Felt) -> PathBuf { |
| 51 | + self.persistent_root.join(self.get_proof_dir(facts_hash)) |
| 52 | + } |
| 53 | + |
| 54 | + #[allow(dead_code)] |
| 55 | + fn get_persistent_dir_with_create(&self, facts_hash: Felt) -> FsProofStorageResult<PathBuf> { |
| 56 | + let path = self.get_persistent_dir(facts_hash); |
| 57 | + if let Some(parent) = path.parent() { |
| 58 | + std::fs::create_dir_all(parent)?; |
| 59 | + } |
| 60 | + |
| 61 | + Ok(path) |
| 62 | + } |
| 63 | + |
| 64 | + #[allow(dead_code)] |
| 65 | + fn create_tmp_dir( |
| 66 | + &self, |
| 67 | + facts_hash: Felt, |
| 68 | + ) -> FsProofStorageResult<(tempfile::TempDir, PathBuf)> { |
| 69 | + // Compute the final persistent directory for this `facts_hash` |
| 70 | + let persistent_dir = self.get_persistent_dir(facts_hash); |
| 71 | + let parent_dir = persistent_dir |
| 72 | + .parent() |
| 73 | + .expect("Proof persistent dir should have a parent") |
| 74 | + .to_path_buf(); |
| 75 | + std::fs::create_dir_all(&parent_dir)?; |
| 76 | + // Create a temporary directory under the parent of the final persistent directory to ensure |
| 77 | + // `rename` will be atomic. |
| 78 | + let tmp_root = tempfile::tempdir_in(&parent_dir)?; |
| 79 | + // Get the leaf directory name of the final persistent directory. |
| 80 | + let leaf = persistent_dir.file_name().expect("Proof dir leaf should exist"); |
| 81 | + // Create the temporary directory under the temporary root. |
| 82 | + let tmp_dir = tmp_root.path().join(leaf); |
| 83 | + // Returning `TempDir` since without it the handle would drop immediately and the temp |
| 84 | + // directory would be removed before writes/rename. |
| 85 | + Ok((tmp_root, tmp_dir)) |
| 86 | + } |
| 87 | +} |
0 commit comments