use crate::{
common::{
ende::{KeyEnDeOrdered, ValueEnDe},
error::Result,
},
versioned::{BranchId, CommitId, diff::DiffEntry, map::VerMap},
};
use super::{MptCalc, MptProof, SmtCalc, SmtProof, TrieCalc};
pub struct VerMapWithProof<K, V, T: TrieCalc> {
map: VerMap<K, V>,
trie: T,
trie_at_head: Option<T>,
sync_commit: Option<CommitId>,
sync_branch: Option<BranchId>,
dirty_applied: bool,
cache_id: u64,
cache_dirty: bool,
}
impl<K, V, T> VerMapWithProof<K, V, T>
where
K: KeyEnDeOrdered,
V: ValueEnDe,
T: TrieCalc,
{
pub fn new() -> Self {
let map = VerMap::new();
let cache_id = map.instance_id().map_id;
let mut this = Self {
map,
trie: T::default(),
trie_at_head: None,
sync_commit: None,
sync_branch: None,
dirty_applied: false,
cache_id,
cache_dirty: false,
};
this.try_load_cache();
this
}
pub fn from_map(mut map: VerMap<K, V>) -> Self {
map.gc();
let cache_id = map.instance_id().map_id;
let mut this = Self {
map,
trie: T::default(),
trie_at_head: None,
sync_commit: None,
sync_branch: None,
dirty_applied: false,
cache_id,
cache_dirty: false,
};
this.try_load_cache();
this
}
pub fn map(&self) -> &VerMap<K, V> {
&self.map
}
pub fn map_mut(&mut self) -> &mut VerMap<K, V> {
&mut self.map
}
pub fn merkle_root(&mut self, branch: BranchId) -> Result<Vec<u8>> {
if self.sync_branch == Some(branch)
&& !self.dirty_applied
&& let Some(sync_id) = self.sync_commit
{
let head = self.map.head_commit(branch)?;
let head_id = head.as_ref().map(|c| c.id);
let has_dirty = self.map.has_uncommitted(branch)?;
if head_id == Some(sync_id) && !has_dirty {
return self.trie.root_hash();
}
}
self.sync_to_branch(branch)?;
self.trie.root_hash()
}
pub fn merkle_root_at_commit(&mut self, commit: CommitId) -> Result<Vec<u8>> {
self.sync_to_commit(commit)?;
self.trie.root_hash()
}
fn sync_to_branch(&mut self, branch: BranchId) -> Result<()> {
let head = self.map.head_commit(branch)?;
let head_id = head.as_ref().map(|c| c.id);
if self.dirty_applied {
if let Some(ref snapshot) = self.trie_at_head {
self.trie = snapshot.clone();
}
self.dirty_applied = false;
}
if let Some(hid) = head_id {
if self.sync_commit != Some(hid) {
self.sync_to_commit(hid)?;
}
} else if self.sync_commit.is_some() {
self.trie = T::default();
self.trie_at_head = None;
self.sync_commit = None;
}
if self.map.has_uncommitted(branch)? {
let diff = self.map.diff_uncommitted(branch)?;
let snapshot = self.trie.clone();
if let Err(e) = self.apply_diff(&diff) {
self.trie = snapshot;
self.trie_at_head = None;
return Err(e);
}
self.trie_at_head = Some(snapshot);
self.dirty_applied = true;
} else {
self.trie_at_head = None;
self.dirty_applied = false;
}
self.sync_branch = Some(branch);
Ok(())
}
fn sync_to_commit(&mut self, target: CommitId) -> Result<()> {
if self.sync_commit == Some(target) && !self.dirty_applied {
return Ok(());
}
if self.dirty_applied {
if let Some(ref snapshot) = self.trie_at_head {
self.trie = snapshot.clone();
}
self.dirty_applied = false;
self.trie_at_head = None;
}
if self.sync_commit == Some(target) {
return Ok(());
}
match self.sync_commit {
Some(current) => {
match self.map.diff_commits(current, target) {
Ok(diff) => {
if let Err(e) = self.apply_diff(&diff) {
self.trie = T::default();
self.trie_at_head = None;
self.sync_commit = None;
self.sync_branch = None;
self.dirty_applied = false;
return Err(e);
}
}
Err(_) => self.full_rebuild_commit(target)?,
}
}
None => {
self.full_rebuild_commit(target)?;
}
}
self.sync_commit = Some(target);
self.sync_branch = None;
self.cache_dirty = true;
if self
.trie
.save_cache(&self.map.namespace().system_dir(), self.cache_id, target)
.is_ok()
{
self.cache_dirty = false;
}
Ok(())
}
fn full_rebuild_commit(&mut self, commit: CommitId) -> Result<()> {
let entries: Vec<_> = self.map.raw_iter_at_commit(commit)?.collect();
self.trie = T::from_entries(entries)?;
Ok(())
}
fn apply_diff(&mut self, diff: &[DiffEntry]) -> Result<()> {
let ops: Vec<(&[u8], Option<&[u8]>)> = diff
.iter()
.map(|entry| match entry {
DiffEntry::Added { key, value } => {
(key.as_slice(), Some(value.as_slice()))
}
DiffEntry::Removed { key, .. } => (key.as_slice(), None),
DiffEntry::Modified { key, new_value, .. } => {
(key.as_slice(), Some(new_value.as_slice()))
}
})
.collect();
self.trie.batch_update(&ops)
}
}
impl<K, V> VerMapWithProof<K, V, SmtCalc>
where
K: KeyEnDeOrdered,
V: ValueEnDe,
{
pub fn prove(&self, key: &[u8]) -> Result<SmtProof> {
self.trie.prove(key)
}
pub fn prove_key(&self, key: &K) -> Result<SmtProof> {
self.trie.prove(&key.to_bytes())
}
pub fn verify_proof(
root_hash: &[u8; 32],
expected_key: &[u8],
proof: &SmtProof,
) -> Result<bool> {
SmtCalc::verify_proof(root_hash, expected_key, proof)
}
pub fn verify_key_proof(
root_hash: &[u8; 32],
expected_key: &K,
proof: &SmtProof,
) -> Result<bool> {
SmtCalc::verify_proof(root_hash, &expected_key.to_bytes(), proof)
}
}
impl<K, V> VerMapWithProof<K, V, MptCalc>
where
K: KeyEnDeOrdered,
V: ValueEnDe,
{
pub fn prove_mpt(&self, key: &K) -> Result<MptProof> {
self.trie.prove(&key.to_bytes())
}
pub fn verify_mpt_proof(
root_hash: &[u8; 32],
expected_key: &[u8],
proof: &MptProof,
) -> Result<bool> {
MptCalc::verify_proof(root_hash, expected_key, proof)
}
}
impl<K, V, T> Default for VerMapWithProof<K, V, T>
where
K: KeyEnDeOrdered,
V: ValueEnDe,
T: TrieCalc,
{
fn default() -> Self {
Self::new()
}
}
impl<K, V, T: TrieCalc> VerMapWithProof<K, V, T> {
fn try_load_cache(&mut self) {
if let Ok((mut trie, sync_tag, saved_hash)) =
T::load_cache(&self.map.namespace().system_dir(), self.cache_id)
{
let ok = trie.root_hash().map(|h| h == saved_hash).unwrap_or(false);
if !ok {
return;
}
self.trie = trie;
self.trie_at_head = None;
self.sync_commit = Some(sync_tag);
self.sync_branch = None;
self.dirty_applied = false;
self.cache_dirty = false;
}
}
fn try_save_cache(&mut self) {
let Some(tag) = self.sync_commit else {
return;
};
if self.dirty_applied {
return;
}
let _ =
self.trie
.save_cache(&self.map.namespace().system_dir(), self.cache_id, tag);
}
}
impl<K, V, T: TrieCalc> Drop for VerMapWithProof<K, V, T> {
fn drop(&mut self) {
if self.cache_dirty {
self.try_save_cache();
}
}
}