use crate::dag::{DagLike, MaxSharing, SharingTracker};
use crate::encode;
use crate::jet::Jet;
use crate::{types, BitWriter, Cmr, FailEntropy, HasCmr, Value};
use std::sync::Arc;
use std::{fmt, hash, io};
mod commit;
mod construct;
mod convert;
mod disconnect;
mod display;
mod hiding;
mod inner;
mod redeem;
use crate::value::Word;
pub use commit::{Commit, CommitData, CommitNode};
pub use construct::{Construct, ConstructData, ConstructNode};
pub use convert::{Converter, Hide, SimpleFinalizer};
pub use disconnect::{Disconnectable, NoDisconnect};
pub use display::{Display, DisplayExpr};
pub use hiding::Hiding;
pub use inner::Inner;
pub use redeem::{Redeem, RedeemData, RedeemNode};
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;
impl From<NoWitness> for Option<Value> {
fn from(_: NoWitness) -> Self {
None
}
}
pub trait Constructible<'brand, J, X, W>:
JetConstructible<'brand, J>
+ DisconnectConstructible<'brand, X>
+ WitnessConstructible<'brand, W>
+ CoreConstructible<'brand>
+ Sized
{
fn from_inner(
inference_context: &types::Context<'brand>,
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<'brand, J, X, W, T> Constructible<'brand, J, X, W> for T where
T: DisconnectConstructible<'brand, X>
+ JetConstructible<'brand, J>
+ WitnessConstructible<'brand, W>
+ CoreConstructible<'brand>
+ Sized
{
}
pub trait CoreConstructible<'brand>: Sized {
fn iden(inference_context: &types::Context<'brand>) -> Self;
fn unit(inference_context: &types::Context<'brand>) -> 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<'brand>, entropy: FailEntropy) -> Self;
fn const_word(inference_context: &types::Context<'brand>, word: Word) -> Self;
fn inference_context(&self) -> &types::Context<'brand>;
fn scribe(ctx: &types::Context<'brand>, value: &Value) -> Self {
use crate::value::ValueRef;
#[derive(Debug, Clone)]
enum Task<'v> {
Process(ValueRef<'v>),
MakeLeft,
MakeRight,
MakeProduct,
}
let mut input = vec![Task::Process(value.as_ref())];
let mut output = vec![];
while let Some(top) = input.pop() {
match top {
Task::Process(value) => {
if value.is_unit() {
output.push(Self::unit(ctx));
} else if let Some(word) = value.to_word() {
output.push(Self::const_word(ctx, word));
} else if let Some(left) = value.as_left() {
input.push(Task::MakeLeft);
input.push(Task::Process(left));
} else if let Some(right) = value.as_right() {
input.push(Task::MakeRight);
input.push(Task::Process(right));
} else if let Some((left, right)) = value.as_product() {
input.push(Task::MakeProduct);
input.push(Task::Process(right));
input.push(Task::Process(left));
}
}
Task::MakeLeft => {
let inner = output.pop().unwrap();
output.push(Self::injl(&inner));
}
Task::MakeRight => {
let inner = output.pop().unwrap();
output.push(Self::injr(&inner));
}
Task::MakeProduct => {
let right = output.pop().unwrap();
let left = output.pop().unwrap();
output.push(
Self::pair(&left, &right).expect(
"`pair` should always succeed because input type is unrestricted",
),
);
}
}
}
debug_assert_eq!(output.len(), 1);
output.pop().unwrap()
}
fn bit_false(inference_context: &types::Context<'brand>) -> Self {
let unit = Self::unit(inference_context);
Self::injl(&unit)
}
fn bit_true(inference_context: &types::Context<'brand>) -> 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<'brand, X>: Sized {
fn disconnect(left: &Self, right: &X) -> Result<Self, types::Error>;
}
pub trait JetConstructible<'brand, J>: Sized {
fn jet(inference_context: &types::Context<'brand>, jet: J) -> Self;
}
pub trait WitnessConstructible<'brand, W>: Sized {
fn witness(inference_context: &types::Context<'brand>, 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 {
self.post_order_iter::<MaxSharing<N>>().into_display(
f,
|node, f| fmt::Display::fmt(&node.inner, f),
|_, _| Ok(()),
)
}
}
#[cfg(feature = "base64")]
impl<N: Marker> fmt::Display for Node<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::base64::display::Base64Display;
use crate::base64::engine::general_purpose;
let v = self.to_vec_without_witness();
Base64Display::new(&v, &general_purpose::STANDARD).fmt(f)
}
}
impl<N: Marker> HasCmr for Node<N> {
fn cmr(&self) -> Cmr {
self.cmr
}
}
impl<N: Marker> HasCmr for Arc<Node<N>> {
fn cmr(&self) -> Cmr {
self.cmr
}
}
impl<'brand, N> CoreConstructible<'brand> for Arc<Node<N>>
where
N: Marker,
N::CachedData: CoreConstructible<'brand>,
{
fn iden(inference_context: &types::Context<'brand>) -> Self {
Arc::new(Node {
cmr: Cmr::iden(),
data: N::CachedData::iden(inference_context),
inner: Inner::Iden,
})
}
fn unit(inference_context: &types::Context<'brand>) -> 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<'brand>, 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<'brand>, word: Word) -> Self {
Arc::new(Node {
cmr: Cmr::const_word(&word),
data: N::CachedData::const_word(inference_context, word.shallow_clone()),
inner: Inner::Word(word),
})
}
fn inference_context(&self) -> &types::Context<'brand> {
self.data.inference_context()
}
}
impl<'brand, N> DisconnectConstructible<'brand, N::Disconnect> for Arc<Node<N>>
where
N: Marker,
N::CachedData: DisconnectConstructible<'brand, 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<'brand, N> WitnessConstructible<'brand, N::Witness> for Arc<Node<N>>
where
N: Marker,
N::CachedData: WitnessConstructible<'brand, N::Witness>,
{
fn witness(inference_context: &types::Context<'brand>, value: N::Witness) -> Self {
Arc::new(Node {
cmr: Cmr::witness(),
data: N::CachedData::witness(inference_context, value.clone()),
inner: Inner::Witness(value),
})
}
}
impl<'brand, N> JetConstructible<'brand, N::Jet> for Arc<Node<N>>
where
N: Marker,
N::CachedData: JetConstructible<'brand, N::Jet>,
{
fn jet(inference_context: &types::Context<'brand>, 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(&self) -> Display<'_, N> {
Display::from(self)
}
pub fn display_expr(&self) -> DisplayExpr<'_, N> {
DisplayExpr::from(self)
}
pub fn encode_without_witness<W: io::Write>(&self, prog: W) -> io::Result<usize> {
let mut w = BitWriter::new(prog);
let program_bits = encode::encode_program(self, &mut w)?;
w.flush_all()?;
Ok(program_bits)
}
pub fn to_vec_without_witness(&self) -> Vec<u8> {
let mut program = Vec::<u8>::new();
self.encode_without_witness(&mut program)
.expect("Vec::write is infallible");
debug_assert!(!program.is_empty());
program
}
}
impl<N: Marker<Witness = Value>> Node<N> {
pub fn encode_with_witness<W1: io::Write, W2: io::Write>(
&self,
prog: W1,
witness: W2,
) -> io::Result<(usize, usize)> {
let mut prog = BitWriter::new(prog);
let mut witness = BitWriter::new(witness);
let sharing_iter = self.post_order_iter::<MaxSharing<N>>();
let program_bits = encode::encode_program(self, &mut prog)?;
prog.flush_all()?;
let witness_bits = encode::encode_witness(sharing_iter.into_witnesses(), &mut witness)?;
witness.flush_all()?;
Ok((program_bits, witness_bits))
}
pub fn to_vec_with_witness(&self) -> (Vec<u8>, Vec<u8>) {
let mut ret_1 = vec![];
let mut ret_2 = vec![];
self.encode_with_witness(&mut ret_1, &mut ret_2)
.expect("Vec::write is infallible");
(ret_1, ret_2)
}
}
#[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,
None,
None,
)
.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.ihr().to_byte_array(), test.ihr);
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);
}
}