use crate::common::ende::{KeyEnDeOrdered, ValueEnDe};
use crate::versioned::diff::DiffEntry;
use crate::versioned::map::VerMap;
use crate::versioned::{BranchId, CommitId};
use ruc::*;
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();
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();
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().c(d!());
}
}
self.sync_to_branch(branch)?;
self.trie.root_hash().c(d!())
}
pub fn merkle_root_at_commit(&mut self, commit: CommitId) -> Result<Vec<u8>> {
self.sync_to_commit(commit)?;
self.trie.root_hash().c(d!())
}
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)? {
self.trie_at_head = Some(self.trie.clone());
let diff = self.map.diff_uncommitted(branch)?;
self.apply_diff(&diff)?;
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) => self.apply_diff(&diff)?,
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.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).c(d!())?;
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).c(d!())
}
}
impl<K, V> VerMapWithProof<K, V, SmtCalc>
where
K: KeyEnDeOrdered,
V: ValueEnDe,
{
pub fn prove(&self, key: &[u8]) -> Result<SmtProof> {
self.trie.prove(key).c(d!())
}
pub fn verify_proof(root_hash: &[u8; 32], proof: &SmtProof) -> Result<bool> {
SmtCalc::verify_proof(root_hash, proof).c(d!())
}
}
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()).c(d!())
}
pub fn verify_mpt_proof(
root_hash: &[u8; 32],
expected_key: &[u8],
proof: &MptProof,
) -> Result<bool> {
MptCalc::verify_proof(root_hash, expected_key, proof).c(d!())
}
}
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.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.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();
}
}
}