Skip to content
Open
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
19 changes: 19 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,22 @@ jobs:
echo "fuzzing $target"
cargo fuzz run --fuzz-dir fuzz "$target" -- -runs=1
done

ExternalJetLib:
name: External Jet Library
runs-on: ubuntu-latest
steps:
- name: "Checkout repo"
uses: actions/checkout@v4

- name: "Setup cargo-rbmt"
uses: ./.github/actions/setup-rbmt

- name: "Build external jet dylib"
run: cargo rbmt --lock-file existing run --toolchain stable -- build --manifest-path external-jet-lib-example/Cargo.toml --lib

- name: "Build external-lib-consumer binary"
run: cargo rbmt --lock-file existing run --toolchain stable -- build --manifest-path external-jet-lib-example/Cargo.toml --bin external-lib-consumer

- name: "Run external-lib-consumer with compiled dylib"
run: ./target/debug/external-lib-consumer ./target/debug/libexternal_jet_lib_example.so
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ required-features = ["docs", "serde"]
default = [ "serde" ]
serde = ["dep:serde", "dep:serde_json"]
docs = []
external-jets = []

[dependencies]
base64 = "0.21.2"
Expand All @@ -48,7 +49,7 @@ chumsky = "0.11.2"
getrandom = { version = "0.2", features = ["js"] }

[workspace]
members = ["codegen", "fuzz"]
members = ["codegen", "fuzz", "external-jet-lib-example"]
exclude = ["bitcoind-tests"]

[workspace.metadata.rbmt.toolchains]
Expand Down
15 changes: 15 additions & 0 deletions external-jet-lib-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "external-jet-lib-example"
version = "0.1.0"
edition = "2021"
rust-version = "1.79.0"

[[bin]]
name = "external-lib-consumer"
path = "src/main.rs"

[lib]
crate-type = ["cdylib"]

[dependencies]
simplicityhl = { path = "..", features = ["external-jets"] }
57 changes: 57 additions & 0 deletions external-jet-lib-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# External Jet Loading

SimplicityHL supports loading custom jet sets at runtime from a shared library (`.so` / `.dylib` / `.dll`). This crate is an end-to-end example of how to build such a library and consume it.

## How It Works

### 1. Enable the feature flag

The external jet machinery lives behind the `external-jets` Cargo feature in the main `simplicityhl` crate. Both the library and its consumer must opt in:

```toml
[dependencies]
simplicityhl = { path = "..", features = ["external-jets"] }
```

### 2. Build a `cdylib` that exports the required symbols

See [src/lib.rs](src/lib.rs) and [src/jet.rs](src/jet.rs) for a minimal implementation with a single `verify` jet.

### 3. Initialize the library at program startup

Before compiling any Simplicity programs, call `init_external_jet_lib` with the path to the compiled `.so` / `.dylib` / `.dll`:

```rust
unsafe {
simplicityhl::jet::external::init_external_jet_lib("/path/to/libexternal_jet_lib_example.dylib")
.expect("failed to load external jet lib");
}
```

### 4. Pass `ExternalJetHinter` to the compiler

`ExternalJetHinter` implements `JetHinter` and delegates `parse_jet` / `construct_verify` to the loaded library. Pass it when constructing a `TemplateProgram`:

```rust
use simplicityhl::{jet::external::ExternalJetHinter, TemplateProgram};

let program = TemplateProgram::new(simf_code, Box::new(ExternalJetHinter::new()))
.expect("compilation failed");
```

The compiler will call `parse_jet` whenever it encounters an unknown jet name, forwarding the lookup to your shared library.

## Building the Example

```sh
# Build the shared library
cargo build -p external-jet-lib-example

# Run the consumer binary, pointing it at the compiled dylib
cargo run -p external-jet-lib-example --bin external-lib-consumer -- \
target/debug/libexternal_jet_lib_example.dylib
```

## Security Notes

Loading a shared library executes arbitrary native code in the current process. Only load libraries from trusted, verified sources.
Loading
Loading