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
536 changes: 83 additions & 453 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = [
]

[workspace.dependencies]
hal-simplicity = "0.2.0"
simplicity-lang = { version = "0.9.0", features = ["base64"] }
clap = { version = "4.5.53", features = ["derive"] }
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ path = "src/main.rs"
anyhow = "1.0"
clap = { workspace = true }
core = { path = "../core" }
hal-simplicity = { workspace = true }
simplicity-lang = { workspace = true }
hex = { workspace = true }
reqwest = { version = "0.11", features = ["blocking", "json"] }
serde = { workspace = true }
Expand Down
12 changes: 7 additions & 5 deletions cli/src/commands/address/multisig.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use anyhow::{Context, Result};
use hal_simplicity::bitcoin::hashes::Hash;
use hal_simplicity::{bitcoin::TapNodeHash, simplicity::elements::AddressParams};
use serde_json::json;
use simplicity::bitcoin::hashes::Hash;
use simplicity::{bitcoin::TapNodeHash, elements::AddressParams};

use hal_simplicity::simplicity::elements::bitcoin::PublicKey;
use hal_simplicity::simplicity::{bitcoin, elements};
use simplicity::elements::bitcoin::PublicKey;
use simplicity::{bitcoin, elements};

use simplicity_unchained_core::utils::{generate_p2tr_address_btc, generate_p2tr_address_elements};

Expand All @@ -29,7 +29,9 @@ pub fn execute(
let output = execute_over_elements(
&pk1,
&pk2,
elements::hashes::sha256::Hash::from_slice(&user_leaf_hash_bytes)?,
elements::hashes::sha256::Hash::from_byte_array(
user_leaf_hash_bytes.as_slice().try_into()?,
),
network,
)?;
println!("{}", serde_json::to_string_pretty(&output)?);
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/btc_tx/create.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{Context, Result, anyhow};

use hal_simplicity::simplicity::bitcoin;
use simplicity::bitcoin;

use bitcoin::{
Address, Amount, Network, OutPoint, Transaction, TxIn, TxOut, psbt::Psbt, script::ScriptBuf,
Expand Down
4 changes: 2 additions & 2 deletions cli/src/commands/btc_tx/finalize.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::{Context, Result, anyhow};
use hal_simplicity::bitcoin::{
use serde_json::json;
use simplicity::bitcoin::{
self, Psbt, TapLeafHash, Transaction, Witness, consensus::serialize, psbt, taproot::LeafVersion,
};
use serde_json::json;
use simplicity_unchained_core::utils::extract_pubkeys_from_p2tr_multisig_leaf_btc;

pub fn execute(psbt_hex: &str) -> Result<()> {
Expand Down
14 changes: 6 additions & 8 deletions cli/src/commands/btc_tx/sign.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use anyhow::{Context, Result};
use hal_simplicity::bitcoin::taproot::{LeafVersion, TaprootBuilder};
use hal_simplicity::bitcoin::{self, TapNodeHash};
use hal_simplicity::simplicity::bitcoin::{
PublicKey, hashes::Hash, psbt::Psbt, sighash::SighashCache,
};
use hal_simplicity::simplicity::elements::secp256k1_zkp::{Message, Secp256k1, SecretKey};
use serde_json::json;
use simplicity::bitcoin::taproot::{LeafVersion, TaprootBuilder};
use simplicity::bitcoin::{self, TapNodeHash};
use simplicity::bitcoin::{PublicKey, hashes::Hash, psbt::Psbt, sighash::SighashCache};
use simplicity::elements::secp256k1_zkp::{Message, Secp256k1, SecretKey};
use simplicity_unchained_core::utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc};

pub fn execute(
Expand Down Expand Up @@ -34,9 +32,9 @@ pub fn execute(

let public_key = PublicKey::from_private_key(
&secp,
&hal_simplicity::simplicity::elements::bitcoin::PrivateKey {
&simplicity::elements::bitcoin::PrivateKey {
compressed: true,
network: hal_simplicity::simplicity::elements::bitcoin::NetworkKind::Main,
network: simplicity::elements::bitcoin::NetworkKind::Main,
inner: secret_key,
},
);
Expand Down
6 changes: 3 additions & 3 deletions cli/src/commands/btc_tx/spend_user_leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use bitcoin::{
sighash::{Prevouts, SighashCache},
taproot::{LeafVersion, Signature as TapSig},
};
use hal_simplicity::bitcoin::base64::Engine;
use hal_simplicity::bitcoin::hashes::Hash;
use hal_simplicity::bitcoin::{self, base64};
use serde_json::json;
use simplicity::bitcoin::base64::Engine;
use simplicity::bitcoin::hashes::Hash;
use simplicity::bitcoin::{self, base64};
use simplicity_unchained_core::utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc};
use std::str::FromStr;

Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/tx/create.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{Context, Result, anyhow};

use hal_simplicity::simplicity::elements;
use simplicity::elements;

use elements::{
Address, AddressParams, AssetId, OutPoint, Transaction, TxIn, TxOut, confidential,
Expand Down
9 changes: 5 additions & 4 deletions cli/src/commands/tx/finalize.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use anyhow::{Context, Result, anyhow};
use hal_simplicity::simplicity::elements::taproot::TapLeafHash;
use hal_simplicity::simplicity::elements::{
use serde_json::json;
use simplicity::elements::taproot::TapLeafHash;
use simplicity::elements::{
Transaction,
encode::{deserialize, serialize},
pset::{self, PartiallySignedTransaction},
};
use serde_json::json;
use simplicity_unchained_core::utils::extract_pubkeys_from_p2tr_multisig_leaf_elements;

pub fn execute(pset_hex: &str) -> Result<()> {
Expand Down Expand Up @@ -94,7 +94,8 @@ fn finalize_p2tr(tx: &mut Transaction, i: usize, input: &pset::Input) -> Result<
cosigner_sig.to_vec(),
leaf_script.to_bytes(),
control_block.serialize(),
];
]
.into();

Ok(())
}
39 changes: 19 additions & 20 deletions cli/src/commands/tx/sign.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
use std::str::FromStr;

use anyhow::{Context, Result, anyhow};
use hal_simplicity::{
bitcoin,
simplicity::{
ToXOnlyPubkey,
elements::{
self, SchnorrSig,
encode::{deserialize, serialize},
hashes::Hash,
pset::PartiallySignedTransaction,
secp256k1_zkp::{Message, Secp256k1, SecretKey},
sighash::{SchnorrSighashType, SighashCache},
taproot::{LeafVersion, TaprootBuilder},
},
use serde_json::json;
use simplicity::{
ToXOnlyPubkey, bitcoin,
elements::{
self, SchnorrSig,
encode::{deserialize, serialize},
pset::PartiallySignedTransaction,
secp256k1_zkp::{Message, Secp256k1, SecretKey},
sighash::{SchnorrSighashType, SighashCache},
taproot::{LeafVersion, TapNodeHash, TaprootBuilder},
},
};
use serde_json::json;
use simplicity_unchained_core::{
ElementsNetwork,
utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_elements},
Expand Down Expand Up @@ -54,9 +50,9 @@ pub fn execute(

let public_key = bitcoin::PublicKey::from_private_key(
&secp,
&hal_simplicity::simplicity::elements::bitcoin::PrivateKey {
&simplicity::elements::bitcoin::PrivateKey {
compressed: true,
network: hal_simplicity::simplicity::elements::bitcoin::NetworkKind::Main,
network: simplicity::elements::bitcoin::NetworkKind::Main,
inner: secret_key,
},
);
Expand All @@ -68,10 +64,13 @@ pub fn execute(

let multisig_leaf = p2tr_multisig_leaf_elements(&cosigner_pubkey, &public_key);

let user_leaf = elements::hashes::sha256::Hash::from_slice(
&hex::decode(user_leaf_hash_hex).context("Failed to decode user leaf hash hex")?,
)
.context("Invalid user leaf hash")?;
let user_leaf = TapNodeHash::from_byte_array(
hex::decode(user_leaf_hash_hex)
.context("Failed to decode user leaf hash hex")?
.as_slice()
.try_into()
.context("Invalid user leaf hash")?,
);

let spend_info = TaprootBuilder::new()
.add_leaf(1, multisig_leaf.clone())
Expand Down
7 changes: 3 additions & 4 deletions cli/src/commands/tx/spend_user_leaf.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use anyhow::{Context, Result, anyhow};
use hal_simplicity::{
use serde_json::json;
use simplicity::{
bitcoin::{self, key::UntweakedKeypair},
simplicity::elements::{
elements::{
self, SchnorrSig,
encode::{deserialize, serialize},
hashes::Hash,
pset::PartiallySignedTransaction,
secp256k1_zkp::{Message, Secp256k1, SecretKey},
sighash::{Prevouts, SchnorrSighashType, SighashCache},
taproot::{LeafVersion, TapLeafHash, TaprootBuilder},
},
};
use serde_json::json;
use simplicity_unchained_core::{
ElementsNetwork,
utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_elements},
Expand Down
10 changes: 5 additions & 5 deletions cli/src/demo_script/csv_leaf_btc.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! Timelocked user script for demo purposes
use anyhow::{Context, Result, anyhow};
use hal_simplicity::{
use serde_json::json;
use simplicity::{
ToXOnlyPubkey,
bitcoin::{
self, Psbt, TapLeafHash, Transaction, Witness, consensus::serialize, hashes::Hash, psbt,
taproot::LeafVersion,
self, Psbt, PublicKey, TapLeafHash, Transaction, Witness, consensus::serialize,
hashes::Hash, psbt, taproot::LeafVersion,
},
simplicity::{ToXOnlyPubkey, bitcoin::PublicKey},
};
use serde_json::json;
use std::str::FromStr;

pub fn build_csv_script(user_pubkey_hex: &str, timelock: u16) -> Result<()> {
Expand Down
22 changes: 10 additions & 12 deletions cli/src/demo_script/csv_leaf_elements.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
//! Timelocked user script for demo purposes
use anyhow::{Context, Result, anyhow};
use hal_simplicity::{
use serde_json::json;
use simplicity::{
ToXOnlyPubkey,
bitcoin::PublicKey,
simplicity::{
ToXOnlyPubkey,
elements::{
self,
encode::{deserialize, serialize},
hashes::Hash,
pset::{self, PartiallySignedTransaction},
taproot::{LeafVersion, TapLeafHash},
},
elements::{
self,
encode::{deserialize, serialize},
pset::{self, PartiallySignedTransaction},
taproot::{LeafVersion, TapLeafHash},
},
};
use serde_json::json;

pub fn build_csv_script(user_pubkey_hex: &str, timelock: u16) -> Result<()> {
let user_pubkey_bytes =
Expand Down Expand Up @@ -78,7 +75,8 @@ fn finalize_internal(tx: &mut elements::Transaction, i: usize, input: &pset::Inp
sig.to_vec(),
leaf_script.to_bytes(),
control_block.serialize(),
];
]
.into();

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ path = "src/lib.rs"

[dependencies]
hex-literal = "1.1.0"
hal-simplicity = { workspace = true }
simplicity-lang = { workspace = true }
thiserror = { workspace = true }
hex = { workspace = true }
99 changes: 99 additions & 0 deletions core/src/jets/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Runs base jets (`Core`, `Elements`) on an [`UnchainedEnv`].
//!
//! `JetEnvironment::c_jet_ptr` must return a plain `fn` pointer, so a base jet can't be wrapped in
//! a closure that swaps the unchained env for the base C env. Instead, every base jet gets its own
//! trampoline, a `fn` monomorphized over the jet's index in `ALL`.
use simplicity::ffi::CFrameItem;
use simplicity::jet::{Core, CoreEnv, Elements, ElementsTxEnv, Jet, JetEnvironment};

use super::environments::UnchainedEnv;

/// C jet function running on an [`UnchainedEnv`].
pub type UnchainedJetFn<E> = fn(&mut CFrameItem, CFrameItem, &UnchainedEnv<E>) -> bool;

/// Upper bound on the number of jets in a base jet set, see [`fn_table`].
const MAX_BASE_JETS: usize = 512;

/// A base jet set together with the environment its C implementations run on.
pub trait BaseJet: Jet + Copy {
type Env: JetEnvironment<Jet = Self>;

/// All jets of the set, indexed by [`BaseJet::index`].
const ALL: &'static [Self];

/// Position of `self` inside [`BaseJet::ALL`].
fn index(&self) -> usize;
}

impl BaseJet for Core {
type Env = CoreEnv;
const ALL: &'static [Self] = &Core::ALL;

fn index(&self) -> usize {
*self as usize
}
}

impl BaseJet for Elements {
type Env = ElementsTxEnv;
const ALL: &'static [Self] = &Elements::ALL;

fn index(&self) -> usize {
*self as usize
}
}

/// Returns the C implementation of a base jet, adapted to run on an [`UnchainedEnv`].
pub fn base_c_jet_ptr<J: BaseJet>(jet: &J) -> UnchainedJetFn<J::Env> {
const { assert!(J::ALL.len() <= MAX_BASE_JETS) };

Trampolines::<J>::TABLE.as_flattened().as_flattened()[jet.index()]
}

fn trampoline<J: BaseJet, const I: usize>(
dst: &mut CFrameItem,
src: CFrameItem,
env: &UnchainedEnv<J::Env>,
) -> bool {
let c_jet = <J::Env as JetEnvironment>::c_jet_ptr(&J::ALL[I]);
c_jet(dst, src, env.env.c_jet_env())
}

/// Builds a `[[[_; 8]; 8]; 8]` table of `$f::<J, I>` for every `I` below [`MAX_BASE_JETS`].
// TODO: refactor into something less ugly, e.g. a flat table generated by a proc macro.
macro_rules! fn_table {
($f:ident) => {
fn_table!(@a $f; 0 1 2 3 4 5 6 7)
};
(@a $f:ident; $($a:literal)*) => {
[$(fn_table!(@b $f; $a; 0 1 2 3 4 5 6 7)),*]
};
(@b $f:ident; $a:literal; $($b:literal)*) => {
[$(fn_table!(@c $f; $a; $b; 0 1 2 3 4 5 6 7)),*]
};
(@c $f:ident; $a:literal; $b:literal; $($c:literal)*) => {
[$($f::<J, { $a * 64 + $b * 8 + $c }> as UnchainedJetFn<J::Env>),*]
};
}

struct Trampolines<J>(J);

impl<J: BaseJet> Trampolines<J> {
#[allow(clippy::type_complexity)]
const TABLE: [[[UnchainedJetFn<J::Env>; 8]; 8]; 8] = fn_table!(trampoline);
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn index_matches_position_in_all() {
for (i, jet) in Core::ALL.iter().enumerate() {
assert_eq!(jet.index(), i, "{jet}");
}
for (i, jet) in Elements::ALL.iter().enumerate() {
assert_eq!(jet.index(), i, "{jet}");
}
}
}
Loading
Loading