Skip to content

Commit 7949187

Browse files
committed
Merge BlockstreamResearch#244: Typed value
5a5a4e2 refactor: Value constructors (Christian Lewe) a3fe129 refactor: Type-check Simplicity values (Christian Lewe) d58710a feat: Add word type constructors (Christian Lewe) 5483fec feat: Compute padding (Christian Lewe) d4e78a7 feat: Add BitCollector (Christian Lewe) 0be5b02 fix: Remove iterations counter (Christian Lewe) Pull request description: We need to encode values with padding on the Bit Machine. This encoding requires information about the type of the value. It turns out that universally typing all Simplicity values produces the smallest diff, and it is the safest API. This PR refactors the `Value` struct to include type information. I also have ideas for a `Word` struct that wraps values of the word type. Only these values are safe to use in word jets. I will leave this for a follow-up PR. ACKs for top commit: apoelstra: ACK 5a5a4e2 successfully ran local tests; nice! And does the API changes needed to later replace the `Value` internal representation with a flat bit array, which should greatly speed things up Tree-SHA512: 17343a5642946d486533b67e083f5b5dc891de9154dd492c479e7f03a031e6e2e2d4eda95a3d424e990647e0bf74dc885308d7ac9374afc30777c0e27b6156ca
2 parents f6e7ecf + 5a5a4e2 commit 7949187

26 files changed

+519
-396
lines changed

jets-bench/benches/elements/main.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use simplicity::elements;
77
use simplicity::jet::elements::ElementsEnv;
88
use simplicity::jet::{Elements, Jet};
99
use simplicity::types;
10+
use simplicity::types::Final;
1011
use simplicity::Value;
1112
use simplicity_bench::input::{
1213
self, EqProduct, GenericProduct, InputSample, PrefixBit, Sha256Ctx, UniformBits,
@@ -751,36 +752,36 @@ fn bench(c: &mut Criterion) {
751752
}
752753

753754
// Input to outpoint hash jet
754-
fn outpoint_hash() -> Arc<Value> {
755+
fn outpoint_hash() -> Value {
755756
let ctx8 = SimplicityCtx8::with_len(511).value();
756757
let genesis_pegin = genesis_pegin();
757758
let outpoint = elements::OutPoint::sample().value();
758759
Value::product(ctx8, Value::product(genesis_pegin, outpoint))
759760
}
760761

761-
fn asset_amount_hash() -> Arc<Value> {
762+
fn asset_amount_hash() -> Value {
762763
let ctx8 = SimplicityCtx8::with_len(511).value();
763764
let asset = confidential::Asset::sample().value();
764765
let amount = confidential::Value::sample().value();
765766
Value::product(ctx8, Value::product(asset, amount))
766767
}
767768

768-
fn nonce_hash() -> Arc<Value> {
769+
fn nonce_hash() -> Value {
769770
let ctx8 = SimplicityCtx8::with_len(511).value();
770771
let nonce = confidential::Nonce::sample().value();
771772
Value::product(ctx8, nonce)
772773
}
773774

774-
fn annex_hash() -> Arc<Value> {
775+
fn annex_hash() -> Value {
775776
let ctx8 = SimplicityCtx8::with_len(511).value();
776777
let annex = if rand::random() {
777-
Value::right(Value::u256(rand::random::<[u8; 32]>()))
778+
Value::some(Value::u256(rand::random::<[u8; 32]>()))
778779
} else {
779-
Value::left(Value::unit())
780+
Value::none(Final::u256())
780781
};
781782
Value::product(ctx8, annex)
782783
}
783-
let arr: [(Elements, Arc<dyn Fn() -> Arc<Value>>); 4] = [
784+
let arr: [(Elements, Arc<dyn Fn() -> Value>); 4] = [
784785
(Elements::OutpointHash, Arc::new(&outpoint_hash)),
785786
(Elements::AssetAmountHash, Arc::new(&asset_amount_hash)),
786787
(Elements::NonceHash, Arc::new(nonce_hash)),
@@ -814,7 +815,7 @@ fn bench(c: &mut Criterion) {
814815
}
815816

816817
// Operations that use tx input or output index.
817-
fn index_value(bound: u32) -> Arc<Value> {
818+
fn index_value(bound: u32) -> Value {
818819
let v = rand::random::<u32>() % bound;
819820
Value::u32(v)
820821
}

jets-bench/src/data_structures.rs

+33-30
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ use simplicity::{
88
bitcoin, elements,
99
hashes::Hash,
1010
hex::FromHex,
11-
types::{self, Type},
11+
types::Final,
1212
BitIter, Error, Value,
1313
};
14-
use std::sync::Arc;
1514

1615
/// Engine to compute SHA256 hash function.
1716
/// We can't use hashes::sha256::HashEngine because it does not accept
@@ -55,21 +54,19 @@ impl SimplicityCtx8 {
5554
/// # Panics:
5655
///
5756
/// Panics if the length of the slice is >= 2^(n + 1) bytes
58-
pub fn var_len_buf_from_slice(v: &[u8], mut n: usize) -> Result<Arc<Value>, Error> {
57+
pub fn var_len_buf_from_slice(v: &[u8], mut n: usize) -> Result<Value, Error> {
5958
// Simplicity consensus rule for n < 16 while reading buffers.
6059
assert!(n < 16);
6160
assert!(v.len() < (1 << (n + 1)));
6261
let mut iter = BitIter::new(v.iter().copied());
63-
let ctx = types::Context::new();
64-
let types = Type::powers_of_two(&ctx, n); // size n + 1
6562
let mut res = None;
6663
while n > 0 {
64+
let ty = Final::two_two_n(n);
6765
let v = if v.len() >= (1 << (n + 1)) {
68-
let ty = &types[n];
69-
let val = iter.read_value(&ty.final_data().unwrap())?;
70-
Value::right(val)
66+
let val = iter.read_value(&ty)?;
67+
Value::some(val)
7168
} else {
72-
Value::left(Value::unit())
69+
Value::none(ty)
7370
};
7471
res = match res {
7572
Some(prod) => Some(Value::product(prod, v)),
@@ -155,11 +152,11 @@ pub struct SimplicityPoint(pub bitcoin::secp256k1::PublicKey);
155152
/// Trait defining how to encode a data structure into a Simplicity value
156153
/// This is then used to write these vales into the bit machine.
157154
pub trait SimplicityEncode {
158-
fn value(&self) -> Arc<Value>;
155+
fn value(&self) -> Value;
159156
}
160157

161158
impl SimplicityEncode for SimplicityCtx8 {
162-
fn value(&self) -> Arc<Value> {
159+
fn value(&self) -> Value {
163160
let buf_len = self.length % 512;
164161
let buf = var_len_buf_from_slice(&self.buffer[..buf_len], 8).unwrap();
165162
let len = Value::u64(self.length as u64);
@@ -174,74 +171,80 @@ impl SimplicityEncode for SimplicityCtx8 {
174171
}
175172

176173
impl SimplicityEncode for elements::OutPoint {
177-
fn value(&self) -> Arc<Value> {
174+
fn value(&self) -> Value {
178175
let txid = Value::u256(self.txid.to_byte_array());
179176
let vout = Value::u32(self.vout);
180177
Value::product(txid, vout)
181178
}
182179
}
183180

184181
impl SimplicityEncode for elements::confidential::Asset {
185-
fn value(&self) -> Arc<Value> {
182+
fn value(&self) -> Value {
186183
match self {
187184
elements::confidential::Asset::Explicit(a) => {
188-
Value::right(Value::u256(a.into_inner().to_byte_array()))
185+
let left = Final::product(Final::u1(), Final::u256());
186+
Value::right(left, Value::u256(a.into_inner().to_byte_array()))
189187
}
190188
elements::confidential::Asset::Confidential(gen) => {
191189
let ser = gen.serialize();
192190
let odd_gen = ser[0] & 1 == 1;
193191
let x_bytes = (&ser[1..33]).try_into().unwrap();
194192
let x_pt = Value::u256(x_bytes);
195193
let y_pt = Value::u1(odd_gen as u8);
196-
Value::left(Value::product(y_pt, x_pt))
194+
Value::left(Value::product(y_pt, x_pt), Final::u256())
197195
}
198196
elements::confidential::Asset::Null => panic!("Tried to encode Null asset"),
199197
}
200198
}
201199
}
202200

203201
impl SimplicityEncode for elements::confidential::Value {
204-
fn value(&self) -> Arc<Value> {
202+
fn value(&self) -> Value {
205203
match self {
206-
elements::confidential::Value::Explicit(v) => Value::right(Value::u64(*v)),
204+
elements::confidential::Value::Explicit(v) => {
205+
let left = Final::product(Final::u1(), Final::u256());
206+
Value::right(left, Value::u64(*v))
207+
},
207208
elements::confidential::Value::Confidential(v) => {
208209
let ser = v.serialize();
209210
let x_bytes = (&ser[1..33]).try_into().unwrap();
210211
let x_pt = Value::u256(x_bytes);
211212
let y_pt = Value::u1((ser[0] & 1 == 1) as u8);
212-
Value::left(Value::product(y_pt, x_pt))
213+
Value::left(Value::product(y_pt, x_pt), Final::u64())
213214
}
214215
elements::confidential::Value::Null => panic!("Tried to encode Null value"),
215216
}
216217
}
217218
}
218219

219220
impl SimplicityEncode for elements::confidential::Nonce {
220-
fn value(&self) -> Arc<Value> {
221+
fn value(&self) -> Value {
222+
let ty_l = Final::product(Final::u1(), Final::u256());
223+
let ty_r = Final::u256();
221224
match self {
222225
elements::confidential::Nonce::Explicit(n) => {
223-
Value::right(Value::right(Value::u256(*n)))
226+
Value::some(Value::right(ty_l, Value::u256(*n)))
224227
}
225228
elements::confidential::Nonce::Confidential(n) => {
226229
let ser = n.serialize();
227230
let x_bytes = (&ser[1..33]).try_into().unwrap();
228231
let x_pt = Value::u256(x_bytes);
229232
let y_pt = Value::u1((ser[0] & 1 == 1) as u8);
230-
Value::right(Value::left(Value::product(y_pt, x_pt)))
233+
Value::some(Value::left(Value::product(y_pt, x_pt), ty_r))
231234
}
232-
elements::confidential::Nonce::Null => Value::left(Value::unit()),
235+
elements::confidential::Nonce::Null => Value::none(Final::sum(ty_l, ty_r)),
233236
}
234237
}
235238
}
236239

237240
impl SimplicityEncode for SimplicityFe {
238-
fn value(&self) -> Arc<Value> {
241+
fn value(&self) -> Value {
239242
Value::u256(*self.as_inner())
240243
}
241244
}
242245

243246
impl SimplicityEncode for SimplicityGe {
244-
fn value(&self) -> Arc<Value> {
247+
fn value(&self) -> Value {
245248
let ser = match &self {
246249
SimplicityGe::ValidPoint(p) => p.serialize_uncompressed(),
247250
SimplicityGe::InvalidPoint(x, y) => {
@@ -261,21 +264,21 @@ impl SimplicityEncode for SimplicityGe {
261264
}
262265

263266
impl SimplicityEncode for SimplicityGej {
264-
fn value(&self) -> Arc<Value> {
267+
fn value(&self) -> Value {
265268
let ge = self.ge.value();
266269
let z = self.z.value();
267270
Value::product(ge, z)
268271
}
269272
}
270273

271274
impl SimplicityEncode for SimplicityScalar {
272-
fn value(&self) -> Arc<Value> {
275+
fn value(&self) -> Value {
273276
Value::u256(self.0)
274277
}
275278
}
276279

277280
impl SimplicityEncode for SimplicityPoint {
278-
fn value(&self) -> Arc<Value> {
281+
fn value(&self) -> Value {
279282
let ser = self.0.serialize(); // compressed
280283
let x_bytes = (&ser[1..33]).try_into().unwrap();
281284
let y_pt = Value::u1((ser[0] & 1 == 1) as u8);
@@ -407,11 +410,11 @@ impl BenchSample for SimplicityPoint {
407410
}
408411

409412
// Sample genesis pegin with 50% probability
410-
pub fn genesis_pegin() -> Arc<Value> {
413+
pub fn genesis_pegin() -> Value {
411414
if rand::random() {
412-
Value::left(Value::unit())
415+
Value::none(Final::two_two_n(8))
413416
} else {
414417
let genesis_hash = rand::random::<[u8; 32]>();
415-
Value::right(Value::u256(genesis_hash))
418+
Value::some(Value::u256(genesis_hash))
416419
}
417420
}

jets-bench/src/input.rs

+22-16
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ use simplicity::jet::Elements;
1010
use simplicity::types::{self, CompleteBound};
1111
use simplicity::Value;
1212

13-
pub fn random_value(ty: &types::Final, rng: &mut ThreadRng) -> Arc<Value> {
13+
pub fn random_value(ty: &types::Final, rng: &mut ThreadRng) -> Value {
1414
enum StackItem<'a> {
1515
Type(&'a types::Final),
16-
LeftSum,
17-
RightSum,
16+
LeftSum(Arc<types::Final>),
17+
RightSum(Arc<types::Final>),
1818
Product,
1919
}
2020

@@ -27,10 +27,10 @@ pub fn random_value(ty: &types::Final, rng: &mut ThreadRng) -> Arc<Value> {
2727
CompleteBound::Unit => value_stack.push(Value::unit()),
2828
CompleteBound::Sum(left, right) => {
2929
if rng.gen() {
30-
call_stack.push(StackItem::LeftSum);
30+
call_stack.push(StackItem::LeftSum(Arc::clone(right)));
3131
call_stack.push(StackItem::Type(left));
3232
} else {
33-
call_stack.push(StackItem::RightSum);
33+
call_stack.push(StackItem::RightSum(Arc::clone(left)));
3434
call_stack.push(StackItem::Type(right));
3535
}
3636
}
@@ -40,13 +40,13 @@ pub fn random_value(ty: &types::Final, rng: &mut ThreadRng) -> Arc<Value> {
4040
call_stack.push(StackItem::Type(left));
4141
}
4242
},
43-
StackItem::LeftSum => {
43+
StackItem::LeftSum(right) => {
4444
let left = value_stack.pop().unwrap();
45-
value_stack.push(Value::left(left));
45+
value_stack.push(Value::left(left, right));
4646
}
47-
StackItem::RightSum => {
47+
StackItem::RightSum(left) => {
4848
let right = value_stack.pop().unwrap();
49-
value_stack.push(Value::right(right));
49+
value_stack.push(Value::right(left, right));
5050
}
5151
StackItem::Product => {
5252
let right = value_stack.pop().unwrap();
@@ -411,10 +411,10 @@ pub enum InputSampling {
411411
Random,
412412
/// A given, fixed bit string (whose length is multiple of 8)
413413
/// Worst-case inputs
414-
Fixed(Arc<Value>),
414+
Fixed(Value),
415415
/// Custom sampling method, read first src type bits from input
416416
/// Useful for cases where we want to sample inputs according to some distributions
417-
Custom(Arc<dyn Fn() -> Arc<Value>>),
417+
Custom(Arc<dyn Fn() -> Value>),
418418
}
419419

420420
impl InputSampling {
@@ -424,19 +424,25 @@ impl InputSampling {
424424
src_ty: &types::Final,
425425
rng: &mut ThreadRng,
426426
) {
427-
let write_bit = |bit: bool| unsafe { c_writeBit(src_frame, bit) };
427+
let mut write_bit = |bit: bool| unsafe { c_writeBit(src_frame, bit) };
428428

429429
match self {
430430
InputSampling::Random => {
431431
let value = random_value(src_ty, rng);
432-
value.do_each_bit(write_bit);
432+
for bit in value.iter_padded() {
433+
write_bit(bit);
434+
}
433435
}
434-
InputSampling::Fixed(v) => {
435-
v.do_each_bit(write_bit);
436+
InputSampling::Fixed(value) => {
437+
for bit in value.iter_padded() {
438+
write_bit(bit);
439+
}
436440
}
437441
InputSampling::Custom(gen_bytes) => {
438442
let value = gen_bytes();
439-
value.do_each_bit(write_bit);
443+
for bit in value.iter_padded() {
444+
write_bit(bit);
445+
}
440446
}
441447
}
442448
}

src/analysis.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ impl NodeBounds {
317317
NodeBounds {
318318
extra_cells: 0,
319319
extra_frames: 0,
320-
cost: Cost::OVERHEAD + Cost::of_type(value.len()),
320+
cost: Cost::OVERHEAD + Cost::of_type(value.padded_len()),
321321
}
322322
}
323323

0 commit comments

Comments
 (0)