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 ba0d446 commit d8853e9
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 5 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
57 changes: 57 additions & 0 deletions ergotree-ir/src/ergo_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ 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;
Expand All @@ -29,6 +30,7 @@ pub struct ParsedErgoTree {
header: ErgoTreeHeader,
constants: Vec<Constant>,
root: Expr,
has_deserialize: OnceLock<bool>,
}

impl ParsedErgoTree {
Expand Down Expand Up @@ -146,14 +148,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 +202,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 +230,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 +394,7 @@ impl SigmaSerializable for ErgoTree {
header,
constants,
root,
has_deserialize: OnceLock::new(),
}))
}
}
Expand Down Expand Up @@ -454,7 +477,9 @@ 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 +728,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!();

Check failure on line 737 in ergotree-ir/src/ergo_tree.rs

View workflow job for this annotation

GitHub Actions / clippy

usage of the `unreachable!` macro

error: usage of the `unreachable!` macro --> ergotree-ir/src/ergo_tree.rs:737:13 | 737 | unreachable!(); | ^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unreachable note: the lint level is defined here --> ergotree-ir/src/lib.rs:19:9 | 19 | #![deny(clippy::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 d8853e9

Please sign in to comment.