Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo-recent.lock
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ dependencies = [
"wasm-bindgen",
]

[[package]]
name = "ghost-cell"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7"

[[package]]
name = "hex-conservative"
version = "0.1.2"
Expand Down Expand Up @@ -456,6 +462,7 @@ dependencies = [
"byteorder",
"elements",
"getrandom",
"ghost-cell",
"hex-conservative 0.2.1",
"miniscript",
"santiago",
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ bitcoin = { version = "0.32.0", optional = true }
bitcoin-miniscript = { package = "miniscript", version = "12.0.0" }
byteorder = "1.3"
elements = { version = "0.25.0", optional = true, default-features = false }
ghost-cell = { version = "0.2.6", default-features = false }
hashes = { package = "bitcoin_hashes", version = "0.14" }
hex = { package = "hex-conservative", version = "0.2.1" }
santiago = "1.3"
Expand Down
20 changes: 10 additions & 10 deletions fuzz/fuzz_lib/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ impl ProgramControl {
}

impl Extractor<'_> {
pub fn extract_core_construct_node(
pub fn extract_core_construct_node<'brand>(
&mut self,
ctx: &types::Context<'brand>,
force_control: Option<ProgramControl>,
) -> Option<Arc<ConstructNode<Core>>> {
type ArcNode = Arc<ConstructNode<Core>>;
) -> Option<Arc<ConstructNode<'brand, Core>>> {
type ArcNode<'brand> = Arc<ConstructNode<'brand, Core>>;

let ctx = types::Context::new();
let mut stack: Vec<ArcNode> = vec![];

let program_control =
Expand Down Expand Up @@ -89,8 +89,8 @@ impl Extractor<'_> {
}
}
// 1 through 63
1 => stack.push(ArcNode::unit(&ctx)),
2 => stack.push(ArcNode::iden(&ctx)),
1 => stack.push(ArcNode::unit(ctx)),
2 => stack.push(ArcNode::iden(ctx)),
3 => {
use simplicity::dag::DagLike as _;

Expand All @@ -105,9 +105,9 @@ impl Extractor<'_> {
return None;
}
}
stack.push(ArcNode::scribe(&ctx, &val));
stack.push(ArcNode::scribe(ctx, &val));
}
4 if program_control.enable_witness => stack.push(ArcNode::witness(&ctx, None)),
4 if program_control.enable_witness => stack.push(ArcNode::witness(ctx, None)),
5 => {
let child = stack.pop()?;
stack.push(ArcNode::injl(&child));
Expand Down Expand Up @@ -139,7 +139,7 @@ impl Extractor<'_> {
11 if program_control.enable_fail => {
let fail_u8 = self.extract_u8()?;
let fail = FailEntropy::from_byte_array([fail_u8; 64]);
stack.push(ArcNode::fail(&ctx, fail));
stack.push(ArcNode::fail(ctx, fail));
}
12 => {
let rchild = stack.pop()?;
Expand All @@ -165,7 +165,7 @@ impl Extractor<'_> {
_ => {
let extra_bits = usize::from(control >> 6);
let idx = (extra_bits << 8) + usize::from(self.extract_u8()?);
stack.push(ArcNode::jet(&ctx, Core::ALL[idx % Core::ALL.len()]));
stack.push(ArcNode::jet(ctx, Core::ALL[idx % Core::ALL.len()]));
}
}
}
Expand Down
15 changes: 10 additions & 5 deletions fuzz/fuzz_targets/c_rust_merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ fn do_test(data: &[u8]) {
use simplicity::ffi::tests::{ffi::SimplicityErr, run_program, TestUpTo};
use simplicity::hashes::sha256::Midstate;
use simplicity::jet::Elements;
use simplicity::types;
use simplicity::{BitIter, RedeemNode};

// To decode the program length, we first try decoding the program using
Expand All @@ -15,15 +16,19 @@ fn do_test(data: &[u8]) {
// from the error return.
//
// If the program doesn't decode, just use the first byte as a "length".
let prog_len = {
let prog_len: Option<usize> = types::Context::with_context(|ctx| {
let mut iter = BitIter::from(data);
match simplicity::decode::decode_expression::<_, Elements>(&mut iter) {
Ok(_) => (iter.n_total_read() + 7) / 8,
match simplicity::decode::decode_expression::<_, Elements>(&ctx, &mut iter) {
Ok(_) => Some((iter.n_total_read() + 7) / 8),
Err(_) => match data.first() {
Some(&n) => core::cmp::min(data.len(), n.into()),
None => return,
Some(&n) => Some(core::cmp::min(data.len(), n.into())),
None => return None,
},
}
});
let prog_len = match prog_len {
Some(x) => x,
None => return,
};

let (program, witness) = data.split_at(prog_len);
Expand Down
58 changes: 31 additions & 27 deletions fuzz/fuzz_targets/regression_286.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,40 @@

#[cfg(any(fuzzing, test))]
fn do_test(data: &[u8]) {
use simplicity::{jet::Core, BitIter, CommitNode};

let mut extractor = simplicity_fuzz::Extractor::new(data);

let construct =
match extractor.extract_core_construct_node(Some(simplicity_fuzz::ProgramControl {
enable_type_bomb: false,
enable_disconnect: false,
enable_witness: false,
enable_fail: false,
enable_asserts: true,
max_nodes: Some(25),
})) {
use simplicity::{jet::Core, types, BitIter, CommitNode};

types::Context::with_context(|ctx| {
let mut extractor = simplicity_fuzz::Extractor::new(data);

let construct = match extractor.extract_core_construct_node(
&ctx,
Some(simplicity_fuzz::ProgramControl {
enable_type_bomb: false,
enable_disconnect: false,
enable_witness: false,
enable_fail: false,
enable_asserts: true,
max_nodes: Some(25),
}),
) {
Some(x) => x,
None => return,
};
//println!("constructed {construct}");
let finalized = match construct.finalize_types() {
Ok(x) => x,
Err(_) => return,
};
//println!("finalized {finalized}");
let prog = finalized.encode_to_vec();
//println!("{}", simplicity::bitcoin::hex::DisplayHex::as_hex(&prog));
let prog = BitIter::from(prog);
let decode = CommitNode::<Core>::decode(prog).unwrap();
assert_eq!(
finalized, decode,
"Constructed committed LHS; encoded and decoded to get RHS",
);
//println!("constructed {construct}");
let finalized = match construct.finalize_types() {
Ok(x) => x,
Err(_) => return,
};
//println!("finalized {finalized}");
let prog = finalized.encode_to_vec();
//println!("{}", simplicity::bitcoin::hex::DisplayHex::as_hex(&prog));
let prog = BitIter::from(prog);
let decode = CommitNode::<Core>::decode(prog).unwrap();
assert_eq!(
finalized, decode,
"Constructed committed LHS; encoded and decoded to get RHS",
);
});
}

#[cfg(fuzzing)]
Expand Down
6 changes: 4 additions & 2 deletions src/bit_encoding/bitwriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ mod tests {

#[test]
fn vec() {
let program = Arc::<ConstructNode<Core>>::unit(&types::Context::new());
let _ = write_to_vec(|w| program.encode_without_witness(w));
types::Context::with_context(|ctx| {
let program = Arc::<ConstructNode<Core>>::unit(&ctx);
let _ = write_to_vec(|w| program.encode_without_witness(w));
})
}

#[test]
Expand Down
38 changes: 19 additions & 19 deletions src/bit_encoding/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::{cmp, error, fmt};

use super::bititer::{u2, DecodeNaturalError};

type ArcNode<J> = Arc<ConstructNode<J>>;
type ArcNode<'brand, J> = Arc<ConstructNode<'brand, J>>;

/// Decoding error
#[non_exhaustive]
Expand All @@ -48,13 +48,13 @@ pub enum Error {
}

impl From<crate::EarlyEndOfStreamError> for Error {
fn from(_: crate::EarlyEndOfStreamError) -> Error {
fn from(_: crate::EarlyEndOfStreamError) -> Self {
Error::EndOfStream
}
}

impl From<crate::BitIterCloseError> for Error {
fn from(e: crate::BitIterCloseError) -> Error {
fn from(e: crate::BitIterCloseError) -> Self {
Error::BitIter(e)
}
}
Expand Down Expand Up @@ -153,16 +153,17 @@ impl<J: Jet> DagLike for (usize, &'_ [DecodeNode<J>]) {
}
}

pub fn decode_expression<I: Iterator<Item = u8>, J: Jet>(
pub fn decode_expression<'brand, I: Iterator<Item = u8>, J: Jet>(
ctx: &types::Context<'brand>,
bits: &mut BitIter<I>,
) -> Result<ArcNode<J>, Error> {
enum Converted<J: Jet> {
Node(ArcNode<J>),
) -> Result<ArcNode<'brand, J>, Error> {
enum Converted<'brand, J: Jet> {
Node(ArcNode<'brand, J>),
Hidden(Cmr),
}
use Converted::{Hidden, Node};
impl<J: Jet> Converted<J> {
fn get(&self) -> Result<&ArcNode<J>, Error> {
impl<'brand, J: Jet> Converted<'brand, J> {
fn get(&self) -> Result<&ArcNode<'brand, J>, Error> {
match self {
Node(arc) => Ok(arc),
Hidden(_) => Err(Error::HiddenNode),
Expand All @@ -173,7 +174,6 @@ pub fn decode_expression<I: Iterator<Item = u8>, J: Jet>(
let len = bits.read_natural::<usize>(None)?;
assert_ne!(len, 0, "impossible to encode 0 in Simplicity");

let inference_context = types::Context::new();
let mut nodes = Vec::with_capacity(cmp::min(len, 10_000));
for _ in 0..len {
let new_node = decode_node(bits, nodes.len())?;
Expand All @@ -191,8 +191,8 @@ pub fn decode_expression<I: Iterator<Item = u8>, J: Jet>(
}

let new = match nodes[data.node.0] {
DecodeNode::Unit => Node(ArcNode::unit(&inference_context)),
DecodeNode::Iden => Node(ArcNode::iden(&inference_context)),
DecodeNode::Unit => Node(ArcNode::unit(ctx)),
DecodeNode::Iden => Node(ArcNode::iden(ctx)),
DecodeNode::InjL(i) => Node(ArcNode::injl(converted[i].get()?)),
DecodeNode::InjR(i) => Node(ArcNode::injr(converted[i].get()?)),
DecodeNode::Take(i) => Node(ArcNode::take(converted[i].get()?)),
Expand All @@ -218,18 +218,16 @@ pub fn decode_expression<I: Iterator<Item = u8>, J: Jet>(
converted[i].get()?,
&Some(Arc::clone(converted[j].get()?)),
)?),
DecodeNode::Witness => Node(ArcNode::witness(&inference_context, None)),
DecodeNode::Fail(entropy) => Node(ArcNode::fail(&inference_context, entropy)),
DecodeNode::Witness => Node(ArcNode::witness(ctx, None)),
DecodeNode::Fail(entropy) => Node(ArcNode::fail(ctx, entropy)),
DecodeNode::Hidden(cmr) => {
if !hidden_set.insert(cmr) {
return Err(Error::SharingNotMaximal);
}
Hidden(cmr)
}
DecodeNode::Jet(j) => Node(ArcNode::jet(&inference_context, j)),
DecodeNode::Word(ref w) => {
Node(ArcNode::const_word(&inference_context, w.shallow_clone()))
}
DecodeNode::Jet(j) => Node(ArcNode::jet(ctx, j)),
DecodeNode::Word(ref w) => Node(ArcNode::const_word(ctx, w.shallow_clone())),
};
converted.push(new);
}
Expand Down Expand Up @@ -318,7 +316,9 @@ mod tests {
let justjet = [0x6d, 0xb8, 0x80];
// Should be able to decode this as an expression...
let mut iter = BitIter::from(&justjet[..]);
decode_expression::<_, Core>(&mut iter).unwrap();
types::Context::with_context(|ctx| {
decode_expression::<_, Core>(&ctx, &mut iter).unwrap();
});
// ...but NOT as a CommitNode
let iter = BitIter::from(&justjet[..]);
CommitNode::<Core>::decode(iter).unwrap_err();
Expand Down
22 changes: 12 additions & 10 deletions src/bit_machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,15 +772,17 @@ mod tests {
fn crash_regression2() {
use crate::node::{CoreConstructible as _, JetConstructible as _};

type Node = Arc<crate::ConstructNode<crate::jet::Core>>;

let mut bomb = Node::jet(
&crate::types::Context::new(),
crate::jet::Core::Ch8, // arbitrary jet with nonzero output size
);
for _ in 0..100 {
bomb = Node::pair(&bomb, &bomb).unwrap();
}
let _ = bomb.finalize_pruned(&());
type Node<'brand> = Arc<crate::ConstructNode<'brand, crate::jet::Core>>;

crate::types::Context::with_context(|ctx| {
let mut bomb = Node::jet(
&ctx,
crate::jet::Core::Ch8, // arbitrary jet with nonzero output size
);
for _ in 0..100 {
bomb = Node::pair(&bomb, &bomb).unwrap();
}
let _ = bomb.finalize_pruned(&());
});
}
}
10 changes: 6 additions & 4 deletions src/human_encoding/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ pub enum Error {
/// us from knowing the whole program.
HoleAtCommitTime {
name: Arc<str>,
arrow: types::arrow::Arrow,
arrow_source: Arc<types::Incomplete>,
arrow_target: Arc<types::Incomplete>,
},
/// When converting to a `CommitNode`, a disconnect node had an actual node rather
/// than a hole.
Expand Down Expand Up @@ -303,11 +304,12 @@ impl fmt::Display for Error {
),
Error::HoleAtCommitTime {
ref name,
ref arrow,
ref arrow_source,
ref arrow_target,
} => write!(
f,
"unfilled hole ?{} at commitment time; type arrow {}",
name, arrow
"unfilled hole ?{} at commitment time; type arrow {} -> {}",
name, arrow_source, arrow_target,
),
Error::HoleFilledAtCommitTime => {
f.write_str("disconnect node has a non-hole child at commit time")
Expand Down
Loading
Loading