use crate::dag::{DagLike, MaxSharing, NoSharing, SharingTracker};
use crate::jet::Jet;
use crate::{types, Cmr, FailEntropy, Value};
use std::sync::Arc;
use std::{fmt, hash};
mod commit;
mod construct;
mod convert;
mod disconnect;
mod display;
mod inner;
mod redeem;
mod witness;
pub use commit::{Commit, CommitData, CommitNode};
pub use construct::{Construct, ConstructData, ConstructNode};
pub use convert::{Converter, Hide, SimpleFinalizer};
pub use disconnect::{Disconnectable, NoDisconnect};
use display::DisplayExpr;
pub use inner::Inner;
pub use redeem::{Redeem, RedeemData, RedeemNode};
pub use witness::{Witness, WitnessData, WitnessNode};
pub trait Marker:
Copy + Clone + PartialEq + Eq + PartialOrd + Ord + fmt::Debug + hash::Hash
{
type CachedData: Clone;
type Witness: Clone;
type Disconnect: Disconnectable<Node<Self>> + Clone;
type SharingId: hash::Hash + Clone + Eq;
type Jet: Jet;
fn compute_sharing_id(cmr: Cmr, cached_data: &Self::CachedData) -> Option<Self::SharingId>;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct NoWitness;
pub trait Constructible<J, X, W>:
JetConstructible<J>
+ DisconnectConstructible<X>
+ WitnessConstructible<W>
+ CoreConstructible
+ Sized
{
fn from_inner(
inference_context: &types::Context,
inner: Inner<&Self, J, &X, W>,
) -> Result<Self, types::Error> {
match inner {
Inner::Iden => Ok(Self::iden(inference_context)),
Inner::Unit => Ok(Self::unit(inference_context)),
Inner::InjL(child) => Ok(Self::injl(child)),
Inner::InjR(child) => Ok(Self::injr(child)),
Inner::Take(child) => Ok(Self::take(child)),
Inner::Drop(child) => Ok(Self::drop_(child)),
Inner::Comp(left, right) => Self::comp(left, right),
Inner::Case(left, right) => Self::case(left, right),
Inner::AssertL(left, r_cmr) => Self::assertl(left, r_cmr),
Inner::AssertR(l_cmr, right) => Self::assertr(l_cmr, right),
Inner::Pair(left, right) => Self::pair(left, right),
Inner::Disconnect(left, right) => Self::disconnect(left, right),
Inner::Fail(entropy) => Ok(Self::fail(inference_context, entropy)),
Inner::Word(ref w) => Ok(Self::const_word(inference_context, w.shallow_clone())),
Inner::Jet(j) => Ok(Self::jet(inference_context, j)),
Inner::Witness(w) => Ok(Self::witness(inference_context, w)),
}
}
}
impl<J, X, W, T> Constructible<J, X, W> for T where
T: DisconnectConstructible<X>
+ JetConstructible<J>
+ WitnessConstructible<W>
+ CoreConstructible
+ Sized
{
}
pub trait CoreConstructible: Sized {
fn iden(inference_context: &types::Context) -> Self;
fn unit(inference_context: &types::Context) -> Self;
fn injl(child: &Self) -> Self;
fn injr(child: &Self) -> Self;
fn take(child: &Self) -> Self;
fn drop_(child: &Self) -> Self;
fn comp(left: &Self, right: &Self) -> Result<Self, types::Error>;
fn case(left: &Self, right: &Self) -> Result<Self, types::Error>;
fn assertl(left: &Self, right: Cmr) -> Result<Self, types::Error>;
fn assertr(left: Cmr, right: &Self) -> Result<Self, types::Error>;
fn pair(left: &Self, right: &Self) -> Result<Self, types::Error>;
fn fail(inference_context: &types::Context, entropy: FailEntropy) -> Self;
fn const_word(inference_context: &types::Context, word: Value) -> Self;
fn inference_context(&self) -> &types::Context;
fn scribe(inference_context: &types::Context, value: &Value) -> Self {
let mut stack = vec![];
for data in value.post_order_iter::<NoSharing>() {
if data.node.is_unit() {
stack.push(Self::unit(inference_context));
} else if data.node.as_left().is_some() {
let child = stack.pop().unwrap();
stack.push(Self::injl(&child));
} else if data.node.as_right().is_some() {
let child = stack.pop().unwrap();
stack.push(Self::injr(&child));
} else if data.node.as_product().is_some() {
let right = stack.pop().unwrap();
let left = stack.pop().unwrap();
stack.push(Self::pair(&left, &right).expect("source of scribe has no constraints"));
}
}
assert_eq!(stack.len(), 1);
stack.pop().unwrap()
}
fn bit_false(inference_context: &types::Context) -> Self {
let unit = Self::unit(inference_context);
Self::injl(&unit)
}
fn bit_true(inference_context: &types::Context) -> Self {
let unit = Self::unit(inference_context);
Self::injr(&unit)
}
fn cond(left: &Self, right: &Self) -> Result<Self, types::Error> {
let drop_left = Self::drop_(left);
let drop_right = Self::drop_(right);
Self::case(&drop_right, &drop_left)
}
fn assert(child: &Self, hash: Cmr) -> Result<Self, types::Error> {
let unit = Self::unit(child.inference_context());
let pair_child_unit = Self::pair(child, &unit)?;
let assertr_hidden_unit = Self::assertr(hash, &unit)?;
Self::comp(&pair_child_unit, &assertr_hidden_unit)
}
#[allow(clippy::should_implement_trait)]
fn not(child: &Self) -> Result<Self, types::Error> {
let unit = Self::unit(child.inference_context());
let pair_child_unit = Self::pair(child, &unit)?;
let bit_true = Self::bit_true(child.inference_context());
let bit_false = Self::bit_false(child.inference_context());
let case_true_false = Self::case(&bit_true, &bit_false)?;
Self::comp(&pair_child_unit, &case_true_false)
}
fn and(left: &Self, right: &Self) -> Result<Self, types::Error> {
left.inference_context()
.check_eq(right.inference_context())?;
let iden = Self::iden(left.inference_context());
let pair_left_iden = Self::pair(left, &iden)?;
let bit_false = Self::bit_false(left.inference_context());
let drop_right = Self::drop_(right);
let case_false_right = Self::case(&bit_false, &drop_right)?;
Self::comp(&pair_left_iden, &case_false_right)
}
fn or(left: &Self, right: &Self) -> Result<Self, types::Error> {
left.inference_context()
.check_eq(right.inference_context())?;
let iden = Self::iden(left.inference_context());
let pair_left_iden = Self::pair(left, &iden)?;
let drop_right = Self::drop_(right);
let bit_true = Self::bit_true(left.inference_context());
let case_right_true = Self::case(&drop_right, &bit_true)?;
Self::comp(&pair_left_iden, &case_right_true)
}
}
pub trait DisconnectConstructible<X>: Sized {
fn disconnect(left: &Self, right: &X) -> Result<Self, types::Error>;
}
pub trait JetConstructible<J>: Sized {
fn jet(inference_context: &types::Context, jet: J) -> Self;
}
pub trait WitnessConstructible<W>: Sized {
fn witness(inference_context: &types::Context, witness: W) -> Self;
}
pub struct Node<N: Marker> {
inner: Inner<Arc<Node<N>>, N::Jet, N::Disconnect, N::Witness>,
cmr: Cmr,
data: N::CachedData,
}
impl<N: Marker> PartialEq for Node<N>
where
N::CachedData: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.cmr == other.cmr && self.data == other.data
}
}
impl<N: Marker> Eq for Node<N> where N::CachedData: Eq {}
impl<N: Marker> hash::Hash for Node<N>
where
N::CachedData: hash::Hash,
{
fn hash<H: hash::Hasher>(&self, h: &mut H) {
self.cmr.hash(h);
self.data.hash(h);
}
}
impl<N: Marker> fmt::Debug for Node<N>
where
for<'a> &'a Node<N>: DagLike,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<N: Marker> fmt::Display for Node<N>
where
for<'a> &'a Node<N>: DagLike,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.post_order_iter::<MaxSharing<N>>().into_display(
f,
|node, f| fmt::Display::fmt(&node.inner, f),
|_, _| Ok(()),
)
}
}
impl<N> CoreConstructible for Arc<Node<N>>
where
N: Marker,
N::CachedData: CoreConstructible,
{
fn iden(inference_context: &types::Context) -> Self {
Arc::new(Node {
cmr: Cmr::iden(),
data: N::CachedData::iden(inference_context),
inner: Inner::Iden,
})
}
fn unit(inference_context: &types::Context) -> Self {
Arc::new(Node {
cmr: Cmr::unit(),
data: N::CachedData::unit(inference_context),
inner: Inner::Unit,
})
}
fn injl(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::injl(child.cmr()),
data: N::CachedData::injl(&child.data),
inner: Inner::InjL(Arc::clone(child)),
})
}
fn injr(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::injr(child.cmr()),
data: N::CachedData::injr(&child.data),
inner: Inner::InjR(Arc::clone(child)),
})
}
fn take(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::take(child.cmr()),
data: N::CachedData::take(&child.data),
inner: Inner::Take(Arc::clone(child)),
})
}
fn drop_(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::drop(child.cmr()),
data: N::CachedData::drop_(&child.data),
inner: Inner::Drop(Arc::clone(child)),
})
}
fn comp(left: &Self, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::comp(left.cmr(), right.cmr()),
data: N::CachedData::comp(&left.data, &right.data)?,
inner: Inner::Comp(Arc::clone(left), Arc::clone(right)),
}))
}
fn case(left: &Self, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::case(left.cmr(), right.cmr()),
data: N::CachedData::case(&left.data, &right.data)?,
inner: Inner::Case(Arc::clone(left), Arc::clone(right)),
}))
}
fn assertl(left: &Self, r_cmr: Cmr) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::case(left.cmr(), r_cmr),
data: N::CachedData::assertl(&left.data, r_cmr)?,
inner: Inner::AssertL(Arc::clone(left), r_cmr),
}))
}
fn assertr(l_cmr: Cmr, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::case(l_cmr, right.cmr()),
data: N::CachedData::assertr(l_cmr, &right.data)?,
inner: Inner::AssertR(l_cmr, Arc::clone(right)),
}))
}
fn pair(left: &Self, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::pair(left.cmr(), right.cmr()),
data: N::CachedData::pair(&left.data, &right.data)?,
inner: Inner::Pair(Arc::clone(left), Arc::clone(right)),
}))
}
fn fail(inference_context: &types::Context, entropy: FailEntropy) -> Self {
Arc::new(Node {
cmr: Cmr::fail(entropy),
data: N::CachedData::fail(inference_context, entropy),
inner: Inner::Fail(entropy),
})
}
fn const_word(inference_context: &types::Context, value: Value) -> Self {
Arc::new(Node {
cmr: Cmr::const_word(&value),
data: N::CachedData::const_word(inference_context, value.shallow_clone()),
inner: Inner::Word(value),
})
}
fn inference_context(&self) -> &types::Context {
self.data.inference_context()
}
}
impl<N> DisconnectConstructible<N::Disconnect> for Arc<Node<N>>
where
N: Marker,
N::CachedData: DisconnectConstructible<N::Disconnect>,
{
fn disconnect(left: &Self, right: &N::Disconnect) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::disconnect(left.cmr()),
data: N::CachedData::disconnect(&left.data, right)?,
inner: Inner::Disconnect(Arc::clone(left), right.clone()),
}))
}
}
impl<N> WitnessConstructible<N::Witness> for Arc<Node<N>>
where
N: Marker,
N::CachedData: WitnessConstructible<N::Witness>,
{
fn witness(inference_context: &types::Context, value: N::Witness) -> Self {
Arc::new(Node {
cmr: Cmr::witness(),
data: N::CachedData::witness(inference_context, value.clone()),
inner: Inner::Witness(value),
})
}
}
impl<N> JetConstructible<N::Jet> for Arc<Node<N>>
where
N: Marker,
N::CachedData: JetConstructible<N::Jet>,
{
fn jet(inference_context: &types::Context, jet: N::Jet) -> Self {
Arc::new(Node {
cmr: Cmr::jet(jet),
data: N::CachedData::jet(inference_context, jet),
inner: Inner::Jet(jet),
})
}
}
impl<N: Marker> Node<N> {
pub fn inner(&self) -> &Inner<Arc<Node<N>>, N::Jet, N::Disconnect, N::Witness> {
&self.inner
}
pub fn cmr(&self) -> Cmr {
self.cmr
}
pub fn sharing_id(&self) -> Option<N::SharingId> {
N::compute_sharing_id(self.cmr, &self.data)
}
pub fn cached_data(&self) -> &N::CachedData {
&self.data
}
pub fn from_parts(
inner: Inner<Arc<Self>, N::Jet, N::Disconnect, N::Witness>,
data: N::CachedData,
) -> Self {
let cmr = match inner {
Inner::Unit => Cmr::unit(),
Inner::Iden => Cmr::iden(),
Inner::InjL(ref c) => Cmr::injl(c.cmr()),
Inner::InjR(ref c) => Cmr::injr(c.cmr()),
Inner::Take(ref c) => Cmr::take(c.cmr()),
Inner::Drop(ref c) => Cmr::drop(c.cmr()),
Inner::Comp(ref cl, ref cr) => Cmr::comp(cl.cmr(), cr.cmr()),
Inner::Case(ref cl, ref cr) => Cmr::case(cl.cmr(), cr.cmr()),
Inner::AssertL(ref c, cmr) => Cmr::case(c.cmr(), cmr),
Inner::AssertR(cmr, ref c) => Cmr::case(cmr, c.cmr()),
Inner::Pair(ref cl, ref cr) => Cmr::pair(cl.cmr(), cr.cmr()),
Inner::Disconnect(ref cl, _) => Cmr::disconnect(cl.cmr()),
Inner::Witness(_) => Cmr::witness(),
Inner::Fail(entropy) => Cmr::fail(entropy),
Inner::Jet(j) => Cmr::jet(j),
Inner::Word(ref w) => Cmr::const_word(w),
};
Node { cmr, inner, data }
}
pub fn convert<S, M, C>(&self, converter: &mut C) -> Result<Arc<Node<M>>, C::Error>
where
S: for<'a> SharingTracker<&'a Self> + Default,
M: Marker<Jet = <N as Marker>::Jet>,
C: Converter<N, M>,
{
let mut converted: Vec<Arc<Node<M>>> = vec![];
for data in self.post_order_iter::<S>() {
converter.visit_node(&data);
let indexed_inner: Inner<usize, N::Jet, &N::Disconnect, &N::Witness> = data
.node
.inner
.as_ref()
.map_left_right(|_| data.left_index.unwrap(), |_| data.right_index.unwrap());
let witness_inner: Inner<&usize, M::Jet, &&N::Disconnect, M::Witness> = indexed_inner
.as_ref()
.map_witness_result(|wit| converter.convert_witness(&data, wit))?;
let maybe_converted = data.right_index.map(|idx| &converted[idx]);
let witness_inner: Inner<&usize, N::Jet, M::Disconnect, M::Witness> = witness_inner
.map_disconnect_result(|disc| {
converter.convert_disconnect(&data, maybe_converted, disc)
})?;
let converted_inner: Inner<Arc<Node<M>>, M::Jet, M::Disconnect, M::Witness> =
witness_inner.map(|idx| Arc::clone(&converted[*idx]));
let pruned_inner = if let Inner::Case(left, right) = converted_inner {
let hide = converter.prune_case(&data, &left, &right)?;
match hide {
Hide::Neither => Inner::Case(left, right),
Hide::Left => Inner::AssertR(left.cmr(), right),
Hide::Right => Inner::AssertL(left, right.cmr()),
}
} else {
converted_inner
};
converted.push(Arc::new(Node {
data: converter.convert_data(&data, pruned_inner.as_ref())?,
cmr: data.node.cmr,
inner: pruned_inner,
}));
}
Ok(converted.pop().unwrap())
}
pub fn display_expr(&self) -> DisplayExpr<N> {
DisplayExpr::from(self)
}
}
#[cfg(test)]
#[cfg(all(feature = "test-utils", feature = "elements"))]
mod tests {
use ffi::tests::TestData;
use crate::analysis::Cost;
use crate::ffi;
use crate::jet::Elements;
use crate::BitIter;
use crate::RedeemNode;
fn check_merkle_roots(test: &TestData) {
let prog = BitIter::from(test.prog.as_slice());
let witness = BitIter::from(test.witness.as_slice());
ffi::tests::run_program(&test.prog, &test.witness, ffi::tests::TestUpTo::CheckOneOne)
.unwrap();
let prog = RedeemNode::<Elements>::decode(prog, witness).unwrap();
assert_eq!(prog.cmr().to_byte_array(), test.cmr);
assert_eq!(prog.amr().to_byte_array(), test.amr);
assert_eq!(prog.imr().to_byte_array(), test.imr);
assert_eq!(prog.bounds().cost, Cost::from_milliweight(test.cost))
}
#[test]
fn progs_cmr() {
let schnorr0 = ffi::tests::schnorr0_test_data();
let schnorr6 = ffi::tests::schnorr6_test_data();
let ctx8_unpruned = ffi::tests::ctx8_unpruned_test_data();
let ctx8_pruned = ffi::tests::ctx8_pruned_test_data();
check_merkle_roots(&schnorr0);
check_merkle_roots(&schnorr6);
check_merkle_roots(&ctx8_unpruned);
check_merkle_roots(&ctx8_pruned);
}
}