use std::convert::TryFrom;
use std::convert::TryInto;
use crate::types::stype::LiftIntoSType;
use crate::types::stype::SType;
use super::and::And;
use super::apply::Apply;
use super::bin_op::BinOp;
use super::bit_inversion::BitInversion;
use super::block::BlockValue;
use super::bool_to_sigma::BoolToSigmaProp;
use super::byte_array_to_long::ByteArrayToLong;
use super::calc_blake2b256::CalcBlake2b256;
use super::calc_sha256::CalcSha256;
use super::coll_append::Append;
use super::coll_by_index::ByIndex;
use super::coll_exists::Exists;
use super::coll_filter::Filter;
use super::coll_fold::Fold;
use super::coll_forall::ForAll;
use super::coll_map::Map;
use super::coll_size::SizeOf;
use super::coll_slice::Slice;
use super::collection::Collection;
use super::constant::Constant;
use super::constant::ConstantPlaceholder;
use super::constant::Literal;
use super::constant::TryExtractFrom;
use super::constant::TryExtractFromError;
use super::create_avl_tree::CreateAvlTree;
use super::create_provedlog::CreateProveDlog;
use super::decode_point::DecodePoint;
use super::exponentiate::Exponentiate;
use super::extract_amount::ExtractAmount;
use super::extract_bytes::ExtractBytes;
use super::extract_bytes_with_no_ref::ExtractBytesWithNoRef;
use super::extract_creation_info::ExtractCreationInfo;
use super::extract_id::ExtractId;
use super::extract_reg_as::ExtractRegisterAs;
use super::extract_script_bytes::ExtractScriptBytes;
use super::func_value::FuncValue;
use super::global_vars::GlobalVars;
use super::if_op::If;
use super::logical_not::LogicalNot;
use super::long_to_byte_array::LongToByteArray;
use super::method_call::MethodCall;
use super::multiply_group::MultiplyGroup;
use super::negation::Negation;
use super::option_get::OptionGet;
use super::option_get_or_else::OptionGetOrElse;
use super::option_is_defined::OptionIsDefined;
use super::or::Or;
use super::property_call::PropertyCall;
use super::select_field::SelectField;
use super::sigma_and::SigmaAnd;
use super::sigma_or::SigmaOr;
use super::sigma_prop_bytes::SigmaPropBytes;
use super::subst_const::SubstConstants;
use super::tree_lookup::TreeLookup;
use super::tuple::Tuple;
use super::upcast::Upcast;
use super::val_def::ValDef;
use super::val_use::ValUse;
use super::xor::Xor;
extern crate derive_more;
use crate::mir::atleast::Atleast;
use crate::mir::byte_array_to_bigint::ByteArrayToBigInt;
use crate::mir::create_prove_dh_tuple::CreateProveDhTuple;
use crate::mir::deserialize_context::DeserializeContext;
use crate::mir::deserialize_register::DeserializeRegister;
use crate::mir::downcast::Downcast;
use crate::mir::get_var::GetVar;
use crate::mir::xor_of::XorOf;
use bounded_vec::BoundedVecOutOfBounds;
use derive_more::From;
use derive_more::TryInto;
use thiserror::Error;
#[derive(PartialEq, Eq, Debug, Clone, From, TryInto)]
pub enum Expr {
Append(Append),
Const(Constant),
ConstPlaceholder(ConstantPlaceholder),
SubstConstants(SubstConstants),
ByteArrayToLong(ByteArrayToLong),
ByteArrayToBigInt(ByteArrayToBigInt),
LongToByteArray(LongToByteArray),
Collection(Collection),
Tuple(Tuple),
CalcBlake2b256(CalcBlake2b256),
CalcSha256(CalcSha256),
Context,
Global,
GlobalVars(GlobalVars),
FuncValue(FuncValue),
Apply(Apply),
MethodCall(MethodCall),
ProperyCall(PropertyCall),
BlockValue(BlockValue),
ValDef(ValDef),
ValUse(ValUse),
If(If),
BinOp(BinOp),
And(And),
Or(Or),
Xor(Xor),
Atleast(Atleast),
LogicalNot(LogicalNot),
Negation(Negation),
BitInversion(BitInversion),
OptionGet(OptionGet),
OptionIsDefined(OptionIsDefined),
OptionGetOrElse(OptionGetOrElse),
ExtractAmount(ExtractAmount),
ExtractRegisterAs(ExtractRegisterAs),
ExtractBytes(ExtractBytes),
ExtractBytesWithNoRef(ExtractBytesWithNoRef),
ExtractScriptBytes(ExtractScriptBytes),
ExtractCreationInfo(ExtractCreationInfo),
ExtractId(ExtractId),
ByIndex(ByIndex),
SizeOf(SizeOf),
Slice(Slice),
Fold(Fold),
Map(Map),
Filter(Filter),
Exists(Exists),
ForAll(ForAll),
SelectField(SelectField),
BoolToSigmaProp(BoolToSigmaProp),
Upcast(Upcast),
Downcast(Downcast),
CreateProveDlog(CreateProveDlog),
CreateProveDhTuple(CreateProveDhTuple),
SigmaPropBytes(SigmaPropBytes),
DecodePoint(DecodePoint),
SigmaAnd(SigmaAnd),
SigmaOr(SigmaOr),
GetVar(GetVar),
DeserializeRegister(DeserializeRegister),
DeserializeContext(DeserializeContext),
MultiplyGroup(MultiplyGroup),
Exponentiate(Exponentiate),
XorOf(XorOf),
TreeLookup(TreeLookup),
CreateAvlTree(CreateAvlTree),
}
impl Expr {
pub fn tpe(&self) -> SType {
match self {
Expr::Append(ap) => ap.tpe(),
Expr::Const(v) => v.tpe.clone(),
Expr::Collection(v) => v.tpe(),
Expr::SubstConstants(v) => v.tpe(),
Expr::ByteArrayToLong(v) => v.tpe(),
Expr::ByteArrayToBigInt(v) => v.tpe(),
Expr::LongToByteArray(v) => v.tpe(),
Expr::ConstPlaceholder(v) => v.tpe.clone(),
Expr::CalcBlake2b256(v) => v.tpe(),
Expr::CalcSha256(v) => v.tpe(),
Expr::Global => SType::SGlobal,
Expr::Context => SType::SContext,
Expr::GlobalVars(v) => v.tpe(),
Expr::FuncValue(v) => v.tpe(),
Expr::Apply(v) => v.tpe(),
Expr::MethodCall(v) => v.tpe(),
Expr::ProperyCall(v) => v.tpe(),
Expr::BlockValue(v) => v.tpe(),
Expr::ValDef(v) => v.tpe(),
Expr::ValUse(v) => v.tpe.clone(),
Expr::BinOp(v) => v.tpe(),
Expr::OptionGet(v) => v.tpe(),
Expr::ExtractRegisterAs(v) => v.tpe(),
Expr::Fold(v) => v.tpe(),
Expr::SelectField(v) => v.tpe(),
Expr::ExtractAmount(v) => v.tpe(),
Expr::And(v) => v.tpe(),
Expr::Or(v) => v.tpe(),
Expr::Xor(v) => v.tpe(),
Expr::Atleast(v) => v.tpe(),
Expr::LogicalNot(v) => v.tpe(),
Expr::Map(v) => v.tpe(),
Expr::Filter(v) => v.tpe(),
Expr::BoolToSigmaProp(v) => v.tpe(),
Expr::Upcast(v) => v.tpe(),
Expr::Downcast(v) => v.tpe(),
Expr::If(v) => v.tpe(),
Expr::ByIndex(v) => v.tpe(),
Expr::ExtractScriptBytes(v) => v.tpe(),
Expr::SizeOf(v) => v.tpe(),
Expr::Slice(v) => v.tpe(),
Expr::CreateProveDlog(v) => v.tpe(),
Expr::CreateProveDhTuple(v) => v.tpe(),
Expr::ExtractCreationInfo(v) => v.tpe(),
Expr::Exists(v) => v.tpe(),
Expr::ExtractId(v) => v.tpe(),
Expr::SigmaPropBytes(v) => v.tpe(),
Expr::OptionIsDefined(v) => v.tpe(),
Expr::OptionGetOrElse(v) => v.tpe(),
Expr::Negation(v) => v.tpe(),
Expr::BitInversion(v) => v.tpe(),
Expr::ForAll(v) => v.tpe(),
Expr::Tuple(v) => v.tpe(),
Expr::DecodePoint(v) => v.tpe(),
Expr::SigmaAnd(v) => v.tpe(),
Expr::SigmaOr(v) => v.tpe(),
Expr::DeserializeRegister(v) => v.tpe(),
Expr::DeserializeContext(v) => v.tpe(),
Expr::GetVar(v) => v.tpe(),
Expr::MultiplyGroup(v) => v.tpe(),
Expr::Exponentiate(v) => v.tpe(),
Expr::XorOf(v) => v.tpe(),
Expr::ExtractBytes(v) => v.tpe(),
Expr::ExtractBytesWithNoRef(v) => v.tpe(),
Expr::TreeLookup(v) => v.tpe(),
Expr::CreateAvlTree(v) => v.tpe(),
}
}
pub fn post_eval_tpe(&self) -> SType {
match self.tpe() {
SType::SFunc(sfunc) => *sfunc.t_range,
tpe => tpe,
}
}
pub fn check_post_eval_tpe(
&self,
expected_tpe: &SType,
) -> Result<(), InvalidExprEvalTypeError> {
let expr_tpe = self.post_eval_tpe();
if &expr_tpe == expected_tpe {
Ok(())
} else {
Err(InvalidExprEvalTypeError(format!(
"expected: {0:?}, got: {1:?}",
expected_tpe, expr_tpe
)))
}
}
pub fn debug_tree(&self) -> String {
let tree = format!("{:#?}", self);
tree
}
}
impl<T: Into<Literal> + LiftIntoSType> From<T> for Expr {
fn from(t: T) -> Self {
Expr::Const(Constant {
tpe: T::stype(),
v: t.into(),
})
}
}
#[derive(Error, PartialEq, Eq, Debug, Clone)]
#[error("InvalidArgumentError: {0}")]
pub struct InvalidArgumentError(pub String);
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct InvalidExprEvalTypeError(pub String);
impl From<InvalidExprEvalTypeError> for InvalidArgumentError {
fn from(e: InvalidExprEvalTypeError) -> Self {
InvalidArgumentError(format!("{0:?}", e))
}
}
impl From<BoundedVecOutOfBounds> for InvalidArgumentError {
fn from(e: BoundedVecOutOfBounds) -> Self {
InvalidArgumentError(format!("{0:?}", e))
}
}
impl<T: TryFrom<Expr>> TryExtractFrom<Expr> for T {
fn try_extract_from(v: Expr) -> Result<Self, TryExtractFromError> {
let res: Result<Self, TryExtractFromError> = v.clone().try_into().map_err(|_| {
TryExtractFromError(format!(
"Cannot extract {0:?} from {1:?}",
std::any::type_name::<T>(),
v
))
});
res
}
}
#[cfg(feature = "arbitrary")]
#[allow(clippy::unwrap_used)]
#[allow(clippy::panic)]
#[allow(clippy::todo)]
pub(crate) mod arbitrary {
use super::*;
use crate::mir::func_value::FuncArg;
use crate::sigma_protocol::sigma_boolean::ProveDlog;
use crate::types::sfunc::SFunc;
use proptest::collection::*;
use proptest::prelude::*;
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct ArbExprParams {
pub tpe: SType,
pub depth: usize,
}
impl Default for ArbExprParams {
fn default() -> Self {
ArbExprParams {
tpe: SType::SBoolean,
depth: 1,
}
}
}
fn numeric_nested_expr(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
prop_oneof![any_with::<BinOp>(ArbExprParams {
tpe: elem_tpe.clone(),
depth
})
.prop_map_into(),]
.boxed()
}
fn bool_nested_expr(depth: usize) -> BoxedStrategy<Expr> {
prop_oneof![
any_with::<BinOp>(ArbExprParams {
tpe: SType::SBoolean,
depth
})
.prop_map_into(),
any_with::<And>(depth).prop_map_into(),
any_with::<Or>(depth).prop_map_into(),
any_with::<LogicalNot>(depth).prop_map_into(),
]
.boxed()
}
fn coll_nested_numeric(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
let ty = elem_tpe.clone();
vec(numeric_nested_expr(depth, elem_tpe), 0..10)
.prop_map(move |items| Collection::new(ty.clone(), items).unwrap())
.prop_map_into()
.boxed()
}
fn sigma_prop_nested_expr(_depth: usize) -> BoxedStrategy<Expr> {
prop_oneof![
any::<ProveDlog>().prop_map(|pk| Expr::Const(pk.into())),
any::<SigmaAnd>().prop_map_into(),
any::<SigmaOr>().prop_map_into(),
]
.boxed()
}
fn coll_nested_expr(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
match elem_tpe {
SType::SBoolean => vec(bool_nested_expr(depth), 0..10)
.prop_map(|items| Collection::new(SType::SBoolean, items).unwrap())
.prop_map_into()
.boxed(),
SType::SByte => coll_nested_numeric(depth, elem_tpe),
SType::SShort => coll_nested_numeric(depth, elem_tpe),
SType::SInt => coll_nested_numeric(depth, elem_tpe),
SType::SLong => coll_nested_numeric(depth, elem_tpe),
SType::SBigInt => coll_nested_numeric(depth, elem_tpe),
SType::STypeVar(_) => prop_oneof![
vec(bool_nested_expr(depth), 0..10).prop_map(|items| Collection::new(
SType::SBoolean,
items
)
.unwrap()),
vec(numeric_nested_expr(depth, &SType::SInt), 0..10)
.prop_map(|items| Collection::new(SType::SInt, items).unwrap())
]
.prop_map_into()
.boxed(),
SType::SSigmaProp => vec(sigma_prop_nested_expr(depth), 0..10)
.prop_map(|items| Collection::new(SType::SSigmaProp, items).unwrap())
.prop_map_into()
.boxed(),
_ => panic!("Nested expression not implemented for {:?}", &elem_tpe),
}
}
fn any_nested_expr(depth: usize) -> BoxedStrategy<Expr> {
prop_oneof![
bool_nested_expr(depth),
numeric_nested_expr(depth, &SType::SByte),
numeric_nested_expr(depth, &SType::SShort),
numeric_nested_expr(depth, &SType::SInt),
numeric_nested_expr(depth, &SType::SLong),
numeric_nested_expr(depth, &SType::SBigInt),
]
.boxed()
}
fn nested_expr(tpe: SType, depth: usize) -> BoxedStrategy<Expr> {
match tpe {
SType::SAny => any_nested_expr(depth),
SType::SBoolean => bool_nested_expr(depth),
SType::SByte => numeric_nested_expr(depth, &tpe),
SType::SShort => numeric_nested_expr(depth, &tpe),
SType::SInt => numeric_nested_expr(depth, &tpe),
SType::SLong => numeric_nested_expr(depth, &tpe),
SType::SBigInt => numeric_nested_expr(depth, &tpe),
SType::SColl(elem_type) => coll_nested_expr(depth, elem_type.as_ref()),
SType::SSigmaProp => sigma_prop_nested_expr(depth),
_ => todo!("nested expr is not implemented for type: {:?}", tpe),
}
.boxed()
}
fn int_non_nested_expr() -> BoxedStrategy<Expr> {
prop_oneof![Just(GlobalVars::Height.into()),].boxed()
}
fn constant(tpe: &SType) -> BoxedStrategy<Expr> {
any_with::<Constant>(tpe.clone().into())
.prop_map_into()
.boxed()
}
fn bool_non_nested_expr() -> BoxedStrategy<Expr> {
prop_oneof![any_with::<Constant>(SType::SBoolean.into()).prop_map_into()].boxed()
}
fn any_non_nested_expr() -> BoxedStrategy<Expr> {
prop_oneof![int_non_nested_expr(), bool_non_nested_expr()].boxed()
}
fn coll_non_nested_expr(elem_tpe: &SType) -> BoxedStrategy<Expr> {
match elem_tpe {
SType::SBoolean => any_with::<Constant>(SType::SColl(Box::new(SType::SBoolean)).into())
.prop_map(Expr::Const)
.boxed(),
SType::SByte => any_with::<Constant>(SType::SColl(Box::new(SType::SByte)).into())
.prop_map(Expr::Const)
.boxed(),
SType::SShort => any_with::<Constant>(SType::SColl(Box::new(SType::SShort)).into())
.prop_map(Expr::Const)
.boxed(),
SType::SInt => any_with::<Constant>(SType::SColl(Box::new(SType::SInt)).into())
.prop_map(Expr::Const)
.boxed(),
SType::SLong => any_with::<Constant>(SType::SColl(Box::new(SType::SLong)).into())
.prop_map(Expr::Const)
.boxed(),
_ => todo!("Collection of {0:?} is not yet implemented", elem_tpe),
}
}
fn non_nested_expr(tpe: &SType) -> BoxedStrategy<Expr> {
match tpe {
SType::SAny => any_non_nested_expr(),
SType::SInt => int_non_nested_expr(),
SType::SBoolean => bool_non_nested_expr(),
SType::SColl(elem_type) => coll_non_nested_expr(elem_type),
t => constant(t),
}
}
fn sfunc_expr(sfunc: SFunc) -> BoxedStrategy<Expr> {
match (sfunc.t_dom.first().unwrap(), *sfunc.t_range) {
(SType::SBoolean, SType::SBoolean) => any_with::<Expr>(ArbExprParams {
tpe: SType::SBoolean,
depth: 2,
})
.prop_map(|expr| {
Expr::FuncValue(FuncValue::new(
vec![FuncArg {
idx: 1.into(),
tpe: SType::SBoolean,
}],
expr,
))
})
.boxed(),
_ => todo!(),
}
}
impl Arbitrary for Expr {
type Parameters = ArbExprParams;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
if args.depth == 0 {
match args.tpe {
SType::SFunc(sfunc) => sfunc_expr(sfunc),
_ => prop_oneof![
any_with::<Constant>(args.tpe.clone().into())
.prop_map(Expr::Const)
.boxed(),
non_nested_expr(&args.tpe)
]
.boxed(),
}
} else {
nested_expr(args.tpe, args.depth - 1)
}
}
}
}
#[cfg(test)]
mod tests {}