diff --git a/src/human_encoding/named_node.rs b/src/human_encoding/named_node.rs index 9548abd1..1b4919ac 100644 --- a/src/human_encoding/named_node.rs +++ b/src/human_encoding/named_node.rs @@ -8,8 +8,7 @@ use crate::node::{ self, Commit, CommitData, CommitNode, Construct, ConstructData, Constructible as _, Converter, CoreConstructible as _, Inner, NoDisconnect, NoWitness, Node, }; -use crate::types; -use crate::types::arrow::{Arrow, FinalArrow}; +use crate::types::{self, Arrow, FinalArrow}; use crate::{encode, ConstructNode, Value}; use crate::{BitWriter, Cmr, Ihr}; diff --git a/src/merkle/amr.rs b/src/merkle/amr.rs index 21dbeb15..6ae8efda 100644 --- a/src/merkle/amr.rs +++ b/src/merkle/amr.rs @@ -2,7 +2,7 @@ use crate::jet::Jet; use crate::merkle::compact_value; -use crate::types::arrow::FinalArrow; +use crate::types::FinalArrow; use crate::value::Word; use crate::{Cmr, Tmr, Value}; use hashes::sha256::Midstate; diff --git a/src/merkle/cmr.rs b/src/merkle/cmr.rs index 653940ed..920aaeb8 100644 --- a/src/merkle/cmr.rs +++ b/src/merkle/cmr.rs @@ -1,8 +1,10 @@ // SPDX-License-Identifier: CC0-1.0 use crate::jet::Jet; +#[cfg(feature = "elements")] use crate::node::{CoreConstructible, DisconnectConstructible, WitnessConstructible}; -use crate::types::{self, Error}; +#[cfg(feature = "elements")] +use crate::types::{self, Arrow, Error}; use crate::value::Word; use crate::{FailEntropy, Tmr}; use hashes::sha256::Midstate; @@ -254,118 +256,118 @@ impl Cmr { /// Wrapper around a CMR which allows it to be constructed with the /// `*Constructible*` traits, allowing CMRs to be computed using the /// same generic construction code that nodes are. +#[cfg(feature = "elements")] // only used by policy module pub struct ConstructibleCmr<'brand> { pub cmr: Cmr, - pub inference_context: types::Context<'brand>, + pub arrow: Arrow<'brand>, } +#[cfg(feature = "elements")] // only used by policy module impl<'brand> CoreConstructible<'brand> for ConstructibleCmr<'brand> { fn iden(inference_context: &types::Context<'brand>) -> Self { ConstructibleCmr { cmr: Cmr::iden(), - inference_context: inference_context.shallow_clone(), + arrow: Arrow::iden(inference_context), } } fn unit(inference_context: &types::Context<'brand>) -> Self { ConstructibleCmr { cmr: Cmr::unit(), - inference_context: inference_context.shallow_clone(), + arrow: Arrow::unit(inference_context), } } fn injl(child: &Self) -> Self { ConstructibleCmr { cmr: Cmr::injl(child.cmr), - inference_context: child.inference_context.shallow_clone(), + arrow: Arrow::injl(child.arrow()), } } fn injr(child: &Self) -> Self { ConstructibleCmr { cmr: Cmr::injr(child.cmr), - inference_context: child.inference_context.shallow_clone(), + arrow: Arrow::injr(child.arrow()), } } fn take(child: &Self) -> Self { ConstructibleCmr { cmr: Cmr::take(child.cmr), - inference_context: child.inference_context.shallow_clone(), + arrow: Arrow::take(child.arrow()), } } fn drop_(child: &Self) -> Self { ConstructibleCmr { cmr: Cmr::drop(child.cmr), - inference_context: child.inference_context.shallow_clone(), + arrow: Arrow::drop_(child.arrow()), } } fn comp(left: &Self, right: &Self) -> Result { - left.inference_context.check_eq(&right.inference_context)?; Ok(ConstructibleCmr { cmr: Cmr::comp(left.cmr, right.cmr), - inference_context: left.inference_context.shallow_clone(), + arrow: Arrow::comp(left.arrow(), right.arrow())?, }) } fn case(left: &Self, right: &Self) -> Result { - left.inference_context.check_eq(&right.inference_context)?; Ok(ConstructibleCmr { cmr: Cmr::case(left.cmr, right.cmr), - inference_context: left.inference_context.shallow_clone(), + arrow: Arrow::case(left.arrow(), right.arrow())?, }) } fn assertl(left: &Self, right: Cmr) -> Result { Ok(ConstructibleCmr { cmr: Cmr::case(left.cmr, right), - inference_context: left.inference_context.shallow_clone(), + arrow: Arrow::assertl(left.arrow())?, }) } fn assertr(left: Cmr, right: &Self) -> Result { Ok(ConstructibleCmr { cmr: Cmr::case(left, right.cmr), - inference_context: right.inference_context.shallow_clone(), + arrow: Arrow::assertr(right.arrow())?, }) } fn pair(left: &Self, right: &Self) -> Result { - left.inference_context.check_eq(&right.inference_context)?; Ok(ConstructibleCmr { cmr: Cmr::pair(left.cmr, right.cmr), - inference_context: left.inference_context.shallow_clone(), + arrow: Arrow::pair(left.arrow(), right.arrow())?, }) } fn fail(inference_context: &types::Context<'brand>, entropy: FailEntropy) -> Self { ConstructibleCmr { cmr: Cmr::fail(entropy), - inference_context: inference_context.shallow_clone(), + arrow: Arrow::fail(inference_context), } } fn const_word(inference_context: &types::Context<'brand>, word: Word) -> Self { ConstructibleCmr { cmr: Cmr::const_word(&word), - inference_context: inference_context.shallow_clone(), + arrow: Arrow::const_word(inference_context, &word), } } fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { ConstructibleCmr { cmr: jet.cmr(), - inference_context: inference_context.shallow_clone(), + arrow: Arrow::jet(inference_context, jet), } } - fn inference_context(&self) -> &types::Context<'brand> { - &self.inference_context + fn arrow(&self) -> &Arrow<'brand> { + &self.arrow } } +#[cfg(feature = "elements")] // only used by policy module impl<'brand, X> DisconnectConstructible<'brand, X> for ConstructibleCmr<'brand> { // Specifically with disconnect we don't check for consistency between the // type inference context of the disconnected node, if any, and that of @@ -374,16 +376,17 @@ impl<'brand, X> DisconnectConstructible<'brand, X> for ConstructibleCmr<'brand> fn disconnect(left: &Self, _right: &X) -> Result { Ok(ConstructibleCmr { cmr: Cmr::disconnect(left.cmr), - inference_context: left.inference_context.shallow_clone(), + arrow: left.arrow.shallow_clone(), }) } } +#[cfg(feature = "elements")] // only used by policy module impl<'brand, W> WitnessConstructible<'brand, W> for ConstructibleCmr<'brand> { fn witness(inference_context: &types::Context<'brand>, _witness: W) -> Self { ConstructibleCmr { + arrow: Arrow::witness(inference_context), cmr: Cmr::witness(), - inference_context: inference_context.shallow_clone(), } } } @@ -393,6 +396,7 @@ mod tests { use super::*; use crate::node::{ConstructNode, CoreConstructible}; + use crate::types; use std::str::FromStr; use std::sync::Arc; diff --git a/src/merkle/ihr.rs b/src/merkle/ihr.rs index 14c1d4fe..8a6f492d 100644 --- a/src/merkle/ihr.rs +++ b/src/merkle/ihr.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: CC0-1.0 use crate::jet::Jet; -use crate::types::arrow::FinalArrow; +use crate::types::FinalArrow; use crate::value::Word; use crate::{Cmr, Tmr, Value}; use hashes::sha256::Midstate; diff --git a/src/node/commit.rs b/src/node/commit.rs index 616a6001..501ae76d 100644 --- a/src/node/commit.rs +++ b/src/node/commit.rs @@ -2,13 +2,13 @@ use crate::dag::{DagLike, MaxSharing, NoSharing, PostOrderIterItem}; use crate::jet::Jet; -use crate::types::arrow::{Arrow, FinalArrow}; +use crate::types::{Arrow, FinalArrow}; use crate::{encode, types, Value}; use crate::{Amr, BitIter, BitWriter, Cmr, DecodeError, Ihr, Imr}; use super::{ - Construct, ConstructData, ConstructNode, Constructible, Converter, Inner, Marker, NoDisconnect, - NoWitness, Node, Redeem, RedeemNode, + Construct, ConstructData, ConstructNode, Converter, Inner, Marker, NoDisconnect, NoWitness, + Node, Redeem, RedeemNode, }; use std::io; @@ -213,14 +213,30 @@ impl CommitNode { &Option, >, ) -> Result, Self::Error> { - let inner = inner - .map(|node| node.arrow()) - .map_disconnect(|maybe_node| maybe_node.as_ref().map(|node| node.arrow())); - let inner = inner.disconnect_as_ref(); // lol sigh rust - Ok(ConstructData::new(Arrow::from_inner( - self.inference_context, - inner, - )?)) + use crate::node::DisconnectConstructible as _; + + let new_arrow = match inner { + Inner::Iden => Arrow::iden(self.inference_context), + Inner::Unit => Arrow::unit(self.inference_context), + Inner::InjL(child) => Arrow::injl(child.arrow()), + Inner::InjR(child) => Arrow::injr(child.arrow()), + Inner::Take(child) => Arrow::take(child.arrow()), + Inner::Drop(child) => Arrow::drop_(child.arrow()), + Inner::Comp(lft, rgt) => Arrow::comp(lft.arrow(), rgt.arrow())?, + Inner::Case(lft, rgt) => Arrow::case(lft.arrow(), rgt.arrow())?, + Inner::Pair(lft, rgt) => Arrow::pair(lft.arrow(), rgt.arrow())?, + Inner::Disconnect(lft, rgt) => { + Arrow::disconnect(lft.arrow(), &rgt.as_ref().map(|node| node.arrow()))? + } + Inner::AssertL(lft, _) => Arrow::assertl(lft.arrow())?, + Inner::AssertR(_, rgt) => Arrow::assertr(rgt.arrow())?, + Inner::Witness(_) => Arrow::witness(self.inference_context), + Inner::Fail(_) => Arrow::fail(self.inference_context), + Inner::Jet(ref jet) => Arrow::jet(self.inference_context, jet.as_ref()), + Inner::Word(ref word) => Arrow::const_word(self.inference_context, word), + }; + + Ok(ConstructData::new(new_arrow)) } } diff --git a/src/node/construct.rs b/src/node/construct.rs index ad3eff45..9895cb3e 100644 --- a/src/node/construct.rs +++ b/src/node/construct.rs @@ -2,7 +2,7 @@ use crate::dag::{InternalSharing, PostOrderIterItem}; use crate::jet::{Jet, JetEnvironment}; -use crate::types::{self, arrow::Arrow}; +use crate::types::{self, Arrow}; use crate::{encode, BitIter, BitWriter, Cmr, FailEntropy, FinalizeError, RedeemNode, Value, Word}; use std::io; @@ -330,15 +330,15 @@ impl<'brand> CoreConstructible<'brand> for ConstructData<'brand> { }) } - fn assertl(left: &Self, right: Cmr) -> Result { + fn assertl(left: &Self, _: Cmr) -> Result { Ok(ConstructData { - arrow: Arrow::assertl(&left.arrow, right)?, + arrow: Arrow::assertl(&left.arrow)?, }) } - fn assertr(left: Cmr, right: &Self) -> Result { + fn assertr(_: Cmr, right: &Self) -> Result { Ok(ConstructData { - arrow: Arrow::assertr(left, &right.arrow)?, + arrow: Arrow::assertr(&right.arrow)?, }) } @@ -348,15 +348,15 @@ impl<'brand> CoreConstructible<'brand> for ConstructData<'brand> { }) } - fn fail(inference_context: &types::Context<'brand>, entropy: FailEntropy) -> Self { + fn fail(inference_context: &types::Context<'brand>, _: FailEntropy) -> Self { ConstructData { - arrow: Arrow::fail(inference_context, entropy), + arrow: Arrow::fail(inference_context), } } fn const_word(inference_context: &types::Context<'brand>, word: Word) -> Self { ConstructData { - arrow: Arrow::const_word(inference_context, word), + arrow: Arrow::const_word(inference_context, &word), } } @@ -366,8 +366,8 @@ impl<'brand> CoreConstructible<'brand> for ConstructData<'brand> { } } - fn inference_context(&self) -> &types::Context<'brand> { - self.arrow.inference_context() + fn arrow(&self) -> &Arrow<'brand> { + &self.arrow } } @@ -388,7 +388,7 @@ impl<'brand> DisconnectConstructible<'brand, Option>>> impl<'brand> WitnessConstructible<'brand, Option> for ConstructData<'brand> { fn witness(inference_context: &types::Context<'brand>, _witness: Option) -> Self { ConstructData { - arrow: Arrow::witness(inference_context, NoWitness), + arrow: Arrow::witness(inference_context), } } } diff --git a/src/node/hiding.rs b/src/node/hiding.rs index 2e7bc57c..12c3697f 100644 --- a/src/node/hiding.rs +++ b/src/node/hiding.rs @@ -1,8 +1,14 @@ use crate::jet::Jet; use crate::node::{CoreConstructible, DisconnectConstructible, WitnessConstructible}; -use crate::types::{Context, Error}; +use crate::types::{Arrow, Context, Error}; use crate::{Cmr, FailEntropy, HasCmr, Word}; +#[derive(Clone, Debug)] +enum HidingInner<'brand, N> { + Node(N), + Hidden { cmr: Cmr, arrow: Arrow<'brand> }, +} + /// Wrapper that allows a node to be "hidden" during program construction. /// /// ## Program construction @@ -30,103 +36,147 @@ use crate::{Cmr, FailEntropy, HasCmr, Word}; /// The wrapper merely _simulates_ hidden nodes. /// At no point are actual hidden nodes created. /// To stress this fact, I write "hidden" in quotation marks. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug)] pub struct Hiding<'brand, N> { - result: HidingResult, - /// Inference context for program construction. - /// - /// Even a "hidden" node needs an inference context - /// because the context may be queried via [`CoreConstructible::inference_context`]. - /// When a "hidden" node is converted into an assertion via the - /// [`CoreConstructible::case`] constructor, this context is required to build the case node. - /// For soundness, the same context should be returned for all nodes of the same program. - ctx: Context<'brand>, + inner: HidingInner<'brand, N>, } -type HidingResult = Result; - impl<'brand, N> Hiding<'brand, N> { /// Create a "hidden" node with the given CMR. /// /// To enable the construction of possible parent nodes, /// the inference context of the current program must be passed. - pub const fn hidden(cmr: Cmr, ctx: Context<'brand>) -> Self { - // # Soundness - // The hidden node introduces no type variables. + pub fn hidden(cmr: Cmr, ctx: &Context<'brand>) -> Self { Self { - result: Err(cmr), - ctx, + inner: HidingInner::Hidden { + cmr, + arrow: Arrow::hidden(ctx), + }, } } - fn hidden_cloned_ctx(&self, cmr: Cmr) -> Self { - Self { - result: Err(cmr), - ctx: self.ctx.shallow_clone(), + /// If the node is not hidden, apply a function to the underlying node. If it is hidden, + /// apply a function to its CMR. + #[inline] + pub fn map_ref( + &self, + mapfn: impl FnOnce(&N) -> M, + cmrfn: impl FnOnce(Cmr) -> Cmr, + ) -> Hiding<'brand, M> { + use core::convert::Infallible; + match self.map_ref_result::<_, Infallible>(|node| Ok(mapfn(node)), cmrfn) { + Ok(res) => res, + Err(inf) => match inf {}, } } + /// If the node is not hidden, apply a function to the underlying node. If it is hidden, + /// do nothing. + #[inline] + pub fn map_ref_result( + &self, + mapfn: impl FnOnce(&N) -> Result, + cmrfn: impl FnOnce(Cmr) -> Cmr, + ) -> Result, Err> { + Ok(Hiding { + inner: match self.inner { + HidingInner::Node(ref n) => HidingInner::Node(mapfn(n)?), + HidingInner::Hidden { cmr, ref arrow } => HidingInner::Hidden { + cmr: cmrfn(cmr), + arrow: arrow.shallow_clone(), + }, + }, + }) + } + /// Access the non-hidden node inside in the wrapper. /// /// Return `None` if the wrapped node is "hidden". pub fn as_node(&self) -> Option<&N> { - self.result.as_ref().ok() + match self.inner { + HidingInner::Node(ref n) => Some(n), + HidingInner::Hidden { .. } => None, + } } /// Consume the wrapper and return the non-hidden node that was inside. /// /// Return `None` if the wrapped node is "hidden". - pub fn get_node(self) -> Option { - self.result.ok() + pub fn into_node(self) -> Option { + match self.inner { + HidingInner::Node(n) => Some(n), + HidingInner::Hidden { .. } => None, + } } } -impl Hiding<'_, N> { - /// Ensure that the wrapped node is "hidden". - /// Convert non-hidden nodes into "hidden" nodes with the same CMR. +impl<'brand, N: HasCmr + CoreConstructible<'brand>> Hiding<'brand, N> { + /// If neither node is hidden, apply a function to the underlying nodes to produce a new + /// non-hidden node. If either node is hidden, apply an alternate function to the CMRs + /// to produce a new CMR. + /// + /// Non-public since the API is kinda messy. + fn zip_ref>( + &self, + other: &Self, + node_zipfn: impl FnOnce(&N, &N) -> Result, + cmr_zipfn: impl FnOnce(Cmr, Cmr) -> Cmr, + ) -> Result, Error> { + Ok(Hiding { + inner: match (&self.inner, &other.inner) { + (HidingInner::Node(ref left), HidingInner::Node(ref right)) => { + node_zipfn(left, right).map(HidingInner::Node)? + } + _ => { + self.inference_context() + .check_eq(other.inference_context())?; + HidingInner::Hidden { + cmr: cmr_zipfn(self.cmr(), other.cmr()), + arrow: Arrow::hidden(self.inference_context()), + } + } + }, + }) + } + + /// Replace the node, if any, with its CMR; replace its type arrow with a new free arrow. + /// + /// Once hidden, a node's original type arrow loses its original bounds. In effect, a + /// hidden node is a completely separate node from its "original" node and is typechecked + /// and shared independently. pub fn hide(self) -> Self { - // # Soundness - // Hiding a node means converting it into its CMR. - // The node's type variables remain in the inference context. - // - // The type variables of a "hidden" child don't influence the construction of a parent, - // because merely the child's CMR is passed to the parent constructor. - // A CMR has no connection to any type variables. - // - // Hiding a node creates a CMR that is independent from the original node. - // The CMR can be used in one part of the program - // while the node itself is used in a different part. - match self.result { - Ok(node) => Self::hidden(node.cmr(), self.ctx), - Err(..) => self, + match self.inner { + HidingInner::Node(node) => Self { + inner: HidingInner::Hidden { + cmr: node.cmr(), + arrow: Arrow::hidden(node.inference_context()), + }, + }, + HidingInner::Hidden { .. } => self, } } } impl HasCmr for Hiding<'_, N> { fn cmr(&self) -> Cmr { - match &self.result { - Ok(node) => node.cmr(), - Err(cmr) => *cmr, + match self.inner { + HidingInner::Node(ref node) => node.cmr(), + HidingInner::Hidden { cmr, .. } => cmr, } } } -// We need `N: CoreConstructible` to access the inference context. -// Because of this, implementations of `{Jet, Disconnect, Witness}Constructible` -// for `Hiding` require `N: CoreConstructible`. -impl<'brand, N: CoreConstructible<'brand>> From for Hiding<'brand, N> { +impl<'brand, N> From for Hiding<'brand, N> { fn from(node: N) -> Self { Self { - ctx: node.inference_context().shallow_clone(), - result: Ok(node), + inner: HidingInner::Node(node), } } } // # Soundness // See [`Hiding::hide`]. -impl<'brand, N: CoreConstructible<'brand> + HasCmr> CoreConstructible<'brand> +impl<'brand, N: HasCmr + CoreConstructible<'brand>> CoreConstructible<'brand> for Hiding<'brand, N> { fn iden(inference_context: &Context<'brand>) -> Self { @@ -138,68 +188,59 @@ impl<'brand, N: CoreConstructible<'brand> + HasCmr> CoreConstructible<'brand> } fn injl(child: &Self) -> Self { - match &child.result { - Ok(child) => N::injl(child).into(), - Err(cmr) => child.hidden_cloned_ctx(Cmr::injl(*cmr)), - } + child.map_ref(N::injl, Cmr::injl) } fn injr(child: &Self) -> Self { - match &child.result { - Ok(child) => N::injr(child).into(), - Err(cmr) => child.hidden_cloned_ctx(Cmr::injr(*cmr)), - } + child.map_ref(N::injr, Cmr::injr) } fn take(child: &Self) -> Self { - match &child.result { - Ok(child) => N::take(child).into(), - Err(cmr) => child.hidden_cloned_ctx(Cmr::take(*cmr)), - } + child.map_ref(N::take, Cmr::take) } fn drop_(child: &Self) -> Self { - match &child.result { - Ok(child) => N::drop_(child).into(), - Err(cmr) => child.hidden_cloned_ctx(Cmr::drop(*cmr)), - } + child.map_ref(N::drop_, Cmr::drop) } fn comp(left: &Self, right: &Self) -> Result { - match (&left.result, &right.result) { - (Ok(left), Ok(right)) => N::comp(left, right).map(Self::from), - _ => Ok(left.hidden_cloned_ctx(Cmr::comp(left.cmr(), right.cmr()))), - } + left.zip_ref(right, N::comp, Cmr::comp) } fn case(left: &Self, right: &Self) -> Result { - match (&left.result, &right.result) { - (Ok(left), Ok(right)) => N::case(left, right).map(Self::from), - (Err(left), Ok(right)) => N::assertr(*left, right).map(Self::from), - (Ok(left), Err(right)) => N::assertl(left, *right).map(Self::from), - _ => Ok(left.hidden_cloned_ctx(Cmr::case(left.cmr(), right.cmr()))), - } + use HidingInner as I; + + left.inference_context() + .check_eq(right.inference_context())?; + let inner = match (&left.inner, &right.inner) { + (I::Node(left), I::Node(right)) => I::Node(N::case(left, right)?), + (I::Hidden { cmr, .. }, I::Node(right)) => I::Node(N::assertr(*cmr, right)?), + (I::Node(left), I::Hidden { cmr, .. }) => I::Node(N::assertl(left, *cmr)?), + (I::Hidden { cmr: l_cmr, .. }, I::Hidden { cmr: r_cmr, .. }) => I::Hidden { + cmr: Cmr::case(*l_cmr, *r_cmr), + arrow: Arrow::hidden(left.inference_context()), + }, + }; + + Ok(Self { inner }) } fn assertl(left: &Self, right: Cmr) -> Result { - match &left.result { - Ok(left) => N::assertl(left, right).map(Self::from), - _ => Ok(left.hidden_cloned_ctx(Cmr::case(left.cmr(), right))), - } + left.map_ref_result( + |left| N::assertl(left, right), + |lcmr| Cmr::case(lcmr, right), + ) } fn assertr(left: Cmr, right: &Self) -> Result { - match &right.result { - Ok(right) => N::assertr(left, right).map(Self::from), - _ => Ok(right.hidden_cloned_ctx(Cmr::case(left, right.cmr()))), - } + right.map_ref_result( + |right| N::assertr(left, right), + |rcmr| Cmr::case(left, rcmr), + ) } fn pair(left: &Self, right: &Self) -> Result { - match (&left.result, &right.result) { - (Ok(left), Ok(right)) => N::pair(left, right).map(Self::from), - _ => Ok(left.hidden_cloned_ctx(Cmr::pair(left.cmr(), right.cmr()))), - } + left.zip_ref(right, N::pair, Cmr::pair) } fn fail(inference_context: &Context<'brand>, entropy: FailEntropy) -> Self { @@ -214,8 +255,11 @@ impl<'brand, N: CoreConstructible<'brand> + HasCmr> CoreConstructible<'brand> N::jet(inference_context, jet).into() } - fn inference_context(&self) -> &Context<'brand> { - &self.ctx + fn arrow(&self) -> &Arrow<'brand> { + match self.inner { + HidingInner::Node(ref node) => node.arrow(), + HidingInner::Hidden { ref arrow, .. } => arrow, + } } } @@ -224,10 +268,7 @@ where N: DisconnectConstructible<'brand, Option> + CoreConstructible<'brand> + HasCmr, { fn disconnect(left: &Self, right: &Option) -> Result { - match &left.result { - Ok(left) => N::disconnect(left, right).map(Self::from), - Err(..) => Ok(left.hidden_cloned_ctx(Cmr::disconnect(left.cmr()))), - } + left.map_ref_result(|left| N::disconnect(left, right), Cmr::disconnect) } } diff --git a/src/node/mod.rs b/src/node/mod.rs index 7f6ac600..4f7dba76 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -192,7 +192,11 @@ pub trait CoreConstructible<'brand>: Sized { fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self; /// Accessor for the type inference context used to create the object. - fn inference_context(&self) -> &types::Context<'brand>; + #[inline] + fn inference_context(&self) -> &types::Context<'brand> { + &self.arrow().inference_context + } + fn arrow(&self) -> &types::Arrow<'brand>; /// Create an expression that produces the given `value`. /// @@ -550,8 +554,8 @@ where }) } - fn inference_context(&self) -> &types::Context<'brand> { - self.data.inference_context() + fn arrow(&self) -> &types::Arrow<'brand> { + self.data.arrow() } } diff --git a/src/node/redeem.rs b/src/node/redeem.rs index 1b67d106..ea4c4270 100644 --- a/src/node/redeem.rs +++ b/src/node/redeem.rs @@ -4,7 +4,7 @@ use crate::analysis::NodeBounds; use crate::bit_machine::{ExecutionError, PruneTracker, SetTracker}; use crate::dag::{DagLike, InternalSharing, MaxSharing, PostOrderIterItem}; use crate::jet::{Jet, JetEnvironment}; -use crate::types::{self, arrow::FinalArrow}; +use crate::types::{self, FinalArrow}; use crate::{encode, BitMachine}; use crate::{Amr, BitIter, BitWriter, Cmr, DecodeError, Ihr, Imr, Value}; diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index da7c866c..534c5b5a 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -271,7 +271,7 @@ impl Policy { env: &ElementsEnv>, ) -> Result, SatisfierError> { let result = self.satisfy_internal(satisfier)?; - match result.get_node() { + match result.into_node() { Some(program) => program .finalize_unpruned() .expect("serialization should be sound") @@ -715,4 +715,34 @@ mod tests { } }); } + + #[test] + fn satisfy_hidden_branch_cmr_matches_commit() { + // Regression test: when a policy branch is unsatisfiable it becomes a + // "hidden" node during satisfaction. That hidden node flows through + // `drop_` (in `policy::serialize::or`) and then a `case` that becomes an + // assertion. The hidden node's CMR must be wrapped as it passes through + // the `drop_` combinator, otherwise the satisfied program's Merkle root + // no longer matches the committed policy's Merkle root. + types::Context::with_context(|ctx| { + let env = ElementsEnv::dummy(); + let satisfier = get_satisfier(ctx, &env); + let images: Vec<_> = satisfier.preimages.keys().copied().collect(); + + // Left branch is unsatisfiable (unknown preimage) => hidden. + let policy = Policy::Or { + left: Arc::new(Policy::Sha256(sha256::Hash::from_byte_array([9; 32]))), + right: Arc::new(Policy::Sha256(images[0])), + }; + + let committed_cmr = policy.commit().cmr(); + let program = policy.satisfy(&satisfier, &env).expect("satisfiable"); + let satisfied_cmr = program.cmr(); + + assert_eq!( + committed_cmr, satisfied_cmr, + "satisfied program CMR ({satisfied_cmr}) must equal committed policy CMR ({committed_cmr})", + ); + }); + } } diff --git a/src/types/arrow.rs b/src/types/arrow.rs index a9df5c42..8839c30e 100644 --- a/src/types/arrow.rs +++ b/src/types/arrow.rs @@ -15,7 +15,7 @@ use std::fmt; use std::sync::Arc; use crate::jet::Jet; -use crate::node::{CoreConstructible, DisconnectConstructible, NoDisconnect, WitnessConstructible}; +use crate::node::{DisconnectConstructible, NoDisconnect}; use crate::types::{Context, Error, Final, Type}; use crate::value::Word; @@ -67,7 +67,7 @@ impl fmt::Display for FinalArrow { impl FinalArrow { /// Same as [`Self::clone`] but named to make it clearer that this is cheap pub fn shallow_clone(&self) -> Self { - FinalArrow { + Self { source: Arc::clone(&self.source), target: Arc::clone(&self.target), } @@ -85,7 +85,7 @@ impl<'brand> Arrow<'brand> { /// Same as [`Self::clone`] but named to make it clearer that this is cheap pub fn shallow_clone(&self) -> Self { - Arrow { + Self { source: self.source.shallow_clone(), target: self.target.shallow_clone(), inference_context: self.inference_context.shallow_clone(), @@ -143,7 +143,7 @@ impl<'brand> Arrow<'brand> { )?; } - Ok(Arrow { + Ok(Self { source: prod_sum_a_b_c, target, inference_context: ctx, @@ -177,39 +177,58 @@ impl<'brand> Arrow<'brand> { let prod_b_d = Type::product(ctx, b, d); - Ok(Arrow { + Ok(Self { source: a, target: prod_b_d, inference_context: lchild_arrow.inference_context.shallow_clone(), }) } -} -impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { - fn iden(inference_context: &Context<'brand>) -> Self { + /// A type arrow where both source and target types are new free variables. + /// + /// This should be used for hidden nodes or other nodes where nothing about + /// the node is known. + pub fn hidden(inference_context: &Context<'brand>) -> Self { + Self { + source: Type::free(inference_context, new_name("hidden_src_")), + target: Type::free(inference_context, new_name("hidden_tgt_")), + inference_context: inference_context.shallow_clone(), + } + } + + /// A type arrow where both source and target types fixed to the unit type. + pub fn program(inference_context: &Context<'brand>) -> Self { + Self { + source: Type::unit(inference_context), + target: Type::unit(inference_context), + inference_context: inference_context.shallow_clone(), + } + } + + pub fn iden(inference_context: &Context<'brand>) -> Self { // Throughout this module, when two types are the same, we reuse a // pointer to them rather than creating distinct types and unifying // them. This theoretically could lead to more confusing errors for // the user during type inference, but in practice type inference // is completely opaque and there's no harm in making it moreso. let new = Type::free(inference_context, new_name("iden_src_")); - Arrow { + Self { source: new.shallow_clone(), target: new, inference_context: inference_context.shallow_clone(), } } - fn unit(inference_context: &Context<'brand>) -> Self { - Arrow { + pub fn unit(inference_context: &Context<'brand>) -> Self { + Self { source: Type::free(inference_context, new_name("unit_src_")), target: Type::unit(inference_context), inference_context: inference_context.shallow_clone(), } } - fn injl(child: &Self) -> Self { - Arrow { + pub fn injl(child: &Self) -> Self { + Self { source: child.source.shallow_clone(), target: Type::sum( &child.inference_context, @@ -220,8 +239,8 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { } } - fn injr(child: &Self) -> Self { - Arrow { + pub fn injr(child: &Self) -> Self { + Self { source: child.source.shallow_clone(), target: Type::sum( &child.inference_context, @@ -232,8 +251,8 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { } } - fn take(child: &Self) -> Self { - Arrow { + pub fn take(child: &Self) -> Self { + Self { source: Type::product( &child.inference_context, child.source.shallow_clone(), @@ -244,8 +263,8 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { } } - fn drop_(child: &Self) -> Self { - Arrow { + pub fn drop_(child: &Self) -> Self { + Self { source: Type::product( &child.inference_context, Type::free(&child.inference_context, new_name("drop_src_")), @@ -256,40 +275,40 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { } } - fn comp(left: &Self, right: &Self) -> Result { + pub fn comp(left: &Self, right: &Self) -> Result { left.inference_context.check_eq(&right.inference_context)?; left.inference_context.unify( &left.target, &right.source, "comp combinator: left target = right source", )?; - Ok(Arrow { + Ok(Self { source: left.source.shallow_clone(), target: right.target.shallow_clone(), inference_context: left.inference_context.shallow_clone(), }) } - fn case(left: &Self, right: &Self) -> Result { + pub fn case(left: &Self, right: &Self) -> Result { Self::for_case(Some(left), Some(right)) } - fn assertl(left: &Self, _: crate::Cmr) -> Result { + pub fn assertl(left: &Self) -> Result { Self::for_case(Some(left), None) } - fn assertr(_: crate::Cmr, right: &Self) -> Result { + pub fn assertr(right: &Self) -> Result { Self::for_case(None, Some(right)) } - fn pair(left: &Self, right: &Self) -> Result { + pub fn pair(left: &Self, right: &Self) -> Result { left.inference_context.check_eq(&right.inference_context)?; left.inference_context.unify( &left.source, &right.source, "pair combinator: left source = right source", )?; - Ok(Arrow { + Ok(Self { source: left.source.shallow_clone(), target: Type::product( &left.inference_context, @@ -300,38 +319,46 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { }) } - fn fail(inference_context: &Context<'brand>, _: crate::FailEntropy) -> Self { - Arrow { + pub fn fail(inference_context: &Context<'brand>) -> Self { + Self { source: Type::free(inference_context, new_name("fail_src_")), target: Type::free(inference_context, new_name("fail_tgt_")), inference_context: inference_context.shallow_clone(), } } - fn const_word(inference_context: &Context<'brand>, word: Word) -> Self { - Arrow { + pub fn const_word(inference_context: &Context<'brand>, word: &Word) -> Self { + Self { source: Type::unit(inference_context), target: Type::two_two_n(inference_context, word.n()), inference_context: inference_context.shallow_clone(), } } - fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self { + pub fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self { inference_context.check_jet(jet); - Arrow { + Self { source: jet.source_ty().to_type(inference_context), target: jet.target_ty().to_type(inference_context), inference_context: inference_context.shallow_clone(), } } - fn inference_context(&self) -> &Context<'brand> { + pub fn witness(inference_context: &Context<'brand>) -> Self { + Self { + source: Type::free(inference_context, new_name("witness_src_")), + target: Type::free(inference_context, new_name("witness_tgt_")), + inference_context: inference_context.shallow_clone(), + } + } + + pub fn inference_context(&self) -> &Context<'brand> { &self.inference_context } } -impl<'brand> DisconnectConstructible<'brand, Arrow<'brand>> for Arrow<'brand> { +impl<'brand> DisconnectConstructible<'brand, Self> for Arrow<'brand> { fn disconnect(left: &Self, right: &Self) -> Result { Self::for_disconnect(left, right) } @@ -343,7 +370,7 @@ impl<'brand> DisconnectConstructible<'brand, NoDisconnect> for Arrow<'brand> { let target = Type::free(&left.inference_context, "disc_tgt".into()); Self::for_disconnect( left, - &Arrow { + &Self { source, target, inference_context: left.inference_context.shallow_clone(), @@ -352,7 +379,7 @@ impl<'brand> DisconnectConstructible<'brand, NoDisconnect> for Arrow<'brand> { } } -impl<'brand> DisconnectConstructible<'brand, Option<&Arrow<'brand>>> for Arrow<'brand> { +impl<'brand> DisconnectConstructible<'brand, Option<&Self>> for Arrow<'brand> { fn disconnect(left: &Self, right: &Option<&Self>) -> Result { match *right { Some(right) => Self::disconnect(left, right), @@ -360,13 +387,3 @@ impl<'brand> DisconnectConstructible<'brand, Option<&Arrow<'brand>>> for Arrow<' } } } - -impl<'brand, W> WitnessConstructible<'brand, W> for Arrow<'brand> { - fn witness(inference_context: &Context<'brand>, _: W) -> Self { - Arrow { - source: Type::free(inference_context, new_name("witness_src_")), - target: Type::free(inference_context, new_name("witness_tgt_")), - inference_context: inference_context.shallow_clone(), - } - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs index bef9550e..ef6d39a8 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -86,6 +86,7 @@ mod precomputed; mod union_bound; mod variable; +pub use arrow::{Arrow, FinalArrow}; pub use context::{BoundRef, Context}; pub use final_data::{CompleteBound, Final, TypeTooLargeError}; pub use incomplete::Incomplete;