From 46fd4448a953c9b7c8589be579b59a8461e7a7a9 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Mon, 16 Feb 2026 18:38:19 +0200 Subject: [PATCH 1/9] dirty sdk --- Cargo.lock | 27 ++++ crates/artifacts/Cargo.toml | 16 ++ crates/artifacts/src/asset_auth.rs | 87 +++++++++++ crates/artifacts/src/lib.rs | 1 + crates/macros-core/Cargo.toml | 1 - crates/sdk/Cargo.toml | 16 ++ crates/sdk/src/constants.rs | 133 ++++++++++++++++ crates/sdk/src/error.rs | 49 ++++++ crates/sdk/src/lib.rs | 6 + crates/sdk/src/program.rs | 223 +++++++++++++++++++++++++++ crates/sdk/src/signed_transaction.rs | 104 +++++++++++++ crates/sdk/src/signer.rs | 66 ++++++++ crates/sdk/src/utils.rs | 10 ++ crates/user/Cargo.toml | 16 ++ crates/user/src/main.rs | 17 ++ 15 files changed, 771 insertions(+), 1 deletion(-) create mode 100644 crates/artifacts/Cargo.toml create mode 100644 crates/artifacts/src/asset_auth.rs create mode 100644 crates/artifacts/src/lib.rs create mode 100644 crates/sdk/Cargo.toml create mode 100644 crates/sdk/src/constants.rs create mode 100644 crates/sdk/src/error.rs create mode 100644 crates/sdk/src/lib.rs create mode 100644 crates/sdk/src/program.rs create mode 100644 crates/sdk/src/signed_transaction.rs create mode 100644 crates/sdk/src/signer.rs create mode 100644 crates/sdk/src/utils.rs create mode 100644 crates/user/Cargo.toml create mode 100644 crates/user/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index e5ce080..32f8208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1688,6 +1688,15 @@ dependencies = [ "trybuild", ] +[[package]] +name = "simplex-build" +version = "0.1.0" +dependencies = [ + "simplex-sdk", + "simplicityhl", + "thiserror", +] + [[package]] name = "simplex-cli" version = "0.1.0" @@ -1756,6 +1765,24 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "simplex-sdk" +version = "0.1.0" +dependencies = [ + "sha2", + "simplicityhl", + "thiserror", +] + +[[package]] +name = "simplex-user" +version = "0.1.0" +dependencies = [ + "simplex-build", + "simplex-sdk", + "thiserror", +] + [[package]] name = "simplicity-lang" version = "0.7.0" diff --git a/crates/artifacts/Cargo.toml b/crates/artifacts/Cargo.toml new file mode 100644 index 0000000..f57cd3f --- /dev/null +++ b/crates/artifacts/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "simplex-build" +version = "0.1.0" +edition = "2024" +description = "Simplex Build" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[lints] +workspace = true + +[dependencies] +thiserror = "2" + +simplex-sdk = { path = "../sdk" } +simplicityhl = { workspace = true } diff --git a/crates/artifacts/src/asset_auth.rs b/crates/artifacts/src/asset_auth.rs new file mode 100644 index 0000000..1da7cac --- /dev/null +++ b/crates/artifacts/src/asset_auth.rs @@ -0,0 +1,87 @@ +use simplex_sdk::program::{Arguments, Program}; +use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; + +// use simplex_macros::simplex_build; + +// #[derive(SimplexBuild)] +// #[simplex("../../contracts/src/asset_auth/source_simf/asset_auth.simf")] +pub struct AssetAuth<'a> { + program: Program<'a>, +} + +impl<'a> AssetAuth<'a> { + // the path is autogenerated + pub const SOURCE: &'static str = ""; + // include_str!("../../contracts/src/asset_auth/source_simf/asset_auth.simf"); + + pub fn new(public_key: &'a XOnlyPublicKey, arguments: &'a impl Arguments) -> Self { + Self { + program: Program::new(Self::SOURCE, public_key, arguments), + } + } + + pub fn get_program(&self) -> &Program<'a> { + &self.program + } +} + +// Expanded by macro + +pub mod asset_auth_build { + use simplex_sdk::program::Witness; + use simplex_sdk::program::Arguments; + use simplicityhl::value::UIntValue; + use simplicityhl::value::ValueConstructible; + use simplicityhl::{Value, WitnessValues}; + use std::collections::HashMap; + + pub struct AssetAuthWitness { + pub path: (bool, u64, u64), + } + + pub struct AssetAuthArguments { + pub first: u64, + pub second: bool, + } + + impl Witness for AssetAuthWitness { + fn build_witness(&self) -> WitnessValues { + WitnessValues::from(HashMap::from([( + simplicityhl::str::WitnessName::from_str_unchecked("PATH"), + Value::tuple([ + Value::from(self.path.0), + Value::from(UIntValue::U64(self.path.1)), + Value::from(UIntValue::U64(self.path.1)), + ]), + )])) + } + + fn from_witness(_witness: &::simplicityhl::WitnessValues) -> Self { + Self { + path: (false, 0, 0), + } + } + } + + impl Arguments for AssetAuthArguments { + fn build_arguments(&self) -> simplicityhl::Arguments { + simplicityhl::Arguments::from(HashMap::from([ + ( + simplicityhl::str::WitnessName::from_str_unchecked("FIRST"), + Value::from(UIntValue::U64(self.first)), + ), + ( + simplicityhl::str::WitnessName::from_str_unchecked("SECOND"), + Value::from(self.second), + ), + ])) + } + + fn from_arguments(_args: &simplicityhl::Arguments) -> Self { + Self { + first: 0, + second: false, + } + } + } +} diff --git a/crates/artifacts/src/lib.rs b/crates/artifacts/src/lib.rs new file mode 100644 index 0000000..c37f1ff --- /dev/null +++ b/crates/artifacts/src/lib.rs @@ -0,0 +1 @@ +pub mod asset_auth; diff --git a/crates/macros-core/Cargo.toml b/crates/macros-core/Cargo.toml index 964592d..5b4bc4f 100644 --- a/crates/macros-core/Cargo.toml +++ b/crates/macros-core/Cargo.toml @@ -13,7 +13,6 @@ bincode = [] serde = ["bincode"] default = ["bincode", "serde"] - [dependencies] proc-macro-error = { version = "1.0" } proc-macro2 = { version = "1.0.106", features = ["span-locations"] } diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml new file mode 100644 index 0000000..9b871f0 --- /dev/null +++ b/crates/sdk/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "simplex-sdk" +version = "0.1.0" +edition = "2024" +description = "Simplex SDK" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[lints] +workspace = true + +[dependencies] +thiserror = "2" + +sha2 = { workspace = true } +simplicityhl = { workspace = true } diff --git a/crates/sdk/src/constants.rs b/crates/sdk/src/constants.rs new file mode 100644 index 0000000..e584c66 --- /dev/null +++ b/crates/sdk/src/constants.rs @@ -0,0 +1,133 @@ +//! Common Liquid network constants and helpers. +//! +//! Exposes policy asset identifiers and the Liquid testnet genesis hash. +//! +//! These are used throughout the CLI and examples to ensure consistent +//! parameters when constructing Elements transactions. + +use simplicityhl::simplicity::elements; +use simplicityhl::simplicity::hashes::{Hash, sha256}; + +use std::str::FromStr; + +pub const PUBLIC_SECRET_BLINDER_KEY: [u8; 32] = [1; 32]; + +/// Policy asset id (hex, BE) for Liquid mainnet. +pub const LIQUID_POLICY_ASSET_STR: &str = + "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d"; + +/// Policy asset id (hex, BE) for Liquid testnet. +pub const LIQUID_TESTNET_POLICY_ASSET_STR: &str = + "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49"; + +/// Policy asset id (hex, BE) for Elements regtest. +pub const LIQUID_DEFAULT_REGTEST_ASSET_STR: &str = + "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"; + +/// Example test asset id (hex, BE) on Liquid testnet. +pub static LIQUID_TESTNET_TEST_ASSET_ID_STR: &str = + "38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5"; + +/// LBTC asset id for Liquid testnet. +pub static LIQUID_TESTNET_BITCOIN_ASSET: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + elements::AssetId::from_inner(sha256::Midstate([ + 0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3, 0x9f, 0xc0, 0x3b, 0x63, 0x7f, 0x2a, + 0x4e, 0x1e, 0x64, 0xe5, 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f, 0x6d, 0x71, 0xaa, 0x44, + 0x43, 0x65, 0x4c, 0x14, + ])) + }); + +/// Genesis block hash for Liquid mainnet. +pub static LIQUID_MAINNET_GENESIS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + elements::BlockHash::from_byte_array([ + 0x03, 0x60, 0x20, 0x8a, 0x88, 0x96, 0x92, 0x37, 0x2c, 0x8d, 0x68, 0xb0, 0x84, 0xa6, + 0x2e, 0xfd, 0xf6, 0x0e, 0xa1, 0xa3, 0x59, 0xa0, 0x4c, 0x94, 0xb2, 0x0d, 0x22, 0x36, + 0x58, 0x27, 0x66, 0x14, + ]) + }); + +/// Genesis block hash for Liquid testnet. +pub static LIQUID_TESTNET_GENESIS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + elements::BlockHash::from_byte_array([ + 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, + 0x79, 0x3b, 0x5b, 0x5e, 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, + 0x8e, 0xda, 0x71, 0xa7, + ]) + }); + +/// Genesis block hash for Liquid regtest. +pub static LIQUID_REGTEST_GENESIS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + elements::BlockHash::from_byte_array([ + 0x21, 0xca, 0xb1, 0xe5, 0xda, 0x47, 0x18, 0xea, 0x14, 0x0d, 0x97, 0x16, 0x93, 0x17, + 0x02, 0x42, 0x2f, 0x0e, 0x6a, 0xd9, 0x15, 0xc8, 0xd9, 0xb5, 0x83, 0xca, 0xc2, 0x70, + 0x6b, 0x2a, 0x90, 0x00, + ]) + }); + +/// The network of the elements blockchain. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SimplicityNetwork { + // Liquid mainnet policy asset + Liquid, + // Liquid testnet policy asset + LiquidTestnet, + /// Liquid regtest with a custom policy asset. + ElementsRegtest { + /// The policy asset to use for this regtest network. + /// You can use the default one using [`SimplicityNetwork::default_regtest()`]. + policy_asset: elements::AssetId, + }, +} + +impl SimplicityNetwork { + /// Return the default policy asset for regtest network. + /// + /// # Panics + /// + /// Doesn't panic as constants are defined correctly. + #[must_use] + pub fn default_regtest() -> Self { + let policy_asset = elements::AssetId::from_str(LIQUID_DEFAULT_REGTEST_ASSET_STR).unwrap(); + Self::ElementsRegtest { policy_asset } + } + + /// Return the policy asset for specific network. + /// + /// # Panics + /// + /// Doesn't panic as constants are defined correctly. + #[must_use] + pub fn policy_asset(&self) -> elements::AssetId { + match self { + Self::Liquid => elements::AssetId::from_str(LIQUID_POLICY_ASSET_STR).unwrap(), + Self::LiquidTestnet => { + elements::AssetId::from_str(LIQUID_TESTNET_POLICY_ASSET_STR).unwrap() + } + Self::ElementsRegtest { policy_asset } => *policy_asset, + } + } + + /// Return the genesis block hash for this network. + #[must_use] + pub fn genesis_block_hash(&self) -> elements::BlockHash { + match self { + Self::Liquid => *LIQUID_MAINNET_GENESIS, + Self::LiquidTestnet => *LIQUID_TESTNET_GENESIS, + Self::ElementsRegtest { .. } => *LIQUID_REGTEST_GENESIS, + } + } + + /// Return the address parameters for this network to generate addresses compatible for this network. + #[must_use] + pub const fn address_params(&self) -> &'static elements::AddressParams { + match self { + Self::Liquid => &elements::AddressParams::LIQUID, + Self::LiquidTestnet => &elements::AddressParams::LIQUID_TESTNET, + Self::ElementsRegtest { .. } => &elements::AddressParams::ELEMENTS, + } + } +} diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs new file mode 100644 index 0000000..3d5fa9a --- /dev/null +++ b/crates/sdk/src/error.rs @@ -0,0 +1,49 @@ +use simplicityhl::elements::secp256k1_zkp; + +#[derive(Debug, thiserror::Error)] +pub enum ProgramError { + #[error("Failed to compile Simplicity program: {0}")] + Compilation(String), + + /// Returned when witness values cannot satisfy the program's requirements. + #[error("Failed to satisfy witness: {0}")] + WitnessSatisfaction(String), + + /// Returned when the program cannot be pruned against the transaction environment. + #[error("Failed to prune program: {0}")] + Pruning(#[from] simplicityhl::simplicity::bit_machine::ExecutionError), + + #[error("Failed to construct a Bit Machine with enough space: {0}")] + BitMachineCreation(#[from] simplicityhl::simplicity::bit_machine::LimitError), + + #[error("Failed to execute program on the Bit Machine: {0}")] + Execution(simplicityhl::simplicity::bit_machine::ExecutionError), + + #[error("UTXO index {input_index} out of bounds (have {utxo_count} UTXOs)")] + UtxoIndexOutOfBounds { + input_index: usize, + utxo_count: usize, + }, + + /// Returned when the UTXO's script does not match the expected program address. + #[error("Script pubkey mismatch: expected hash {expected_hash}, got {actual_hash}")] + ScriptPubkeyMismatch { + expected_hash: String, + actual_hash: String, + }, + + #[error("Input index exceeds u32 maximum: {0}")] + InputIndexOverflow(#[from] std::num::TryFromIntError), +} + +#[derive(Debug, thiserror::Error)] +pub enum SignerError { + #[error("Invalid seed length: expected 32 bytes, got {0}")] + InvalidSeedLength(usize), + + #[error("Invalid secret key")] + InvalidSecretKey(#[from] secp256k1_zkp::UpstreamError), + + #[error("Program error")] + Address(#[from] ProgramError), +} diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs new file mode 100644 index 0000000..bce32c8 --- /dev/null +++ b/crates/sdk/src/lib.rs @@ -0,0 +1,6 @@ +pub mod program; +pub mod signer; +pub mod signed_transaction; +pub mod utils; +pub mod constants; +pub mod error; diff --git a/crates/sdk/src/program.rs b/crates/sdk/src/program.rs new file mode 100644 index 0000000..e6680b3 --- /dev/null +++ b/crates/sdk/src/program.rs @@ -0,0 +1,223 @@ +use std::sync::Arc; + +use sha2::{Digest, Sha256}; + +use simplicityhl::CompiledProgram; +use simplicityhl::elements::{Address, Script, Transaction, TxInWitness, TxOut, script, taproot}; +use simplicityhl::simplicity::bitcoin::{XOnlyPublicKey, secp256k1}; +use simplicityhl::simplicity::jet::Elements; +use simplicityhl::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; +use simplicityhl::simplicity::{BitMachine, RedeemNode, Value}; +use simplicityhl::tracker::{DefaultTracker, TrackerLogLevel}; + +use crate::constants::SimplicityNetwork; +use crate::error::ProgramError; + +pub trait ArgumentsTrait { + fn build_arguments(&self) -> simplicityhl::Arguments; +} + +pub trait WitnessTrait { + fn build_witness(&self) -> simplicityhl::WitnessValues; +} + +pub trait ProgramTrait { + fn get_env( + &self, + tx: &Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result>, ProgramError>; + + fn execute( + &self, + witness: &dyn WitnessTrait, + tx: &Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result<(Arc>, Value), ProgramError>; + + fn finalize( + &self, + witness: &dyn WitnessTrait, + tx: Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result; +} + +pub struct Program<'a> { + source: &'static str, + pub_key: &'a XOnlyPublicKey, + arguments: &'a dyn ArgumentsTrait, +} + +impl<'a> ProgramTrait for Program<'a> { + fn get_env( + &self, + tx: &Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result>, ProgramError> { + let genesis_hash = network.genesis_block_hash(); + let cmr = self.load()?.commit().cmr(); + + if utxos.len() <= input_index { + return Err(ProgramError::UtxoIndexOutOfBounds { + input_index, + utxo_count: utxos.len(), + }); + } + + let target_utxo = &utxos[input_index]; + let script_pubkey = self.get_tr_address(network)?.script_pubkey(); + + if target_utxo.script_pubkey != script_pubkey { + return Err(ProgramError::ScriptPubkeyMismatch { + expected_hash: script_pubkey.script_hash().to_string(), + actual_hash: target_utxo.script_pubkey.script_hash().to_string(), + }); + } + + Ok(ElementsEnv::new( + Arc::new(tx.clone()), + utxos + .iter() + .map(|utxo| ElementsUtxo { + script_pubkey: utxo.script_pubkey.clone(), + asset: utxo.asset, + value: utxo.value, + }) + .collect(), + u32::try_from(input_index)?, + cmr, + self.control_block()?, + None, + genesis_hash, + )) + } + + fn execute( + &self, + witness: &dyn WitnessTrait, + tx: &Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result<(Arc>, Value), ProgramError> { + let satisfied = self + .load()? + .satisfy(witness.build_witness()) + .map_err(ProgramError::WitnessSatisfaction)?; + + let mut tracker = DefaultTracker::new(satisfied.debug_symbols()).with_log_level(TrackerLogLevel::Debug); + + let env = self.get_env(tx, utxos, input_index, network)?; + + let pruned = satisfied.redeem().prune_with_tracker(&env, &mut tracker)?; + let mut mac = BitMachine::for_program(&pruned)?; + + let result = mac.exec(&pruned, &env).map_err(ProgramError::Execution)?; + + Ok((pruned, result)) + } + + fn finalize( + &self, + witness: &dyn WitnessTrait, + mut tx: Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result { + let pruned = self.execute(witness, &tx, utxos, input_index, network)?.0; + + let (simplicity_program_bytes, simplicity_witness_bytes) = pruned.to_vec_with_witness(); + let cmr = pruned.cmr(); + + tx.input[input_index].witness = TxInWitness { + amount_rangeproof: None, + inflation_keys_rangeproof: None, + script_witness: vec![ + simplicity_witness_bytes, + simplicity_program_bytes, + cmr.as_ref().to_vec(), + self.control_block()?.serialize(), + ], + pegin_witness: vec![], + }; + + Ok(tx) + } +} + +impl<'a> Program<'a> { + pub fn new(source: &'static str, pub_key: &'a XOnlyPublicKey, arguments: &'a impl ArgumentsTrait) -> Self { + Self { + source: source, + pub_key: pub_key, + arguments: arguments, + } + } + + pub fn get_tr_address(&self, network: SimplicityNetwork) -> Result { + let spend_info = self.taproot_spending_info()?; + + Ok(Address::p2tr( + secp256k1::SECP256K1, + spend_info.internal_key(), + spend_info.merkle_root(), + None, + network.address_params(), + )) + } + + pub fn get_script_pubkey(&self, network: SimplicityNetwork) -> Result { + Ok(self.get_tr_address(network)?.script_pubkey()) + } + + pub fn get_script_hash(&self, network: SimplicityNetwork) -> Result<[u8; 32], ProgramError> { + let script = self.get_script_pubkey(network)?; + let mut hasher = Sha256::new(); + + sha2::digest::Update::update(&mut hasher, script.as_bytes()); + Ok(hasher.finalize().into()) + } + + fn load(&self) -> Result { + let compiled = CompiledProgram::new(self.source, self.arguments.build_arguments(), true) + .map_err(ProgramError::Compilation)?; + Ok(compiled) + } + + fn script_version(&self) -> Result<(Script, taproot::LeafVersion), ProgramError> { + let cmr = self.load()?.commit().cmr(); + let script = script::Script::from(cmr.as_ref().to_vec()); + + Ok((script, simplicityhl::simplicity::leaf_version())) + } + + fn taproot_spending_info(&self) -> Result { + let builder = taproot::TaprootBuilder::new(); + let (script, version) = self.script_version()?; + + let builder = builder + .add_leaf_with_ver(0, script, version) + .expect("tap tree should be valid"); + + Ok(builder + .finalize(secp256k1::SECP256K1, *self.pub_key) + .expect("tap tree should be valid")) + } + + fn control_block(&self) -> Result { + let info = self.taproot_spending_info()?; + let script_ver = self.script_version()?; + + Ok(info.control_block(&script_ver).expect("control block should exist")) + } +} diff --git a/crates/sdk/src/signed_transaction.rs b/crates/sdk/src/signed_transaction.rs new file mode 100644 index 0000000..064b5c9 --- /dev/null +++ b/crates/sdk/src/signed_transaction.rs @@ -0,0 +1,104 @@ +use simplicityhl::elements::pset::{Input, Output, PartiallySignedTransaction}; +use simplicityhl::elements::secp256k1_zkp::schnorr::Signature; +use simplicityhl::elements::{Address, Script, Transaction, TxInWitness, TxOut, script, taproot}; +use simplicityhl::{Value, WitnessValues}; + +use crate::constants::SimplicityNetwork; +use crate::error::SignerError; +use crate::program::{ProgramTrait, WitnessTrait}; +use crate::signer::SignerTrait; + +struct SignedInput<'a> { + program: &'a dyn ProgramTrait, + witness: &'a dyn WitnessTrait, + signer: Option<&'a dyn SignerTrait>, + signer_lambda: Option<&'a dyn FnOnce(WitnessValues, Signature) -> Result>, +} + +pub struct SignedTransaction<'a> { + tx: Transaction, + utxos: &'a [TxOut], + network: SimplicityNetwork, + inputs: Vec>, +} + +impl<'a> SignedTransaction<'a> { + pub fn new(tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork) -> Self { + Self { + tx: tx, + utxos: utxos, + network: network, + inputs: Vec::new(), + } + } + + pub fn add_input(&mut self, program: &'a dyn ProgramTrait, witness: &'a dyn WitnessTrait) { + let signed_input = SignedInput { + program: program, + witness: witness, + signer: Option::None, + signer_lambda: Option::None, + }; + + self.inputs.push(signed_input); + } + + pub fn add_signed_input( + &mut self, + program: &'a dyn ProgramTrait, + witness: &'a dyn WitnessTrait, + signer: &'a dyn SignerTrait, + signer_lambda: &'a dyn FnOnce(WitnessValues, Signature) -> Result, + ) { + let signed_input = SignedInput { + program: program, + witness: witness, + signer: Option::Some(signer), + signer_lambda: Option::Some(signer_lambda), + }; + + self.inputs.push(signed_input); + } + + pub fn finalize(&mut self) -> Result { + for index in 0..self.inputs.len() { + let (program, witness, signer, signer_lambda) = { + let input = &self.inputs[index]; + (input.program, input.witness, input.signer, input.signer_lambda) + }; + + if signer.is_some() { + self.finalize_with_signer(program, witness, index, signer.unwrap(), signer_lambda.unwrap())?; + } else { + self.finalize_as_is(program, witness, index)?; + } + } + + Ok(self.tx.clone()) + } + + fn finalize_with_signer( + &mut self, + program: &dyn ProgramTrait, + witness: &dyn WitnessTrait, + index: usize, + signer: &dyn SignerTrait, + signer_lambda: &'a dyn FnOnce(WitnessValues, Signature) -> Result, + ) -> Result<(), SignerError> { + let signature = signer.sign(program, &self.tx, self.utxos, index, self.network)?; + let new_witness = signer_lambda(witness.build_witness(), signature)?; + + self.finalize_as_is(program, WitnessTrait::from_witness(new_witness), index) + } + + fn finalize_as_is( + &mut self, + program: &dyn ProgramTrait, + witness: &dyn WitnessTrait, + index: usize, + ) -> Result<(), SignerError> { + self.tx = program.finalize(witness, self.tx.clone(), self.utxos, index, self.network)?; + + Ok(()) + } +} diff --git a/crates/sdk/src/signer.rs b/crates/sdk/src/signer.rs new file mode 100644 index 0000000..0e4925c --- /dev/null +++ b/crates/sdk/src/signer.rs @@ -0,0 +1,66 @@ +use simplicityhl::elements::secp256k1_zkp::{self as secp256k1, Keypair, Message, schnorr::Signature}; +use simplicityhl::elements::{Transaction, TxOut}; +use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; +use simplicityhl::simplicity::hashes::Hash as _; + +use crate::constants::SimplicityNetwork; +use crate::error::SignerError; +use crate::program::{ProgramTrait}; + +pub trait SignerTrait { + fn public_key(&self) -> XOnlyPublicKey; + + fn personal_sign(&self, message: Message) -> Result; + + fn sign<'a>( + &self, + program: &dyn ProgramTrait, + tx: &Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result; +} + +pub struct Signer { + keypair: Keypair, +} + +impl SignerTrait for Signer { + fn public_key(&self) -> XOnlyPublicKey { + self.keypair.x_only_public_key().0 + } + + fn personal_sign(&self, message: Message) -> Result { + Ok(self.keypair.sign_schnorr(message)) + } + + fn sign<'a>( + &self, + program: &dyn ProgramTrait, + tx: &Transaction, + utxos: &[TxOut], + input_index: usize, + network: SimplicityNetwork, + ) -> Result { + let env = program.get_env(tx, utxos, input_index, network)?; + + let sighash_all = Message::from_digest(env.c_tx_env().sighash_all().to_byte_array()); + + Ok(self.keypair.sign_schnorr(sighash_all)) + } +} + +impl Signer { + pub const SEED_LEN: usize = secp256k1::constants::SECRET_KEY_SIZE; + + pub fn from_seed(seed: &[u8; Self::SEED_LEN]) -> Result { + let secp = secp256k1::Secp256k1::new(); + + let secret_key = secp256k1::SecretKey::from_slice(seed)?; + + let keypair = Keypair::from_secret_key(&secp, &secret_key); + + Ok(Self { keypair }) + } +} diff --git a/crates/sdk/src/utils.rs b/crates/sdk/src/utils.rs new file mode 100644 index 0000000..a8398c1 --- /dev/null +++ b/crates/sdk/src/utils.rs @@ -0,0 +1,10 @@ +use simplicityhl::simplicity::bitcoin::secp256k1; + +pub fn tr_unspendable_key() -> secp256k1::XOnlyPublicKey { + secp256k1::XOnlyPublicKey::from_slice(&[ + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, + 0x5e, 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, + 0x3a, 0xc0, + ]) + .expect("key should be valid") +} diff --git a/crates/user/Cargo.toml b/crates/user/Cargo.toml new file mode 100644 index 0000000..ab0f2b7 --- /dev/null +++ b/crates/user/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "simplex-user" +version = "0.1.0" +edition = "2024" +description = "Simplex SDK" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[lints] +workspace = true + +[dependencies] +thiserror = "2" + +simplex-sdk = { path = "../sdk" } +simplex-build = { path = "../artifacts" } diff --git a/crates/user/src/main.rs b/crates/user/src/main.rs new file mode 100644 index 0000000..99ea508 --- /dev/null +++ b/crates/user/src/main.rs @@ -0,0 +1,17 @@ +use simplex_build::asset_auth::AssetAuth; +use simplex_build::asset_auth::asset_auth_build::{AssetAuthArguments, AssetAuthWitness}; + +use simplex_sdk::utils::tr_unspendable_key; + +fn main() { + let witness = AssetAuthWitness { + path: (false, 1, 1), + }; + + let arguments = AssetAuthArguments { + first: 1, + second: false, + }; + + let asset_auth = AssetAuth::new(&tr_unspendable_key(), &arguments); +} From 2a1948b9a8aeb7c70a3fcc2766da1e307a05b82d Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Mon, 16 Feb 2026 20:04:21 +0200 Subject: [PATCH 2/9] tx update --- crates/sdk/src/program.rs | 15 +++++------ crates/sdk/src/signed_transaction.rs | 37 +++++++++++++++++----------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/crates/sdk/src/program.rs b/crates/sdk/src/program.rs index e6680b3..87124d1 100644 --- a/crates/sdk/src/program.rs +++ b/crates/sdk/src/program.rs @@ -9,16 +9,17 @@ use simplicityhl::simplicity::jet::Elements; use simplicityhl::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; use simplicityhl::simplicity::{BitMachine, RedeemNode, Value}; use simplicityhl::tracker::{DefaultTracker, TrackerLogLevel}; +use simplicityhl::{Arguments, WitnessValues}; use crate::constants::SimplicityNetwork; use crate::error::ProgramError; pub trait ArgumentsTrait { - fn build_arguments(&self) -> simplicityhl::Arguments; + fn build_arguments(&self) -> Arguments; } pub trait WitnessTrait { - fn build_witness(&self) -> simplicityhl::WitnessValues; + fn build_witness(&self) -> WitnessValues; } pub trait ProgramTrait { @@ -32,7 +33,7 @@ pub trait ProgramTrait { fn execute( &self, - witness: &dyn WitnessTrait, + witness: WitnessValues, tx: &Transaction, utxos: &[TxOut], input_index: usize, @@ -41,7 +42,7 @@ pub trait ProgramTrait { fn finalize( &self, - witness: &dyn WitnessTrait, + witness: WitnessValues, tx: Transaction, utxos: &[TxOut], input_index: usize, @@ -103,7 +104,7 @@ impl<'a> ProgramTrait for Program<'a> { fn execute( &self, - witness: &dyn WitnessTrait, + witness: WitnessValues, tx: &Transaction, utxos: &[TxOut], input_index: usize, @@ -111,7 +112,7 @@ impl<'a> ProgramTrait for Program<'a> { ) -> Result<(Arc>, Value), ProgramError> { let satisfied = self .load()? - .satisfy(witness.build_witness()) + .satisfy(witness) .map_err(ProgramError::WitnessSatisfaction)?; let mut tracker = DefaultTracker::new(satisfied.debug_symbols()).with_log_level(TrackerLogLevel::Debug); @@ -128,7 +129,7 @@ impl<'a> ProgramTrait for Program<'a> { fn finalize( &self, - witness: &dyn WitnessTrait, + witness: WitnessValues, mut tx: Transaction, utxos: &[TxOut], input_index: usize, diff --git a/crates/sdk/src/signed_transaction.rs b/crates/sdk/src/signed_transaction.rs index 064b5c9..9c18d81 100644 --- a/crates/sdk/src/signed_transaction.rs +++ b/crates/sdk/src/signed_transaction.rs @@ -8,21 +8,24 @@ use crate::error::SignerError; use crate::program::{ProgramTrait, WitnessTrait}; use crate::signer::SignerTrait; -struct SignedInput<'a> { +struct SignedInput<'a, T> { program: &'a dyn ProgramTrait, witness: &'a dyn WitnessTrait, signer: Option<&'a dyn SignerTrait>, - signer_lambda: Option<&'a dyn FnOnce(WitnessValues, Signature) -> Result>, + signer_lambda: Option, } -pub struct SignedTransaction<'a> { +pub struct SignedTransaction<'a, T> { tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork, - inputs: Vec>, + inputs: Vec>, } -impl<'a> SignedTransaction<'a> { +impl<'a, T> SignedTransaction<'a, T> +where + T: Fn(WitnessValues, Signature) -> Result + Clone, +{ pub fn new(tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork) -> Self { Self { tx: tx, @@ -48,7 +51,7 @@ impl<'a> SignedTransaction<'a> { program: &'a dyn ProgramTrait, witness: &'a dyn WitnessTrait, signer: &'a dyn SignerTrait, - signer_lambda: &'a dyn FnOnce(WitnessValues, Signature) -> Result, + signer_lambda: T, ) { let signed_input = SignedInput { program: program, @@ -64,13 +67,19 @@ impl<'a> SignedTransaction<'a> { for index in 0..self.inputs.len() { let (program, witness, signer, signer_lambda) = { let input = &self.inputs[index]; - (input.program, input.witness, input.signer, input.signer_lambda) + (input.program, input.witness, input.signer, input.signer_lambda.clone()) }; if signer.is_some() { - self.finalize_with_signer(program, witness, index, signer.unwrap(), signer_lambda.unwrap())?; + self.finalize_with_signer( + program, + witness.build_witness(), + index, + signer.unwrap(), + signer_lambda.unwrap(), + )?; } else { - self.finalize_as_is(program, witness, index)?; + self.finalize_as_is(program, witness.build_witness(), index)?; } } @@ -80,21 +89,21 @@ impl<'a> SignedTransaction<'a> { fn finalize_with_signer( &mut self, program: &dyn ProgramTrait, - witness: &dyn WitnessTrait, + witness: WitnessValues, index: usize, signer: &dyn SignerTrait, - signer_lambda: &'a dyn FnOnce(WitnessValues, Signature) -> Result, + signer_lambda: T, ) -> Result<(), SignerError> { let signature = signer.sign(program, &self.tx, self.utxos, index, self.network)?; - let new_witness = signer_lambda(witness.build_witness(), signature)?; + let new_witness = signer_lambda(witness, signature)?; - self.finalize_as_is(program, WitnessTrait::from_witness(new_witness), index) + self.finalize_as_is(program, new_witness, index) } fn finalize_as_is( &mut self, program: &dyn ProgramTrait, - witness: &dyn WitnessTrait, + witness: WitnessValues, index: usize, ) -> Result<(), SignerError> { self.tx = program.finalize(witness, self.tx.clone(), self.utxos, index, self.network)?; From 88662b67e8c4b003f1d5f5edd8082b9dbc8cc544 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Tue, 17 Feb 2026 13:08:35 +0200 Subject: [PATCH 3/9] build fix --- crates/artifacts/src/asset_auth.rs | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/artifacts/src/asset_auth.rs b/crates/artifacts/src/asset_auth.rs index 1da7cac..b0fd940 100644 --- a/crates/artifacts/src/asset_auth.rs +++ b/crates/artifacts/src/asset_auth.rs @@ -1,4 +1,4 @@ -use simplex_sdk::program::{Arguments, Program}; +use simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; // use simplex_macros::simplex_build; @@ -14,7 +14,7 @@ impl<'a> AssetAuth<'a> { pub const SOURCE: &'static str = ""; // include_str!("../../contracts/src/asset_auth/source_simf/asset_auth.simf"); - pub fn new(public_key: &'a XOnlyPublicKey, arguments: &'a impl Arguments) -> Self { + pub fn new(public_key: &'a XOnlyPublicKey, arguments: &'a impl ArgumentsTrait) -> Self { Self { program: Program::new(Self::SOURCE, public_key, arguments), } @@ -28,8 +28,8 @@ impl<'a> AssetAuth<'a> { // Expanded by macro pub mod asset_auth_build { - use simplex_sdk::program::Witness; - use simplex_sdk::program::Arguments; + use simplex_sdk::program::WitnessTrait; + use simplex_sdk::program::ArgumentsTrait; use simplicityhl::value::UIntValue; use simplicityhl::value::ValueConstructible; use simplicityhl::{Value, WitnessValues}; @@ -44,7 +44,7 @@ pub mod asset_auth_build { pub second: bool, } - impl Witness for AssetAuthWitness { + impl WitnessTrait for AssetAuthWitness { fn build_witness(&self) -> WitnessValues { WitnessValues::from(HashMap::from([( simplicityhl::str::WitnessName::from_str_unchecked("PATH"), @@ -56,14 +56,14 @@ pub mod asset_auth_build { )])) } - fn from_witness(_witness: &::simplicityhl::WitnessValues) -> Self { - Self { - path: (false, 0, 0), - } - } + // fn from_witness(_witness: &::simplicityhl::WitnessValues) -> Self { + // Self { + // path: (false, 0, 0), + // } + // } } - impl Arguments for AssetAuthArguments { + impl ArgumentsTrait for AssetAuthArguments { fn build_arguments(&self) -> simplicityhl::Arguments { simplicityhl::Arguments::from(HashMap::from([ ( @@ -77,11 +77,11 @@ pub mod asset_auth_build { ])) } - fn from_arguments(_args: &simplicityhl::Arguments) -> Self { - Self { - first: 0, - second: false, - } - } + // fn from_arguments(_args: &simplicityhl::Arguments) -> Self { + // Self { + // first: 0, + // second: false, + // } + // } } } From 4116b90ecae5ec5e66a8a8d9e16ec31dea1fedbd Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Tue, 17 Feb 2026 18:30:29 +0200 Subject: [PATCH 4/9] signed tx fee estimate --- Cargo.lock | 1 + crates/artifacts/src/asset_auth.rs | 10 ++-- crates/sdk/Cargo.toml | 1 + crates/sdk/src/arguments.rs | 6 +++ crates/sdk/src/constants.rs | 38 ++------------ crates/sdk/src/error.rs | 28 ++++------ crates/sdk/src/lib.rs | 5 +- crates/sdk/src/program.rs | 49 ++++++++---------- crates/sdk/src/provider.rs | 76 ++++++++++++++++++++++++++++ crates/sdk/src/signed_transaction.rs | 60 +++++++++++++++------- crates/sdk/src/signer.rs | 14 ++--- crates/sdk/src/witness.rs | 5 ++ 12 files changed, 184 insertions(+), 109 deletions(-) create mode 100644 crates/sdk/src/arguments.rs create mode 100644 crates/sdk/src/provider.rs create mode 100644 crates/sdk/src/witness.rs diff --git a/Cargo.lock b/Cargo.lock index 32f8208..79a519e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1769,6 +1769,7 @@ dependencies = [ name = "simplex-sdk" version = "0.1.0" dependencies = [ + "minreq", "sha2", "simplicityhl", "thiserror", diff --git a/crates/artifacts/src/asset_auth.rs b/crates/artifacts/src/asset_auth.rs index b0fd940..fea1b84 100644 --- a/crates/artifacts/src/asset_auth.rs +++ b/crates/artifacts/src/asset_auth.rs @@ -1,4 +1,6 @@ -use simplex_sdk::program::{ArgumentsTrait, Program}; +use simplex_sdk::arguments::ArgumentsTrait; +use simplex_sdk::program::Program; + use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; // use simplex_macros::simplex_build; @@ -12,7 +14,7 @@ pub struct AssetAuth<'a> { impl<'a> AssetAuth<'a> { // the path is autogenerated pub const SOURCE: &'static str = ""; - // include_str!("../../contracts/src/asset_auth/source_simf/asset_auth.simf"); + // include_str!("../../contracts/src/asset_auth/source_simf/asset_auth.simf"); pub fn new(public_key: &'a XOnlyPublicKey, arguments: &'a impl ArgumentsTrait) -> Self { Self { @@ -28,8 +30,8 @@ impl<'a> AssetAuth<'a> { // Expanded by macro pub mod asset_auth_build { - use simplex_sdk::program::WitnessTrait; - use simplex_sdk::program::ArgumentsTrait; + use simplex_sdk::arguments::ArgumentsTrait; + use simplex_sdk::witness::WitnessTrait; use simplicityhl::value::UIntValue; use simplicityhl::value::ValueConstructible; use simplicityhl::{Value, WitnessValues}; diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 9b871f0..6bd0c5e 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -13,4 +13,5 @@ workspace = true thiserror = "2" sha2 = { workspace = true } +minreq = { workspace = true } simplicityhl = { workspace = true } diff --git a/crates/sdk/src/arguments.rs b/crates/sdk/src/arguments.rs new file mode 100644 index 0000000..f539c33 --- /dev/null +++ b/crates/sdk/src/arguments.rs @@ -0,0 +1,6 @@ +use simplicityhl::{Arguments}; + +pub trait ArgumentsTrait { + fn build_arguments(&self) -> Arguments; +} + diff --git a/crates/sdk/src/constants.rs b/crates/sdk/src/constants.rs index e584c66..93a3c78 100644 --- a/crates/sdk/src/constants.rs +++ b/crates/sdk/src/constants.rs @@ -1,10 +1,3 @@ -//! Common Liquid network constants and helpers. -//! -//! Exposes policy asset identifiers and the Liquid testnet genesis hash. -//! -//! These are used throughout the CLI and examples to ensure consistent -//! parameters when constructing Elements transactions. - use simplicityhl::simplicity::elements; use simplicityhl::simplicity::hashes::{Hash, sha256}; @@ -12,6 +5,11 @@ use std::str::FromStr; pub const PUBLIC_SECRET_BLINDER_KEY: [u8; 32] = [1; 32]; +pub const DEFAULT_TARGET_BLOCKS: u32 = 0; +pub const DEFAULT_FEE_RATE: f32 = 100.0; +pub const WITNESS_SCALE_FACTOR: usize = 4; +pub const PLACEHOLDER_FEE: u64 = 1; + /// Policy asset id (hex, BE) for Liquid mainnet. pub const LIQUID_POLICY_ASSET_STR: &str = "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d"; @@ -28,7 +26,6 @@ pub const LIQUID_DEFAULT_REGTEST_ASSET_STR: &str = pub static LIQUID_TESTNET_TEST_ASSET_ID_STR: &str = "38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5"; -/// LBTC asset id for Liquid testnet. pub static LIQUID_TESTNET_BITCOIN_ASSET: std::sync::LazyLock = std::sync::LazyLock::new(|| { elements::AssetId::from_inner(sha256::Midstate([ @@ -38,7 +35,6 @@ pub static LIQUID_TESTNET_BITCOIN_ASSET: std::sync::LazyLock ])) }); -/// Genesis block hash for Liquid mainnet. pub static LIQUID_MAINNET_GENESIS: std::sync::LazyLock = std::sync::LazyLock::new(|| { elements::BlockHash::from_byte_array([ @@ -48,7 +44,6 @@ pub static LIQUID_MAINNET_GENESIS: std::sync::LazyLock = ]) }); -/// Genesis block hash for Liquid testnet. pub static LIQUID_TESTNET_GENESIS: std::sync::LazyLock = std::sync::LazyLock::new(|| { elements::BlockHash::from_byte_array([ @@ -58,7 +53,6 @@ pub static LIQUID_TESTNET_GENESIS: std::sync::LazyLock = ]) }); -/// Genesis block hash for Liquid regtest. pub static LIQUID_REGTEST_GENESIS: std::sync::LazyLock = std::sync::LazyLock::new(|| { elements::BlockHash::from_byte_array([ @@ -68,39 +62,21 @@ pub static LIQUID_REGTEST_GENESIS: std::sync::LazyLock = ]) }); -/// The network of the elements blockchain. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum SimplicityNetwork { - // Liquid mainnet policy asset Liquid, - // Liquid testnet policy asset LiquidTestnet, - /// Liquid regtest with a custom policy asset. ElementsRegtest { - /// The policy asset to use for this regtest network. - /// You can use the default one using [`SimplicityNetwork::default_regtest()`]. policy_asset: elements::AssetId, }, } impl SimplicityNetwork { - /// Return the default policy asset for regtest network. - /// - /// # Panics - /// - /// Doesn't panic as constants are defined correctly. - #[must_use] pub fn default_regtest() -> Self { let policy_asset = elements::AssetId::from_str(LIQUID_DEFAULT_REGTEST_ASSET_STR).unwrap(); Self::ElementsRegtest { policy_asset } } - /// Return the policy asset for specific network. - /// - /// # Panics - /// - /// Doesn't panic as constants are defined correctly. - #[must_use] pub fn policy_asset(&self) -> elements::AssetId { match self { Self::Liquid => elements::AssetId::from_str(LIQUID_POLICY_ASSET_STR).unwrap(), @@ -111,8 +87,6 @@ impl SimplicityNetwork { } } - /// Return the genesis block hash for this network. - #[must_use] pub fn genesis_block_hash(&self) -> elements::BlockHash { match self { Self::Liquid => *LIQUID_MAINNET_GENESIS, @@ -121,8 +95,6 @@ impl SimplicityNetwork { } } - /// Return the address parameters for this network to generate addresses compatible for this network. - #[must_use] pub const fn address_params(&self) -> &'static elements::AddressParams { match self { Self::Liquid => &elements::AddressParams::LIQUID, diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs index 3d5fa9a..201e798 100644 --- a/crates/sdk/src/error.rs +++ b/crates/sdk/src/error.rs @@ -1,15 +1,13 @@ use simplicityhl::elements::secp256k1_zkp; #[derive(Debug, thiserror::Error)] -pub enum ProgramError { +pub enum SimplexError { #[error("Failed to compile Simplicity program: {0}")] Compilation(String), - /// Returned when witness values cannot satisfy the program's requirements. #[error("Failed to satisfy witness: {0}")] WitnessSatisfaction(String), - /// Returned when the program cannot be pruned against the transaction environment. #[error("Failed to prune program: {0}")] Pruning(#[from] simplicityhl::simplicity::bit_machine::ExecutionError), @@ -20,30 +18,26 @@ pub enum ProgramError { Execution(simplicityhl::simplicity::bit_machine::ExecutionError), #[error("UTXO index {input_index} out of bounds (have {utxo_count} UTXOs)")] - UtxoIndexOutOfBounds { - input_index: usize, - utxo_count: usize, - }, + UtxoIndexOutOfBounds { input_index: usize, utxo_count: usize }, - /// Returned when the UTXO's script does not match the expected program address. #[error("Script pubkey mismatch: expected hash {expected_hash}, got {actual_hash}")] - ScriptPubkeyMismatch { - expected_hash: String, - actual_hash: String, - }, + ScriptPubkeyMismatch { expected_hash: String, actual_hash: String }, #[error("Input index exceeds u32 maximum: {0}")] InputIndexOverflow(#[from] std::num::TryFromIntError), -} -#[derive(Debug, thiserror::Error)] -pub enum SignerError { #[error("Invalid seed length: expected 32 bytes, got {0}")] InvalidSeedLength(usize), #[error("Invalid secret key")] InvalidSecretKey(#[from] secp256k1_zkp::UpstreamError), - #[error("Program error")] - Address(#[from] ProgramError), + #[error("HTTP request failed: {0}")] + Request(String), + + #[error("Failed to deserialize response: {0}")] + Deserialize(String), + + #[error("Invalid txid format: {0}")] + InvalidTxid(String), } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index bce32c8..680a2c0 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -1,6 +1,9 @@ +pub mod signed_transaction; pub mod program; +pub mod arguments; +pub mod witness; pub mod signer; -pub mod signed_transaction; +pub mod provider; pub mod utils; pub mod constants; pub mod error; diff --git a/crates/sdk/src/program.rs b/crates/sdk/src/program.rs index 87124d1..14e7cc9 100644 --- a/crates/sdk/src/program.rs +++ b/crates/sdk/src/program.rs @@ -3,24 +3,17 @@ use std::sync::Arc; use sha2::{Digest, Sha256}; use simplicityhl::CompiledProgram; +use simplicityhl::WitnessValues; use simplicityhl::elements::{Address, Script, Transaction, TxInWitness, TxOut, script, taproot}; use simplicityhl::simplicity::bitcoin::{XOnlyPublicKey, secp256k1}; use simplicityhl::simplicity::jet::Elements; use simplicityhl::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; use simplicityhl::simplicity::{BitMachine, RedeemNode, Value}; use simplicityhl::tracker::{DefaultTracker, TrackerLogLevel}; -use simplicityhl::{Arguments, WitnessValues}; +use crate::arguments::ArgumentsTrait; use crate::constants::SimplicityNetwork; -use crate::error::ProgramError; - -pub trait ArgumentsTrait { - fn build_arguments(&self) -> Arguments; -} - -pub trait WitnessTrait { - fn build_witness(&self) -> WitnessValues; -} +use crate::error::SimplexError; pub trait ProgramTrait { fn get_env( @@ -29,7 +22,7 @@ pub trait ProgramTrait { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result>, ProgramError>; + ) -> Result>, SimplexError>; fn execute( &self, @@ -38,7 +31,7 @@ pub trait ProgramTrait { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result<(Arc>, Value), ProgramError>; + ) -> Result<(Arc>, Value), SimplexError>; fn finalize( &self, @@ -47,7 +40,7 @@ pub trait ProgramTrait { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result; + ) -> Result; } pub struct Program<'a> { @@ -63,12 +56,12 @@ impl<'a> ProgramTrait for Program<'a> { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result>, ProgramError> { + ) -> Result>, SimplexError> { let genesis_hash = network.genesis_block_hash(); let cmr = self.load()?.commit().cmr(); if utxos.len() <= input_index { - return Err(ProgramError::UtxoIndexOutOfBounds { + return Err(SimplexError::UtxoIndexOutOfBounds { input_index, utxo_count: utxos.len(), }); @@ -78,7 +71,7 @@ impl<'a> ProgramTrait for Program<'a> { let script_pubkey = self.get_tr_address(network)?.script_pubkey(); if target_utxo.script_pubkey != script_pubkey { - return Err(ProgramError::ScriptPubkeyMismatch { + return Err(SimplexError::ScriptPubkeyMismatch { expected_hash: script_pubkey.script_hash().to_string(), actual_hash: target_utxo.script_pubkey.script_hash().to_string(), }); @@ -109,11 +102,11 @@ impl<'a> ProgramTrait for Program<'a> { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result<(Arc>, Value), ProgramError> { + ) -> Result<(Arc>, Value), SimplexError> { let satisfied = self .load()? .satisfy(witness) - .map_err(ProgramError::WitnessSatisfaction)?; + .map_err(SimplexError::WitnessSatisfaction)?; let mut tracker = DefaultTracker::new(satisfied.debug_symbols()).with_log_level(TrackerLogLevel::Debug); @@ -122,7 +115,7 @@ impl<'a> ProgramTrait for Program<'a> { let pruned = satisfied.redeem().prune_with_tracker(&env, &mut tracker)?; let mut mac = BitMachine::for_program(&pruned)?; - let result = mac.exec(&pruned, &env).map_err(ProgramError::Execution)?; + let result = mac.exec(&pruned, &env).map_err(SimplexError::Execution)?; Ok((pruned, result)) } @@ -134,7 +127,7 @@ impl<'a> ProgramTrait for Program<'a> { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result { + ) -> Result { let pruned = self.execute(witness, &tx, utxos, input_index, network)?.0; let (simplicity_program_bytes, simplicity_witness_bytes) = pruned.to_vec_with_witness(); @@ -165,7 +158,7 @@ impl<'a> Program<'a> { } } - pub fn get_tr_address(&self, network: SimplicityNetwork) -> Result { + pub fn get_tr_address(&self, network: SimplicityNetwork) -> Result { let spend_info = self.taproot_spending_info()?; Ok(Address::p2tr( @@ -177,11 +170,11 @@ impl<'a> Program<'a> { )) } - pub fn get_script_pubkey(&self, network: SimplicityNetwork) -> Result { + pub fn get_script_pubkey(&self, network: SimplicityNetwork) -> Result { Ok(self.get_tr_address(network)?.script_pubkey()) } - pub fn get_script_hash(&self, network: SimplicityNetwork) -> Result<[u8; 32], ProgramError> { + pub fn get_script_hash(&self, network: SimplicityNetwork) -> Result<[u8; 32], SimplexError> { let script = self.get_script_pubkey(network)?; let mut hasher = Sha256::new(); @@ -189,20 +182,20 @@ impl<'a> Program<'a> { Ok(hasher.finalize().into()) } - fn load(&self) -> Result { + fn load(&self) -> Result { let compiled = CompiledProgram::new(self.source, self.arguments.build_arguments(), true) - .map_err(ProgramError::Compilation)?; + .map_err(SimplexError::Compilation)?; Ok(compiled) } - fn script_version(&self) -> Result<(Script, taproot::LeafVersion), ProgramError> { + fn script_version(&self) -> Result<(Script, taproot::LeafVersion), SimplexError> { let cmr = self.load()?.commit().cmr(); let script = script::Script::from(cmr.as_ref().to_vec()); Ok((script, simplicityhl::simplicity::leaf_version())) } - fn taproot_spending_info(&self) -> Result { + fn taproot_spending_info(&self) -> Result { let builder = taproot::TaprootBuilder::new(); let (script, version) = self.script_version()?; @@ -215,7 +208,7 @@ impl<'a> Program<'a> { .expect("tap tree should be valid")) } - fn control_block(&self) -> Result { + fn control_block(&self) -> Result { let info = self.taproot_spending_info()?; let script_ver = self.script_version()?; diff --git a/crates/sdk/src/provider.rs b/crates/sdk/src/provider.rs new file mode 100644 index 0000000..50b25c9 --- /dev/null +++ b/crates/sdk/src/provider.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; + +use crate::constants::DEFAULT_FEE_RATE; +use crate::error::SimplexError; + +pub type FeeEstimates = HashMap; + +pub trait Provider { + fn fetch_fee_estimates(&self) -> Result; + + fn get_fee_rate(&self, target_blocks: u32) -> Result { + if target_blocks == 0 { + return Ok(DEFAULT_FEE_RATE); + } + + let estimates = self.fetch_fee_estimates()?; + + let target_str = target_blocks.to_string(); + + if let Some(&rate) = estimates.get(&target_str) { + return Ok((rate * 1000.0) as f32); // Convert sat/vB to sats/kvb + } + + let fallback_targets = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 144, 504, 1008, + ]; + + for &target in fallback_targets.iter().filter(|&&t| t >= target_blocks) { + let key = target.to_string(); + + if let Some(&rate) = estimates.get(&key) { + return Ok((rate * 1000.0) as f32); + } + } + + for &target in &fallback_targets { + let key = target.to_string(); + + if let Some(&rate) = estimates.get(&key) { + return Ok((rate * 1000.0) as f32); + } + } + + Err(SimplexError::Request("No fee estimates available".to_string())) + } +} + +pub struct EsploraProvider { + esplora_url: String, +} + +impl EsploraProvider { + pub fn new(url: String) -> Self { + Self { esplora_url: url } + } +} + +impl Provider for EsploraProvider { + fn fetch_fee_estimates(&self) -> Result { + let url = self.esplora_url.clone() + "/fee-estimates"; + let response = minreq::get(&url) + .send() + .map_err(|e| SimplexError::Request(e.to_string()))?; + + if response.status_code != 200 { + return Err(SimplexError::Request(format!( + "HTTP {}: {}", + response.status_code, response.reason_phrase + ))); + } + + let estimates: FeeEstimates = response.json().map_err(|e| SimplexError::Deserialize(e.to_string()))?; + + Ok(estimates) + } +} diff --git a/crates/sdk/src/signed_transaction.rs b/crates/sdk/src/signed_transaction.rs index 9c18d81..a94d2a3 100644 --- a/crates/sdk/src/signed_transaction.rs +++ b/crates/sdk/src/signed_transaction.rs @@ -1,12 +1,13 @@ -use simplicityhl::elements::pset::{Input, Output, PartiallySignedTransaction}; +use simplicityhl::WitnessValues; use simplicityhl::elements::secp256k1_zkp::schnorr::Signature; -use simplicityhl::elements::{Address, Script, Transaction, TxInWitness, TxOut, script, taproot}; -use simplicityhl::{Value, WitnessValues}; +use simplicityhl::elements::{Transaction, TxOut}; -use crate::constants::SimplicityNetwork; -use crate::error::SignerError; -use crate::program::{ProgramTrait, WitnessTrait}; +use crate::constants::{SimplicityNetwork, WITNESS_SCALE_FACTOR}; +use crate::error::SimplexError; +use crate::program::ProgramTrait; +use crate::provider::Provider; use crate::signer::SignerTrait; +use crate::witness::WitnessTrait; struct SignedInput<'a, T> { program: &'a dyn ProgramTrait, @@ -24,7 +25,7 @@ pub struct SignedTransaction<'a, T> { impl<'a, T> SignedTransaction<'a, T> where - T: Fn(WitnessValues, Signature) -> Result + Clone, + T: Fn(WitnessValues, Signature) -> Result + Clone, { pub fn new(tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork) -> Self { Self { @@ -63,7 +64,22 @@ where self.inputs.push(signed_input); } - pub fn finalize(&mut self) -> Result { + pub fn finalize_with_fee( + &self, + target_blocks: u32, + provider: impl Provider, + ) -> Result<(Transaction, u64), SimplexError> { + let fee_rate = provider.get_fee_rate(target_blocks)?; + let final_tx = self.finalize()?; + + let fee = self.calculate_fee(final_tx.weight(), fee_rate); + + Ok((final_tx, fee)) + } + + pub fn finalize(&self) -> Result { + let mut final_tx = self.tx.clone(); + for index in 0..self.inputs.len() { let (program, witness, signer, signer_lambda) = { let input = &self.inputs[index]; @@ -71,7 +87,8 @@ where }; if signer.is_some() { - self.finalize_with_signer( + final_tx = self.finalize_with_signer( + final_tx, program, witness.build_witness(), index, @@ -79,35 +96,40 @@ where signer_lambda.unwrap(), )?; } else { - self.finalize_as_is(program, witness.build_witness(), index)?; + final_tx = self.finalize_as_is(final_tx, program, witness.build_witness(), index)?; } } - Ok(self.tx.clone()) + Ok(final_tx) } fn finalize_with_signer( - &mut self, + &self, + final_tx: Transaction, program: &dyn ProgramTrait, witness: WitnessValues, index: usize, signer: &dyn SignerTrait, signer_lambda: T, - ) -> Result<(), SignerError> { - let signature = signer.sign(program, &self.tx, self.utxos, index, self.network)?; + ) -> Result { + let signature = signer.sign(program, &final_tx, self.utxos, index, self.network)?; let new_witness = signer_lambda(witness, signature)?; - self.finalize_as_is(program, new_witness, index) + Ok(self.finalize_as_is(final_tx, program, new_witness, index)?) } fn finalize_as_is( - &mut self, + &self, + final_tx: Transaction, program: &dyn ProgramTrait, witness: WitnessValues, index: usize, - ) -> Result<(), SignerError> { - self.tx = program.finalize(witness, self.tx.clone(), self.utxos, index, self.network)?; + ) -> Result { + Ok(program.finalize(witness, final_tx, self.utxos, index, self.network)?) + } - Ok(()) + fn calculate_fee(&self, weight: usize, fee_rate: f32) -> u64 { + let vsize = weight.div_ceil(WITNESS_SCALE_FACTOR); + (vsize as f32 * fee_rate / 1000.0).ceil() as u64 } } diff --git a/crates/sdk/src/signer.rs b/crates/sdk/src/signer.rs index 0e4925c..fa3002a 100644 --- a/crates/sdk/src/signer.rs +++ b/crates/sdk/src/signer.rs @@ -4,13 +4,13 @@ use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; use simplicityhl::simplicity::hashes::Hash as _; use crate::constants::SimplicityNetwork; -use crate::error::SignerError; -use crate::program::{ProgramTrait}; +use crate::error::SimplexError; +use crate::program::ProgramTrait; pub trait SignerTrait { fn public_key(&self) -> XOnlyPublicKey; - fn personal_sign(&self, message: Message) -> Result; + fn personal_sign(&self, message: Message) -> Result; fn sign<'a>( &self, @@ -19,7 +19,7 @@ pub trait SignerTrait { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result; + ) -> Result; } pub struct Signer { @@ -31,7 +31,7 @@ impl SignerTrait for Signer { self.keypair.x_only_public_key().0 } - fn personal_sign(&self, message: Message) -> Result { + fn personal_sign(&self, message: Message) -> Result { Ok(self.keypair.sign_schnorr(message)) } @@ -42,7 +42,7 @@ impl SignerTrait for Signer { utxos: &[TxOut], input_index: usize, network: SimplicityNetwork, - ) -> Result { + ) -> Result { let env = program.get_env(tx, utxos, input_index, network)?; let sighash_all = Message::from_digest(env.c_tx_env().sighash_all().to_byte_array()); @@ -54,7 +54,7 @@ impl SignerTrait for Signer { impl Signer { pub const SEED_LEN: usize = secp256k1::constants::SECRET_KEY_SIZE; - pub fn from_seed(seed: &[u8; Self::SEED_LEN]) -> Result { + pub fn from_seed(seed: &[u8; Self::SEED_LEN]) -> Result { let secp = secp256k1::Secp256k1::new(); let secret_key = secp256k1::SecretKey::from_slice(seed)?; diff --git a/crates/sdk/src/witness.rs b/crates/sdk/src/witness.rs new file mode 100644 index 0000000..526c48e --- /dev/null +++ b/crates/sdk/src/witness.rs @@ -0,0 +1,5 @@ +use simplicityhl::WitnessValues; + +pub trait WitnessTrait { + fn build_witness(&self) -> WitnessValues; +} From 5480eafee170a508d384061fecbd96f2af5de7ab Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Tue, 17 Feb 2026 19:37:59 +0200 Subject: [PATCH 5/9] some provider methods --- crates/sdk/src/error.rs | 3 ++ crates/sdk/src/provider.rs | 59 ++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs index 201e798..dafe435 100644 --- a/crates/sdk/src/error.rs +++ b/crates/sdk/src/error.rs @@ -35,6 +35,9 @@ pub enum SimplexError { #[error("HTTP request failed: {0}")] Request(String), + #[error("Broadcast failed with HTTP {status} for {url}: {message}")] + BroadcastRejected { status: u16, url: String, message: String }, + #[error("Failed to deserialize response: {0}")] Deserialize(String), diff --git a/crates/sdk/src/provider.rs b/crates/sdk/src/provider.rs index 50b25c9..7568f30 100644 --- a/crates/sdk/src/provider.rs +++ b/crates/sdk/src/provider.rs @@ -1,12 +1,18 @@ use std::collections::HashMap; +use simplicityhl::elements::encode; +use simplicityhl::elements::hex::ToHex; +use simplicityhl::elements::{Transaction, Txid}; + use crate::constants::DEFAULT_FEE_RATE; use crate::error::SimplexError; -pub type FeeEstimates = HashMap; - pub trait Provider { - fn fetch_fee_estimates(&self) -> Result; + fn broadcast_transaction(&self, tx: &Transaction) -> Result; + + fn fetch_fee_estimates(&self) -> Result, SimplexError>; + + fn fetch_transaction(&self, txid: Txid) -> Result; fn get_fee_rate(&self, target_blocks: u32) -> Result { if target_blocks == 0 { @@ -56,7 +62,31 @@ impl EsploraProvider { } impl Provider for EsploraProvider { - fn fetch_fee_estimates(&self) -> Result { + fn broadcast_transaction(&self, tx: &Transaction) -> Result { + let tx_hex = encode::serialize_hex(tx); + let url = format!("{}/tx", self.esplora_url); + + let response = minreq::post(&url) + .with_body(tx_hex) + .send() + .map_err(|e| SimplexError::Request(e.to_string()))?; + + let status = response.status_code; + let body = response.as_str().unwrap_or("").trim().to_owned(); + + if !(200..300).contains(&status) { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + return Err(SimplexError::BroadcastRejected { + status: status as u16, + url: format!("{}/tx", self.esplora_url), + message: body, + }); + } + + Ok(body) + } + + fn fetch_fee_estimates(&self) -> Result, SimplexError> { let url = self.esplora_url.clone() + "/fee-estimates"; let response = minreq::get(&url) .send() @@ -69,8 +99,27 @@ impl Provider for EsploraProvider { ))); } - let estimates: FeeEstimates = response.json().map_err(|e| SimplexError::Deserialize(e.to_string()))?; + let estimates: HashMap = response.json().map_err(|e| SimplexError::Deserialize(e.to_string()))?; Ok(estimates) } + + fn fetch_transaction(&self, txid: Txid) -> Result { + let url = self.esplora_url.clone() + "/tx/" + txid.to_hex().as_str() + "/raw"; + let response = minreq::get(&url) + .send() + .map_err(|e| SimplexError::Request(e.to_string()))?; + + if response.status_code != 200 { + return Err(SimplexError::Request(format!( + "HTTP {}: {}", + response.status_code, response.reason_phrase + ))); + } + + let bytes = response.as_bytes(); + let tx: Transaction = encode::deserialize(bytes).map_err(|e| SimplexError::Deserialize(e.to_string()))?; + + Ok(tx) + } } From 1fde8db7d2c012e04a4ab1a94f7f6f8c7d8952c1 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Wed, 18 Feb 2026 17:35:32 +0200 Subject: [PATCH 6/9] p2pk preset --- crates/sdk/src/constants.rs | 88 +++++------- crates/sdk/src/lib.rs | 3 +- crates/sdk/src/presets/mod.rs | 1 + crates/sdk/src/presets/p2pk.rs | 130 ++++++++++++++++++ crates/sdk/src/presets/simf/p2pk.simf | 3 + crates/sdk/src/provider.rs | 2 +- crates/sdk/src/signer.rs | 1 + ..._transaction.rs => witness_transaction.rs} | 8 +- crates/simplex/examples/example.rs | 48 +++---- crates/simplex/examples/source_simf/p2pk.simf | 3 + crates/user/src/main.rs | 28 ++-- 11 files changed, 225 insertions(+), 90 deletions(-) create mode 100644 crates/sdk/src/presets/mod.rs create mode 100644 crates/sdk/src/presets/p2pk.rs create mode 100644 crates/sdk/src/presets/simf/p2pk.simf rename crates/sdk/src/{signed_transaction.rs => witness_transaction.rs} (94%) create mode 100644 crates/simplex/examples/source_simf/p2pk.simf diff --git a/crates/sdk/src/constants.rs b/crates/sdk/src/constants.rs index 93a3c78..e7fab55 100644 --- a/crates/sdk/src/constants.rs +++ b/crates/sdk/src/constants.rs @@ -4,71 +4,59 @@ use simplicityhl::simplicity::hashes::{Hash, sha256}; use std::str::FromStr; pub const PUBLIC_SECRET_BLINDER_KEY: [u8; 32] = [1; 32]; +pub const DUMMY_SIGNATURE: [u8; 64] = [1; 64]; pub const DEFAULT_TARGET_BLOCKS: u32 = 0; pub const DEFAULT_FEE_RATE: f32 = 100.0; -pub const WITNESS_SCALE_FACTOR: usize = 4; pub const PLACEHOLDER_FEE: u64 = 1; +pub const WITNESS_SCALE_FACTOR: usize = 4; + /// Policy asset id (hex, BE) for Liquid mainnet. -pub const LIQUID_POLICY_ASSET_STR: &str = - "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d"; +pub const LIQUID_POLICY_ASSET_STR: &str = "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d"; /// Policy asset id (hex, BE) for Liquid testnet. -pub const LIQUID_TESTNET_POLICY_ASSET_STR: &str = - "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49"; +pub const LIQUID_TESTNET_POLICY_ASSET_STR: &str = "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49"; /// Policy asset id (hex, BE) for Elements regtest. -pub const LIQUID_DEFAULT_REGTEST_ASSET_STR: &str = - "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"; +pub const LIQUID_DEFAULT_REGTEST_ASSET_STR: &str = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"; /// Example test asset id (hex, BE) on Liquid testnet. -pub static LIQUID_TESTNET_TEST_ASSET_ID_STR: &str = - "38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5"; - -pub static LIQUID_TESTNET_BITCOIN_ASSET: std::sync::LazyLock = - std::sync::LazyLock::new(|| { - elements::AssetId::from_inner(sha256::Midstate([ - 0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3, 0x9f, 0xc0, 0x3b, 0x63, 0x7f, 0x2a, - 0x4e, 0x1e, 0x64, 0xe5, 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f, 0x6d, 0x71, 0xaa, 0x44, - 0x43, 0x65, 0x4c, 0x14, - ])) - }); - -pub static LIQUID_MAINNET_GENESIS: std::sync::LazyLock = - std::sync::LazyLock::new(|| { - elements::BlockHash::from_byte_array([ - 0x03, 0x60, 0x20, 0x8a, 0x88, 0x96, 0x92, 0x37, 0x2c, 0x8d, 0x68, 0xb0, 0x84, 0xa6, - 0x2e, 0xfd, 0xf6, 0x0e, 0xa1, 0xa3, 0x59, 0xa0, 0x4c, 0x94, 0xb2, 0x0d, 0x22, 0x36, - 0x58, 0x27, 0x66, 0x14, - ]) - }); - -pub static LIQUID_TESTNET_GENESIS: std::sync::LazyLock = - std::sync::LazyLock::new(|| { - elements::BlockHash::from_byte_array([ - 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, - 0x79, 0x3b, 0x5b, 0x5e, 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, - 0x8e, 0xda, 0x71, 0xa7, - ]) - }); - -pub static LIQUID_REGTEST_GENESIS: std::sync::LazyLock = - std::sync::LazyLock::new(|| { - elements::BlockHash::from_byte_array([ - 0x21, 0xca, 0xb1, 0xe5, 0xda, 0x47, 0x18, 0xea, 0x14, 0x0d, 0x97, 0x16, 0x93, 0x17, - 0x02, 0x42, 0x2f, 0x0e, 0x6a, 0xd9, 0x15, 0xc8, 0xd9, 0xb5, 0x83, 0xca, 0xc2, 0x70, - 0x6b, 0x2a, 0x90, 0x00, - ]) - }); +pub static LIQUID_TESTNET_TEST_ASSET_ID_STR: &str = "38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5"; + +pub static LIQUID_TESTNET_BITCOIN_ASSET: std::sync::LazyLock = std::sync::LazyLock::new(|| { + elements::AssetId::from_inner(sha256::Midstate([ + 0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3, 0x9f, 0xc0, 0x3b, 0x63, 0x7f, 0x2a, 0x4e, 0x1e, 0x64, 0xe5, + 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f, 0x6d, 0x71, 0xaa, 0x44, 0x43, 0x65, 0x4c, 0x14, + ])) +}); + +pub static LIQUID_MAINNET_GENESIS: std::sync::LazyLock = std::sync::LazyLock::new(|| { + elements::BlockHash::from_byte_array([ + 0x03, 0x60, 0x20, 0x8a, 0x88, 0x96, 0x92, 0x37, 0x2c, 0x8d, 0x68, 0xb0, 0x84, 0xa6, 0x2e, 0xfd, 0xf6, 0x0e, + 0xa1, 0xa3, 0x59, 0xa0, 0x4c, 0x94, 0xb2, 0x0d, 0x22, 0x36, 0x58, 0x27, 0x66, 0x14, + ]) +}); + +pub static LIQUID_TESTNET_GENESIS: std::sync::LazyLock = std::sync::LazyLock::new(|| { + elements::BlockHash::from_byte_array([ + 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, 0x79, 0x3b, 0x5b, 0x5e, + 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, 0x8e, 0xda, 0x71, 0xa7, + ]) +}); + +pub static LIQUID_REGTEST_GENESIS: std::sync::LazyLock = std::sync::LazyLock::new(|| { + elements::BlockHash::from_byte_array([ + 0x21, 0xca, 0xb1, 0xe5, 0xda, 0x47, 0x18, 0xea, 0x14, 0x0d, 0x97, 0x16, 0x93, 0x17, 0x02, 0x42, 0x2f, 0x0e, + 0x6a, 0xd9, 0x15, 0xc8, 0xd9, 0xb5, 0x83, 0xca, 0xc2, 0x70, 0x6b, 0x2a, 0x90, 0x00, + ]) +}); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum SimplicityNetwork { Liquid, LiquidTestnet, - ElementsRegtest { - policy_asset: elements::AssetId, - }, + ElementsRegtest { policy_asset: elements::AssetId }, } impl SimplicityNetwork { @@ -80,9 +68,7 @@ impl SimplicityNetwork { pub fn policy_asset(&self) -> elements::AssetId { match self { Self::Liquid => elements::AssetId::from_str(LIQUID_POLICY_ASSET_STR).unwrap(), - Self::LiquidTestnet => { - elements::AssetId::from_str(LIQUID_TESTNET_POLICY_ASSET_STR).unwrap() - } + Self::LiquidTestnet => elements::AssetId::from_str(LIQUID_TESTNET_POLICY_ASSET_STR).unwrap(), Self::ElementsRegtest { policy_asset } => *policy_asset, } } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index 680a2c0..a0a5dc9 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -1,4 +1,4 @@ -pub mod signed_transaction; +pub mod witness_transaction; pub mod program; pub mod arguments; pub mod witness; @@ -7,3 +7,4 @@ pub mod provider; pub mod utils; pub mod constants; pub mod error; +pub mod presets; diff --git a/crates/sdk/src/presets/mod.rs b/crates/sdk/src/presets/mod.rs new file mode 100644 index 0000000..5083e09 --- /dev/null +++ b/crates/sdk/src/presets/mod.rs @@ -0,0 +1 @@ +pub mod p2pk; diff --git a/crates/sdk/src/presets/p2pk.rs b/crates/sdk/src/presets/p2pk.rs new file mode 100644 index 0000000..411f0be --- /dev/null +++ b/crates/sdk/src/presets/p2pk.rs @@ -0,0 +1,130 @@ +use crate::arguments::ArgumentsTrait; +use crate::program::Program; + +use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; + +// TODO macro +pub struct P2PK<'a> { + program: Program<'a>, +} + +impl<'a> P2PK<'a> { + pub const SOURCE: &'static str = include_str!("./simf/p2pk.simf"); + + pub fn new(public_key: &'a XOnlyPublicKey, arguments: &'a impl ArgumentsTrait) -> Self { + Self { + program: Program::new(Self::SOURCE, public_key, arguments), + } + } + + pub fn get_program(&self) -> &Program<'a> { + &self.program + } + + pub fn get_program_mut(&mut self) -> &mut Program<'a> { + &mut self.program + } +} + +pub mod p2pk_build { + use crate::arguments::ArgumentsTrait; + use crate::witness::WitnessTrait; + use simplicityhl::num::U256; + use simplicityhl::str::WitnessName; + use simplicityhl::types::TypeConstructible; + use simplicityhl::value::UIntValue; + use simplicityhl::value::ValueConstructible; + use simplicityhl::{Arguments, ResolvedType, Value, WitnessValues}; + use std::collections::HashMap; + + pub struct P2PKWitness { + pub signature: [u8; 64usize], + } + + pub struct P2PKArguments { + pub public_key: [u8; 32], + } + + impl WitnessTrait for P2PKWitness { + fn build_witness(&self) -> WitnessValues { + WitnessValues::from(HashMap::from([(WitnessName::from_str_unchecked("SIGNATURE"), { + let elements = [ + Value::from(UIntValue::U8(self.signature[0])), + Value::from(UIntValue::U8(self.signature[1])), + Value::from(UIntValue::U8(self.signature[2])), + Value::from(UIntValue::U8(self.signature[3])), + Value::from(UIntValue::U8(self.signature[4])), + Value::from(UIntValue::U8(self.signature[5])), + Value::from(UIntValue::U8(self.signature[6])), + Value::from(UIntValue::U8(self.signature[7])), + Value::from(UIntValue::U8(self.signature[8])), + Value::from(UIntValue::U8(self.signature[9])), + Value::from(UIntValue::U8(self.signature[10])), + Value::from(UIntValue::U8(self.signature[11])), + Value::from(UIntValue::U8(self.signature[12])), + Value::from(UIntValue::U8(self.signature[13])), + Value::from(UIntValue::U8(self.signature[14])), + Value::from(UIntValue::U8(self.signature[15])), + Value::from(UIntValue::U8(self.signature[16])), + Value::from(UIntValue::U8(self.signature[17])), + Value::from(UIntValue::U8(self.signature[18])), + Value::from(UIntValue::U8(self.signature[19])), + Value::from(UIntValue::U8(self.signature[20])), + Value::from(UIntValue::U8(self.signature[21])), + Value::from(UIntValue::U8(self.signature[22])), + Value::from(UIntValue::U8(self.signature[23])), + Value::from(UIntValue::U8(self.signature[24])), + Value::from(UIntValue::U8(self.signature[25])), + Value::from(UIntValue::U8(self.signature[26])), + Value::from(UIntValue::U8(self.signature[27])), + Value::from(UIntValue::U8(self.signature[28])), + Value::from(UIntValue::U8(self.signature[29])), + Value::from(UIntValue::U8(self.signature[30])), + Value::from(UIntValue::U8(self.signature[31])), + Value::from(UIntValue::U8(self.signature[32])), + Value::from(UIntValue::U8(self.signature[33])), + Value::from(UIntValue::U8(self.signature[34])), + Value::from(UIntValue::U8(self.signature[35])), + Value::from(UIntValue::U8(self.signature[36])), + Value::from(UIntValue::U8(self.signature[37])), + Value::from(UIntValue::U8(self.signature[38])), + Value::from(UIntValue::U8(self.signature[39])), + Value::from(UIntValue::U8(self.signature[40])), + Value::from(UIntValue::U8(self.signature[41])), + Value::from(UIntValue::U8(self.signature[42])), + Value::from(UIntValue::U8(self.signature[43])), + Value::from(UIntValue::U8(self.signature[44])), + Value::from(UIntValue::U8(self.signature[45])), + Value::from(UIntValue::U8(self.signature[46])), + Value::from(UIntValue::U8(self.signature[47])), + Value::from(UIntValue::U8(self.signature[48])), + Value::from(UIntValue::U8(self.signature[49])), + Value::from(UIntValue::U8(self.signature[50])), + Value::from(UIntValue::U8(self.signature[51])), + Value::from(UIntValue::U8(self.signature[52])), + Value::from(UIntValue::U8(self.signature[53])), + Value::from(UIntValue::U8(self.signature[54])), + Value::from(UIntValue::U8(self.signature[55])), + Value::from(UIntValue::U8(self.signature[56])), + Value::from(UIntValue::U8(self.signature[57])), + Value::from(UIntValue::U8(self.signature[58])), + Value::from(UIntValue::U8(self.signature[59])), + Value::from(UIntValue::U8(self.signature[60])), + Value::from(UIntValue::U8(self.signature[61])), + Value::from(UIntValue::U8(self.signature[62])), + Value::from(UIntValue::U8(self.signature[63])), + ]; + Value::array(elements, ResolvedType::u8()) + })])) + } + } + + impl ArgumentsTrait for P2PKArguments { + fn build_arguments(&self) -> Arguments { + Arguments::from(HashMap::from([( + WitnessName::from_str_unchecked("PUBLIC_KEY"), + Value::from(UIntValue::U256(U256::from_byte_array(self.public_key))), + )])) + } + } +} diff --git a/crates/sdk/src/presets/simf/p2pk.simf b/crates/sdk/src/presets/simf/p2pk.simf new file mode 100644 index 0000000..db4f27c --- /dev/null +++ b/crates/sdk/src/presets/simf/p2pk.simf @@ -0,0 +1,3 @@ +fn main() { + jet::bip_0340_verify((param::PUBLIC_KEY, jet::sig_all_hash()), witness::SIGNATURE) +} \ No newline at end of file diff --git a/crates/sdk/src/provider.rs b/crates/sdk/src/provider.rs index 7568f30..93f137f 100644 --- a/crates/sdk/src/provider.rs +++ b/crates/sdk/src/provider.rs @@ -7,6 +7,7 @@ use simplicityhl::elements::{Transaction, Txid}; use crate::constants::DEFAULT_FEE_RATE; use crate::error::SimplexError; +// TODO make everything async pub trait Provider { fn broadcast_transaction(&self, tx: &Transaction) -> Result; @@ -75,7 +76,6 @@ impl Provider for EsploraProvider { let body = response.as_str().unwrap_or("").trim().to_owned(); if !(200..300).contains(&status) { - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] return Err(SimplexError::BroadcastRejected { status: status as u16, url: format!("{}/tx", self.esplora_url), diff --git a/crates/sdk/src/signer.rs b/crates/sdk/src/signer.rs index fa3002a..d607309 100644 --- a/crates/sdk/src/signer.rs +++ b/crates/sdk/src/signer.rs @@ -31,6 +31,7 @@ impl SignerTrait for Signer { self.keypair.x_only_public_key().0 } + // TODO EIP-191 or EIP-712 messages fn personal_sign(&self, message: Message) -> Result { Ok(self.keypair.sign_schnorr(message)) } diff --git a/crates/sdk/src/signed_transaction.rs b/crates/sdk/src/witness_transaction.rs similarity index 94% rename from crates/sdk/src/signed_transaction.rs rename to crates/sdk/src/witness_transaction.rs index a94d2a3..e8c99b3 100644 --- a/crates/sdk/src/signed_transaction.rs +++ b/crates/sdk/src/witness_transaction.rs @@ -16,16 +16,16 @@ struct SignedInput<'a, T> { signer_lambda: Option, } -pub struct SignedTransaction<'a, T> { +pub struct WitnessTransaction<'a, T> { tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork, inputs: Vec>, } -impl<'a, T> SignedTransaction<'a, T> +impl<'a, T> WitnessTransaction<'a, T> where - T: Fn(WitnessValues, Signature) -> Result + Clone, + T: Fn(&WitnessValues, &Signature) -> Result + Clone, { pub fn new(tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork) -> Self { Self { @@ -113,7 +113,7 @@ where signer_lambda: T, ) -> Result { let signature = signer.sign(program, &final_tx, self.utxos, index, self.network)?; - let new_witness = signer_lambda(witness, signature)?; + let new_witness = signer_lambda(&witness, &signature)?; Ok(self.finalize_as_is(final_tx, program, new_witness, index)?) } diff --git a/crates/simplex/examples/example.rs b/crates/simplex/examples/example.rs index d5f6da7..58d1864 100644 --- a/crates/simplex/examples/example.rs +++ b/crates/simplex/examples/example.rs @@ -1,35 +1,35 @@ use simplex_macros::include_simf; -include_simf!("examples/source_simf/options.simf"); +include_simf!("examples/source_simf/p2pk.simf"); fn main() -> Result<(), String> { - let original_witness = derived_options::OptionsWitness { - path: simplicityhl::either::Either::Right(simplicityhl::either::Either::Left((true, 100, 200))), - }; + // let original_witness = derived_options::OptionsWitness { + // path: simplicityhl::either::Either::Right(simplicityhl::either::Either::Left((true, 100, 200))), + // }; - let witness_values = original_witness.build_witness(); - let recovered_witness = derived_options::OptionsWitness::from_witness(&witness_values)?; - assert_eq!(original_witness, recovered_witness); + // let witness_values = original_witness.build_witness(); + // let recovered_witness = derived_options::OptionsWitness::from_witness(&witness_values)?; + // assert_eq!(original_witness, recovered_witness); - let original_arguments = derived_options::OptionsArguments { - start_time: 0, - expiry_time: 0, - grantor_reissuance_token_asset: Default::default(), - grantor_token_asset: Default::default(), - settlement_per_contract: Default::default(), - settlement_asset_id: Default::default(), - collateral_per_contract: Default::default(), - collateral_asset_id: Default::default(), - option_reissuance_token_asset: Default::default(), - option_token_asset: Default::default(), - }; + // let original_arguments = derived_options::OptionsArguments { + // start_time: 0, + // expiry_time: 0, + // grantor_reissuance_token_asset: Default::default(), + // grantor_token_asset: Default::default(), + // settlement_per_contract: Default::default(), + // settlement_asset_id: Default::default(), + // collateral_per_contract: Default::default(), + // collateral_asset_id: Default::default(), + // option_reissuance_token_asset: Default::default(), + // option_token_asset: Default::default(), + // }; - let witness_values = original_arguments.build_arguments(); - let recovered_witness = derived_options::OptionsArguments::from_arguments(&witness_values)?; - assert_eq!(original_arguments, recovered_witness); + // let witness_values = original_arguments.build_arguments(); + // let recovered_witness = derived_options::OptionsArguments::from_arguments(&witness_values)?; + // assert_eq!(original_arguments, recovered_witness); - let _template = derived_options::get_template_program(); - let _compiled = derived_options::get_compiled_program(&original_arguments); + // let _template = derived_options::get_template_program(); + // let _compiled = derived_options::get_compiled_program(&original_arguments); Ok(()) } diff --git a/crates/simplex/examples/source_simf/p2pk.simf b/crates/simplex/examples/source_simf/p2pk.simf new file mode 100644 index 0000000..db4f27c --- /dev/null +++ b/crates/simplex/examples/source_simf/p2pk.simf @@ -0,0 +1,3 @@ +fn main() { + jet::bip_0340_verify((param::PUBLIC_KEY, jet::sig_all_hash()), witness::SIGNATURE) +} \ No newline at end of file diff --git a/crates/user/src/main.rs b/crates/user/src/main.rs index 99ea508..65d8931 100644 --- a/crates/user/src/main.rs +++ b/crates/user/src/main.rs @@ -1,17 +1,27 @@ -use simplex_build::asset_auth::AssetAuth; -use simplex_build::asset_auth::asset_auth_build::{AssetAuthArguments, AssetAuthWitness}; +use simplex_sdk::presets::p2pk::P2PK; +use simplex_sdk::presets::p2pk::p2pk_build::{P2PKArguments, P2PKWitness}; +use simplex_sdk::signer::{Signer, SignerTrait}; + +use simplex_sdk::constants::DUMMY_SIGNATURE; use simplex_sdk::utils::tr_unspendable_key; fn main() { - let witness = AssetAuthWitness { - path: (false, 1, 1), - }; + let signer = Signer::from_seed( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + .as_bytes() + .try_into() + .unwrap(), + ) + .unwrap(); - let arguments = AssetAuthArguments { - first: 1, - second: false, + let witness = P2PKWitness { + signature: DUMMY_SIGNATURE, }; - let asset_auth = AssetAuth::new(&tr_unspendable_key(), &arguments); + // let arguments = P2PKArguments { + // public_key: signer.public_key(), + // }; + + // let p2pk = P2PK::new(&tr_unspendable_key(), &arguments); } From ca526de59749a7f517241dfd877db3724835a678 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Wed, 18 Feb 2026 19:29:28 +0200 Subject: [PATCH 7/9] wintess tx fee estimation rewrite --- crates/sdk/src/constants.rs | 1 + crates/sdk/src/error.rs | 9 ++ crates/sdk/src/witness_transaction.rs | 120 ++++++++++++++++++++++---- 3 files changed, 113 insertions(+), 17 deletions(-) diff --git a/crates/sdk/src/constants.rs b/crates/sdk/src/constants.rs index e7fab55..87339cf 100644 --- a/crates/sdk/src/constants.rs +++ b/crates/sdk/src/constants.rs @@ -8,6 +8,7 @@ pub const DUMMY_SIGNATURE: [u8; 64] = [1; 64]; pub const DEFAULT_TARGET_BLOCKS: u32 = 0; pub const DEFAULT_FEE_RATE: f32 = 100.0; +pub const MIN_FEE: u64 = 10; pub const PLACEHOLDER_FEE: u64 = 1; pub const WITNESS_SCALE_FACTOR: usize = 4; diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs index dafe435..5ef80ad 100644 --- a/crates/sdk/src/error.rs +++ b/crates/sdk/src/error.rs @@ -2,6 +2,15 @@ use simplicityhl::elements::secp256k1_zkp; #[derive(Debug, thiserror::Error)] pub enum SimplexError { + #[error("Fee amount is too low: {0}")] + PstFailure(#[from] simplicityhl::elements::pset::Error), + + #[error("Fee amount is too low: {0}")] + DustAmount(u64), + + #[error("Not enough fee amount {0} to cover transaction costs: {1}")] + NotEnoughFeeAmount(u64, u64), + #[error("Failed to compile Simplicity program: {0}")] Compilation(String), diff --git a/crates/sdk/src/witness_transaction.rs b/crates/sdk/src/witness_transaction.rs index e8c99b3..a42e4dc 100644 --- a/crates/sdk/src/witness_transaction.rs +++ b/crates/sdk/src/witness_transaction.rs @@ -1,14 +1,16 @@ use simplicityhl::WitnessValues; +use simplicityhl::elements::pset::{Output, PartiallySignedTransaction}; use simplicityhl::elements::secp256k1_zkp::schnorr::Signature; -use simplicityhl::elements::{Transaction, TxOut}; +use simplicityhl::elements::{Script, Transaction, TxOut}; -use crate::constants::{SimplicityNetwork, WITNESS_SCALE_FACTOR}; +use crate::constants::{MIN_FEE, PLACEHOLDER_FEE, SimplicityNetwork, WITNESS_SCALE_FACTOR}; use crate::error::SimplexError; use crate::program::ProgramTrait; use crate::provider::Provider; use crate::signer::SignerTrait; use crate::witness::WitnessTrait; + struct SignedInput<'a, T> { program: &'a dyn ProgramTrait, witness: &'a dyn WitnessTrait, @@ -17,8 +19,7 @@ struct SignedInput<'a, T> { } pub struct WitnessTransaction<'a, T> { - tx: Transaction, - utxos: &'a [TxOut], + pst: PartiallySignedTransaction, network: SimplicityNetwork, inputs: Vec>, } @@ -27,10 +28,9 @@ impl<'a, T> WitnessTransaction<'a, T> where T: Fn(&WitnessValues, &Signature) -> Result + Clone, { - pub fn new(tx: Transaction, utxos: &'a [TxOut], network: SimplicityNetwork) -> Self { + pub fn new(pst: PartiallySignedTransaction, network: SimplicityNetwork) -> Self { Self { - tx: tx, - utxos: utxos, + pst: pst, network: network, inputs: Vec::new(), } @@ -67,19 +67,70 @@ where pub fn finalize_with_fee( &self, target_blocks: u32, + change_recipient_script: Script, provider: impl Provider, ) -> Result<(Transaction, u64), SimplexError> { - let fee_rate = provider.get_fee_rate(target_blocks)?; - let final_tx = self.finalize()?; + let policy_amount_delta = self.calculate_fee_delta(); + + if policy_amount_delta < MIN_FEE { + return Err(SimplexError::DustAmount(policy_amount_delta)); + } + + let mut fee_pst = self.pst.clone(); + + fee_pst.add_output(Output::new_explicit( + change_recipient_script.clone(), + PLACEHOLDER_FEE, + self.network.policy_asset(), + None, + )); + + fee_pst.add_output(Output::new_explicit( + Script::new(), + PLACEHOLDER_FEE, + self.network.policy_asset(), + None, + )); + let (final_tx, utxos) = self.extract_tx_and_utxos(&fee_pst)?; + let final_tx = self.finalize_tx(final_tx, utxos.as_slice())?; + + let fee_rate = provider.get_fee_rate(target_blocks)?; let fee = self.calculate_fee(final_tx.weight(), fee_rate); + if policy_amount_delta < fee || policy_amount_delta - fee < MIN_FEE { + return Err(SimplexError::NotEnoughFeeAmount(policy_amount_delta, fee)); + } + + let mut fee_pst = self.pst.clone(); + + fee_pst.add_output(Output::new_explicit( + change_recipient_script, + policy_amount_delta - fee, + self.network.policy_asset(), + None, + )); + + fee_pst.add_output(Output::new_explicit( + Script::new(), + fee, + self.network.policy_asset(), + None, + )); + + let (final_tx, utxos) = self.extract_tx_and_utxos(&fee_pst)?; + let final_tx = self.finalize_tx(final_tx, utxos.as_slice())?; + Ok((final_tx, fee)) } pub fn finalize(&self) -> Result { - let mut final_tx = self.tx.clone(); + let (final_tx, utxos) = self.extract_tx_and_utxos(&self.pst)?; + + Ok(self.finalize_tx(final_tx, utxos.as_slice())?) + } + fn finalize_tx(&self, mut final_tx: Transaction, utxos: &[TxOut]) -> Result { for index in 0..self.inputs.len() { let (program, witness, signer, signer_lambda) = { let input = &self.inputs[index]; @@ -87,8 +138,9 @@ where }; if signer.is_some() { - final_tx = self.finalize_with_signer( + final_tx = self.finalize_tx_with_signer( final_tx, + utxos, program, witness.build_witness(), index, @@ -96,40 +148,74 @@ where signer_lambda.unwrap(), )?; } else { - final_tx = self.finalize_as_is(final_tx, program, witness.build_witness(), index)?; + final_tx = self.finalize_tx_as_is(final_tx, utxos, program, witness.build_witness(), index)?; } } Ok(final_tx) } - fn finalize_with_signer( + fn finalize_tx_with_signer( &self, final_tx: Transaction, + utxos: &[TxOut], program: &dyn ProgramTrait, witness: WitnessValues, index: usize, signer: &dyn SignerTrait, signer_lambda: T, ) -> Result { - let signature = signer.sign(program, &final_tx, self.utxos, index, self.network)?; + let signature = signer.sign(program, &final_tx, utxos, index, self.network)?; let new_witness = signer_lambda(&witness, &signature)?; - Ok(self.finalize_as_is(final_tx, program, new_witness, index)?) + Ok(self.finalize_tx_as_is(final_tx, utxos, program, new_witness, index)?) } - fn finalize_as_is( + fn finalize_tx_as_is( &self, final_tx: Transaction, + utxos: &[TxOut], program: &dyn ProgramTrait, witness: WitnessValues, index: usize, ) -> Result { - Ok(program.finalize(witness, final_tx, self.utxos, index, self.network)?) + Ok(program.finalize(witness, final_tx, utxos, index, self.network)?) + } + + fn calculate_fee_delta(&self) -> u64 { + let available_amount = self + .pst + .inputs() + .iter() + .filter(|input| input.asset.unwrap() == self.network.policy_asset()) + .fold(0 as u64, |acc, input| acc + input.amount.unwrap()); + + let consumed_amount = self + .pst + .outputs() + .iter() + .filter(|output| output.asset.unwrap() == self.network.policy_asset()) + .fold(0 as u64, |acc, output| acc + output.amount.unwrap()); + + available_amount - consumed_amount } fn calculate_fee(&self, weight: usize, fee_rate: f32) -> u64 { let vsize = weight.div_ceil(WITNESS_SCALE_FACTOR); (vsize as f32 * fee_rate / 1000.0).ceil() as u64 } + + fn extract_tx_and_utxos( + &self, + pst: &PartiallySignedTransaction, + ) -> Result<(Transaction, Vec), SimplexError> { + let final_tx = pst.extract_tx()?; + let mut utxos: Vec = vec![]; + + for input in pst.inputs() { + utxos.push(input.witness_utxo.clone().unwrap()); + } + + Ok((final_tx, utxos)) + } } From 761d0f3139ae47ad972ac47f90022eebc8d6e0f7 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Thu, 19 Feb 2026 14:44:51 +0200 Subject: [PATCH 8/9] third estimation --- crates/sdk/src/witness_transaction.rs | 47 +++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/crates/sdk/src/witness_transaction.rs b/crates/sdk/src/witness_transaction.rs index a42e4dc..ef6b05d 100644 --- a/crates/sdk/src/witness_transaction.rs +++ b/crates/sdk/src/witness_transaction.rs @@ -10,7 +10,6 @@ use crate::provider::Provider; use crate::signer::SignerTrait; use crate::witness::WitnessTrait; - struct SignedInput<'a, T> { program: &'a dyn ProgramTrait, witness: &'a dyn WitnessTrait, @@ -76,12 +75,15 @@ where return Err(SimplexError::DustAmount(policy_amount_delta)); } + // estimate the tx fee with the change + let fee_rate = provider.get_fee_rate(target_blocks)?; let mut fee_pst = self.pst.clone(); fee_pst.add_output(Output::new_explicit( change_recipient_script.clone(), PLACEHOLDER_FEE, self.network.policy_asset(), + None, )); @@ -94,23 +96,54 @@ where let (final_tx, utxos) = self.extract_tx_and_utxos(&fee_pst)?; let final_tx = self.finalize_tx(final_tx, utxos.as_slice())?; - - let fee_rate = provider.get_fee_rate(target_blocks)?; let fee = self.calculate_fee(final_tx.weight(), fee_rate); - if policy_amount_delta < fee || policy_amount_delta - fee < MIN_FEE { - return Err(SimplexError::NotEnoughFeeAmount(policy_amount_delta, fee)); + if policy_amount_delta > fee && policy_amount_delta - fee >= MIN_FEE { + // we have enough funds to cover change UTXO + let mut fee_pst = self.pst.clone(); + + fee_pst.add_output(Output::new_explicit( + change_recipient_script, + policy_amount_delta - fee, + self.network.policy_asset(), + None, + )); + + fee_pst.add_output(Output::new_explicit( + Script::new(), + fee, + self.network.policy_asset(), + None, + )); + + let (final_tx, utxos) = self.extract_tx_and_utxos(&fee_pst)?; + let final_tx = self.finalize_tx(final_tx, utxos.as_slice())?; + + return Ok((final_tx, fee)); } + // not enough funds, so we need to estimate without the change let mut fee_pst = self.pst.clone(); fee_pst.add_output(Output::new_explicit( - change_recipient_script, - policy_amount_delta - fee, + Script::new(), + PLACEHOLDER_FEE, self.network.policy_asset(), None, )); + let (final_tx, utxos) = self.extract_tx_and_utxos(&fee_pst)?; + let final_tx = self.finalize_tx(final_tx, utxos.as_slice())?; + let fee = self.calculate_fee(final_tx.weight(), fee_rate); + + // policy amount is not exact + if policy_amount_delta != fee { + return Err(SimplexError::NotEnoughFeeAmount(policy_amount_delta, fee)); + } + + // finalize the tx with fee and without the change + let mut fee_pst = self.pst.clone(); + fee_pst.add_output(Output::new_explicit( Script::new(), fee, From 5654271ea53afb5036e47a224d7734da3150947e Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Thu, 19 Feb 2026 16:57:14 +0200 Subject: [PATCH 9/9] finish conflicts --- Cargo.lock | 3025 ++++++++++++++++++++++++++++++++++++ crates/sdk/src/provider.rs | 125 -- crates/user/Cargo.toml | 1 - 3 files changed, 3025 insertions(+), 126 deletions(-) create mode 100644 Cargo.lock delete mode 100644 crates/sdk/src/provider.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..72f6600 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3025 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals", + "bitcoin_hashes", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + +[[package]] +name = "bitcoin" +version = "0.32.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" +dependencies = [ + "base58ck", + "bech32", + "bitcoin-internals", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes", + "hex-conservative", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +dependencies = [ + "bitcoin-io", + "hex-conservative", + "serde", +] + +[[package]] +name = "bitcoincore-rpc" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" +dependencies = [ + "bitcoincore-rpc-json", + "jsonrpc", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoincore-rpc-json" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" +dependencies = [ + "bitcoin", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoind" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce6620b7c942dbe28cc49c21d95e792feb9ffd95a093205e7875ccfa69c2925" +dependencies = [ + "anyhow", + "bitcoincore-rpc", + "log", + "tempfile", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6f81257d10a0f602a294ae4182251151ff97dbb504ef9afcdda4a64b24d9b4" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chumsky" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc17a6284abccac6e50db35c1cee87f605474a72939b959a3a67d9371800efd" +dependencies = [ + "hashbrown 0.15.5", + "regex-automata 0.3.9", + "serde", + "stacker", + "unicode-ident", + "unicode-segmentation", +] + +[[package]] +name = "clap" +version = "4.5.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctrlc" +version = "3.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" +dependencies = [ + "dispatch2", + "nix 0.31.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.11.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "electrsd" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91435161fb2ad5098e7ac7a4b793bf9c34723b0208a3fcf6f33707489e771396" +dependencies = [ + "bitcoind", + "electrum-client", + "log", + "nix 0.25.1", + "which", +] + +[[package]] +name = "electrum-client" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0bd443023f9f5c4b7153053721939accc7113cbdf810a024434eed454b3db1" +dependencies = [ + "bitcoin", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "elements" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b2569d3495bfdfce36c504fd4d78752ff4a7699f8a33e6f3ee523bddf9f6ad" +dependencies = [ + "bech32", + "bitcoin", + "secp256k1-zkp", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "ghost-cell" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7685beb53fc20efc2605f32f5d51e9ba18b8ef237961d1760169d2290d3bee" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls 0.23.36", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 1.0.6", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" +dependencies = [ + "base64 0.13.1", + "minreq", + "serde", + "serde_json", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata 0.4.14", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniscript" +version = "12.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" +dependencies = [ + "bech32", + "bitcoin", +] + +[[package]] +name = "minreq" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" +dependencies = [ + "rustls 0.21.12", + "rustls-webpki 0.101.7", + "serde", + "serde_json", + "webpki-roots 0.25.4", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nix" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.116", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.36", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.36", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.14", + "regex-syntax 0.8.9", +] + +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.9", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.36", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 1.0.6", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.9", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "santiago" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de36022292bc2086eb8f55bffa460fef3475e4459b478820711f4c421feb87ec" +dependencies = [ + "regex", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-zkp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" +dependencies = [ + "bitcoin-private", + "rand 0.8.5", + "secp256k1", + "secp256k1-zkp-sys", +] + +[[package]] +name = "secp256k1-zkp-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f08b2d0b143a22e07f798ae4f0ab20d5590d7c68e0d090f2088a48a21d1654" +dependencies = [ + "cc", + "secp256k1-sys", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simplex" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "either", + "serde", + "simplex-core", + "simplex-macros", + "simplex-runtime", + "simplex-test", + "simplicityhl", + "tokio", + "trybuild", +] + +[[package]] +name = "simplex-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "ctrlc", + "dotenvy", + "electrsd", + "simplex-config", + "simplex-test", + "simplicityhl", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "simplex-config" +version = "0.1.0" +dependencies = [ + "serde", + "simplex-core", + "simplicityhl", + "thiserror", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "simplex-core" +version = "0.1.0" +dependencies = [ + "bincode", + "hex", + "minreq", + "serde", + "sha2", + "simplicityhl", + "thiserror", +] + +[[package]] +name = "simplex-macro-core" +version = "0.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "simplicityhl", + "syn 2.0.116", + "thiserror", +] + +[[package]] +name = "simplex-macros" +version = "0.1.0" +dependencies = [ + "serde", + "simplex-macro-core", + "syn 2.0.116", +] + +[[package]] +name = "simplex-runtime" +version = "0.1.0" +dependencies = [ + "async-trait", + "bitcoin_hashes", + "electrsd", + "hex-simd", + "reqwest", + "serde", + "serde_json", + "simplex-core", + "simplicityhl", + "thiserror", + "tokio", +] + +[[package]] +name = "simplex-sdk" +version = "0.1.0" +dependencies = [ + "minreq", + "sha2", + "simplex-runtime", + "simplicityhl", + "thiserror", +] + +[[package]] +name = "simplex-test" +version = "0.1.0" +dependencies = [ + "electrsd", + "simplex-config", + "simplex-core", + "simplex-runtime", + "simplex-sdk", + "simplicityhl", + "thiserror", +] + +[[package]] +name = "simplex-user" +version = "0.1.0" +dependencies = [ + "simplex-sdk", + "thiserror", +] + +[[package]] +name = "simplicity-lang" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e57bd4d84853974a212eab24ed89da54f49fbccf5e33e93bcd29f0a6591cd5" +dependencies = [ + "bitcoin", + "bitcoin_hashes", + "byteorder", + "elements", + "getrandom 0.2.17", + "ghost-cell", + "hex-conservative", + "miniscript", + "santiago", + "simplicity-sys", +] + +[[package]] +name = "simplicity-sys" +version = "0.6.1" +source = "git+https://github.com/BlockstreamResearch/rust-simplicity?tag=simplicity-sys-0.6.1#7f42b532fdcad8b88e65af467a238af28204bc8b" +dependencies = [ + "bitcoin_hashes", + "cc", +] + +[[package]] +name = "simplicityhl" +version = "0.4.1" +source = "git+https://github.com/ikripaka/SimplicityHL/?branch=feature%2Frich-params#77755254414093ff0e1b51beedbc27367745388e" +dependencies = [ + "base64 0.21.7", + "chumsky", + "clap", + "either", + "getrandom 0.2.17", + "itertools", + "miniscript", + "serde", + "serde_json", + "simplicity-lang", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "target-triple" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix 1.1.3", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls 0.23.36", + "tokio", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml" +version = "1.0.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata 0.4.14", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "trybuild" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml 1.0.3+spec-1.1.0", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.116", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.116", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.116", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/crates/sdk/src/provider.rs b/crates/sdk/src/provider.rs deleted file mode 100644 index 93f137f..0000000 --- a/crates/sdk/src/provider.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::collections::HashMap; - -use simplicityhl::elements::encode; -use simplicityhl::elements::hex::ToHex; -use simplicityhl::elements::{Transaction, Txid}; - -use crate::constants::DEFAULT_FEE_RATE; -use crate::error::SimplexError; - -// TODO make everything async -pub trait Provider { - fn broadcast_transaction(&self, tx: &Transaction) -> Result; - - fn fetch_fee_estimates(&self) -> Result, SimplexError>; - - fn fetch_transaction(&self, txid: Txid) -> Result; - - fn get_fee_rate(&self, target_blocks: u32) -> Result { - if target_blocks == 0 { - return Ok(DEFAULT_FEE_RATE); - } - - let estimates = self.fetch_fee_estimates()?; - - let target_str = target_blocks.to_string(); - - if let Some(&rate) = estimates.get(&target_str) { - return Ok((rate * 1000.0) as f32); // Convert sat/vB to sats/kvb - } - - let fallback_targets = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 144, 504, 1008, - ]; - - for &target in fallback_targets.iter().filter(|&&t| t >= target_blocks) { - let key = target.to_string(); - - if let Some(&rate) = estimates.get(&key) { - return Ok((rate * 1000.0) as f32); - } - } - - for &target in &fallback_targets { - let key = target.to_string(); - - if let Some(&rate) = estimates.get(&key) { - return Ok((rate * 1000.0) as f32); - } - } - - Err(SimplexError::Request("No fee estimates available".to_string())) - } -} - -pub struct EsploraProvider { - esplora_url: String, -} - -impl EsploraProvider { - pub fn new(url: String) -> Self { - Self { esplora_url: url } - } -} - -impl Provider for EsploraProvider { - fn broadcast_transaction(&self, tx: &Transaction) -> Result { - let tx_hex = encode::serialize_hex(tx); - let url = format!("{}/tx", self.esplora_url); - - let response = minreq::post(&url) - .with_body(tx_hex) - .send() - .map_err(|e| SimplexError::Request(e.to_string()))?; - - let status = response.status_code; - let body = response.as_str().unwrap_or("").trim().to_owned(); - - if !(200..300).contains(&status) { - return Err(SimplexError::BroadcastRejected { - status: status as u16, - url: format!("{}/tx", self.esplora_url), - message: body, - }); - } - - Ok(body) - } - - fn fetch_fee_estimates(&self) -> Result, SimplexError> { - let url = self.esplora_url.clone() + "/fee-estimates"; - let response = minreq::get(&url) - .send() - .map_err(|e| SimplexError::Request(e.to_string()))?; - - if response.status_code != 200 { - return Err(SimplexError::Request(format!( - "HTTP {}: {}", - response.status_code, response.reason_phrase - ))); - } - - let estimates: HashMap = response.json().map_err(|e| SimplexError::Deserialize(e.to_string()))?; - - Ok(estimates) - } - - fn fetch_transaction(&self, txid: Txid) -> Result { - let url = self.esplora_url.clone() + "/tx/" + txid.to_hex().as_str() + "/raw"; - let response = minreq::get(&url) - .send() - .map_err(|e| SimplexError::Request(e.to_string()))?; - - if response.status_code != 200 { - return Err(SimplexError::Request(format!( - "HTTP {}: {}", - response.status_code, response.reason_phrase - ))); - } - - let bytes = response.as_bytes(); - let tx: Transaction = encode::deserialize(bytes).map_err(|e| SimplexError::Deserialize(e.to_string()))?; - - Ok(tx) - } -} diff --git a/crates/user/Cargo.toml b/crates/user/Cargo.toml index ab0f2b7..6538daa 100644 --- a/crates/user/Cargo.toml +++ b/crates/user/Cargo.toml @@ -13,4 +13,3 @@ workspace = true thiserror = "2" simplex-sdk = { path = "../sdk" } -simplex-build = { path = "../artifacts" }