Skip to content

Commit eb362c2

Browse files
committed
node: add from_str method to Construct, Commit and RedeemNode
We cannot use the std from_str trait because these all construct an Arc<Self> rather than a Self. And we cannot implement this generically on Node because the rules for decoding are different for each node type (we have progressively tighter correctness requirements, and with RedeemNode we need extra witness data).
1 parent 5f23146 commit eb362c2

5 files changed

Lines changed: 87 additions & 0 deletions

File tree

src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,41 @@ impl std::error::Error for DecodeError {
113113
}
114114
}
115115

116+
/// Error parsing a program or witness (decoding it from a string encoding).
117+
#[non_exhaustive]
118+
#[derive(Debug)]
119+
pub enum ParseError {
120+
/// Bit-encoding error.
121+
Decode(DecodeError),
122+
/// Base64 decoding error
123+
#[cfg(feature = "base64")]
124+
Base64(base64::DecodeError),
125+
/// Hex decoding error
126+
Hex(hex::error::HexToBytesError),
127+
}
128+
129+
impl fmt::Display for ParseError {
130+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131+
match self {
132+
Self::Decode(ref e) => e.fmt(f),
133+
#[cfg(feature = "base64")]
134+
Self::Base64(ref e) => e.fmt(f),
135+
Self::Hex(ref e) => e.fmt(f),
136+
}
137+
}
138+
}
139+
140+
impl std::error::Error for ParseError {
141+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
142+
match self {
143+
Self::Decode(ref e) => Some(e),
144+
#[cfg(feature = "base64")]
145+
Self::Base64(ref e) => Some(e),
146+
Self::Hex(ref e) => Some(e),
147+
}
148+
}
149+
}
150+
116151
/// Error finalizing a program (i.e. typechecking and pruning).
117152
#[non_exhaustive]
118153
#[derive(Debug)]

src/node/commit.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,19 @@ impl<J: Jet> CommitNode<J> {
262262
}
263263
}
264264

265+
#[cfg(feature = "base64")]
266+
#[allow(clippy::should_implement_trait)] // returns Arc<Self>
267+
pub fn from_str(s: &str) -> Result<Arc<Self>, crate::ParseError> {
268+
use crate::base64::engine::general_purpose;
269+
use crate::base64::Engine as _;
270+
271+
let v = general_purpose::STANDARD
272+
.decode(s)
273+
.map_err(crate::ParseError::Base64)?;
274+
let iter = crate::BitIter::new(v.into_iter());
275+
Self::decode(iter).map_err(crate::ParseError::Decode)
276+
}
277+
265278
/// Encode a Simplicity expression to bits without any witness data
266279
#[deprecated(since = "0.5.0", note = "use Self::encode_without_witness instead")]
267280
pub fn encode<W: io::Write>(&self, w: &mut BitWriter<W>) -> io::Result<usize> {
@@ -357,6 +370,7 @@ mod tests {
357370
{
358371
assert_eq!(prog.to_string(), b64_str);
359372
assert_eq!(prog.display().program().to_string(), b64_str);
373+
assert_eq!(prog, CommitNode::from_str(b64_str).unwrap());
360374
}
361375

362376
prog

src/node/construct.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,21 @@ impl<J: Jet> ConstructNode<J> {
237237
Ok(res)
238238
}
239239

240+
#[cfg(feature = "base64")]
241+
#[allow(clippy::should_implement_trait)] // returns Arc<Self>
242+
pub fn from_str(s: &str) -> Result<Arc<Self>, crate::ParseError> {
243+
use crate::base64::engine::general_purpose;
244+
use crate::base64::Engine as _;
245+
246+
let v = general_purpose::STANDARD
247+
.decode(s)
248+
.map_err(crate::ParseError::Base64)?;
249+
let iter = crate::BitIter::new(v.into_iter());
250+
Self::decode(iter)
251+
.map_err(crate::DecodeError::Decode)
252+
.map_err(crate::ParseError::Decode)
253+
}
254+
240255
/// Encode a Simplicity expression to bits, with no witness data
241256
#[deprecated(since = "0.5.0", note = "use Self::encode_without_witness instead")]
242257
pub fn encode<W: io::Write>(&self, w: &mut BitWriter<W>) -> io::Result<usize> {

src/node/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ pub trait Marker:
125125
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
126126
pub struct NoWitness;
127127

128+
impl From<NoWitness> for Option<Value> {
129+
fn from(_: NoWitness) -> Self {
130+
None
131+
}
132+
}
133+
128134
pub trait Constructible<J, X, W>:
129135
JetConstructible<J>
130136
+ DisconnectConstructible<X>

src/node/redeem.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,23 @@ impl<J: Jet> RedeemNode<J> {
532532
Ok(program)
533533
}
534534

535+
#[cfg(feature = "base64")]
536+
#[allow(clippy::should_implement_trait)] // returns Arc<Self>
537+
pub fn from_str(prog: &str, wit: &str) -> Result<Arc<Self>, crate::ParseError> {
538+
use crate::base64::engine::general_purpose;
539+
use crate::base64::Engine as _;
540+
use crate::hex::FromHex as _;
541+
542+
let v = general_purpose::STANDARD
543+
.decode(prog)
544+
.map_err(crate::ParseError::Base64)?;
545+
let prog_iter = crate::BitIter::new(v.into_iter());
546+
547+
let v = Vec::from_hex(wit).map_err(crate::ParseError::Hex)?;
548+
let wit_iter = crate::BitIter::new(v.into_iter());
549+
Self::decode(prog_iter, wit_iter).map_err(crate::ParseError::Decode)
550+
}
551+
535552
/// Encode the program to bits.
536553
///
537554
/// Includes witness data. Returns the number of written bits.

0 commit comments

Comments
 (0)