diff --git a/piecrust/src/store.rs b/piecrust/src/store.rs index e5684877..077dbd1a 100644 --- a/piecrust/src/store.rs +++ b/piecrust/src/store.rs @@ -32,6 +32,7 @@ use tree::{Hash, NewContractIndex}; use crate::store::commit::Hulk; use crate::store::tree::{ position_from_contract, BaseInfo, ContractIndexElement, ContractsMerkle, + TreePos, }; pub use bytecode::Bytecode; pub use memory::{Memory, PAGE_SIZE}; @@ -44,6 +45,7 @@ const BYTECODE_DIR: &str = "bytecode"; const MEMORY_DIR: &str = "memory"; const LEAF_DIR: &str = "leaf"; const BASE_FILE: &str = "base"; +const TREE_POS_FILE: &str = "tree_pos"; const ELEMENT_FILE: &str = "element"; const OBJECTCODE_EXTENSION: &str = "a"; const METADATA_EXTENSION: &str = "m"; @@ -283,7 +285,7 @@ impl ContractStore { fn session_with_base(&self, base: Option) -> ContractSession { let base_commit = base.and_then(|hash| { - self.commit_store.lock().unwrap().get_commit(&hash).cloned() //todo: clone + self.commit_store.lock().unwrap().get_commit(&hash).cloned() }); ContractSession::new( &self.root_dir, @@ -362,6 +364,16 @@ fn base_path_main, S: AsRef>( Ok(dir.join(BASE_FILE)) } +fn tree_pos_path_main, S: AsRef>( + main_dir: P, + commit_id: S, +) -> io::Result { + let commit_id = commit_id.as_ref(); + let dir = main_dir.as_ref().join(commit_id); + fs::create_dir_all(&dir)?; + Ok(dir.join(TREE_POS_FILE)) +} + fn commit_id_to_hash>(commit_id: S) -> Hash { let hash: [u8; 32] = hex::decode(commit_id.as_ref()) .expect("Hex decoding of commit id string should succeed") @@ -408,8 +420,20 @@ fn commit_from_dir>( let leaf_dir = main_dir.join(LEAF_DIR); tracing::trace!("before index_merkle_from_path"); - let index = - index_from_path(main_dir, leaf_dir, &maybe_hash, commit_store.clone())?; + let tree_pos = if let Some(ref hash_hex) = commit_id { + let tree_pos_path = main_dir.join(hash_hex).join(TREE_POS_FILE); + Some(tree_pos_from_path(tree_pos_path)?.tree_pos) + } else { + None + }; + + let (index, contracts_merkle) = index_merkle_from_path( + main_dir, + leaf_dir, + &maybe_hash, + commit_store.clone(), + tree_pos.as_ref(), + )?; tracing::trace!("after index_merkle_from_path"); let bytecode_dir = main_dir.join(BYTECODE_DIR); @@ -465,12 +489,11 @@ fn commit_from_dir>( } } - let (base, contracts_merkle) = if let Some(hash_hex) = commit_id { + let base = if let Some(ref hash_hex) = commit_id { let base_info_path = main_dir.join(hash_hex).join(BASE_FILE); - let base_info = base_from_path(base_info_path)?; - (base_info.maybe_base, base_info.contracts_merkle) + base_from_path(base_info_path)?.maybe_base } else { - (None, ContractsMerkle::default()) + None }; Ok(Commit { @@ -482,15 +505,17 @@ fn commit_from_dir>( }) } -fn index_from_path( +fn index_merkle_from_path( main_path: impl AsRef, leaf_dir: impl AsRef, maybe_commit_id: &Option, commit_store: Arc>, -) -> io::Result { + maybe_tree_pos: Option<&BTreeMap>, +) -> io::Result<(NewContractIndex, ContractsMerkle)> { let leaf_dir = leaf_dir.as_ref(); let mut index: NewContractIndex = NewContractIndex::new(); + let mut merkle: ContractsMerkle = ContractsMerkle::default(); for entry in fs::read_dir(leaf_dir)? { let entry = entry?; @@ -533,7 +558,18 @@ fn index_from_path( } } - Ok(index) + match maybe_tree_pos { + Some(tree_pos) => { + for (int_pos, (hash, pos)) in tree_pos.iter() { + merkle.insert_with_int_pos(*pos, *int_pos as u64, *hash); + } + } + None => { + unreachable!() + } + } + + Ok((index, merkle)) } fn base_from_path>(path: P) -> io::Result { @@ -550,6 +586,20 @@ fn base_from_path>(path: P) -> io::Result { Ok(base_info) } +fn tree_pos_from_path>(path: P) -> io::Result { + let path = path.as_ref(); + + let tree_pos_bytes = fs::read(path)?; + let tree_pos = rkyv::from_bytes(&tree_pos_bytes).map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Invalid tree positions file \"{path:?}\": {err}"), + ) + })?; + + Ok(tree_pos) +} + #[derive(Debug, Clone)] pub(crate) struct Commit { index: NewContractIndex, @@ -1028,8 +1078,6 @@ fn write_commit_inner, S: AsRef>( tracing::trace!("persisting index started"); for (contract_id, element) in commit.index.iter() { if commit_contracts.contains_key(contract_id) { - // todo: write element to disk at - // main/leaf/{contract_id}/{commit_id} let element_dir_path = directories .leaf_main_dir .join(hex::encode(contract_id.as_bytes())) @@ -1048,10 +1096,8 @@ fn write_commit_inner, S: AsRef>( } tracing::trace!("persisting index finished"); - base_info.contracts_merkle = commit.contracts_merkle.clone(); //todo: clone - let base_main_path = - base_path_main(directories.main_dir, commit_id.as_ref())?; + base_path_main(&directories.main_dir, commit_id.as_ref())?; let base_info_bytes = rkyv::to_bytes::<_, 128>(&base_info).map_err(|err| { io::Error::new( @@ -1061,6 +1107,19 @@ fn write_commit_inner, S: AsRef>( })?; fs::write(base_main_path, base_info_bytes)?; + let tree_pos_main_path = + tree_pos_path_main(&directories.main_dir, commit_id.as_ref())?; + let tree_pos_bytes = rkyv::to_bytes::<_, 128>( + commit.contracts_merkle.tree_pos(), + ) + .map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Failed serializing tree positions file: {err}"), + ) + })?; + fs::write(tree_pos_main_path, tree_pos_bytes)?; + Ok(()) } @@ -1101,6 +1160,7 @@ fn finalize_commit>( let root = hex::encode(root); let commit_path = main_dir.join(&root); let base_info_path = commit_path.join(BASE_FILE); + let tree_pos_path = commit_path.join(TREE_POS_FILE); let base_info = base_from_path(&base_info_path)?; for contract_hint in base_info.contract_hints { let contract_hex = hex::encode(contract_hint); @@ -1130,6 +1190,7 @@ fn finalize_commit>( } fs::remove_file(base_info_path)?; + fs::remove_file(tree_pos_path)?; fs::remove_dir(commit_path)?; Ok(()) diff --git a/piecrust/src/store/session.rs b/piecrust/src/store/session.rs index c50a5a21..a50a8892 100644 --- a/piecrust/src/store/session.rs +++ b/piecrust/src/store/session.rs @@ -96,7 +96,7 @@ impl ContractSession { .base .as_ref() .map(|c| c.fast_clone(&mut self.contracts.keys())) - .unwrap_or(Commit::new(&self.commit_store, None)); // todo: what about None here + .unwrap_or(Commit::new(&self.commit_store, None)); for (contract, entry) in &self.contracts { commit.insert(*contract, &entry.memory); } @@ -116,7 +116,7 @@ impl ContractSession { let mut commit = self .base .clone() - .unwrap_or(Commit::new(&self.commit_store, None)); // todo: what about None here + .unwrap_or(Commit::new(&self.commit_store, None)); for (contract, entry) in &self.contracts { commit.insert(*contract, &entry.memory); } @@ -152,7 +152,7 @@ impl ContractSession { let (replier, receiver) = mpsc::sync_channel(1); let mut contracts = BTreeMap::new(); - let base = self.base.clone(); // todo: clone + let base = self.base.clone(); mem::swap(&mut self.contracts, &mut contracts); diff --git a/piecrust/src/store/tree.rs b/piecrust/src/store/tree.rs index c481bf69..96e5d1ee 100644 --- a/piecrust/src/store/tree.rs +++ b/piecrust/src/store/tree.rs @@ -97,6 +97,7 @@ impl NewContractIndex { pub struct ContractsMerkle { inner_tree: Tree, dict: BTreeMap, + tree_pos: BTreeMap, } impl Default for ContractsMerkle { @@ -104,6 +105,7 @@ impl Default for ContractsMerkle { Self { inner_tree: Tree::new(), dict: BTreeMap::new(), + tree_pos: BTreeMap::new(), } } } @@ -119,12 +121,14 @@ impl ContractsMerkle { Some(p) => *p, }; self.inner_tree.insert(new_pos, hash); + self.tree_pos.insert(new_pos as u32, (hash, pos)); new_pos } pub fn insert_with_int_pos(&mut self, pos: u64, int_pos: u64, hash: Hash) { self.dict.insert(pos, int_pos); self.inner_tree.insert(int_pos, hash); + self.tree_pos.insert(int_pos as u32, (hash, pos)); } pub fn opening(&self, pos: u64) -> Option { @@ -135,6 +139,14 @@ impl ContractsMerkle { pub fn root(&self) -> Ref { self.inner_tree.root() } + + pub fn tree_pos(&self) -> &BTreeMap { + &self.tree_pos + } + + pub fn len(&self) -> u64 { + self.inner_tree.len() + } } #[derive(Debug, Clone, Archive, Deserialize, Serialize)] @@ -151,7 +163,12 @@ pub struct ContractIndex { pub struct BaseInfo { pub contract_hints: Vec, pub maybe_base: Option, - pub contracts_merkle: ContractsMerkle, +} + +#[derive(Debug, Clone, Default, Archive, Deserialize, Serialize)] +#[archive_attr(derive(CheckBytes))] +pub struct TreePos { + pub tree_pos: BTreeMap, } #[derive(Debug, Clone, Archive, Deserialize, Serialize)]