Skip to content

Commit

Permalink
Lazy has_deserialize for ErgoTree
Browse files Browse the repository at this point in the history
Use serializer flags to avoid traversing tree to check for existence of deserialize nodes
  • Loading branch information
SethDusek committed Oct 25, 2024
1 parent 3e7dd5a commit dc97321
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 6 deletions.
6 changes: 3 additions & 3 deletions ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub struct ReductionResult {
}

/// Evaluate the given expression by reducing it to SigmaBoolean value.
pub fn reduce_to_crypto(expr: &ErgoTree, ctx: &Context) -> Result<ReductionResult, EvalError> {
pub fn reduce_to_crypto(tree: &ErgoTree, ctx: &Context) -> Result<ReductionResult, EvalError> {
fn inner<'ctx>(expr: &'ctx Expr, ctx: &Context<'ctx>) -> Result<ReductionResult, EvalError> {
let mut env_mut = Env::empty();
expr.eval(&mut env_mut, ctx)
Expand All @@ -150,8 +150,8 @@ pub fn reduce_to_crypto(expr: &ErgoTree, ctx: &Context) -> Result<ReductionResul
})
}

let expr = expr.proposition()?;
let expr = if expr.has_deserialize() {
let expr = tree.proposition()?;
let expr = if tree.has_deserialize() {
expr.substitute_deserialize(ctx)?
} else {
expr
Expand Down
66 changes: 65 additions & 1 deletion ergotree-ir/src/ergo_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ use crate::serialization::constant_store::ConstantStore;
use derive_more::From;
use std::convert::TryFrom;
use std::io;
use std::sync::OnceLock;
use thiserror::Error;

mod tree_header;
pub use tree_header::*;

/// Parsed ErgoTree
#[derive(PartialEq, Eq, Debug, Clone)]
#[derive(Eq, Debug, Clone)]
pub struct ParsedErgoTree {
header: ErgoTreeHeader,
constants: Vec<Constant>,
root: Expr,
has_deserialize: OnceLock<bool>,
}

impl ParsedErgoTree {
Expand Down Expand Up @@ -63,6 +65,12 @@ impl ParsedErgoTree {
}
}

impl PartialEq for ParsedErgoTree {
fn eq(&self, other: &Self) -> bool {
self.header == other.header && self.constants == other.constants && self.root == other.root
}
}

/// Errors on fail to set a new constant value
#[derive(Error, PartialEq, Eq, Debug, Clone)]
pub enum SetConstantError {
Expand Down Expand Up @@ -146,14 +154,19 @@ impl ErgoTree {
vec![]
};
r.set_constant_store(ConstantStore::new(constants.clone()));
let was_deserialize = r.was_deserialize();
r.set_deserialize(false);
let root = Expr::sigma_parse(r)?;
let has_deserialize = r.was_deserialize();
r.set_deserialize(was_deserialize);
if root.tpe() != SType::SSigmaProp {
return Err(ErgoTreeError::RootTpeError(root.tpe()));
}
Ok(ParsedErgoTree {
header,
constants,
root,
has_deserialize: has_deserialize.into(),
})
}

Expand Down Expand Up @@ -195,12 +208,14 @@ impl ErgoTree {
header,
constants,
root: parsed_expr,
has_deserialize: OnceLock::new(),
})
} else {
ErgoTree::Parsed(ParsedErgoTree {
header,
constants: Vec::new(),
root: expr.clone(),
has_deserialize: OnceLock::new(),
})
})
}
Expand All @@ -221,6 +236,19 @@ impl ErgoTree {
}
}

/// Check if ErgoTree root has [`crate::mir::deserialize_context::DeserializeContext`] or [`crate::mir::deserialize_register::DeserializeRegister`] nodes
pub fn has_deserialize(&self) -> bool {
match self {
ErgoTree::Unparsed { .. } => false,
ErgoTree::Parsed(ParsedErgoTree {
header: _,
constants: _,
root,
has_deserialize,
}) => *has_deserialize.get_or_init(|| root.has_deserialize()),
}
}

/// Prints with newlines
pub fn debug_tree(&self) -> String {
let tree = format!("{:#?}", self);
Expand Down Expand Up @@ -372,6 +400,7 @@ impl SigmaSerializable for ErgoTree {
header,
constants,
root,
has_deserialize: OnceLock::new(),
}))
}
}
Expand Down Expand Up @@ -447,14 +476,17 @@ pub(crate) mod arbitrary {

#[cfg(test)]
#[cfg(feature = "arbitrary")]
#[allow(clippy::unreachable)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::panic)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
use crate::chain::address::AddressEncoder;
use crate::chain::address::NetworkPrefix;
use crate::mir::bool_to_sigma::BoolToSigmaProp;
use crate::mir::constant::Literal;
use crate::mir::deserialize_context::DeserializeContext;
use crate::sigma_protocol::sigma_boolean::SigmaProp;
use proptest::prelude::*;

Expand Down Expand Up @@ -703,4 +735,36 @@ mod tests {
let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
tree.proposition().unwrap();
}

fn has_deserialize(tree: ErgoTree) -> bool {
let ErgoTree::Parsed(ParsedErgoTree {
has_deserialize, ..
}) = ErgoTree::sigma_parse_bytes(&tree.sigma_serialize_bytes().unwrap()).unwrap()
else {
unreachable!();
};
*has_deserialize.get().unwrap()
}

#[test]
fn test_lazy_has_deserialize() {
let has_deserialize_expr: Expr = BoolToSigmaProp {
input: Box::new(
DeserializeContext {
tpe: SType::SBoolean,
id: 0,
}
.into(),
),
}
.into();
let tree = ErgoTree::new(ErgoTreeHeader::v1(false), &has_deserialize_expr).unwrap();
assert!(has_deserialize(tree));
let no_deserialize_expr: Expr = BoolToSigmaProp {
input: Box::new(true.into()),
}
.into();
let tree = ErgoTree::new(ErgoTreeHeader::v1(false), &no_deserialize_expr).unwrap();
assert!(!has_deserialize(tree));
}
}
3 changes: 1 addition & 2 deletions ergotree-ir/src/mir/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,7 @@ impl Expr {
}

/// Returns true if the [`Expr`] has deserialize nodes, see: [`DeserializeContext`] and [`DeserializeRegister`]
pub fn has_deserialize(&self) -> bool {
// TODO: if expr is deserialized, use has_deserialize flag from reader to skip traversing tree
pub(crate) fn has_deserialize(&self) -> bool {
self.iter().any(|c| {
matches!(
c,
Expand Down

0 comments on commit dc97321

Please sign in to comment.