From c8e256566a1cbfd3add8840b19673377c68167d9 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Sat, 1 Mar 2025 14:04:44 +0100 Subject: [PATCH 1/8] chore: Bump MSRV to 1.78.0 --- Cargo.toml | 2 +- README.md | 2 +- clippy.toml | 2 +- codegen/Cargo.toml | 1 - flake.nix | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f990002..1a1635e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/BlockstreamResearch/simfony/" repository = "https://github.com/BlockstreamResearch/simfony/" description = "Rust-like language that compiles to Simplicity bytecode." edition = "2021" -rust-version = "1.63.0" +rust-version = "1.78.0" [lib] name = "simfony" diff --git a/README.md b/README.md index b0a1b32..6cafce2 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Take a look at the [example programs](https://github.com/BlockstreamResearch/sim ## MSRV -This crate should compile with any feature combination on **Rust 1.63.0** or higher. +This crate should compile with any feature combination on **Rust 1.78.0** or higher. ## Simplicity's need for a high-level language diff --git a/clippy.toml b/clippy.toml index b3c3a24..a93e948 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.63.0" +msrv = "1.78.0" diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 28778e9..6d2bd4d 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -2,7 +2,6 @@ name = "codegen" version = "0.1.0" edition = "2021" -rust-version = "1.63.0" description = "Generator of Rust code as interface between Simfony and Rust." publish = false diff --git a/flake.nix b/flake.nix index b7b46fb..10dd638 100644 --- a/flake.nix +++ b/flake.nix @@ -68,7 +68,7 @@ }; msrv = pkgs.mkShell { buildInputs = [ - (mkRust "stable" "1.63.0" "minimal" [] []) + (mkRust "stable" "1.78.0" "minimal" [] []) pkgs.just ]; }; From b3565e12dd0f1c0c2fc6080655adc06f5afbd031 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Sat, 1 Mar 2025 14:38:57 +0100 Subject: [PATCH 2/8] chore: Update rust-simplicity --- Cargo.lock | 8 +++---- Cargo.toml | 2 +- bitcoind-tests/Cargo.lock | 8 +++---- src/lib.rs | 3 ++- src/named.rs | 8 +++---- src/value.rs | 47 ++++++++++++++++++++------------------- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d093f8e..fff3c5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -551,8 +551,8 @@ dependencies = [ [[package]] name = "simplicity-lang" -version = "0.3.0" -source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=ca0c0ebee295937ab021ad018acc44a5aaa12649#ca0c0ebee295937ab021ad018acc44a5aaa12649" +version = "0.4.0" +source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=2f52e22483079ace09e76754222220b5c230a9d0#2f52e22483079ace09e76754222220b5c230a9d0" dependencies = [ "bitcoin", "bitcoin_hashes", @@ -567,8 +567,8 @@ dependencies = [ [[package]] name = "simplicity-sys" -version = "0.3.0" -source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=ca0c0ebee295937ab021ad018acc44a5aaa12649#ca0c0ebee295937ab021ad018acc44a5aaa12649" +version = "0.4.0" +source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=2f52e22483079ace09e76754222220b5c230a9d0#2f52e22483079ace09e76754222220b5c230a9d0" dependencies = [ "bitcoin_hashes", "cc", diff --git a/Cargo.toml b/Cargo.toml index 1a1635e..81f978b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ pest = "2.1.3" pest_derive = "2.7.1" serde = { version = "1.0.188", features = ["derive"], optional = true } serde_json = { version = "1.0.105", optional = true } -simplicity-lang = { git = "https://github.com/BlockstreamResearch/rust-simplicity", rev = "ca0c0ebee295937ab021ad018acc44a5aaa12649" } +simplicity-lang = { git = "https://github.com/BlockstreamResearch/rust-simplicity", rev = "2f52e22483079ace09e76754222220b5c230a9d0" } miniscript = "12.2.0" either = "1.12.0" itertools = "0.13.0" diff --git a/bitcoind-tests/Cargo.lock b/bitcoind-tests/Cargo.lock index b43c465..a88f2f4 100644 --- a/bitcoind-tests/Cargo.lock +++ b/bitcoind-tests/Cargo.lock @@ -709,8 +709,8 @@ dependencies = [ [[package]] name = "simplicity-lang" -version = "0.3.0" -source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=ca0c0ebee295937ab021ad018acc44a5aaa12649#ca0c0ebee295937ab021ad018acc44a5aaa12649" +version = "0.4.0" +source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=2f52e22483079ace09e76754222220b5c230a9d0#2f52e22483079ace09e76754222220b5c230a9d0" dependencies = [ "bitcoin", "bitcoin_hashes", @@ -725,8 +725,8 @@ dependencies = [ [[package]] name = "simplicity-sys" -version = "0.3.0" -source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=ca0c0ebee295937ab021ad018acc44a5aaa12649#ca0c0ebee295937ab021ad018acc44a5aaa12649" +version = "0.4.0" +source = "git+https://github.com/BlockstreamResearch/rust-simplicity?rev=2f52e22483079ace09e76754222220b5c230a9d0#2f52e22483079ace09e76754222220b5c230a9d0" dependencies = [ "bitcoin_hashes", "cc", diff --git a/src/lib.rs b/src/lib.rs index c06c864..4dee416 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,7 +392,8 @@ mod tests { } fn run(self) -> Result<(), simplicity::bit_machine::ExecutionError> { - let mut mac = BitMachine::for_program(self.program.redeem()); + let mut mac = BitMachine::for_program(self.program.redeem()) + .expect("program should be within reasonable bounds"); let env = dummy_env::dummy_with(self.lock_time, self.sequence, self.include_fee_output); mac.exec(self.program.redeem(), &env).map(|_| ()) } diff --git a/src/named.rs b/src/named.rs index d96e9aa..1a028d5 100644 --- a/src/named.rs +++ b/src/named.rs @@ -3,12 +3,12 @@ use std::sync::Arc; use simplicity::dag::{InternalSharing, PostOrderIterItem}; use simplicity::jet::{Elements, Jet}; use simplicity::node::{ - self, CommitData, Constructible, Converter, CoreConstructible, Inner, JetConstructible, - NoDisconnect, NoWitness, Node, WitnessConstructible, WitnessData, + self, CommitData, ConstructData as WitnessData, Constructible, Converter, CoreConstructible, + Inner, JetConstructible, NoDisconnect, NoWitness, Node, WitnessConstructible, }; use simplicity::types::arrow::Arrow; use simplicity::{types, CommitNode, FailEntropy}; -use simplicity::{Cmr, WitnessNode}; +use simplicity::{Cmr, ConstructNode as WitnessNode}; use crate::str::WitnessName; use crate::value::StructuralValue; @@ -105,7 +105,7 @@ pub fn to_witness_node(node: &ConstructNode, values: WitnessValues) -> Arc Converter, node::Witness> for Populator { + impl Converter, node::Construct> for Populator { type Error = (); fn convert_witness( diff --git a/src/value.rs b/src/value.rs index b429238..5b16550 100644 --- a/src/value.rs +++ b/src/value.rs @@ -5,7 +5,7 @@ use either::Either; use hex_conservative::DisplayHex; use miniscript::iter::{Tree, TreeLike}; use simplicity::types::Final as SimType; -use simplicity::{BitCollector, Value as SimValue}; +use simplicity::{BitCollector, Value as SimValue, ValueRef}; use crate::array::{BTreeSlice, Combiner, Partition, Unfolder}; use crate::error::{Error, RichError, WithSpan}; @@ -730,11 +730,11 @@ impl Value { pub fn reconstruct(value: &StructuralValue, ty: &ResolvedType) -> Option { let mut output = vec![]; for data in value.destruct(ty).post_order_iter() { + let size = data.node.n_children(); let (value, ty) = match data.node { Destructor::Ok { value, ty } => (value, ty), Destructor::WrongType => return None, }; - let size = data.node.n_children(); match ty.as_inner() { TypeInner::Boolean => { let bit = destruct::as_bit(value)?; @@ -877,10 +877,10 @@ impl TreeLike for StructuralValue { fn as_node(&self) -> Tree { use simplicity::dag::{Dag, DagLike}; - match (&self.0).as_dag_node() { + match self.0.as_ref().as_dag_node() { Dag::Nullary => Tree::Nullary, - Dag::Unary(l) => Tree::Unary(Self(l.shallow_clone())), - Dag::Binary(l, r) => Tree::Binary(Self(l.shallow_clone()), Self(r.shallow_clone())), + Dag::Unary(l) => Tree::Unary(Self(l.to_value())), + Dag::Binary(l, r) => Tree::Binary(Self(l.to_value()), Self(r.to_value())), } } } @@ -1059,7 +1059,7 @@ impl StructuralValue { fn destruct<'a>(&'a self, ty: &'a ResolvedType) -> Destructor<'a> { Destructor::Ok { - value: self.as_ref(), + value: self.0.as_ref(), ty, } } @@ -1091,10 +1091,10 @@ impl StructuralValue { /// The leaf values (Boolean, unsigned integer, empty tuple, empty array) are not checked. /// Extraction of actual Simfony values (Boolean, unsigned integer, ...) /// from the leaf Simplicity values may fail, in which case the entire tree is, again, ill-typed. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] enum Destructor<'a> { Ok { - value: &'a SimValue, + value: ValueRef<'a>, ty: &'a ResolvedType, }, WrongType, @@ -1102,11 +1102,11 @@ enum Destructor<'a> { impl<'a> Destructor<'a> { /// Create a destructor for the given Simplicity `value` and the given Simfony type. - pub const fn new(value: &'a SimValue, ty: &'a ResolvedType) -> Self { + pub const fn new(value: ValueRef<'a>, ty: &'a ResolvedType) -> Self { Self::Ok { value, ty } } - const fn new_pair((value, ty): (&'a SimValue, &'a ResolvedType)) -> Self { + const fn new_pair((value, ty): (ValueRef<'a>, &'a ResolvedType)) -> Self { Self::Ok { value, ty } } } @@ -1114,7 +1114,7 @@ impl<'a> Destructor<'a> { impl TreeLike for Destructor<'_> { fn as_node(&self) -> Tree { let (value, ty) = match self { - Self::Ok { value, ty } => (value, ty), + Self::Ok { value, ty } => (value.clone(), ty), Self::WrongType => return Tree::Nullary, }; match ty.as_inner() { @@ -1166,8 +1166,9 @@ impl TreeLike for Destructor<'_> { /// Functions for destructing Simplicity values alongside Simfony types. mod destruct { use super::*; + use simplicity::ValueRef; - pub fn as_bit(value: &SimValue) -> Option { + pub fn as_bit(value: ValueRef) -> Option { match value.as_left() { Some(unit) if unit.is_unit() => Some(false), _ => match value.as_right() { @@ -1177,10 +1178,10 @@ mod destruct { } } - pub fn as_integer(value: &SimValue, ty: UIntType) -> Option { + pub fn as_integer(value: ValueRef, ty: UIntType) -> Option { let bit_len = ty.bit_width().get(); let unfolder = Unfolder::new(value, bit_len); - let bit_values = unfolder.unfold(SimValue::as_product)?; + let bit_values = unfolder.unfold(|v| v.as_product())?; let (bytes, written_bits) = bit_values.into_iter().filter_map(as_bit).collect_bits(); if bit_len != written_bits { return None; @@ -1196,31 +1197,31 @@ mod destruct { } } - pub fn as_tuple(value: &SimValue, size: usize) -> Option> { - Unfolder::new(value, size).unfold(SimValue::as_product) + pub fn as_tuple(value: ValueRef, size: usize) -> Option> { + Unfolder::new(value, size).unfold(|v| v.as_product()) } - pub fn as_array(value: &SimValue, size: usize) -> Option> { - Unfolder::new(value, size).unfold(SimValue::as_product) + pub fn as_array(value: ValueRef, size: usize) -> Option> { + Unfolder::new(value, size).unfold(|v| v.as_product()) } - pub fn as_list<'a>(value: &'a SimValue, bound: NonZeroPow2Usize) -> Option> { - let as_block = |value: &'a SimValue, size: usize| match as_option(value) { + pub fn as_list<'a>(value: ValueRef<'a>, bound: NonZeroPow2Usize) -> Option>> { + let as_block = |value: ValueRef<'a>, size: usize| match as_option(value) { Some(Some(folded)) => as_array(folded, size), Some(None) => Some(vec![]), None => None, }; - Combiner::new(value, bound).unfold(as_block, SimValue::as_product) + Combiner::new(value, bound).unfold(as_block, |v| v.as_product()) } - pub fn as_either(value: &SimValue) -> Option> { + pub fn as_either(value: ValueRef) -> Option> { match value.as_left() { Some(inner) => Some(Either::Left(inner)), None => value.as_right().map(Either::Right), } } - pub fn as_option(value: &SimValue) -> Option> { + pub fn as_option(value: ValueRef) -> Option> { match value.as_left() { Some(inner) if inner.is_unit() => Some(None), _ => value.as_right().map(Some), From 854db1aa3fbfc104c824a35239e1690aae31523d Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Thu, 20 Mar 2025 13:52:21 +0100 Subject: [PATCH 3/8] chore: Use bundled hex crate --- Cargo.lock | 1 - Cargo.toml | 1 - bitcoind-tests/Cargo.lock | 1 - src/value.rs | 4 ++-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fff3c5f..07b77eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -539,7 +539,6 @@ dependencies = [ "base64", "either", "getrandom", - "hex-conservative 0.1.1", "itertools", "miniscript", "pest", diff --git a/Cargo.toml b/Cargo.toml index 81f978b..e6badb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,6 @@ serde = ["dep:serde", "dep:serde_json"] [dependencies] base64 = "0.21.2" -hex-conservative = "0.1.1" pest = "2.1.3" pest_derive = "2.7.1" serde = { version = "1.0.188", features = ["derive"], optional = true } diff --git a/bitcoind-tests/Cargo.lock b/bitcoind-tests/Cargo.lock index a88f2f4..7bd88d3 100644 --- a/bitcoind-tests/Cargo.lock +++ b/bitcoind-tests/Cargo.lock @@ -699,7 +699,6 @@ dependencies = [ "base64 0.21.2", "either", "getrandom", - "hex-conservative 0.1.1", "itertools", "miniscript", "pest", diff --git a/src/value.rs b/src/value.rs index 5b16550..276002e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -2,7 +2,7 @@ use std::fmt; use std::sync::Arc; use either::Either; -use hex_conservative::DisplayHex; +use miniscript::bitcoin::hex::DisplayHex; use miniscript::iter::{Tree, TreeLike}; use simplicity::types::Final as SimType; use simplicity::{BitCollector, Value as SimValue, ValueRef}; @@ -623,7 +623,7 @@ impl Value { /// Create a value from the given `hexadecimal` string and type. pub fn parse_hexadecimal(hexadecimal: &Hexadecimal, ty: &ResolvedType) -> Result { - use hex_conservative::FromHex; + use miniscript::bitcoin::hex::FromHex; let expected_byte_len = match ty.as_inner() { TypeInner::UInt(int) => int.byte_width(), From 826351c9a20ad34b2d7f650a0d917050ef140fe5 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Thu, 20 Mar 2025 15:17:35 +0100 Subject: [PATCH 4/8] chore: Keep elements::Locktime prefix --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4dee416..13c93d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,8 +249,6 @@ pub trait ArbitraryOfType: Sized { mod tests { use base64::display::Base64Display; use base64::engine::general_purpose::STANDARD; - #[cfg(feature = "serde")] - use elements::LockTime; use simplicity::BitMachine; use std::borrow::Cow; use std::path::Path; @@ -426,7 +424,7 @@ mod tests { let mut t = TestCase::program_file("./examples/non_interactive_fee_bump.simf") .with_witness_file("./examples/non_interactive_fee_bump.wit"); t.sequence = elements::Sequence::ENABLE_LOCKTIME_NO_RBF; - t.lock_time = LockTime::from_time(1734967235 + 600).unwrap(); + t.lock_time = elements::LockTime::from_time(1734967235 + 600).unwrap(); t.include_fee_output = true; t.assert_run_success(); } From c4832fe2a999acd85fa203b14bf82182107da42e Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Sat, 1 Mar 2025 14:45:42 +0100 Subject: [PATCH 5/8] fix: Prune test programs --- src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 13c93d6..7185c83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -390,10 +390,11 @@ mod tests { } fn run(self) -> Result<(), simplicity::bit_machine::ExecutionError> { - let mut mac = BitMachine::for_program(self.program.redeem()) - .expect("program should be within reasonable bounds"); let env = dummy_env::dummy_with(self.lock_time, self.sequence, self.include_fee_output); - mac.exec(self.program.redeem(), &env).map(|_| ()) + let pruned = self.program.redeem().prune(&env)?; + let mut mac = BitMachine::for_program(&pruned) + .expect("program should be within reasonable bounds"); + mac.exec(&pruned, &env).map(|_| ()) } pub fn assert_run_success(self) { From 0dd62d25ba07951cfedeff6620cc2ddaff578c84 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Sat, 22 Mar 2025 17:09:44 +0100 Subject: [PATCH 6/8] feat: Support Apple silicon in flake --- flake.nix | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 10dd638..c6e1b90 100644 --- a/flake.nix +++ b/flake.nix @@ -14,11 +14,8 @@ , rust-overlay , ... }: - flake-utils.lib.eachSystem [ - "x86_64-linux" - "aarch64-linux" - "x86_64-darwin" - ] (system: + flake-utils.lib.eachDefaultSystem + (system: let overlays = [ (import rust-overlay) @@ -35,15 +32,17 @@ CC_wasm32_unknown_unknown = "${pkgs.llvmPackages_16.clang-unwrapped}/bin/clang-16"; AR_wasm32_unknown_unknown = "${pkgs.llvmPackages_16.libllvm}/bin/llvm-ar"; CFLAGS_wasm32_unknown_unknown = "-I ${pkgs.llvmPackages_16.libclang.lib}/lib/clang/16/include/"; + gdbSupported = !(pkgs.stdenv.isDarwin && pkgs.stdenv.isAarch64); default_shell = with_elements: pkgs.mkShell { buildInputs = [ defaultRust pkgs.just - pkgs.gdb pkgs.cargo-hack pkgs.mdbook ] ++ ( if with_elements then [ elementsd-simplicity ] else [] + ) ++ ( + if gdbSupported then [ pkgs.gdb ] else [] ); inherit CC_wasm32_unknown_unknown; inherit AR_wasm32_unknown_unknown; @@ -51,7 +50,6 @@ # Constants for IDE RUST_TOOLCHAIN = "${defaultRust}/bin"; RUST_STDLIB = "${defaultRust}/lib/rustlib/src/rust"; - DEBUGGER = "${pkgs.gdb}"; }; in { From fb4a1d3113badd2ff0a2b8f3e70f0adcfb0b1e75 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Thu, 20 Mar 2025 13:31:51 +0100 Subject: [PATCH 7/8] test: Check type error See https://github.com/BlockstreamResearch/simfony/issues/114 --- src/lib.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7185c83..39240cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -613,8 +613,22 @@ fn main() { fn main() { let x: MyAlias = 32; assert!(jet::eq_32(x, 32)); -} -"#; +}"#; + TestCase::program_text(Cow::Borrowed(prog_text)) + .with_witness_values(WitnessValues::default()) + .assert_run_success(); + } + + #[test] + fn type_error_regression() { + let prog_text = r#"fn main() { + let (a, b): (u32, u32) = (0, 1); + assert!(jet::eq_32(a, 0)); + + let (c, d): (u32, u32) = (2, 3); + assert!(jet::eq_32(c, 2)); + assert!(jet::eq_32(d, 3)); +}"#; TestCase::program_text(Cow::Borrowed(prog_text)) .with_witness_values(WitnessValues::default()) .assert_run_success(); From 0a974f1a8200e685cd6277202e7c208d596a5a64 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Sat, 1 Mar 2025 14:39:15 +0100 Subject: [PATCH 8/8] ci: Run tests on other platforms Rust-simplicity supports MacOS and Windows since the latest updates. This commit enables CI for MacOS. I would enable Windows as well, but the install-nix-action doesn't support it. --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 440c76e..5652d90 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,10 @@ jobs: test-stable: name: Test - stable toolchain - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] steps: - name: Checkout