Skip to content

Commit e069d45

Browse files
committed
error: split up top-level error type into DecodeError and FinalizeError
Essentially, DecodeError is used when constructing a program from the bit encoding and FinalizeError is used when constructing a program using the library. Along the way, tighten up some error returns for functions that return e.g. crate::Error when they actually only ever yield type errors. We drop the Error::Policy variant which was never used. The old Error enum now has a single variant, which annoyingly is used by the Haskell-generated code so we can't remove it without roundtripping through upstream. Will try to do this before the next release but it's not a huge priority. In the next commit we'll add a ParseError which wraps DecodeError and adds some string-encoding-related variants.
1 parent b30edbe commit e069d45

File tree

6 files changed

+114
-82
lines changed

6 files changed

+114
-82
lines changed

jets-bench/src/data_structures.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use simplicity::{
99
hashes::Hash,
1010
hex::FromHex,
1111
types::Final,
12-
BitIter, Error, Value,
12+
BitIter, EarlyEndOfStreamError, Value,
1313
};
1414

1515
/// Engine to compute SHA256 hash function.
@@ -54,7 +54,7 @@ impl SimplicityCtx8 {
5454
/// # Panics:
5555
///
5656
/// Panics if the length of the slice is >= 2^(n + 1) bytes
57-
pub fn var_len_buf_from_slice(v: &[u8], mut n: usize) -> Result<Value, Error> {
57+
pub fn var_len_buf_from_slice(v: &[u8], mut n: usize) -> Result<Value, EarlyEndOfStreamError> {
5858
// Simplicity consensus rule for n < 16 while reading buffers.
5959
assert!(n < 16);
6060
assert!(v.len() < (1 << (n + 1)));

src/lib.rs

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -79,76 +79,96 @@ pub fn leaf_version() -> elements::taproot::LeafVersion {
7979
elements::taproot::LeafVersion::from_u8(0xbe).expect("constant leaf version")
8080
}
8181

82-
/// Error type for simplicity
82+
/// Error decoding a program from the bit encoding.
8383
#[non_exhaustive]
8484
#[derive(Debug)]
85-
pub enum Error {
85+
pub enum DecodeError {
8686
/// Decoder error
8787
Decode(decode::Error),
8888
/// A disconnect node was *not* populated at redeem time
8989
DisconnectRedeemTime,
9090
/// Type-checking error
9191
Type(types::Error),
92-
// Execution error
93-
Execution(bit_machine::ExecutionError),
94-
/// Tried to parse a jet but the name wasn't recognized
95-
InvalidJetName(String),
96-
/// Policy error
97-
#[cfg(feature = "elements")]
98-
Policy(policy::Error),
9992
}
10093

101-
impl fmt::Display for Error {
94+
impl fmt::Display for DecodeError {
10295
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10396
match self {
104-
Error::Decode(ref e) => fmt::Display::fmt(e, f),
105-
Error::DisconnectRedeemTime => {
97+
Self::Decode(ref e) => fmt::Display::fmt(e, f),
98+
Self::DisconnectRedeemTime => {
10699
f.write_str("disconnect node had one child (redeem time); must have two")
107100
}
108-
Error::Type(ref e) => fmt::Display::fmt(e, f),
109-
Error::Execution(ref e) => fmt::Display::fmt(e, f),
110-
Error::InvalidJetName(s) => write!(f, "unknown jet `{}`", s),
111-
#[cfg(feature = "elements")]
112-
Error::Policy(ref e) => fmt::Display::fmt(e, f),
101+
Self::Type(ref e) => fmt::Display::fmt(e, f),
113102
}
114103
}
115104
}
116105

117-
impl std::error::Error for Error {
106+
impl std::error::Error for DecodeError {
118107
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
119-
match *self {
120-
Error::Decode(ref e) => Some(e),
121-
Error::DisconnectRedeemTime => None,
122-
Error::Type(ref e) => Some(e),
123-
Error::Execution(ref e) => Some(e),
124-
Error::InvalidJetName(..) => None,
125-
#[cfg(feature = "elements")]
126-
Error::Policy(ref e) => Some(e),
108+
match self {
109+
Self::Decode(ref e) => Some(e),
110+
Self::DisconnectRedeemTime => None,
111+
Self::Type(ref e) => Some(e),
127112
}
128113
}
129114
}
130115

131-
impl From<crate::decode::Error> for Error {
132-
fn from(e: crate::decode::Error) -> Error {
133-
Error::Decode(e)
116+
/// Error finalizing a program (i.e. typechecking and pruning).
117+
#[non_exhaustive]
118+
#[derive(Debug)]
119+
pub enum FinalizeError {
120+
/// A disconnect node was *not* populated at redeem time
121+
DisconnectRedeemTime,
122+
// Execution error
123+
Execution(bit_machine::ExecutionError),
124+
/// Type-checking error
125+
Type(types::Error),
126+
}
127+
128+
impl fmt::Display for FinalizeError {
129+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130+
match self {
131+
Self::DisconnectRedeemTime => {
132+
f.write_str("disconnect node had one child (redeem time); must have two")
133+
}
134+
Self::Execution(ref e) => fmt::Display::fmt(e, f),
135+
Self::Type(ref e) => fmt::Display::fmt(e, f),
136+
}
134137
}
135138
}
136139

137-
impl From<EarlyEndOfStreamError> for Error {
138-
fn from(e: EarlyEndOfStreamError) -> Error {
139-
Error::Decode(e.into())
140+
impl std::error::Error for FinalizeError {
141+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
142+
match self {
143+
Self::DisconnectRedeemTime => None,
144+
Self::Execution(ref e) => Some(e),
145+
Self::Type(ref e) => Some(e),
146+
}
140147
}
141148
}
142149

143-
impl From<crate::types::Error> for Error {
144-
fn from(e: crate::types::Error) -> Error {
145-
Error::Type(e)
150+
/// Error type for simplicity
151+
// FIXME we should collapse this error to its single variant; but need to update
152+
// autogenerated code to do so, so we leave it be for now.
153+
#[non_exhaustive]
154+
#[derive(Debug)]
155+
pub enum Error {
156+
/// Tried to parse a jet but the name wasn't recognized
157+
InvalidJetName(String),
158+
}
159+
160+
impl fmt::Display for Error {
161+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162+
match self {
163+
Error::InvalidJetName(s) => write!(f, "unknown jet `{}`", s),
164+
}
146165
}
147166
}
148167

149-
#[cfg(feature = "elements")]
150-
impl From<policy::Error> for Error {
151-
fn from(e: policy::Error) -> Error {
152-
Error::Policy(e)
168+
impl std::error::Error for Error {
169+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
170+
match *self {
171+
Error::InvalidJetName(..) => None,
172+
}
153173
}
154174
}

src/node/commit.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::dag::{DagLike, MaxSharing, NoSharing, PostOrderIterItem};
44
use crate::jet::Jet;
55
use crate::types::arrow::{Arrow, FinalArrow};
66
use crate::{encode, types, Value};
7-
use crate::{Amr, BitIter, BitWriter, Cmr, Error, Ihr, Imr};
7+
use crate::{Amr, BitIter, BitWriter, Cmr, DecodeError, Ihr, Imr};
88

99
use super::{
1010
Construct, ConstructData, ConstructNode, Constructible, Converter, Inner, Marker, NoDisconnect,
@@ -248,15 +248,17 @@ impl<J: Jet> CommitNode<J> {
248248
/// or the witness is provided by other means.
249249
///
250250
/// If the serialization contains the witness data, then use [`RedeemNode::decode()`].
251-
pub fn decode<I: Iterator<Item = u8>>(bits: BitIter<I>) -> Result<Arc<Self>, Error> {
251+
pub fn decode<I: Iterator<Item = u8>>(bits: BitIter<I>) -> Result<Arc<Self>, DecodeError> {
252+
use crate::decode;
253+
252254
// 1. Decode program with out witnesses.
253-
let construct = crate::ConstructNode::decode(bits)?;
254-
let program = construct.finalize_types()?;
255+
let construct = crate::ConstructNode::decode(bits).map_err(DecodeError::Decode)?;
256+
let program = construct.finalize_types().map_err(DecodeError::Type)?;
255257
// 2. Do sharing check, using incomplete IHRs
256258
if program.as_ref().is_shared_as::<MaxSharing<Commit<J>>>() {
257259
Ok(program)
258260
} else {
259-
Err(Error::Decode(crate::decode::Error::SharingNotMaximal))
261+
Err(DecodeError::Decode(decode::Error::SharingNotMaximal))
260262
}
261263
}
262264

src/node/construct.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::dag::{InternalSharing, PostOrderIterItem};
44
use crate::jet::Jet;
55
use crate::types::{self, arrow::Arrow};
6-
use crate::{encode, BitIter, BitWriter, Cmr, FailEntropy, RedeemNode, Value, Word};
6+
use crate::{encode, BitIter, BitWriter, Cmr, FailEntropy, FinalizeError, RedeemNode, Value, Word};
77

88
use std::io;
99
use std::marker::PhantomData;
@@ -73,19 +73,20 @@ impl<J: Jet> ConstructNode<J> {
7373
/// certainly what you want, since the resulting `CommitNode` cannot be further
7474
/// composed, and needs to be 1->1 to go on-chain. But if you don't, call
7575
/// [`Self::finalize_types_non_program`] instead.
76-
pub fn finalize_types(&self) -> Result<Arc<CommitNode<J>>, crate::Error> {
76+
pub fn finalize_types(&self) -> Result<Arc<CommitNode<J>>, types::Error> {
7777
self.set_arrow_to_program()?;
7878
self.finalize_types_non_program()
7979
}
8080

8181
/// Convert a [`ConstructNode`] to a [`CommitNode`] by finalizing all of the types.
8282
///
8383
/// Does *not* sets the source and target type of this node to unit.
84-
pub fn finalize_types_non_program(&self) -> Result<Arc<CommitNode<J>>, crate::Error> {
84+
pub fn finalize_types_non_program(&self) -> Result<Arc<CommitNode<J>>, types::Error> {
8585
struct FinalizeTypes<J: Jet>(PhantomData<J>);
8686

8787
impl<J: Jet> Converter<Construct<J>, Commit<J>> for FinalizeTypes<J> {
88-
type Error = crate::Error;
88+
type Error = types::Error;
89+
8990
fn convert_witness(
9091
&mut self,
9192
_: &PostOrderIterItem<&ConstructNode<J>>,
@@ -109,9 +110,7 @@ impl<J: Jet> ConstructNode<J> {
109110
inner: Inner<&Arc<CommitNode<J>>, J, &NoDisconnect, &NoWitness>,
110111
) -> Result<Arc<CommitData<J>>, Self::Error> {
111112
let converted_data = inner.map(|node| node.cached_data());
112-
CommitData::new(&data.node.data.arrow, converted_data)
113-
.map(Arc::new)
114-
.map_err(crate::Error::from)
113+
CommitData::new(&data.node.data.arrow, converted_data).map(Arc::new)
115114
}
116115
}
117116

@@ -130,11 +129,11 @@ impl<J: Jet> ConstructNode<J> {
130129
/// ## See
131130
///
132131
/// [`RedeemNode::prune`]
133-
pub fn finalize_unpruned(&self) -> Result<Arc<RedeemNode<J>>, crate::Error> {
132+
pub fn finalize_unpruned(&self) -> Result<Arc<RedeemNode<J>>, FinalizeError> {
134133
struct Finalizer<J>(PhantomData<J>);
135134

136135
impl<J: Jet> Converter<Construct<J>, Redeem<J>> for Finalizer<J> {
137-
type Error = crate::Error;
136+
type Error = FinalizeError;
138137

139138
fn convert_witness(
140139
&mut self,
@@ -159,7 +158,12 @@ impl<J: Jet> ConstructNode<J> {
159158
// didn't provide any witness data. However, this is only the case for the
160159
// most trivial programs. The only place where we must be careful is our
161160
// unit tests, which tend to include these kinds of trivial programs.
162-
let ty = data.node.arrow().target.finalize()?;
161+
let ty = data
162+
.node
163+
.arrow()
164+
.target
165+
.finalize()
166+
.map_err(FinalizeError::Type)?;
163167
Ok(Value::zero(&ty))
164168
}
165169
}
@@ -173,7 +177,7 @@ impl<J: Jet> ConstructNode<J> {
173177
if let Some(child) = maybe_converted {
174178
Ok(Arc::clone(child))
175179
} else {
176-
Err(crate::Error::DisconnectRedeemTime)
180+
Err(FinalizeError::DisconnectRedeemTime)
177181
}
178182
}
179183

@@ -187,7 +191,7 @@ impl<J: Jet> ConstructNode<J> {
187191
.map_disconnect(|node| node.cached_data())
188192
.map_witness(Value::shallow_clone);
189193
Ok(Arc::new(RedeemData::new(
190-
data.node.arrow().finalize()?,
194+
data.node.arrow().finalize().map_err(FinalizeError::Type)?,
191195
converted_data,
192196
)))
193197
}
@@ -211,9 +215,9 @@ impl<J: Jet> ConstructNode<J> {
211215
pub fn finalize_pruned(
212216
&self,
213217
env: &J::Environment,
214-
) -> Result<Arc<RedeemNode<J>>, crate::Error> {
218+
) -> Result<Arc<RedeemNode<J>>, FinalizeError> {
215219
let unpruned = self.finalize_unpruned()?;
216-
unpruned.prune(env).map_err(crate::Error::Execution)
220+
unpruned.prune(env).map_err(FinalizeError::Execution)
217221
}
218222

219223
/// Decode a Simplicity expression from bits, without witness data.
@@ -409,7 +413,7 @@ mod tests {
409413

410414
assert!(matches!(
411415
node.finalize_types_non_program(),
412-
Err(crate::Error::Type(types::Error::OccursCheck { .. })),
416+
Err(types::Error::OccursCheck { .. }),
413417
));
414418
}
415419

@@ -430,7 +434,7 @@ mod tests {
430434

431435
assert!(matches!(
432436
comp2.finalize_types_non_program(),
433-
Err(crate::Error::Type(types::Error::OccursCheck { .. })),
437+
Err(types::Error::OccursCheck { .. }),
434438
));
435439
}
436440

@@ -458,7 +462,7 @@ mod tests {
458462

459463
assert!(matches!(
460464
comp8.finalize_types_non_program(),
461-
Err(crate::Error::Type(types::Error::OccursCheck { .. })),
465+
Err(types::Error::OccursCheck { .. }),
462466
));
463467
}
464468

@@ -560,9 +564,6 @@ mod tests {
560564

561565
#[test]
562566
fn regression_286_2() {
563-
use crate::types::Error as TypeError;
564-
use crate::Error;
565-
566567
// This one is smaller because it starts with a witness node which has a large type.
567568
// This is a bit easier to grok but can't be serialized as a complete/valid program
568569
// without providing the witness data, which limits its ability to share with the
@@ -586,6 +587,6 @@ mod tests {
586587

587588
// In #286 we incorrectly succeed finalizing the types, and then encode a bad program.
588589
let err = c10.finalize_types().unwrap_err();
589-
assert!(matches!(err, Error::Type(TypeError::OccursCheck { .. })));
590+
assert!(matches!(err, types::Error::OccursCheck { .. }));
590591
}
591592
}

src/node/convert.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ impl<W: Iterator<Item = Value>> SimpleFinalizer<W> {
169169
}
170170

171171
impl<W: Iterator<Item = Value>, J: Jet> Converter<Commit<J>, Redeem<J>> for SimpleFinalizer<W> {
172-
type Error = crate::Error;
172+
type Error = crate::FinalizeError;
173173

174174
fn convert_witness(
175175
&mut self,
@@ -188,7 +188,7 @@ impl<W: Iterator<Item = Value>, J: Jet> Converter<Commit<J>, Redeem<J>> for Simp
188188
_: Option<&Arc<RedeemNode<J>>>,
189189
_: &NoDisconnect,
190190
) -> Result<Arc<RedeemNode<J>>, Self::Error> {
191-
Err(crate::Error::DisconnectRedeemTime)
191+
Err(crate::FinalizeError::DisconnectRedeemTime)
192192
}
193193

194194
fn convert_data(

0 commit comments

Comments
 (0)