Skip to content

Commit dec28f3

Browse files
committed
node: take context as argument to all ConstructNode constructors
In the new GhostCell paradigm it will be impossible for a Type (and therefore a ConstructNode) to outlive its context. This means that all the methods which currently return a disembodied ConstructNode need to accept a context as a parameter. In effect, this puts "direct constructors" and parsers/decoders of ConstructNodes on even footing: people doing direct construction of programs have always needed to create a context and keep track of it, since the constructors iden(), unit(), witness() and jets all need one. The other constructors simply copy the context Arc from the objects. This design had a couple goals: * Minimizing pain for users by minimizing the number of places they had to pass a context object * Minimizing errors by using "the correct context object" automatically by storing an Arc and cloning it behind the scenes * Allowing users of decode_expression and ConstructNode::from_str to not think about contexts at all. However, I observe that the "minimizing pain" goal is not that valuable since this only applies to ConstructNode (since CommitNode and RedeemNode have complete types in them, and these are much more commonly used than ConstructNode). Furthermore: * The current logic actually makes it impossible to decode two expressions and then combine them, since they'll have different associated type contexts. (I guess nobody has tried this?? They would get a panic.) * The "minimizing errors" goal we can achieve by GhostCell, at compile time and much more thoroughly, and in a way that "feels Rustic" in that it levers the existing borrowchecker.
1 parent 65aab33 commit dec28f3

File tree

5 files changed

+26
-17
lines changed

5 files changed

+26
-17
lines changed

fuzz/fuzz_targets/c_rust_merkle.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ fn do_test(data: &[u8]) {
77
use simplicity::ffi::tests::{ffi::SimplicityErr, run_program, TestUpTo};
88
use simplicity::hashes::sha256::Midstate;
99
use simplicity::jet::Elements;
10+
use simplicity::types;
1011
use simplicity::{BitIter, RedeemNode};
1112

1213
// To decode the program length, we first try decoding the program using
@@ -17,7 +18,10 @@ fn do_test(data: &[u8]) {
1718
// If the program doesn't decode, just use the first byte as a "length".
1819
let prog_len = {
1920
let mut iter = BitIter::from(data);
20-
match simplicity::decode::decode_expression::<_, Elements>(&mut iter) {
21+
match simplicity::decode::decode_expression::<_, Elements>(
22+
&types::Context::new(),
23+
&mut iter,
24+
) {
2125
Ok(_) => (iter.n_total_read() + 7) / 8,
2226
Err(_) => match data.first() {
2327
Some(&n) => core::cmp::min(data.len(), n.into()),

src/bit_encoding/decode.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ impl<J: Jet> DagLike for (usize, &'_ [DecodeNode<J>]) {
154154
}
155155

156156
pub fn decode_expression<'brand, I: Iterator<Item = u8>, J: Jet>(
157+
ctx: &types::Context<'brand>,
157158
bits: &mut BitIter<I>,
158159
) -> Result<ArcNode<'brand, J>, Error> {
159160
enum Converted<'brand, J: Jet> {
@@ -173,7 +174,6 @@ pub fn decode_expression<'brand, I: Iterator<Item = u8>, J: Jet>(
173174
let len = bits.read_natural::<usize>(None)?;
174175
assert_ne!(len, 0, "impossible to encode 0 in Simplicity");
175176

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

193193
let new = match nodes[data.node.0] {
194-
DecodeNode::Unit => Node(ArcNode::unit(&inference_context)),
195-
DecodeNode::Iden => Node(ArcNode::iden(&inference_context)),
194+
DecodeNode::Unit => Node(ArcNode::unit(ctx)),
195+
DecodeNode::Iden => Node(ArcNode::iden(ctx)),
196196
DecodeNode::InjL(i) => Node(ArcNode::injl(converted[i].get()?)),
197197
DecodeNode::InjR(i) => Node(ArcNode::injr(converted[i].get()?)),
198198
DecodeNode::Take(i) => Node(ArcNode::take(converted[i].get()?)),
@@ -218,18 +218,16 @@ pub fn decode_expression<'brand, I: Iterator<Item = u8>, J: Jet>(
218218
converted[i].get()?,
219219
&Some(Arc::clone(converted[j].get()?)),
220220
)?),
221-
DecodeNode::Witness => Node(ArcNode::witness(&inference_context, None)),
222-
DecodeNode::Fail(entropy) => Node(ArcNode::fail(&inference_context, entropy)),
221+
DecodeNode::Witness => Node(ArcNode::witness(ctx, None)),
222+
DecodeNode::Fail(entropy) => Node(ArcNode::fail(ctx, entropy)),
223223
DecodeNode::Hidden(cmr) => {
224224
if !hidden_set.insert(cmr) {
225225
return Err(Error::SharingNotMaximal);
226226
}
227227
Hidden(cmr)
228228
}
229-
DecodeNode::Jet(j) => Node(ArcNode::jet(&inference_context, j)),
230-
DecodeNode::Word(ref w) => {
231-
Node(ArcNode::const_word(&inference_context, w.shallow_clone()))
232-
}
229+
DecodeNode::Jet(j) => Node(ArcNode::jet(ctx, j)),
230+
DecodeNode::Word(ref w) => Node(ArcNode::const_word(ctx, w.shallow_clone())),
233231
};
234232
converted.push(new);
235233
}
@@ -318,7 +316,8 @@ mod tests {
318316
let justjet = [0x6d, 0xb8, 0x80];
319317
// Should be able to decode this as an expression...
320318
let mut iter = BitIter::from(&justjet[..]);
321-
decode_expression::<_, Core>(&mut iter).unwrap();
319+
let ctx = types::Context::new();
320+
decode_expression::<_, Core>(&ctx, &mut iter).unwrap();
322321
// ...but NOT as a CommitNode
323322
let iter = BitIter::from(&justjet[..]);
324323
CommitNode::<Core>::decode(iter).unwrap_err();

src/node/commit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ impl<J: Jet> CommitNode<J> {
252252
use crate::decode;
253253

254254
// 1. Decode program with out witnesses.
255-
let construct = crate::ConstructNode::decode(bits).map_err(DecodeError::Decode)?;
255+
let ctx = types::Context::new();
256+
let construct = crate::ConstructNode::decode(&ctx, bits).map_err(DecodeError::Decode)?;
256257
let program = construct.finalize_types().map_err(DecodeError::Type)?;
257258
// 2. Do sharing check, using incomplete IHRs
258259
if program.as_ref().is_shared_as::<MaxSharing<Commit<J>>>() {

src/node/construct.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,24 +230,28 @@ impl<'brand, J: Jet> ConstructNode<'brand, J> {
230230
///
231231
/// If the serialization contains the witness data, then use [`crate::RedeemNode::decode()`].
232232
pub fn decode<I: Iterator<Item = u8>>(
233+
context: &types::Context<'brand>,
233234
mut bits: BitIter<I>,
234235
) -> Result<Arc<Self>, crate::decode::Error> {
235-
let res = crate::decode::decode_expression(&mut bits)?;
236+
let res = crate::decode::decode_expression(context, &mut bits)?;
236237
bits.close()?;
237238
Ok(res)
238239
}
239240

240241
#[cfg(feature = "base64")]
241-
#[allow(clippy::should_implement_trait)] // returns Arc<Self>
242-
pub fn from_str(s: &str) -> Result<Arc<Self>, crate::ParseError> {
242+
#[allow(clippy::should_implement_trait)] // returns Arc<Self>, needs tyctx
243+
pub fn from_str(
244+
context: &types::Context<'brand>,
245+
s: &str,
246+
) -> Result<Arc<Self>, crate::ParseError> {
243247
use crate::base64::engine::general_purpose;
244248
use crate::base64::Engine as _;
245249

246250
let v = general_purpose::STANDARD
247251
.decode(s)
248252
.map_err(crate::ParseError::Base64)?;
249253
let iter = crate::BitIter::new(v.into_iter());
250-
Self::decode(iter)
254+
Self::decode(context, iter)
251255
.map_err(crate::DecodeError::Decode)
252256
.map_err(crate::ParseError::Decode)
253257
}

src/node/redeem.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,8 @@ impl<J: Jet> RedeemNode<J> {
500500
}
501501

502502
// 1. Decode program without witnesses as ConstructNode
503-
let construct = crate::ConstructNode::decode(program).map_err(DecodeError::Decode)?;
503+
let ctx = types::Context::new();
504+
let construct = crate::ConstructNode::decode(&ctx, program).map_err(DecodeError::Decode)?;
504505
construct
505506
.set_arrow_to_program()
506507
.map_err(DecodeError::Type)?;

0 commit comments

Comments
 (0)