use crate::base16_str::Base16Str;
use crate::bigint256::BigInt256;
use crate::chain::ergo_box::ErgoBox;
use crate::chain::token::TokenId;
use crate::mir::value::CollKind;
use crate::serialization::SigmaParsingError;
use crate::serialization::SigmaSerializable;
use crate::serialization::SigmaSerializationError;
use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
use crate::sigma_protocol::sigma_boolean::SigmaProp;
use crate::sigma_protocol::sigma_boolean::{ProveDhTuple, ProveDlog};
use crate::types::stuple::STuple;
use crate::types::stuple::TupleItems;
use crate::types::stype::LiftIntoSType;
use crate::types::stype::SType;
use ergo_chain_types::ADDigest;
use ergo_chain_types::Base16DecodedBytes;
use ergo_chain_types::Digest32;
use ergo_chain_types::EcPoint;
use impl_trait_for_tuples::impl_for_tuples;
use sigma_util::AsVecI8;
use sigma_util::AsVecU8;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt::Formatter;
use std::sync::Arc;
mod constant_placeholder;
pub use constant_placeholder::*;
use super::avl_tree_data::AvlTreeData;
use super::avl_tree_data::AvlTreeFlags;
use super::value::NativeColl;
use super::value::StoreWrapped;
use super::value::Value;
use thiserror::Error;
#[derive(PartialEq, Eq, Clone)]
pub struct Constant {
pub tpe: SType,
pub v: Literal,
}
#[derive(PartialEq, Eq, Clone)]
pub enum Literal {
Unit,
Boolean(bool),
Byte(i8),
Short(i16),
Int(i32),
Long(i64),
BigInt(BigInt256),
SigmaProp(Box<SigmaProp>),
GroupElement(Box<EcPoint>),
AvlTree(Box<AvlTreeData>),
CBox(Arc<ErgoBox>),
Coll(CollKind<Literal>),
Opt(Box<Option<Literal>>),
Tup(TupleItems<Literal>),
}
impl std::fmt::Debug for Constant {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
format!("{:?}: {:?}", self.v, self.tpe).fmt(f)
}
}
impl std::fmt::Debug for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(i8_bytes))) => {
base16::encode_lower(&i8_bytes.as_vec_u8()).fmt(f)
}
Literal::Coll(CollKind::WrappedColl { elem_tpe: _, items }) => items.fmt(f),
Literal::Opt(boxed_opt) => boxed_opt.fmt(f),
Literal::Tup(items) => items.fmt(f),
Literal::Unit => ().fmt(f),
Literal::Boolean(v) => v.fmt(f),
Literal::Byte(v) => v.fmt(f),
Literal::Short(v) => v.fmt(f),
Literal::Int(v) => v.fmt(f),
Literal::Long(v) => v.fmt(f),
Literal::BigInt(v) => v.fmt(f),
Literal::SigmaProp(v) => v.fmt(f),
Literal::GroupElement(v) => v.fmt(f),
Literal::AvlTree(v) => v.fmt(f),
Literal::CBox(v) => v.fmt(f),
}
}
}
impl From<()> for Literal {
fn from(_: ()) -> Literal {
Literal::Unit
}
}
impl From<bool> for Literal {
fn from(v: bool) -> Literal {
Literal::Boolean(v)
}
}
impl From<i8> for Literal {
fn from(v: i8) -> Literal {
Literal::Byte(v)
}
}
impl From<i16> for Literal {
fn from(v: i16) -> Literal {
Literal::Short(v)
}
}
impl From<i32> for Literal {
fn from(v: i32) -> Literal {
Literal::Int(v)
}
}
impl From<i64> for Literal {
fn from(v: i64) -> Literal {
Literal::Long(v)
}
}
impl From<BigInt256> for Literal {
fn from(v: BigInt256) -> Literal {
Literal::BigInt(v)
}
}
impl From<SigmaProp> for Literal {
fn from(v: SigmaProp) -> Literal {
Literal::SigmaProp(Box::new(v))
}
}
impl From<EcPoint> for Literal {
fn from(v: EcPoint) -> Literal {
Literal::GroupElement(Box::new(v))
}
}
impl From<Arc<ErgoBox>> for Literal {
fn from(b: Arc<ErgoBox>) -> Self {
Literal::CBox(b)
}
}
impl From<ErgoBox> for Literal {
fn from(b: ErgoBox) -> Self {
Literal::CBox(Arc::new(b))
}
}
impl From<Vec<u8>> for Literal {
fn from(v: Vec<u8>) -> Self {
Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(v.as_vec_i8())))
}
}
impl From<Digest32> for Literal {
fn from(v: Digest32) -> Self {
let bytes: Vec<u8> = v.into();
Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(
bytes.as_vec_i8(),
)))
}
}
impl From<TokenId> for Literal {
fn from(v: TokenId) -> Self {
Digest32::from(v).into()
}
}
impl From<Vec<i8>> for Literal {
fn from(v: Vec<i8>) -> Literal {
Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(v)))
}
}
impl<T: LiftIntoSType + StoreWrapped + Into<Literal>> From<Vec<T>> for Literal {
fn from(v: Vec<T>) -> Self {
Literal::Coll(CollKind::WrappedColl {
elem_tpe: T::stype(),
items: v.into_iter().map(|i| i.into()).collect(),
})
}
}
impl<T: LiftIntoSType + Into<Literal>> From<Option<T>> for Literal {
fn from(opt: Option<T>) -> Self {
Literal::Opt(Box::new(opt.map(|e| e.into())))
}
}
impl TryFrom<Value> for Constant {
type Error = String;
#[allow(clippy::unwrap_used)]
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Boolean(b) => Ok(Constant::from(b)),
Value::Byte(b) => Ok(Constant::from(b)),
Value::Short(s) => Ok(Constant::from(s)),
Value::Int(i) => Ok(Constant::from(i)),
Value::Long(l) => Ok(Constant::from(l)),
Value::BigInt(b) => Ok(Constant::from(b)),
Value::Unit => Ok(Constant {
tpe: SType::SUnit,
v: Literal::Unit,
}),
Value::SigmaProp(s) => Ok(Constant::from(*s)),
Value::GroupElement(e) => Ok(Constant::from(*e)),
Value::CBox(i) => Ok(Constant::from(i)),
Value::Coll(coll) => {
let (v, tpe) = match coll {
CollKind::NativeColl(n) => (
Literal::Coll(CollKind::NativeColl(n)),
SType::SColl(Box::new(SType::SByte)),
),
CollKind::WrappedColl { elem_tpe, items } => {
let mut new_items = Vec::with_capacity(items.len());
for v in items {
let c = Constant::try_from(v)?;
new_items.push(c.v);
}
(
Literal::Coll(CollKind::WrappedColl {
elem_tpe: elem_tpe.clone(),
items: new_items,
}),
SType::SColl(Box::new(elem_tpe)),
)
}
};
Ok(Constant { v, tpe })
}
Value::Opt(lit) => match *lit {
Some(v) => {
let c = Constant::try_from(v)?;
Ok(Constant {
v: Literal::Opt(Box::new(Some(c.v))),
tpe: c.tpe,
})
}
None => Err("Can't convert from Value::Opt(None) to Constant".into()),
},
Value::Tup(t) => {
if let Ok(t) = t.try_mapped::<_, _, String>(|v| {
let c = Constant::try_from(v)?;
Ok((c.v, c.tpe))
}) {
let tuple_items = t.mapped_ref(|(l, _)| l.clone());
let tuple_item_types = SType::STuple(STuple {
items: t.mapped(|(_, tpe)| tpe),
});
Ok(Constant {
v: Literal::Tup(tuple_items),
tpe: tuple_item_types,
})
} else {
Err("Can't convert Value:Tup element".into())
}
}
Value::AvlTree(a) => Ok(Constant::from(*a)),
Value::Context => Err("Cannot convert Value::Context into Constant".into()),
Value::Header(_) => Err("Cannot convert Value::Header(_) into Constant".into()),
Value::PreHeader(_) => Err("Cannot convert Value::PreHeader(_) into Constant".into()),
Value::Global => Err("Cannot convert Value::Global into Constant".into()),
Value::Lambda(_) => Err("Cannot convert Value::Lambda(_) into Constant".into()),
}
}
}
impl From<()> for Constant {
fn from(_: ()) -> Constant {
Constant {
tpe: SType::SUnit,
v: Literal::Unit,
}
}
}
impl From<bool> for Constant {
fn from(v: bool) -> Constant {
Constant {
tpe: bool::stype(),
v: v.into(),
}
}
}
impl From<i8> for Constant {
fn from(v: i8) -> Constant {
Constant {
tpe: i8::stype(),
v: v.into(),
}
}
}
impl From<i16> for Constant {
fn from(v: i16) -> Constant {
Constant {
tpe: i16::stype(),
v: v.into(),
}
}
}
impl From<i32> for Constant {
fn from(v: i32) -> Constant {
Constant {
tpe: i32::stype(),
v: v.into(),
}
}
}
impl From<i64> for Constant {
fn from(v: i64) -> Constant {
Constant {
tpe: i64::stype(),
v: v.into(),
}
}
}
impl From<SigmaProp> for Constant {
fn from(v: SigmaProp) -> Constant {
Constant {
tpe: SType::SSigmaProp,
v: v.into(),
}
}
}
impl From<EcPoint> for Constant {
fn from(v: EcPoint) -> Constant {
Constant {
tpe: SType::SGroupElement,
v: v.into(),
}
}
}
impl From<Arc<ErgoBox>> for Constant {
fn from(b: Arc<ErgoBox>) -> Self {
Constant {
tpe: SType::SBox,
v: b.into(),
}
}
}
impl From<ErgoBox> for Constant {
fn from(b: ErgoBox) -> Self {
Constant {
tpe: SType::SBox,
v: b.into(),
}
}
}
impl From<Vec<u8>> for Constant {
fn from(v: Vec<u8>) -> Self {
Constant {
tpe: SType::SColl(Box::new(SType::SByte)),
v: v.into(),
}
}
}
impl From<Digest32> for Constant {
fn from(v: Digest32) -> Self {
Constant {
tpe: SType::SColl(Box::new(SType::SByte)),
v: v.into(),
}
}
}
impl From<TokenId> for Constant {
fn from(v: TokenId) -> Self {
Digest32::from(v).into()
}
}
impl From<Vec<i8>> for Constant {
fn from(v: Vec<i8>) -> Constant {
Constant {
tpe: SType::SColl(Box::new(SType::SByte)),
v: v.into(),
}
}
}
impl<T: LiftIntoSType + StoreWrapped + Into<Constant>> From<Vec<T>> for Constant {
fn from(v: Vec<T>) -> Self {
Constant {
tpe: Vec::<T>::stype(),
v: Literal::Coll(CollKind::WrappedColl {
elem_tpe: T::stype(),
items: v.into_iter().map(|i| i.into().v).collect(),
}),
}
}
}
impl<T: LiftIntoSType + Into<Constant>> From<Option<T>> for Constant {
fn from(opt: Option<T>) -> Self {
Constant {
tpe: SType::SOption(Box::new(T::stype())),
v: Literal::Opt(Box::new(opt.map(|e| e.into().v))),
}
}
}
impl From<ProveDlog> for Constant {
fn from(v: ProveDlog) -> Self {
Constant::from(SigmaProp::from(SigmaBoolean::from(
SigmaProofOfKnowledgeTree::from(v),
)))
}
}
impl From<ProveDhTuple> for Constant {
fn from(dht: ProveDhTuple) -> Self {
Constant::from(SigmaProp::from(SigmaBoolean::from(
SigmaProofOfKnowledgeTree::from(dht),
)))
}
}
impl From<SigmaBoolean> for Constant {
fn from(sb: SigmaBoolean) -> Self {
Constant::from(SigmaProp::from(sb))
}
}
impl From<BigInt256> for Constant {
fn from(b: BigInt256) -> Self {
Constant {
tpe: SType::SBigInt,
v: Literal::BigInt(b),
}
}
}
impl From<AvlTreeData> for Constant {
fn from(a: AvlTreeData) -> Self {
Constant {
tpe: SType::SAvlTree,
v: Literal::AvlTree(Box::new(a)),
}
}
}
impl From<AvlTreeFlags> for Constant {
fn from(a: AvlTreeFlags) -> Self {
Constant {
tpe: SType::SByte,
v: Literal::Byte(a.serialize() as i8),
}
}
}
impl From<ADDigest> for Constant {
fn from(a: ADDigest) -> Self {
Constant {
tpe: SType::SColl(Box::new(SType::SByte)),
v: Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(a.into()))),
}
}
}
#[allow(clippy::unwrap_used)]
#[allow(clippy::from_over_into)]
#[impl_for_tuples(2, 4)]
impl Into<Constant> for Tuple {
fn into(self) -> Constant {
let constants: Vec<Constant> = [for_tuples!( #( Tuple.into() ),* )].to_vec();
let (types, values): (Vec<SType>, Vec<Literal>) =
constants.into_iter().map(|c| (c.tpe, c.v)).unzip();
Constant {
tpe: SType::STuple(types.try_into().unwrap()),
v: Literal::Tup(values.try_into().unwrap()),
}
}
}
pub trait TryExtractInto<F> {
fn try_extract_into<T: TryExtractFrom<F>>(self) -> Result<T, TryExtractFromError>;
}
impl<F> TryExtractInto<F> for F {
fn try_extract_into<T: TryExtractFrom<F>>(self) -> Result<T, TryExtractFromError> {
T::try_extract_from(self)
}
}
#[derive(Error, PartialEq, Eq, Debug, Clone)]
#[error("Failed TryExtractFrom: {0}")]
pub struct TryExtractFromError(pub String);
pub trait TryExtractFrom<T>: Sized {
fn try_extract_from(v: T) -> Result<Self, TryExtractFromError>;
}
impl<T: TryExtractFrom<Literal>> TryExtractFrom<Constant> for T {
fn try_extract_from(cv: Constant) -> Result<Self, TryExtractFromError> {
T::try_extract_from(cv.v)
}
}
impl TryExtractFrom<Literal> for () {
fn try_extract_from(cv: Literal) -> Result<(), TryExtractFromError> {
match cv {
Literal::Unit => Ok(()),
_ => Err(TryExtractFromError(format!(
"expected Unit, found {:?}",
cv
))),
}
}
}
impl TryExtractFrom<Literal> for bool {
fn try_extract_from(cv: Literal) -> Result<bool, TryExtractFromError> {
match cv {
Literal::Boolean(v) => Ok(v),
_ => Err(TryExtractFromError(format!(
"expected bool, found {:?}",
cv
))),
}
}
}
impl TryExtractFrom<Literal> for i8 {
fn try_extract_from(cv: Literal) -> Result<i8, TryExtractFromError> {
match cv {
Literal::Byte(v) => Ok(v),
_ => Err(TryExtractFromError(format!("expected i8, found {:?}", cv))),
}
}
}
impl TryExtractFrom<Literal> for i16 {
fn try_extract_from(cv: Literal) -> Result<i16, TryExtractFromError> {
match cv {
Literal::Short(v) => Ok(v),
_ => Err(TryExtractFromError(format!("expected i16, found {:?}", cv))),
}
}
}
impl TryExtractFrom<Literal> for i32 {
fn try_extract_from(cv: Literal) -> Result<i32, TryExtractFromError> {
match cv {
Literal::Int(v) => Ok(v),
_ => Err(TryExtractFromError(format!("expected i32, found {:?}", cv))),
}
}
}
impl TryExtractFrom<Literal> for i64 {
fn try_extract_from(cv: Literal) -> Result<i64, TryExtractFromError> {
match cv {
Literal::Long(v) => Ok(v),
_ => Err(TryExtractFromError(format!("expected i64, found {:?}", cv))),
}
}
}
impl TryExtractFrom<Literal> for EcPoint {
fn try_extract_from(cv: Literal) -> Result<EcPoint, TryExtractFromError> {
match cv {
Literal::GroupElement(v) => Ok(*v),
_ => Err(TryExtractFromError(format!(
"expected EcPoint, found {:?}",
cv
))),
}
}
}
impl TryExtractFrom<Literal> for SigmaProp {
fn try_extract_from(cv: Literal) -> Result<SigmaProp, TryExtractFromError> {
match cv {
Literal::SigmaProp(v) => Ok(*v),
_ => Err(TryExtractFromError(format!(
"expected SigmaProp, found {:?}",
cv
))),
}
}
}
impl TryExtractFrom<Literal> for Arc<ErgoBox> {
fn try_extract_from(c: Literal) -> Result<Self, TryExtractFromError> {
match c {
Literal::CBox(b) => Ok(b),
_ => Err(TryExtractFromError(format!(
"expected ErgoBox, found {:?}",
c
))),
}
}
}
impl TryExtractFrom<Literal> for ErgoBox {
fn try_extract_from(c: Literal) -> Result<Self, TryExtractFromError> {
match c {
Literal::CBox(b) => Ok((*b).clone()),
_ => Err(TryExtractFromError(format!(
"expected ErgoBox, found {:?}",
c
))),
}
}
}
impl<T: TryExtractFrom<Literal> + StoreWrapped> TryExtractFrom<Literal> for Vec<T> {
fn try_extract_from(c: Literal) -> Result<Self, TryExtractFromError> {
match c {
Literal::Coll(coll) => match coll {
CollKind::WrappedColl {
elem_tpe: _,
items: v,
} => v.into_iter().map(T::try_extract_from).collect(),
_ => Err(TryExtractFromError(format!(
"expected {:?}, found {:?}",
std::any::type_name::<Self>(),
coll
))),
},
_ => Err(TryExtractFromError(format!(
"expected {:?}, found {:?}",
std::any::type_name::<Self>(),
c
))),
}
}
}
impl TryExtractFrom<Literal> for Vec<i8> {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
match v {
Literal::Coll(v) => match v {
CollKind::NativeColl(NativeColl::CollByte(bs)) => Ok(bs),
_ => Err(TryExtractFromError(format!(
"expected {:?}, found {:?}",
std::any::type_name::<Self>(),
v
))),
},
_ => Err(TryExtractFromError(format!(
"expected {:?}, found {:?}",
std::any::type_name::<Self>(),
v
))),
}
}
}
impl TryExtractFrom<Literal> for Vec<u8> {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
use sigma_util::FromVecI8;
Vec::<i8>::try_extract_from(v).map(Vec::<u8>::from_vec_i8)
}
}
impl TryExtractFrom<Literal> for Digest32 {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
use sigma_util::FromVecI8;
let bytes = Vec::<i8>::try_extract_from(v).map(Vec::<u8>::from_vec_i8)?;
Digest32::try_from(bytes).map_err(|e| {
TryExtractFromError(format!("failed to extract Digest32 with error: {:?}", e))
})
}
}
impl TryExtractFrom<Literal> for TokenId {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
Digest32::try_extract_from(v).map(Into::into)
}
}
impl TryExtractFrom<Literal> for Literal {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
Ok(v)
}
}
impl TryExtractFrom<Literal> for BigInt256 {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
match v {
Literal::BigInt(bi) => Ok(bi),
_ => Err(TryExtractFromError(format!(
"expected {:?}, found {:?}",
std::any::type_name::<Self>(),
v
))),
}
}
}
impl TryExtractFrom<Literal> for AvlTreeData {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
match v {
Literal::AvlTree(a) => Ok(*a),
_ => Err(TryExtractFromError(format!(
"expected {:?}, found {:?}",
std::any::type_name::<Self>(),
v
))),
}
}
}
impl<T: TryExtractFrom<Literal>> TryExtractFrom<Literal> for Option<T> {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
match v {
Literal::Opt(opt) => opt.map(T::try_extract_from).transpose(),
_ => Err(TryExtractFromError(format!(
"expected Option, found {:?}",
v
))),
}
}
}
#[impl_for_tuples(2, 4)]
impl TryExtractFrom<Literal> for Tuple {
fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
match v {
Literal::Tup(items) => {
let mut iter = items.iter();
Ok(for_tuples!( ( #(
Tuple::try_extract_from(
iter
.next()
.cloned()
.ok_or_else(|| TryExtractFromError("not enough items in STuple".to_string()))?
)?
),* ) ))
}
_ => Err(TryExtractFromError(format!(
"expected Context, found {:?}",
v
))),
}
}
}
impl TryFrom<Literal> for ProveDlog {
type Error = TryExtractFromError;
fn try_from(cv: Literal) -> Result<Self, Self::Error> {
match cv {
Literal::SigmaProp(sp) => match sp.value() {
SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
prove_dlog,
)) => Ok(prove_dlog.clone()),
_ => Err(TryExtractFromError(format!(
"expected ProveDlog, found {:?}",
sp
))),
},
_ => Err(TryExtractFromError(format!(
"expected SigmaProp, found {:?}",
cv
))),
}
}
}
impl Base16Str for &Constant {
fn base16_str(&self) -> Result<String, SigmaSerializationError> {
self.sigma_serialize_bytes()
.map(|bytes| base16::encode_lower(&bytes))
}
}
impl Base16Str for Constant {
fn base16_str(&self) -> Result<String, SigmaSerializationError> {
self.sigma_serialize_bytes()
.map(|bytes| base16::encode_lower(&bytes))
}
}
impl TryFrom<Base16DecodedBytes> for Constant {
type Error = SigmaParsingError;
fn try_from(value: Base16DecodedBytes) -> Result<Self, Self::Error> {
Constant::sigma_parse_bytes(&value.0)
}
}
#[cfg(feature = "arbitrary")]
#[allow(clippy::unwrap_used)]
#[allow(clippy::todo)]
pub(crate) mod arbitrary {
use std::convert::TryFrom;
use super::*;
use crate::mir::value::CollKind;
use crate::types::stuple::STuple;
use proptest::collection::vec;
use proptest::prelude::*;
extern crate derive_more;
use derive_more::From;
use derive_more::TryInto;
fn primitive_type_value() -> BoxedStrategy<Constant> {
prop_oneof![
any::<bool>().prop_map_into(),
any::<i8>().prop_map_into(),
any::<i16>().prop_map_into(),
any::<i32>().prop_map_into(),
any::<i64>().prop_map_into(),
any::<i64>().prop_map(|v| BigInt256::from(v).into()),
any::<EcPoint>().prop_map_into(),
any::<SigmaProp>().prop_map_into(),
vec(any::<i8>(), 0..100).prop_map_into(),
]
.boxed()
}
fn coll_from_constant(c: Constant, length: usize) -> Constant {
Constant {
tpe: SType::SColl(Box::new(c.tpe.clone())),
v: Literal::Coll(if c.tpe == SType::SByte {
let mut values: Vec<i8> = Vec::with_capacity(length);
let byte: i8 = c.v.try_extract_into().unwrap();
for _ in 0..length {
values.push(byte);
}
CollKind::NativeColl(NativeColl::CollByte(values))
} else {
let mut values: Vec<Literal> = Vec::with_capacity(length);
for _ in 0..length {
values.push(c.v.clone());
}
CollKind::WrappedColl {
elem_tpe: c.tpe,
items: values,
}
}),
}
}
fn const_with_type(tpe: SType) -> BoxedStrategy<Constant> {
match tpe {
SType::SAny => any::<Constant>(),
SType::SBoolean => any::<bool>().prop_map_into().boxed(),
SType::SByte => any::<i8>().prop_map_into().boxed(),
SType::SShort => any::<i16>().prop_map_into().boxed(),
SType::SInt => any::<i32>().prop_map_into().boxed(),
SType::SLong => any::<i64>().prop_map_into().boxed(),
SType::SBigInt => any::<i64>().prop_map(|v| BigInt256::from(v).into()).boxed(),
SType::SGroupElement => any::<EcPoint>().prop_map_into().boxed(),
SType::SSigmaProp => any::<SigmaProp>().prop_map_into().boxed(),
SType::SBox => any::<ErgoBox>().prop_map_into().boxed(),
SType::SAvlTree => any::<AvlTreeData>().prop_map_into().boxed(),
SType::SOption(tpe) => match *tpe {
SType::SBoolean => any::<Option<bool>>().prop_map_into().boxed(),
SType::SByte => any::<Option<i8>>().prop_map_into().boxed(),
SType::SShort => any::<Option<i16>>().prop_map_into().boxed(),
SType::SInt => any::<Option<i32>>().prop_map_into().boxed(),
SType::SLong => any::<Option<i64>>().prop_map_into().boxed(),
_ => todo!(),
},
SType::SColl(elem_tpe) => match *elem_tpe {
SType::SBoolean => vec(any::<bool>(), 0..400).prop_map_into().boxed(),
SType::SByte => vec(any::<u8>(), 0..400).prop_map_into().boxed(),
SType::SShort => vec(any::<i16>(), 0..400).prop_map_into().boxed(),
SType::SInt => vec(any::<i32>(), 0..400).prop_map_into().boxed(),
SType::SLong => vec(any::<i64>(), 0..400).prop_map_into().boxed(),
SType::SSigmaProp => vec(any::<SigmaProp>(), 0..3).prop_map_into().boxed(),
_ => todo!(),
},
_ => todo!("{0:?} not yet implemented", tpe),
}
}
impl Default for ArbConstantParams {
fn default() -> Self {
ArbConstantParams::AnyWithDepth(1)
}
}
#[derive(PartialEq, Eq, Debug, Clone, From, TryInto)]
pub enum ArbConstantParams {
AnyWithDepth(u8),
Exact(SType),
}
impl Arbitrary for Constant {
type Parameters = ArbConstantParams;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
match args {
ArbConstantParams::AnyWithDepth(depth) => {
prop_oneof![primitive_type_value().prop_recursive(
depth as u32,
16,
8,
|elem| {
prop_oneof![
elem.clone().prop_map(|c| coll_from_constant(c, 0)),
elem.clone().prop_map(|c| coll_from_constant(c, 1)),
elem.clone().prop_map(|c| coll_from_constant(c, 2)),
elem.clone().prop_map(|c| coll_from_constant(c, 10)),
vec(elem, 2..=4).prop_map(|constants| Constant {
tpe: SType::STuple(
STuple::try_from(
constants
.clone()
.into_iter()
.map(|c| c.tpe)
.collect::<Vec<SType>>()
)
.unwrap()
),
v: Literal::Tup(
constants
.into_iter()
.map(|c| c.v)
.collect::<Vec<Literal>>()
.try_into()
.unwrap()
)
}),
]
}
)]
.boxed()
}
ArbConstantParams::Exact(tpe) => const_with_type(tpe),
}
}
}
}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
#[allow(clippy::panic)]
pub mod tests {
use super::*;
use core::fmt;
use proptest::prelude::*;
fn test_constant_roundtrip<T>(v: T)
where
T: TryExtractInto<T> + TryExtractFrom<Literal> + Into<Constant> + fmt::Debug + Eq + Clone,
{
let constant: Constant = v.clone().into();
let v_extracted: T = constant.try_extract_into::<T>().unwrap();
assert_eq!(v, v_extracted);
}
#[test]
fn unit_roundtrip() {
test_constant_roundtrip(());
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(8))]
#[test]
fn bool_roundtrip(v in any::<bool>()) {
test_constant_roundtrip(v);
}
#[test]
fn i8_roundtrip(v in any::<i8>()) {
test_constant_roundtrip(v);
}
#[test]
fn i16_roundtrip(v in any::<i16>()) {
test_constant_roundtrip(v);
}
#[test]
fn i32_roundtrip(v in any::<i32>()) {
test_constant_roundtrip(v);
}
#[test]
fn i64_roundtrip(v in any::<i64>()) {
test_constant_roundtrip(v);
}
#[test]
fn bigint_roundtrip(raw in any::<i64>()) {
let v = BigInt256::from(raw);
test_constant_roundtrip(v);
}
#[test]
fn group_element_roundtrip(v in any::<EcPoint>()) {
test_constant_roundtrip(v);
}
#[test]
fn sigma_prop_roundtrip(v in any::<SigmaProp>()) {
test_constant_roundtrip(v);
}
#[test]
fn vec_i8_roundtrip(v in any::<Vec<i8>>()) {
test_constant_roundtrip(v);
}
#[test]
fn vec_u8_roundtrip(v in any::<Vec<u8>>()) {
test_constant_roundtrip(v);
}
#[test]
fn token_id_roundtrip(v in any::<TokenId>()) {
test_constant_roundtrip(v);
}
#[test]
fn digest32_roundtrip(v in any::<Digest32>()) {
test_constant_roundtrip(v);
}
#[test]
fn vec_i16_roundtrip(v in any::<Vec<i16>>()) {
test_constant_roundtrip(v);
}
#[test]
fn vec_i32_roundtrip(v in any::<Vec<i32>>()) {
test_constant_roundtrip(v);
}
#[test]
fn vec_i64_roundtrip(v in any::<Vec<i64>>()) {
test_constant_roundtrip(v);
}
#[test]
fn vec_bigint_roundtrip(raw in any::<Vec<i64>>()) {
let v: Vec<BigInt256> = raw.into_iter().map(BigInt256::from).collect();
test_constant_roundtrip(v);
}
#[test]
fn vec_option_bigint_roundtrip(raw in any::<Vec<i64>>()) {
let v: Vec<Option<BigInt256>> = raw.into_iter().map(|i| Some(BigInt256::from(i))).collect();
test_constant_roundtrip(v);
}
#[test]
fn vec_sigmaprop_roundtrip(v in any::<Vec<SigmaProp>>()) {
test_constant_roundtrip(v);
}
#[test]
fn option_primitive_type_roundtrip(v in any::<Option<i64>>()) {
test_constant_roundtrip(v);
}
#[test]
fn option_nested_vector_type_roundtrip(v in any::<Option<Vec<(i64, bool)>>>()) {
test_constant_roundtrip(v);
}
#[test]
fn option_nested_tuple_type_roundtrip(v in any::<Option<(i64, bool)>>()) {
test_constant_roundtrip(v);
}
#[test]
fn tuple_primitive_types_roundtrip(v in any::<(i64, bool)>()) {
test_constant_roundtrip(v);
}
#[test]
fn tuple_nested_types_roundtrip(v in any::<(Option<i64>, Vec<SigmaProp>)>()) {
test_constant_roundtrip(v);
}
}
}