diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7764816..1e795ec 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # These owners will be requested for review when someone opens a pull request. -* @ikripaka @ +* @arvolear diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 083f574..63f4e92 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -2,7 +2,7 @@ name: Bug Report description: File a bug report labels: ['bug'] assignees: - - KyrylR + - Arvolear body: - type: markdown attributes: @@ -10,8 +10,8 @@ body: - type: input id: version attributes: - label: "Project version" - placeholder: "1.2.3" + label: "Simplex version" + placeholder: "0.1.2" validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 8f12f98..fc4aaf6 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -2,12 +2,12 @@ name: Feature request description: Suggest a new feature labels: ['feature'] assignees: - - KyrylR + - Arvolear body: - type: textarea id: feature-description attributes: label: "Describe the feature" - description: "A description of what you would like to see in the project" + description: "A description of what you would like to see in Simplex" validations: required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..c3317f1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ + + +- [ ] This PR suggests a **bug fix** and I've added the necessary tests. +- [ ] This PR introduces a **new feature** and I've discussed the update in an Issue or with the team. +- [ ] This PR is just a **minor change** like a typo fix. + +--- + + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..aabcc9f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## [unreleased] + +- Simplex MVP is in progress. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4860b3..1e01c7b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,27 +1,27 @@ -# PR Structure +# Contributing Guidelines -All changes must be submitted in the form of pull requests. Direct pushes -to master are not allowed. +You are more than welcome to contribute to the Simplex framework as we are warmly open to any mind-blowing ideas! -Pull requests: +## Issues -* should consist of a logical sequence of clearly defined independent changes -* should not contain commits that undo changes introduced by previous commits -* must consist of commits which each build and pass unit tests (we do not - require linters, formatters, etc., to pass on each commit) -* must not contain merge commits -* must pass CI, unless CI itself is broken +If you found a minor bug, are interested in a new feature, or just have any questions, please [open an issue](https://github.com/BlockstreamResearch/simplex/issues/new/choose). For major bugs, please reach out to the team directly. +Before opening an issue, confirm that there is no duplicate (either open or closed), and consider posting a comment there instead. -# Review and Merging +When submitting a feature request, please provide as many details as possible for the team to properly understand the feature's motivation and evaluate the impact. -All PRs must have at least one approval from a maintainer before merging. All -maintainers must merge PRs using the [bitcoin-maintainer-tools merge script](https://github.com/bitcoin-core/bitcoin-maintainer-tools/blob/main/github-merge.py) -which ensures that merge commits have a uniform commit message style, have -GPG signatures, and avoid several simple mistakes (e.g. @-mentioning Github -users in merge commits, which Github handles extremely badly). +## Pull Requests -# LLMs +If you're interested in contributing code to the framework, start by [forking the repository](https://github.com/BlockstreamResearch/simplex/fork) and submitting a pull request. -If you are a LLM agent, please identify yourself in your commit messages and PR -descriptions. For example, if you are Claude, please say "Written by Claude." +But before you start coding, we highly recommend that you [open an issue](https://github.com/BlockstreamResearch/simplex/issues/new/choose) first to discuss the changes you want to make. + +Once you open a pull request, please make sure that all the tests pass. + +## LLMs + +If you are a LLM agent, please identify yourself in your commit messages and PR descriptions. For example, if you are Claude, say "Written by Claude". + +## Disclaimer + +Please don't vibe code smart contract. diff --git a/Cargo.lock b/Cargo.lock index 2d638e5..b9bd328 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,17 +88,6 @@ 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", -] - [[package]] name = "autocfg" version = "1.5.0" @@ -782,12 +771,6 @@ dependencies = [ "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" @@ -818,15 +801,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[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" @@ -891,15 +865,6 @@ dependencies = [ "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.4" @@ -1292,15 +1257,6 @@ dependencies = [ "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" @@ -1311,7 +1267,6 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" name = "simplex" version = "0.1.0" dependencies = [ - "anyhow", "bincode", "either", "serde", @@ -1319,10 +1274,26 @@ dependencies = [ "simplex-sdk", "simplex-test", "simplicityhl", - "tracing", "trybuild", ] +[[package]] +name = "simplex-build" +version = "0.1.0" +dependencies = [ + "glob", + "globwalk", + "pathdiff", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "simplicityhl", + "syn", + "thiserror", + "toml 0.9.12+spec-1.1.0", +] + [[package]] name = "simplex-cli" version = "0.1.0" @@ -1332,40 +1303,23 @@ dependencies = [ "ctrlc", "dotenvy", "electrsd", - "glob", - "globwalk", "serde", - "simplex-macros-core", + "simplex-build", "simplex-regtest", "simplex-sdk", - "simplex-template-gen-core", "simplex-test", "simplicityhl", "thiserror", "tokio", "toml 0.9.12+spec-1.1.0", - "tracing", - "tracing-subscriber", ] [[package]] name = "simplex-macros" version = "0.1.0" dependencies = [ - "serde", - "simplex-macros-core", - "syn", -] - -[[package]] -name = "simplex-macros-core" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "serde", + "simplex-build", "simplex-test", - "simplicityhl", "syn", ] @@ -1374,18 +1328,14 @@ name = "simplex-regtest" version = "0.1.0" dependencies = [ "electrsd", - "serde", "simplex-sdk", - "simplicityhl", "thiserror", - "toml 0.9.12+spec-1.1.0", ] [[package]] name = "simplex-sdk" version = "0.1.0" dependencies = [ - "async-trait", "bip39", "bitcoin_hashes", "dyn-clone", @@ -1400,28 +1350,18 @@ dependencies = [ "thiserror", ] -[[package]] -name = "simplex-template-gen-core" -version = "0.1.0" -dependencies = [ - "pathdiff", - "prettyplease", - "proc-macro2", - "quote", - "serde", - "simplex-macros-core", - "syn", - "thiserror", -] - [[package]] name = "simplex-test" version = "0.1.0" dependencies = [ "electrsd", + "proc-macro2", + "quote", "serde", + "simplex-regtest", "simplex-sdk", "simplicityhl", + "syn", "thiserror", "toml 0.9.12+spec-1.1.0", ] @@ -1470,12 +1410,6 @@ dependencies = [ "simplicity-lang", ] -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - [[package]] name = "stacker" version = "0.1.23" @@ -1554,15 +1488,6 @@ dependencies = [ "syn", ] -[[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 = "tinyvec" version = "1.10.0" @@ -1662,67 +1587,6 @@ version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" -[[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", -] - -[[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 = "trybuild" version = "1.0.116" @@ -1789,12 +1653,6 @@ 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" @@ -2046,9 +1904,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] name = "wit-bindgen" diff --git a/Cargo.toml b/Cargo.toml index bfb3f0d..9c0cdd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,32 +6,26 @@ members = [ exclude = ["examples/basic"] [workspace.package] -license = "MIT OR Apache-2.0" +license = "MIT" edition = "2024" [workspace.lints.clippy] multiple_crate_versions = "allow" [workspace.dependencies] -simplex-macros-core = { path = "./crates/macros-core", features = ["bincode", "serde"] } simplex-macros = { path = "./crates/macros" } -simplex-template-gen-core = { path = "./crates/template-gen" } +simplex-build = { path = "./crates/build" } simplex-test = { path = "./crates/test" } simplex-regtest = { path = "./crates/regtest" } simplex-sdk = { path = "./crates/sdk" } simplex = { path = "./crates/simplex" } -async-trait = { version = "0.1.89" } +serde = { version = "1.0.228", features = ["derive"]} bincode = { version = "2.0.1", features = ["serde"] } -ring = { version = "0.17.14" } +hex = { version = "0.4.3" } sha2 = { version = "0.10.9", features = ["compress"] } -serde = { version = "1.0.228" } thiserror = { version = "2.0.18" } toml = { version = "0.9.8" } - -hex = { version = "0.4.3" } -tracing = { version = "0.1.41" } - minreq = { version = "2.14.1", features = ["https", "json-using-serde"] } electrsd = { version = "0.29.0", features = ["legacy"] } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..99784d9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Blockstream + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8e93f93..7eace42 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,101 @@ -# Simplex SDK +![](https://github.com/user-attachments/assets/7d7ca314-b706-47b3-a0be-2d64fc409fab) -This collection of useful crates provides useful utilities for working with Simplicity on Elements. +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -- `simplex-macro` - provides common macros related utitiles which would provide and thrive DX. -- `simplex-core` - provides useful utilities. -- `simplex-cli` - provides common cli interface and ability to setup your contract development environment. +# Simplex -## Quick start +**A blazingly-fast, ux-first simplicity development framework.** + +## What + +Simplex is a Rust-based, comprehensive development framework for [simplicity](https://github.com/BlockstreamResearch/SimplicityHL) smart contracts, aiming to provide a rich tooling suite for implementing, testing, and deploying smart contract on [Liquid](https://liquid.net/). + +- CLI for managing simplicity-based projects. +- SDK with essential simplicity utilities. +- Liquid regtest for local integration testing. +- Extensive framework configuration. + +> [!WARNING] +> The framework is in the extremely early stage of development, unforeseen breaking changes and critical bugs are expected. + +## Installation -Install simplex with command: ```bash cargo install --path ./crates/cli ``` -To run tests for this crate - run -```bash -RUN_SLOW_TESTS=true cargo test +*The proper installer will be provided soon.* + +## Usage + +Simplex is a zero-config framework. However, it requires a `simplex.toml` file to exist in the project root. The default configuration is the following: + +```toml +# Simplex config + +[build] +src_dir = "./simf" +simf_files = ["*.simf"] +out_dir = "./src/artifacts" + +[test] +mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" + +[test.esplora] +url = "" +network = "" + +[test.rpc] +url = "" +username = "" +password = "" ``` -## License +Where: -Dual-licensed under either of: -- Apache License, Version 2.0 (Apache-2.0) -- MIT license (MIT) +- `build` (`simplex build` config) + - `src_dir` - The simplicity contracts source directory. + - `simf_files` - A glob pattern incidating which contracts are in scope. + - `out_dir` - The output directory where contracts artifacts are generated. +- `test` (`simplex test` config) + - `esplora` + - `url` - Esplora API endpoint url + - `network` - Esplora network type (`Liquid`, `LiquidTestnet`, `LiquidRegtest`). + - `rpc` + - `url` - Elements RPC endpoint url + - `username` - Elements RPC username + - `password` - Elements RPC password + +### CLI + +Simplex CLI provides the following commands: + +- `simplex init` - Initializes a Simplex project. +- `simplex config` - Prints the current config. +- `simplex build` - Generates simplicity artifacts. +- `simplex regtest` - Spins up local Electrs + Elements nodes. +- `simplex test` - Runs Simplex tests. +- `simplex clean` - Cleans up the project. + +To view the available options, run the help command: + +```bash +simplex -h +``` + +### Example + +Check out the complete project examples in the `examples` directory to learn more. + +## Future work + +- [ ] Custom signer setup with `simplex regtest`. +- [ ] SDK support for confidential assets. +- [ ] `simplex init` and `simplex clean` tasks. +- [ ] Proper installation scripts. +- [ ] Simplicity dependencies management once the language adds [support for modules](https://github.com/BlockstreamResearch/SimplicityHL/issues/155). +- [ ] Comprehensive documentation. + +## License -at your option. \ No newline at end of file +The framework is released under the MIT License. diff --git a/crates/template-gen/Cargo.toml b/crates/build/Cargo.toml similarity index 59% rename from crates/template-gen/Cargo.toml rename to crates/build/Cargo.toml index 51ab827..b1d7196 100644 --- a/crates/template-gen/Cargo.toml +++ b/crates/build/Cargo.toml @@ -1,25 +1,23 @@ [package] -name = "simplex-template-gen-core" -description = "Macro support core for Simplex, the Rust SimplicityHl toolkit. Not intended to be used directly." +name = "simplex-build" version = "0.1.0" +description = "Simplex build command internal implementation" license.workspace = true edition.workspace = true [lints] workspace = true -[features] -bincode = [] -serde = ["bincode", "dep:serde"] -default = ["bincode", "serde"] - [dependencies] -simplex-macros-core = { workspace = true } thiserror = { workspace = true } +toml = { workspace = true } +serde = { workspace = true } +simplicityhl = { workspace = true } syn = { version = "2.0.114", default-features = false, features = ["proc-macro", "full", "parsing", "derive", "clone-impls", "extra-traits", "printing"] } proc-macro2 = { version = "1.0.106", features = ["span-locations"] } quote = { version = "1.0.44" } -serde = { version = "1.0.228", optional = true } pathdiff = { version = "0.2.3" } prettyplease = { version = "0.2.37" } +glob = { version = "0.3.3"} +globwalk = { version = "0.9.1"} diff --git a/crates/build/src/config.rs b/crates/build/src/config.rs new file mode 100644 index 0000000..3395e75 --- /dev/null +++ b/crates/build/src/config.rs @@ -0,0 +1,40 @@ +use std::fs::OpenOptions; +use std::io::Read; +use std::path::Path; + +use serde::Deserialize; + +use super::error::BuildError; + +pub const DEFAULT_OUT_DIR_NAME: &str = "src/artifacts"; +pub const DEFAULT_INCLUDE_PATH: &str = "**/*.simf"; +pub const DEFAULT_SRC_DIR_NAME: &str = "simf"; + +#[derive(Debug, Clone, Deserialize)] +#[serde(default)] +pub struct BuildConfig { + pub simf_files: Vec, + pub src_dir: String, + pub out_dir: String, +} + +impl BuildConfig { + pub fn from_file(path: impl AsRef) -> Result { + let mut content = String::new(); + let mut file = OpenOptions::new().read(true).open(path)?; + + file.read_to_string(&mut content)?; + + Ok(toml::from_str(&content)?) + } +} + +impl Default for BuildConfig { + fn default() -> Self { + Self { + simf_files: vec![DEFAULT_INCLUDE_PATH.into()], + src_dir: DEFAULT_SRC_DIR_NAME.into(), + out_dir: DEFAULT_OUT_DIR_NAME.into(), + } + } +} diff --git a/crates/build/src/error.rs b/crates/build/src/error.rs new file mode 100644 index 0000000..7659748 --- /dev/null +++ b/crates/build/src/error.rs @@ -0,0 +1,33 @@ +use std::io; +use std::path::PathBuf; + +use globwalk::GlobError; + +#[derive(thiserror::Error, Debug)] +pub enum BuildError { + #[error("IO error: {0}")] + Io(#[from] io::Error), + + #[error("Glob error: {0}")] + Glob(#[from] GlobError), + + #[error("Occurred config deserialization error: '{0}'")] + ConfigDeserialize(#[from] toml::de::Error), + + #[error("Invalid generation path: '{0}'")] + GenerationPath(String), + + #[error("Failed to extract content from path, err: '{0}'")] + FailedToExtractContent(io::Error), + + #[error("Failed to generate file: {0}")] + GenerationFailed(String), + + #[error( + "Failed to resolve correct relative path for include_simf! macro, cwd: '{cwd:?}', simf_file: '{simf_file:?}'" + )] + FailedToFindCorrectRelativePath { cwd: PathBuf, simf_file: PathBuf }, + + #[error("Failed to find prefix for a file: {0}")] + NoBasePathForGeneration(#[from] std::path::StripPrefixError), +} diff --git a/crates/build/src/generator.rs b/crates/build/src/generator.rs new file mode 100644 index 0000000..a714735 --- /dev/null +++ b/crates/build/src/generator.rs @@ -0,0 +1,207 @@ +use std::collections::HashMap; +use std::env; +use std::fs; +use std::io::Write; +use std::path::{Component, Path, PathBuf}; + +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use crate::macros::parse::SimfContent; +use crate::macros::codegen::{ + convert_contract_name_to_contract_module, convert_contract_name_to_contract_source_const, + convert_contract_name_to_struct_name, +}; + +use super::error::BuildError; + +pub struct ArtifactsGenerator {} + +#[derive(Default)] +struct TreeNode { + files: Vec, + dirs: HashMap, +} + +impl ArtifactsGenerator { + pub fn generate_artifacts( + out_dir: impl AsRef, + base_dir: impl AsRef, + simfs: &[impl AsRef], + ) -> Result<(), BuildError> { + let tree = Self::build_directory_tree(&base_dir, simfs)?; + + Self::generate_bindings(out_dir.as_ref(), tree)?; + + Ok(()) + } + + fn build_directory_tree(base_dir: impl AsRef, paths: &[impl AsRef]) -> Result { + let mut root = TreeNode::default(); + + for path in paths { + let path = path.as_ref(); + + let relative_path = path + .strip_prefix(base_dir.as_ref()) + .map_err(BuildError::NoBasePathForGeneration)?; + + let components: Vec<_> = relative_path + .components() + .filter_map(|c| { + if let Component::Normal(name) = c { + Some(name) + } else { + None + } + }) + .collect(); + + let mut current_node = &mut root; + let components_len = components.len(); + + for (i, name) in components.into_iter().enumerate() { + let is_file = i == components_len - 1; + + if is_file { + current_node.files.push(path.to_path_buf()); + } else { + let dir_name = name.to_string_lossy().into_owned(); + + current_node = current_node.dirs.entry(dir_name).or_default(); + } + } + } + + Ok(root) + } + + fn generate_bindings(out_dir: &Path, path_tree: TreeNode) -> Result, BuildError> { + fs::create_dir_all(out_dir)?; + + let mut mod_filenames = Self::generate_simfs(&out_dir, &path_tree.files)?; + + for (dir_name, tree_node) in path_tree.dirs.into_iter() { + Self::generate_bindings(&out_dir.join(&dir_name), tree_node)?; + mod_filenames.push(dir_name); + } + + Self::generate_mod_rs(&out_dir, &mod_filenames)?; + + Ok(mod_filenames) + } + + fn generate_simfs(out_dir: impl AsRef, simfs: &[impl AsRef]) -> Result, BuildError> { + let mut module_files = Vec::with_capacity(simfs.len()); + + for simf_file_path in simfs { + let mod_name = Self::generate_simf_file(out_dir.as_ref(), simf_file_path)?; + module_files.push(mod_name); + } + + Ok(module_files) + } + + fn generate_simf_file(out_dir: impl AsRef, simf_file_path: impl AsRef) -> Result { + let simf_file_buf = PathBuf::from(simf_file_path.as_ref()); + let simf_content = + SimfContent::extract_content_from_path(&simf_file_buf).map_err(BuildError::FailedToExtractContent)?; + + let contract_name = simf_content.contract_name.clone(); + let output_file = out_dir.as_ref().join(format!("{}.rs", &contract_name)); + + let mut file = fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&output_file)?; + let code = Self::generate_simf_binding_code(simf_content, simf_file_buf)?; + + Self::expand_file(code, &mut file)?; + + Ok(contract_name) + } + + fn generate_mod_rs(out_dir: impl AsRef, simfs_mod_names: &[String]) -> Result<(), BuildError> { + let output_file = out_dir.as_ref().join("mod.rs"); + let mut file = fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&output_file)?; + let code = Self::generate_mod_binding_code(simfs_mod_names)?; + + Self::expand_file(code, &mut file)?; + + Ok(()) + } + + fn expand_file(code: TokenStream, buf: &mut dyn Write) -> Result<(), BuildError> { + let file: syn::File = syn::parse2(code).map_err(|e| BuildError::GenerationFailed(e.to_string()))?; + let prettystr = prettyplease::unparse(&file); + + buf.write_all(prettystr.as_bytes())?; + buf.flush()?; + + Ok(()) + } + + fn generate_simf_binding_code(simf_content: SimfContent, simf_file: PathBuf) -> Result { + let cwd = env::current_dir()?; + let contract_name = &simf_content.contract_name; + let program_name = { + let base_name = convert_contract_name_to_struct_name(contract_name); + format_ident!("{base_name}Program") + }; + let include_simf_source_const = convert_contract_name_to_contract_source_const(contract_name); + let include_simf_module = convert_contract_name_to_contract_module(contract_name); + + let pathdiff = pathdiff::diff_paths(&simf_file, &cwd).ok_or(BuildError::FailedToFindCorrectRelativePath { + cwd: cwd, + simf_file: simf_file, + })?; + let pathdiff = pathdiff.to_string_lossy().into_owned(); + + let code = quote! { + use simplex::include_simf; + use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; + use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; + + pub struct #program_name { + program: Program, + } + + impl #program_name { + pub const SOURCE: &'static str = #include_simf_module::#include_simf_source_const; + + pub fn new(public_key: XOnlyPublicKey, arguments: impl ArgumentsTrait + 'static) -> Self { + Self { + program: Program::new(Self::SOURCE, public_key, Box::new(arguments)), + } + } + + pub fn get_program(&self) -> &Program { + &self.program + } + + pub fn get_program_mut(&mut self) -> &mut Program { + &mut self.program + } + } + + include_simf!(#pathdiff); + }; + + Ok(code) + } + + fn generate_mod_binding_code(mod_names: &[String]) -> Result { + let mod_names = mod_names.iter().map(|x| format_ident!("{x}")).collect::>(); + + let code = quote! { + #(pub mod #mod_names);*; + }; + + Ok(code) + } +} diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs new file mode 100644 index 0000000..03e8477 --- /dev/null +++ b/crates/build/src/lib.rs @@ -0,0 +1,9 @@ +pub mod config; +pub mod error; +pub mod generator; +pub mod resolver; +pub mod macros; + +pub use config::BuildConfig; +pub use generator::ArtifactsGenerator; +pub use resolver::ArtifactsResolver; diff --git a/crates/macros-core/src/attr/codegen.rs b/crates/build/src/macros/codegen.rs similarity index 99% rename from crates/macros-core/src/attr/codegen.rs rename to crates/build/src/macros/codegen.rs index 041a61a..1a32515 100644 --- a/crates/macros-core/src/attr/codegen.rs +++ b/crates/build/src/macros/codegen.rs @@ -1,9 +1,11 @@ -use crate::attr::SimfContent; -use crate::attr::types::RustType; use quote::{format_ident, quote}; + use simplicityhl::str::WitnessName; use simplicityhl::{AbiMeta, Parameters, ResolvedType, WitnessTypes}; +use crate::macros::parse::SimfContent; +use crate::macros::types::RustType; + pub struct SimfContractMeta { pub contract_source_const_name: proc_macro2::Ident, pub args_struct: WitnessStruct, diff --git a/crates/macros-core/src/attr/mod.rs b/crates/build/src/macros/macros.rs similarity index 82% rename from crates/macros-core/src/attr/mod.rs rename to crates/build/src/macros/macros.rs index d3b1c03..1b539c3 100644 --- a/crates/macros-core/src/attr/mod.rs +++ b/crates/build/src/macros/macros.rs @@ -1,23 +1,25 @@ -pub mod codegen; -pub mod parse; -pub(crate) mod program; -mod types; - -pub use parse::SimfContent; +use std::error::Error; -use crate::attr::codegen::{ - GeneratedArgumentTokens, GeneratedWitnessTokens, SimfContractMeta, convert_contract_name_to_contract_module, -}; use proc_macro2::Span; use quote::quote; + use simplicityhl::AbiMeta; -use std::error::Error; -/// Expands helper functions for the given Simf content and metadata. -/// -/// # Errors -/// Returns a `syn::Result` with an error if code generation fails. -pub fn expand_helpers(simf_content: SimfContent, meta: AbiMeta) -> syn::Result { +use super::codegen::{ + GeneratedArgumentTokens, GeneratedWitnessTokens, SimfContractMeta, convert_contract_name_to_contract_module, +}; +use super::parse::{SimfContent, SynFilePath}; +use super::program; + +pub fn expand(input: &SynFilePath) -> syn::Result { + let simf_content = SimfContent::eval_path_expr(input)?; + let abi_meta = program::compile_simf(&simf_content)?; + let generated = expand_helpers(simf_content, abi_meta)?; + + Ok(generated) +} + +fn expand_helpers(simf_content: SimfContent, meta: AbiMeta) -> syn::Result { gen_helpers_inner(simf_content, meta).map_err(|e| syn::Error::new(Span::call_site(), e)) } diff --git a/crates/build/src/macros/mod.rs b/crates/build/src/macros/mod.rs new file mode 100644 index 0000000..584b7a8 --- /dev/null +++ b/crates/build/src/macros/mod.rs @@ -0,0 +1,7 @@ +pub mod codegen; +pub mod parse; +pub mod program; +pub mod types; +pub mod macros; + +pub use macros::expand; diff --git a/crates/macros-core/src/attr/parse.rs b/crates/build/src/macros/parse.rs similarity index 99% rename from crates/macros-core/src/attr/parse.rs rename to crates/build/src/macros/parse.rs index 28bb9dc..3cdd34d 100644 --- a/crates/macros-core/src/attr/parse.rs +++ b/crates/build/src/macros/parse.rs @@ -1,8 +1,9 @@ -use proc_macro2::Span; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; use std::str::FromStr; + +use proc_macro2::Span; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{Expr, ExprLit, Lit}; diff --git a/crates/macros-core/src/attr/program.rs b/crates/build/src/macros/program.rs similarity index 92% rename from crates/macros-core/src/attr/program.rs rename to crates/build/src/macros/program.rs index 67cd275..279d4c7 100644 --- a/crates/macros-core/src/attr/program.rs +++ b/crates/build/src/macros/program.rs @@ -1,7 +1,10 @@ -use crate::attr::parse::SimfContent; +use std::error::Error; + use proc_macro2::Span; + use simplicityhl::{AbiMeta, TemplateProgram}; -use std::error::Error; + +use super::parse::SimfContent; pub fn compile_simf(content: &SimfContent) -> syn::Result { compile_program_inner(content).map_err(|e| syn::Error::new(Span::call_site(), e)) @@ -9,5 +12,6 @@ pub fn compile_simf(content: &SimfContent) -> syn::Result { fn compile_program_inner(content: &SimfContent) -> Result> { let program = content.content.as_str(); + Ok(TemplateProgram::new(program)?.generate_abi_meta()?) } diff --git a/crates/macros-core/src/attr/types.rs b/crates/build/src/macros/types.rs similarity index 99% rename from crates/macros-core/src/attr/types.rs rename to crates/build/src/macros/types.rs index 18625f2..d6dda53 100644 --- a/crates/macros-core/src/attr/types.rs +++ b/crates/build/src/macros/types.rs @@ -1,6 +1,8 @@ +use std::fmt::Display; + use quote::quote; + use simplicityhl::ResolvedType; -use std::fmt::Display; #[derive(Debug, Clone)] #[non_exhaustive] diff --git a/crates/build/src/resolver.rs b/crates/build/src/resolver.rs new file mode 100644 index 0000000..4051d0d --- /dev/null +++ b/crates/build/src/resolver.rs @@ -0,0 +1,60 @@ +use std::env; +use std::path::{Path, PathBuf}; + +use globwalk::FileType; + +use super::error::BuildError; + +pub struct ArtifactsResolver {} + +impl ArtifactsResolver { + pub fn resolve_files_to_build(src_dir: &String, simfs: &Vec) -> Result, BuildError> { + let cwd = env::current_dir()?; + let base = cwd.join(src_dir); + + let mut paths = Vec::new(); + + let walker = globwalk::GlobWalkerBuilder::from_patterns(base, &simfs) + .follow_links(true) + .file_type(FileType::FILE) + .build()? + .into_iter() + .filter_map(Result::ok); + + for img in walker { + paths.push(img.path().to_path_buf().canonicalize()?); + } + + Ok(paths) + } + + pub fn resolve_local_dir(path: &impl AsRef) -> Result { + let mut path_outer = PathBuf::from(path.as_ref()); + + if !path_outer.is_absolute() { + let manifest_dir = env::current_dir()?; + + let mut path_local = PathBuf::from(manifest_dir); + path_local.push(path_outer); + + path_outer = path_local; + } + + if path_outer.extension().is_some() { + return Err(BuildError::GenerationPath(format!( + "Directories can't have an extension, path: '{}'", + path_outer.display() + ))); + } + + if path_outer.is_file() { + return Err(BuildError::GenerationPath(format!( + "Directory can't be a path, path: '{}'", + path_outer.display() + ))); + } + + // TODO: canonicalize? but this path may not exist + Ok(path_outer) + } +} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 79ba7de..fa17bf3 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,11 +1,9 @@ [package] name = "simplex-cli" version = "0.1.0" -edition = "2024" -description = "Simplicity helper CLI for Liquid testnet" -license = "MIT OR Apache-2.0" -readme = "README.md" -publish = false +description = "Simplex cli with various utilities to manage a simplicity project" +license.workspace = true +edition.workspace = true [[bin]] name = "simplex" @@ -17,9 +15,8 @@ workspace = true [dependencies] simplex-regtest = { workspace = true } simplex-test = { workspace = true } +simplex-build = { workspace = true } simplex-sdk = { workspace = true } -simplex-macros-core = { workspace = true } -simplex-template-gen-core = { workspace = true } simplicityhl = { workspace = true } electrsd = { workspace = true } @@ -31,8 +28,4 @@ anyhow = "1" dotenvy = "0.15" clap = { version = "4", features = ["derive", "env"] } tokio = { version = "1", features = ["rt-multi-thread", "macros"] } -tracing = { version = "0.1.44" } -tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } ctrlc = { version = "3.5.2", features = ["termination"] } -glob = { version = "0.3.3"} -globwalk = { version = "0.9.1"} diff --git a/crates/cli/Simplex.default.toml b/crates/cli/Simplex.default.toml index 0b7fa1b..b63c7a5 100644 --- a/crates/cli/Simplex.default.toml +++ b/crates/cli/Simplex.default.toml @@ -1,9 +1,18 @@ # TEST CONFIG +# [build] +# simf_files = ["*.simf"] +# out_dir = "./src/artifacts" +# src_dir = "./simf" + # [test] -# esplora = "esplora_api_url" +# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" + +# [test.esplora] +# url = "https://blockstream.info/liquidtestnet/api" +# network = "LiquidTestnet" # [test.rpc] -# url = "rpc_url" -# username = "username" -# password = "password" +# url = "" +# username = "" +# password = "" diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 8852764..3c8d1e6 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use clap::Parser; +use crate::commands::build::Build; use crate::commands::commands::Command; use crate::commands::regtest::Regtest; use crate::commands::test::Test; @@ -41,8 +42,6 @@ impl Cli { let config_path = Config::get_default_path()?; let loaded_config = Config::load(config_path)?; - println!("{loaded_config:#?}"); - let test_config = loaded_config.test.unwrap_or_default(); Ok(Test::run(test_config, command)?) @@ -51,26 +50,11 @@ impl Cli { // TODO: pass config Ok(Regtest::run()?) } - Command::Build { out_dir: _out_dir } => { - // let loaded_config = - // Config::load_or_discover(self.config.clone()).map_err(|e| Error::ConfigDiscoveryFailure(e))?; - - // if loaded_config.build_config.is_none() { - // return Err(Error::Config( - // "No build config to build contracts environment, please add appropriate config".to_string(), - // )); - // } - - // let build_config = loaded_config.build_config.unwrap(); - // if build_config.compile_simf.is_empty() { - // return Err(Error::Config("No files listed to build contracts environment, please check glob patterns or 'compile_simf' field in config.".to_string())); - // } - - // CodeGenerator::generate_files(&build_config.out_dir, &build_config.compile_simf)?; - - // println!("{build_config:#?}"); + Command::Build => { + let config_path = Config::get_default_path()?; + let loaded_config = Config::load(config_path)?; - Ok(()) + Ok(Build::run(loaded_config.build)?) } } } diff --git a/crates/cli/src/commands/build.rs b/crates/cli/src/commands/build.rs new file mode 100644 index 0000000..e5d4011 --- /dev/null +++ b/crates/cli/src/commands/build.rs @@ -0,0 +1,19 @@ +use simplex_build::{ArtifactsGenerator, ArtifactsResolver, BuildConfig}; + +use super::error::CommandError; + +pub struct Build {} + +impl Build { + pub fn run(config: BuildConfig) -> Result<(), CommandError> { + let output_dir = ArtifactsResolver::resolve_local_dir(&config.out_dir)?; + let src_dir = ArtifactsResolver::resolve_local_dir(&config.src_dir)?; + let files_to_build = ArtifactsResolver::resolve_files_to_build(&config.src_dir, &config.simf_files)?; + + Ok(ArtifactsGenerator::generate_artifacts( + &output_dir, + &src_dir, + &files_to_build, + )?) + } +} diff --git a/crates/cli/src/commands/commands.rs b/crates/cli/src/commands/commands.rs index f0297cf..62a73c1 100644 --- a/crates/cli/src/commands/commands.rs +++ b/crates/cli/src/commands/commands.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use clap::{Args, Subcommand}; #[derive(Debug, Subcommand)] @@ -11,9 +9,7 @@ pub enum Command { #[command(subcommand)] command: TestCommand, }, - Build { - out_dir: Option, - }, + Build, } #[derive(Debug, Subcommand)] diff --git a/crates/cli/src/commands/error.rs b/crates/cli/src/commands/error.rs index 612bf82..03e2f40 100644 --- a/crates/cli/src/commands/error.rs +++ b/crates/cli/src/commands/error.rs @@ -8,4 +8,7 @@ pub enum CommandError { #[error(transparent)] Test(#[from] simplex_test::error::TestError), + + #[error(transparent)] + Build(#[from] simplex_build::error::BuildError), } diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index 26f5e87..312b28f 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -1,4 +1,5 @@ pub mod commands; pub mod test; pub mod regtest; +pub mod build; pub mod error; diff --git a/crates/cli/src/config/config.rs b/crates/cli/src/config/config.rs index 68d327c..168ca69 100644 --- a/crates/cli/src/config/config.rs +++ b/crates/cli/src/config/config.rs @@ -1,6 +1,7 @@ use serde::Deserialize; use std::path::{Path, PathBuf}; +use simplex_build::BuildConfig; use simplex_test::TestConfig; use super::error::ConfigError; @@ -8,18 +9,11 @@ use super::error::ConfigError; pub const INIT_CONFIG: &str = include_str!("../../Simplex.default.toml"); pub const CONFIG_FILENAME: &str = "Simplex.toml"; -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Deserialize)] +#[serde(default)] pub struct Config { - #[serde(default)] pub test: Option, - #[serde(default)] - pub build: Option, -} - -#[derive(Debug, Clone, Deserialize)] -pub struct BuildConf { - pub compile_simf: Option>, - pub out_dir: Option, + pub build: BuildConfig, } impl Config { @@ -29,7 +23,6 @@ impl Config { Ok(cwd.join(CONFIG_FILENAME)) } - // TODO: load default values like `simf` path pub fn load(path_buf: impl AsRef) -> Result { let path = path_buf.as_ref().to_path_buf(); @@ -42,7 +35,36 @@ impl Config { } let conf_str = std::fs::read_to_string(path)?; + let config: Config = toml::from_str(conf_str.as_str()).map_err(ConfigError::UnableToDeserialize)?; + + Self::validate(&config)?; + + Ok(config) + } + + fn validate(config: &Config) -> Result<(), ConfigError> { + match config.test.clone() { + Some(test_config) => match test_config.esplora { + Some(esplora_config) => { + Self::validate_network(&esplora_config.network)?; + + if test_config.rpc.is_some() && esplora_config.network != "ElementsRegtest" { + return Err(ConfigError::NetworkNameUnmatched(esplora_config.network.clone())); + } + + Ok(()) + } + None => Ok(()), + }, + None => Ok(()), + } + } + + fn validate_network(network: &String) -> Result<(), ConfigError> { + if network != "Liquid" && network != "LiquidTestnet" && network != "ElementsRegtest" { + return Err(ConfigError::BadNetworkName(network.clone())); + } - Ok(toml::from_str(conf_str.as_str()).map_err(ConfigError::UnableToDeserialize)?) + Ok(()) } } diff --git a/crates/cli/src/config/error.rs b/crates/cli/src/config/error.rs index 86de7ff..d76ca7b 100644 --- a/crates/cli/src/config/error.rs +++ b/crates/cli/src/config/error.rs @@ -8,6 +8,12 @@ pub enum ConfigError { #[error("TOML parse error: {0}")] TomlParse(#[from] toml::de::Error), + #[error("Network name should either be `Liquid`, `LiquidTestnet` or `ElementsRegtest`, got: {0}")] + BadNetworkName(String), + + #[error("Network name should be `ElementsRegtest` when RPC is specified, got: {0}")] + NetworkNameUnmatched(String), + #[error("Unable to deserialize config: {0}")] UnableToDeserialize(toml::de::Error), diff --git a/crates/cli/src/error.rs b/crates/cli/src/error.rs index 5f07829..885a3e1 100644 --- a/crates/cli/src/error.rs +++ b/crates/cli/src/error.rs @@ -1,4 +1,4 @@ -use crate::{commands::error::CommandError, config}; +use crate::{commands, config}; #[derive(thiserror::Error, Debug)] pub enum CliError { @@ -6,11 +6,8 @@ pub enum CliError { Config(#[from] config::error::ConfigError), #[error(transparent)] - Command(#[from] CommandError), + Command(#[from] commands::error::CommandError), #[error("IO error: '{0}'")] Io(#[from] std::io::Error), - - #[error("Occurred code generation error, error: '{0}'")] - CodeGenerator(#[from] simplex_template_gen_core::CodeGeneratorError), } diff --git a/crates/macros-core/Cargo.toml b/crates/macros-core/Cargo.toml deleted file mode 100644 index 3bb3a65..0000000 --- a/crates/macros-core/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "simplex-macros-core" -description = "Macro support core for Simplex, the Rust SimplicityHl toolkit. Not intended to be used directly." -version = "0.1.0" -license.workspace = true -edition.workspace = true - -[lints] -workspace = true - -[features] -bincode = [] -serde = ["bincode", "dep:serde"] -default = ["bincode", "serde"] - -[dependencies] -simplicityhl = { workspace = true } -simplex-test = { workspace = true } - -proc-macro2 = { version = "1.0.106", features = ["span-locations"] } -syn = { version = "2.0.114", default-features = false, features = ["proc-macro", "full", "parsing", "derive", "clone-impls", "extra-traits", "printing"] } -quote = { version = "1.0.44" } -serde = { version = "1.0.228", optional = true } diff --git a/crates/macros-core/src/lib.rs b/crates/macros-core/src/lib.rs deleted file mode 100644 index 27285d0..0000000 --- a/crates/macros-core/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::all, clippy::pedantic)] - -pub mod attr; -pub mod test; - -/// Expands the `include_simf` macro. -/// -/// # Errors -/// Returns a `syn::Result` with an error if parsing, compilation, or expansion fails. -pub fn expand_include_simf(input: &attr::parse::SynFilePath) -> syn::Result { - let simf_content = attr::SimfContent::eval_path_expr(input)?; - let abi_meta = attr::program::compile_simf(&simf_content)?; - let generated = attr::expand_helpers(simf_content, abi_meta)?; - - Ok(generated) -} - -/// Expands the `test` macro. -/// -/// # Errors -/// Returns a `syn::Result` with an error if expansion fails. -pub fn expand_test(args: proc_macro2::TokenStream, input: syn::ItemFn) -> syn::Result { - // test::expand(args, input) - todo!() -} diff --git a/crates/macros-core/src/test/mod.rs b/crates/macros-core/src/test/mod.rs deleted file mode 100644 index 0309d45..0000000 --- a/crates/macros-core/src/test/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::parse::Parser; - -pub(crate) fn expand_inner(input: &syn::ItemFn, args: AttributeArgs) -> syn::Result { - let ret = &input.sig.output; - let name = &input.sig.ident; - let inputs = &input.sig.inputs; - let body = &input.block; - let attrs = &input.attrs; - - let fn_name_str = name.to_string(); - let parsed_attribute_args = parse_args(args)?; - let simplex_test_env = simplex_test::TEST_ENV_NAME; - let ok_path_generation = match parsed_attribute_args.config_option { - ConfigOpt::Config => { - quote! { - Ok(_) => { - let test_context = TestContextBuilder::Default.build().unwrap(); - tracing::trace!("Running '{}' with simplex configuration", #fn_name_str); - test_context - } - } - } - ConfigOpt::None => { - quote! { - Ok(path) => { - let path = PathBuf::from(path); - let test_context = TestContextBuilder::FromConfigPath(path).build().unwrap(); - tracing::trace!("Running '{}' with simplex configuration", #fn_name_str); - test_context - } - } - } - }; - - let expansion = quote::quote! { - #[::core::prelude::v1::test] - #(#attrs)* - fn #name() #ret { - use ::simplex::tracing; - use ::std::path::PathBuf; - use ::simplex::simplex_test::TestContextBuilder; - - fn #name(#inputs) #ret { - #body - } - - let test_context = match std::env::var(#simplex_test_env) { - Err(e) => { - tracing::trace!( - "Test '{}' connected with simplex is disabled, run `simplex test` in order to test it, err: '{e}'", #fn_name_str - ); - panic!("Failed to run this test, required to use `simplex test`"); - } - #ok_path_generation - }; - - #name(test_context) - } - }; - Ok(expansion) -} - -struct Args { - config_option: ConfigOpt, -} - -enum ConfigOpt { - Config, - None, -} - -type AttributeArgs = syn::punctuated::Punctuated; - -pub fn expand(args: TokenStream, input: syn::ItemFn) -> syn::Result { - let parser = AttributeArgs::parse_terminated; - let args = parser.parse2(args)?; - - expand_inner(&input, args) -} - -fn parse_args(attr_args: AttributeArgs) -> syn::Result { - if attr_args.is_empty() { - return Ok(Args { - config_option: ConfigOpt::Config, - }); - } - - if attr_args.len() > 1 { - return Err(syn::Error::new_spanned( - &attr_args, - "only a single `default_rpc` flag is allowed", - )); - } - - match attr_args.iter().next().unwrap() { - syn::Meta::Path(path) if path.is_ident("default_rpc") => Ok(Args { - config_option: ConfigOpt::None, - }), - arg => Err(syn::Error::new_spanned( - arg, - "expected only the `default_rpc` flag with no assignment or value", - )), - } -} diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index 97d32f5..b4f4638 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -1,31 +1,18 @@ [package] name = "simplex-macros" version = "0.1.0" -edition = "2024" -rust-version = "1.90.0" -description = "High-level helpers for delivering boilerplate code for SimplicityHL contracts" -license = "MIT OR Apache-2.0" -repository = "https://github.com/BlockstreamResearch/simplicity-contracts" -homepage = "https://github.com/BlockstreamResearch/simplicity-contracts/tree/dev/crates/simplicityhl-core" -readme = "README.md" -documentation = "https://docs.rs/simplicityhl-core" -keywords = ["simplicity", "liquid", "bitcoin", "elements", "macro"] -categories = ["cryptography::cryptocurrencies"] +description = "Simplex macros re-export package" +license.workspace = true +edition.workspace = true [lib] proc-macro = true -[features] -default = ["macros", "derive",] -serde = ["macros", "dep:serde"] -macros = [] -derive = [] - [lints] workspace = true [dependencies] -simplex-macros-core = { workspace = true } +simplex-build = { workspace = true } +simplex-test = { workspace = true } -serde = { version = "1.0.228", optional = true } syn = { version = "2.0.114", default-features = false, features = ["parsing", "proc-macro"] } diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 861e98e..4758f85 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -1,24 +1,20 @@ -#![warn(clippy::all, clippy::pedantic)] - use proc_macro::TokenStream; -#[cfg(feature = "macros")] #[proc_macro] pub fn include_simf(tokenstream: TokenStream) -> TokenStream { - let input = syn::parse_macro_input!(tokenstream as simplex_macros_core::attr::parse::SynFilePath); + let input = syn::parse_macro_input!(tokenstream as simplex_build::macros::parse::SynFilePath); - match simplex_macros_core::expand_include_simf(&input) { + match simplex_build::macros::expand(&input) { Ok(ts) => ts.into(), Err(e) => e.to_compile_error().into(), } } -#[cfg(feature = "macros")] #[proc_macro_attribute] pub fn test(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input as syn::ItemFn); - match simplex_macros_core::expand_test(args.into(), input) { + match simplex_test::macros::expand(args.into(), input) { Ok(ts) => ts.into(), Err(e) => e.to_compile_error().into(), } diff --git a/crates/regtest/Cargo.toml b/crates/regtest/Cargo.toml index d5ff34a..cddb69c 100644 --- a/crates/regtest/Cargo.toml +++ b/crates/regtest/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "simplex-regtest" version = "0.1.0" +description = "Simplex regtest command internal implementation" license.workspace = true edition.workspace = true @@ -11,7 +12,4 @@ workspace = true simplex-sdk = { workspace = true } thiserror = { workspace = true } -simplicityhl = { workspace = true } electrsd = { workspace = true } -serde = { workspace = true } -toml = { workspace = true } diff --git a/crates/regtest/src/args.rs b/crates/regtest/src/args.rs index 8469f8c..23fcbb1 100644 --- a/crates/regtest/src/args.rs +++ b/crates/regtest/src/args.rs @@ -1,5 +1,3 @@ -pub const DEFAULT_SAT_AMOUNT_FAUCET: u64 = 100000; - pub fn get_elementsd_bin_args() -> Vec { vec![ "-fallbackfee=0.0001".to_string(), @@ -19,5 +17,5 @@ pub fn get_elementsd_bin_args() -> Vec { } pub fn get_electrs_bin_args() -> Vec { - vec!["-v".to_string()] + vec![] } diff --git a/crates/regtest/src/client.rs b/crates/regtest/src/client.rs index 3beae5a..a5ab4bb 100644 --- a/crates/regtest/src/client.rs +++ b/crates/regtest/src/client.rs @@ -1,3 +1,4 @@ +use std::net::TcpListener; use std::path::{Path, PathBuf}; use electrsd::ElectrsD; @@ -17,8 +18,9 @@ impl TestClient { // TODO: pass custom config pub fn new() -> Self { let (electrs_path, elementsd_path) = Self::default_bin_paths(); - let elements = Self::create_bitcoind_node(elementsd_path); - let electrs = Self::create_electrs_node(electrs_path, &elements); + let zmq_addr = Self::get_zmq_addr(); + let elements = Self::create_bitcoind_node(elementsd_path, &zmq_addr); + let electrs = Self::create_electrs_node(electrs_path, &elements, &zmq_addr); Self { electrs: electrs, @@ -43,7 +45,10 @@ impl TestClient { } pub fn esplora_url(&self) -> String { - self.electrs.esplora_url.clone().unwrap() + let url = self.electrs.esplora_url.clone().unwrap(); + let port = url.split_once(":").unwrap().1; + + format!("http://127.0.0.1:{}", port) } pub fn auth(&self) -> Auth { @@ -53,15 +58,29 @@ impl TestClient { } pub fn kill(&mut self) -> Result<(), ClientError> { + // electrs stops elements automatically self.electrs.kill().map_err(|_| ClientError::ElectrsTermination())?; - self.elements.stop().map_err(|_| ClientError::ElementsTermination())?; Ok(()) } - fn create_bitcoind_node(bin_path: impl AsRef) -> BitcoinD { + fn get_zmq_addr() -> String { + TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap() + .to_string() + } + + fn create_bitcoind_node(bin_path: impl AsRef, zmq_addr: &String) -> BitcoinD { let mut conf = Conf::default(); - let bin_args = get_elementsd_bin_args(); + let mut bin_args = get_elementsd_bin_args(); + + bin_args.push(format!("-zmqpubrawtx=tcp://{zmq_addr}")); + bin_args.push(format!("-zmqpubrawblock=tcp://{zmq_addr}")); + bin_args.push(format!("-zmqpubhashtx=tcp://{zmq_addr}")); + bin_args.push(format!("-zmqpubhashblock=tcp://{zmq_addr}")); + bin_args.push(format!("-zmqpubsequence=tcp://{zmq_addr}")); conf.args = bin_args.iter().map(|x| x.as_ref()).collect::>(); conf.network = "liquidregtest"; @@ -70,9 +89,11 @@ impl TestClient { BitcoinD::with_conf(bin_path.as_ref(), &conf).unwrap() } - fn create_electrs_node(bin_path: impl AsRef, elementsd: &BitcoinD) -> ElectrsD { + fn create_electrs_node(bin_path: impl AsRef, elementsd: &BitcoinD, zmq_addr: &String) -> ElectrsD { let mut conf = electrsd::Conf::default(); - let bin_args = get_electrs_bin_args(); + let mut bin_args = get_electrs_bin_args(); + + bin_args.push(format!("--zmq-addr={zmq_addr}")); conf.args = bin_args.iter().map(|x| x.as_ref()).collect::>(); conf.http_enabled = true; diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 62f9067..197a116 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -1,24 +1,22 @@ [package] name = "simplex-sdk" version = "0.1.0" -edition = "2024" -description = "Simplex SDK" -license = "MIT OR Apache-2.0" -readme = "README.md" +description = "Simplex sdk to simplify the development with simplicity" +license.workspace = true +edition.workspace = true [lints] workspace = true [dependencies] -async-trait = { workspace = true } thiserror = { workspace = true } sha2 = { workspace = true } minreq = { workspace = true } simplicityhl = { workspace = true } electrsd = { workspace = true } +serde = { workspace = true } +hex = { workspace = true } -hex = { version = "0.4" } -serde = { version = "1.0.228", features = ["derive"] } serde_json = { version = "1.0" } elements-miniscript = { version = "0.4", features = ["base64", "serde"] } bip39 = { version = "2.0.0", features = ["rand"] } diff --git a/crates/sdk/src/provider/esplora.rs b/crates/sdk/src/provider/esplora.rs index e08becd..fbe640d 100644 --- a/crates/sdk/src/provider/esplora.rs +++ b/crates/sdk/src/provider/esplora.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use std::collections::HashSet; use std::str::FromStr; -use std::thread::sleep; use std::time::Duration; use simplicityhl::elements::hashes::{Hash, sha256}; @@ -11,11 +10,14 @@ use simplicityhl::elements::{Address, OutPoint, Script, Transaction, TxOut, Txid use serde::Deserialize; +use crate::provider::SimplicityNetwork; + use super::error::ProviderError; -use super::provider::{DEFAULT_TIMEOUT_SECS, ProviderTrait}; +use super::provider::{DEFAULT_ESPLORA_TIMEOUT_SECS, ProviderTrait}; pub struct EsploraProvider { pub esplora_url: String, + pub network: SimplicityNetwork, pub timeout: Duration, } @@ -23,7 +25,6 @@ pub struct EsploraProvider { #[allow(dead_code)] struct TxStatus { confirmed: bool, - #[serde(default)] block_height: Option, } @@ -31,11 +32,8 @@ struct TxStatus { #[allow(dead_code)] struct UtxoStatus { pub confirmed: bool, - #[serde(default)] pub block_height: Option, - #[serde(default)] pub block_hash: Option, - #[serde(default)] pub block_time: Option, } @@ -44,22 +42,19 @@ struct UtxoStatus { struct EsploraUtxo { pub txid: String, pub vout: u32, - #[serde(default)] pub value: Option, - #[serde(default)] pub valuecommitment: Option, - #[serde(default)] pub asset: Option, - #[serde(default)] pub assetcommitment: Option, pub status: UtxoStatus, } impl EsploraProvider { - pub fn new(url: String) -> Self { + pub fn new(url: String, network: SimplicityNetwork) -> Self { Self { esplora_url: url, - timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECS), + network: network, + timeout: Duration::from_secs(DEFAULT_ESPLORA_TIMEOUT_SECS), } } @@ -96,6 +91,10 @@ impl EsploraProvider { } impl ProviderTrait for EsploraProvider { + fn get_network(&self) -> &SimplicityNetwork { + &self.network + } + fn broadcast_transaction(&self, tx: &Transaction) -> Result { let tx_hex = encode::serialize_hex(tx); let url = format!("{}/tx", self.esplora_url); @@ -125,6 +124,12 @@ impl ProviderTrait for EsploraProvider { let url = format!("{}/tx/{}/status", self.esplora_url, txid); let timeout_secs = self.timeout.as_secs(); + let confirmation_poll = match self.network.clone() { + SimplicityNetwork::ElementsRegtest { .. } => Duration::from_millis(100), + _ => Duration::from_secs(10), + }; + + // polling needs to be > 1 min on mainnet/testnet for _ in 1..10 { let response = minreq::get(&url) .with_timeout(timeout_secs) @@ -132,7 +137,7 @@ impl ProviderTrait for EsploraProvider { .map_err(|e| ProviderError::Request(e.to_string()))?; if response.status_code != 200 { - sleep(Duration::from_secs(5)); + std::thread::sleep(confirmation_poll); continue; } @@ -142,7 +147,7 @@ impl ProviderTrait for EsploraProvider { return Ok(()); } - sleep(Duration::from_secs(10)); + std::thread::sleep(confirmation_poll); } Err(ProviderError::Confirmation()) diff --git a/crates/sdk/src/provider/mod.rs b/crates/sdk/src/provider/mod.rs index 916b68e..3ad48a2 100644 --- a/crates/sdk/src/provider/mod.rs +++ b/crates/sdk/src/provider/mod.rs @@ -2,11 +2,15 @@ pub mod error; pub mod esplora; pub mod network; pub mod provider; -pub(crate) mod rpc; +pub mod rpc; pub mod simplex; -pub use error::ProviderError; +pub use rpc::elements::ElementsRpc; pub use esplora::EsploraProvider; -pub use network::*; +pub use simplex::SimplexProvider; pub use provider::ProviderTrait; -pub use rpc::elements::ElementsRpc; + +pub use network::*; + +pub use rpc::error::RpcError; +pub use error::ProviderError; diff --git a/crates/sdk/src/provider/provider.rs b/crates/sdk/src/provider/provider.rs index dcc79d1..6bc187f 100644 --- a/crates/sdk/src/provider/provider.rs +++ b/crates/sdk/src/provider/provider.rs @@ -2,12 +2,16 @@ use std::collections::HashMap; use simplicityhl::elements::{Address, OutPoint, Script, Transaction, TxOut, Txid}; +use crate::provider::SimplicityNetwork; + use super::error::ProviderError; pub const DEFAULT_FEE_RATE: f32 = 100.0; -pub const DEFAULT_TIMEOUT_SECS: u64 = 10; +pub const DEFAULT_ESPLORA_TIMEOUT_SECS: u64 = 10; pub trait ProviderTrait { + fn get_network(&self) -> &SimplicityNetwork; + fn broadcast_transaction(&self, tx: &Transaction) -> Result; fn wait(&self, txid: &Txid) -> Result<(), ProviderError>; @@ -20,7 +24,7 @@ pub trait ProviderTrait { fn fetch_fee_estimates(&self) -> Result, ProviderError>; - fn get_fee_rate(&self, target_blocks: u32) -> Result { + fn fetch_fee_rate(&self, target_blocks: u32) -> Result { let estimates = self.fetch_fee_estimates()?; let target_str = target_blocks.to_string(); diff --git a/crates/sdk/src/provider/rpc/elements.rs b/crates/sdk/src/provider/rpc/elements.rs index 3b4dc91..1a7437e 100644 --- a/crates/sdk/src/provider/rpc/elements.rs +++ b/crates/sdk/src/provider/rpc/elements.rs @@ -1,7 +1,6 @@ use std::str::FromStr; -use bitcoind::bitcoincore_rpc::{Auth, Client, RpcApi}; -use electrsd::bitcoind; +use electrsd::bitcoind::bitcoincore_rpc::{Auth, Client, RpcApi}; use serde_json::Value; @@ -12,8 +11,8 @@ use super::error::RpcError; use crate::utils::sat2btc; pub struct ElementsRpc { - inner: Client, - auth: Auth, + pub inner: Client, + pub auth: Auth, pub url: String, } diff --git a/crates/sdk/src/provider/simplex.rs b/crates/sdk/src/provider/simplex.rs index 97af6a9..2fab759 100644 --- a/crates/sdk/src/provider/simplex.rs +++ b/crates/sdk/src/provider/simplex.rs @@ -1,11 +1,13 @@ use std::collections::HashMap; -use bitcoind::bitcoincore_rpc::Auth; -use electrsd::bitcoind; +use electrsd::bitcoind::bitcoincore_rpc::Auth; use simplicityhl::elements::{Address, OutPoint, Script, Transaction, TxOut, Txid}; -use crate::provider::{ProviderError, ProviderTrait}; +use crate::provider::SimplicityNetwork; + +use super::error::ProviderError; +use super::provider::ProviderTrait; use super::{ElementsRpc, EsploraProvider}; @@ -15,15 +17,24 @@ pub struct SimplexProvider { } impl SimplexProvider { - pub fn new(esplora_url: String, elements_url: String, auth: Auth) -> Result { + pub fn new( + esplora_url: String, + elements_url: String, + auth: Auth, + network: SimplicityNetwork, + ) -> Result { Ok(Self { - esplora: EsploraProvider::new(esplora_url), + esplora: EsploraProvider::new(esplora_url, network), elements: ElementsRpc::new(elements_url, auth)?, }) } } impl ProviderTrait for SimplexProvider { + fn get_network(&self) -> &SimplicityNetwork { + self.esplora.get_network() + } + fn broadcast_transaction(&self, tx: &Transaction) -> Result { let txid = self.esplora.broadcast_transaction(tx)?; diff --git a/crates/sdk/src/signer/signer.rs b/crates/sdk/src/signer/signer.rs index fe0d317..532854b 100644 --- a/crates/sdk/src/signer/signer.rs +++ b/crates/sdk/src/signer/signer.rs @@ -56,14 +56,14 @@ pub trait SignerTrait { ) -> Result<(PublicKey, ecdsa::Signature), SignerError>; } -pub struct Signer<'a> { +pub struct Signer { xprv: Xpriv, - provider: Box<&'a dyn ProviderTrait>, + provider: Box, network: SimplicityNetwork, secp: Secp256k1, } -impl<'a> SignerTrait for Signer<'a> { +impl SignerTrait for Signer { fn sign_program( &self, pst: &PartiallySignedTransaction, @@ -108,23 +108,20 @@ enum Estimate { Failure(u64), } -impl<'a> Signer<'a> { - pub fn new( - mnemonic: &str, - provider: &'a impl ProviderTrait, - network: SimplicityNetwork, - ) -> Result { +impl Signer { + pub fn new(mnemonic: &str, provider: Box) -> Result { let secp = Secp256k1::new(); let mnemonic: Mnemonic = mnemonic .parse() .map_err(|e: bip39::Error| SignerError::Mnemonic(e.to_string()))?; let seed = mnemonic.to_seed(""); - let xprv = Xpriv::new_master(NetworkKind::Test, &seed)?; + let network = provider.get_network().clone(); + Ok(Self { xprv, - provider: Box::new(provider), + provider: provider, network: network, secp: secp, }) @@ -147,7 +144,7 @@ impl<'a> Signer<'a> { let mut fee_tx = tx.clone(); let mut curr_fee = MIN_FEE; - let fee_rate = self.provider.get_fee_rate(target_blocks)?; + let fee_rate = self.provider.fetch_fee_rate(target_blocks)?; for utxo in signer_utxos { let policy_amount_delta = fee_tx.calculate_fee_delta(); @@ -162,6 +159,16 @@ impl<'a> Signer<'a> { fee_tx.add_input(PartialInput::new(utxo.0, utxo.1), RequiredSignature::NativeEcdsa)?; } + // need to try one more time after the loop + let policy_amount_delta = fee_tx.calculate_fee_delta(); + + if policy_amount_delta >= curr_fee as i64 { + match self.estimate_tx(fee_tx.clone(), fee_rate, policy_amount_delta as u64)? { + Estimate::Success(tx, fee) => return Ok((tx, fee)), + Estimate::Failure(required_fee) => curr_fee = required_fee, + } + } + Err(SignerError::NotEnoughFunds(curr_fee)) } @@ -176,7 +183,7 @@ impl<'a> Signer<'a> { return Err(SignerError::DustAmount(policy_amount_delta)); } - let fee_rate = self.provider.get_fee_rate(target_blocks)?; + let fee_rate = self.provider.fetch_fee_rate(target_blocks)?; // policy_amount_delta will be > 0 match self.estimate_tx(tx.clone(), fee_rate, policy_amount_delta as u64)? { @@ -185,6 +192,10 @@ impl<'a> Signer<'a> { } } + pub fn get_provider(&self) -> &Box { + &self.provider + } + pub fn get_wpkh_address(&self) -> Result { let fingerprint = self.fingerprint()?; let path = self.get_derivation_path()?; @@ -386,18 +397,19 @@ mod tests { #[test] fn keys_correspond_to_address() { - let provider = EsploraProvider::new("https://blockstream.info/liquidtestnet/api".to_string()); + let url = "https://blockstream.info/liquidtestnet/api".to_string(); + let network = SimplicityNetwork::LiquidTestnet; + let signer = Signer::new( "exist carry drive collect lend cereal occur much tiger just involve mean", - &provider, - SimplicityNetwork::LiquidTestnet, + Box::new(EsploraProvider::new(url, network.clone())), ) .unwrap(); let address = signer.get_wpkh_address().unwrap(); let pubkey = signer.get_ecdsa_public_key().unwrap(); - let derived_addr = Address::p2wpkh(&pubkey, None, SimplicityNetwork::LiquidTestnet.address_params()); + let derived_addr = Address::p2wpkh(&pubkey, None, network.address_params()); assert_eq!(derived_addr.to_string(), address.to_string()); } diff --git a/crates/simplex/Cargo.toml b/crates/simplex/Cargo.toml index 2ef6f72..588e054 100644 --- a/crates/simplex/Cargo.toml +++ b/crates/simplex/Cargo.toml @@ -1,36 +1,28 @@ [package] name = "simplex" version = "0.1.0" -edition = "2024" -rust-version = "1.90" -description = "High-level helpers for compiling and executing Simplicity programs on Liquid" -license = "MIT OR Apache-2.0" +description = "A blazingly-fast, ux-first simplicity development framework" +license.workspace = true +edition.workspace = true repository = "https://github.com/BlockstreamResearch/simplex" -readme = "README.md" +readme = "../README.md" documentation = "https://docs.rs/simplex" -keywords = ["simplicity", "liquid", "bitcoin", "elements", "taproot"] +keywords = ["liquid", "framework", "simplicity", "simplex"] categories = ["cryptography::cryptocurrencies"] [lints] workspace = true -[features] -default = ["macros", "encoding", "sdk"] -macros = ["dep:simplex-macros", "tracing", "encoding"] -encoding = ["dep:bincode"] -sdk = ["dep:simplex-sdk"] - [dependencies] -simplex-macros = { workspace = true, features = [], optional = true } +simplex-macros = { workspace = true } simplex-test = { workspace = true } -simplex-sdk = { workspace = true, optional = true } +simplex-sdk = { workspace = true } -bincode = { workspace = true, optional = true } +bincode = { workspace = true } +serde = { workspace = true } simplicityhl = { workspace = true, features = ["serde"] } -serde = { version = "1.0.228" } + either = { version = "1.15.0", features = ["serde"] } -tracing = { workspace = true, optional = true } [dev-dependencies] trybuild = { version = "1.0.115" } -anyhow = { version = "1.0.101" } diff --git a/crates/simplex/src/lib.rs b/crates/simplex/src/lib.rs index 3e58b17..aaea9f2 100644 --- a/crates/simplex/src/lib.rs +++ b/crates/simplex/src/lib.rs @@ -1,19 +1,12 @@ -#![warn(clippy::all, clippy::pedantic)] +pub use bincode; +pub use either; +pub use serde; +pub use simplicityhl; -pub extern crate either; -pub extern crate serde; +pub use simplex_sdk; -#[cfg(feature = "macros")] -pub extern crate simplex_macros; +pub use simplex_test::config::TestConfig; +pub use simplex_test::context::TestContext; -#[cfg(feature = "sdk")] -pub extern crate simplex_sdk; - -#[cfg(feature = "macros")] -pub extern crate simplex_test; - -#[cfg(feature = "macros")] -pub extern crate tracing; - -#[cfg(feature = "encoding")] -pub extern crate bincode; +pub use simplex_macros; +pub use simplex_macros::{include_simf, test}; diff --git a/crates/simplex/tests/compile_test.rs b/crates/simplex/tests/compile_test.rs new file mode 100644 index 0000000..01ebee1 --- /dev/null +++ b/crates/simplex/tests/compile_test.rs @@ -0,0 +1,12 @@ +const SLOW_TEST_ENV: &str = "RUN_UI_TESTS"; + +#[test] +fn ui() { + if let Err(_) = std::env::var(SLOW_TEST_ENV) { + eprintln!("Set '{SLOW_TEST_ENV}' to true in order to run a test"); + return; + } + + let tests = trybuild::TestCases::new(); + tests.pass("tests/ui/*.rs"); +} diff --git a/crates/simplex/tests/compiletest.rs b/crates/simplex/tests/compiletest.rs deleted file mode 100644 index 0a2eabb..0000000 --- a/crates/simplex/tests/compiletest.rs +++ /dev/null @@ -1,11 +0,0 @@ -const SLOW_TEST_ENV: &str = "RUN_SLOW_TESTS"; -#[test] -fn ui() { - if let Err(_) = std::env::var(SLOW_TEST_ENV) { - tracing::trace!("Set '{SLOW_TEST_ENV}' to true in order to run a test"); - return; - } - - let t = trybuild::TestCases::new(); - t.pass("tests/ui/*.rs"); -} diff --git a/crates/simplex/tests/simplex_test.rs b/crates/simplex/tests/simplex_test.rs deleted file mode 100644 index 72a51e3..0000000 --- a/crates/simplex/tests/simplex_test.rs +++ /dev/null @@ -1,160 +0,0 @@ -// use simplex_provider::elements_rpc::{AddressType, ElementsRpcClient}; -// use simplex_test::{DEFAULT_SAT_AMOUNT_FAUCET, ElementsDConf, TestContext}; -// use simplicityhl::elements::Address; -// use simplicityhl::elements::bitcoin::secp256k1; -// use simplicityhl::elements::secp256k1_zkp::Keypair; - -// use simplex::simplex_sdk::constants::SimplicityNetwork; -// use simplex::simplex_sdk::presets::{P2PK, P2PKArguments}; -// use simplex::simplex_sdk::utils::tr_unspendable_key; - -// #[ignore] -// #[simplex::simplex_macros::test(default_rpc)] -// fn test_execution(x: TestContext) { -// assert!(true) -// } - -// #[ignore] -// #[simplex::simplex_macros::test] -// fn test_execution3(x: TestContext) { -// assert!(true) -// } - -// #[ignore] -// #[test] -// fn test_execution2() { -// use ::simplex::tracing; -// use simplex_test::TestContextBuilder; -// use std::path::PathBuf; - -// fn test_execution2(x: TestContext) { -// assert!(true); -// } - -// let test_context = match std::env::var("SIMPLEX_TEST_ENV") { -// Err(e) => { -// tracing::trace!( -// "Test 'test_in_custom_folder_custom_333' connected with simplex is disabled, run `simplex test` in order to test it, err: '{e}'" -// ); -// panic!("Failed to run this test, required to use `simplex test`"); -// } -// Ok(path) => { -// let path = PathBuf::from(path); -// let test_context = TestContextBuilder::FromConfigPath(path).build().unwrap(); -// tracing::trace!("Running 'test_in_custom_folder_custom_333' with simplex configuration"); -// test_context -// } -// }; -// println!("fn name: {}, \n ident: {}", "test_execution2", "#ident"); -// println!("input: {}, \n AttributeArgs: {}", "#input", "#args"); - -// test_execution2(test_context) -// } - -// #[test] -// fn test_invocation_tx_tracking() -> anyhow::Result<()> { -// use simplex_test::{ConfigOption, TestClientProvider}; - -// fn test_invocation_tx_tracking( -// rpc: TestClientProvider, -// user1_addr: Address, -// user2_addr: Address, -// ) -> anyhow::Result<()> { -// // user input code -// { -// let network = SimplicityNetwork::default_regtest(); -// let keypair = Keypair::from_seckey_slice(&secp256k1::SECP256K1, &[1; 32])?; - -// let arguments = P2PKArguments { -// public_key: keypair.x_only_public_key().0.serialize(), -// }; - -// let p2pk_program = P2PK::new(tr_unspendable_key(), arguments); -// let p2pk = p2pk_program.get_program().get_tr_address(network).unwrap(); - -// dbg!(p2pk.to_string()); - -// // simplex runtime -// // - test provider -// // - fields from config -// // - -// // p2tr - -// dbg!(ElementsRpcClient::validateaddress(rpc.as_ref(), &p2pk.to_string())?); - -// // broadcast, fetch fee transaction - -// let result = ElementsRpcClient::sendtoaddress( -// rpc.as_ref(), -// &p2pk, -// DEFAULT_SAT_AMOUNT_FAUCET, -// Some(network.policy_asset()), -// )?; - -// ElementsRpcClient::generate_blocks(rpc.as_ref(), 5)?; - -// dbg!(ElementsRpcClient::listunspent( -// rpc.as_ref(), -// None, -// None, -// Some(vec![p2pk.to_string()]), -// None, -// None, -// )?,); - -// dbg!(ElementsRpcClient::scantxoutset( -// rpc.as_ref(), -// "start", -// Some(vec![format!("addr({})", p2pk)]), -// )?,); - -// Ok(()) -// } -// } - -// let network = SimplicityNetwork::default_regtest(); -// let rpc = TestClientProvider::init( -// ConfigOption::DefaultRegtest, -// ElementsDConf::obtain_default_elementsd_path(), -// ) -// .unwrap(); -// { -// ElementsRpcClient::generate_blocks(rpc.as_ref(), 1).unwrap(); -// ElementsRpcClient::rescanblockchain(rpc.as_ref(), None, None).unwrap(); -// ElementsRpcClient::sweep_initialfreecoins(rpc.as_ref()).unwrap(); -// ElementsRpcClient::generate_blocks(rpc.as_ref(), 100).unwrap(); -// } -// // TODO: remove berkeley -// // addresstype - bech32 - -// // - -// let user1_addr = ElementsRpcClient::getnewaddress(rpc.as_ref(), "", AddressType::default()).unwrap(); -// let user2_addr = ElementsRpcClient::getnewaddress(rpc.as_ref(), "", AddressType::default()).unwrap(); -// ElementsRpcClient::sendtoaddress( -// rpc.as_ref(), -// &user1_addr, -// DEFAULT_SAT_AMOUNT_FAUCET, -// Some(network.policy_asset()), -// ) -// .unwrap(); - -// ElementsRpcClient::sendtoaddress( -// rpc.as_ref(), -// &user2_addr, -// DEFAULT_SAT_AMOUNT_FAUCET, -// Some(network.policy_asset()), -// ) -// .unwrap(); - -// ElementsRpcClient::generate_blocks(rpc.as_ref(), 3).unwrap(); -// dbg!(ElementsRpcClient::listunspent( -// rpc.as_ref(), -// None, -// None, -// Some(vec![user1_addr.to_string(), user2_addr.to_string()]), -// None, -// None, -// )?,); -// test_invocation_tx_tracking(rpc, user1_addr, user2_addr) -// } diff --git a/crates/simplex/tests/ui/array_tr_storage.rs b/crates/simplex/tests/ui/array_tr_storage.rs index 6a9da79..7ac76cc 100644 --- a/crates/simplex/tests/ui/array_tr_storage.rs +++ b/crates/simplex/tests/ui/array_tr_storage.rs @@ -1,5 +1,5 @@ -use simplex::simplex_macros::*; -use simplex::simplex_sdk::program::{WitnessTrait, ArgumentsTrait}; +use simplex::include_simf; +use simplex::simplex_sdk::program::{ArgumentsTrait, WitnessTrait}; include_simf!("../../../../crates/simplex/tests/ui_simfs/array_tr_storage.simf"); diff --git a/crates/simplex/tests/ui/bytes32_tr_storage.rs b/crates/simplex/tests/ui/bytes32_tr_storage.rs index 4ffc886..0a0868d 100644 --- a/crates/simplex/tests/ui/bytes32_tr_storage.rs +++ b/crates/simplex/tests/ui/bytes32_tr_storage.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::*; +use simplex::include_simf; use simplex::simplex_sdk::program::{WitnessTrait, ArgumentsTrait}; include_simf!("../../../../crates/simplex/tests/ui_simfs/bytes32_tr_storage.simf"); diff --git a/crates/simplex/tests/ui/dual_currency_deposit.rs b/crates/simplex/tests/ui/dual_currency_deposit.rs index 8c48fca..35a5179 100644 --- a/crates/simplex/tests/ui/dual_currency_deposit.rs +++ b/crates/simplex/tests/ui/dual_currency_deposit.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::*; +use simplex::include_simf; use simplex::simplex_sdk::program::{WitnessTrait, ArgumentsTrait}; include_simf!("../../../../crates/simplex/tests/ui_simfs/dual_currency_deposit.simf"); diff --git a/crates/simplex/tests/ui/option_offer.rs b/crates/simplex/tests/ui/option_offer.rs index 8393522..d22bd85 100644 --- a/crates/simplex/tests/ui/option_offer.rs +++ b/crates/simplex/tests/ui/option_offer.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::*; +use simplex::include_simf; use simplex::simplex_sdk::program::{WitnessTrait, ArgumentsTrait}; include_simf!("../../../../crates/simplex/tests/ui_simfs/option_offer.simf"); diff --git a/crates/simplex/tests/ui/options.rs b/crates/simplex/tests/ui/options.rs index 58b5a0e..97126e8 100644 --- a/crates/simplex/tests/ui/options.rs +++ b/crates/simplex/tests/ui/options.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::*; +use simplex::include_simf; use simplex::simplex_sdk::program::{WitnessTrait, ArgumentsTrait}; include_simf!("../../../../crates/simplex/tests/ui_simfs/options.simf"); diff --git a/crates/simplex/tests/ui/simple_storage.rs b/crates/simplex/tests/ui/simple_storage.rs index d2a6ba5..55e13ab 100644 --- a/crates/simplex/tests/ui/simple_storage.rs +++ b/crates/simplex/tests/ui/simple_storage.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::*; +use simplex::include_simf; use simplex::simplex_sdk::program::{WitnessTrait, ArgumentsTrait}; include_simf!("../../../../crates/simplex/tests/ui_simfs/simple_storage.simf"); diff --git a/crates/template-gen/src/env.rs b/crates/template-gen/src/env.rs deleted file mode 100644 index 0afb76a..0000000 --- a/crates/template-gen/src/env.rs +++ /dev/null @@ -1,256 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use simplex_macros_core::attr::SimfContent; -use simplex_macros_core::attr::codegen::{ - convert_contract_name_to_contract_module, convert_contract_name_to_contract_source_const, - convert_contract_name_to_struct_name, -}; -use std::collections::HashMap; -use std::io::Write; -use std::path::{Component, Path, PathBuf}; -use std::{fs, io}; - -#[derive(thiserror::Error, Debug)] -pub enum CodeGeneratorError { - #[error("IO error: {0}")] - Io(#[from] io::Error), - - #[error("Failed to extract content from path, err: '{0}'")] - FailedToExtractContent(io::Error), - - #[error("Failed to generate file: {0}")] - GenerationFailed(String), - - #[error( - "Failed to resolve correct relative path for include_simf! macro, cwd: '{cwd:?}', simf_file: '{simf_file:?}'" - )] - FailedToFindCorrectRelativePath { cwd: PathBuf, simf_file: PathBuf }, - - #[error("Failed to find prefix for a file: {0}")] - NoBasePathForGeneration(#[from] std::path::StripPrefixError), -} - -pub struct CodeGenerator {} - -struct FileDescriptor { - simf_content: SimfContent, - simf_file: PathBuf, - cwd: PathBuf, -} - -type _ContractModName = String; -type _FolderName = String; -type _ContractName = String; - -#[derive(Debug, Default)] -struct _TreeNode { - files: Vec, - folders: HashMap<_FolderName, _TreeNode>, -} - -impl<'b> CodeGenerator { - pub fn generate_files( - cwd: impl AsRef, - out_dir: impl AsRef, - simfs: &[impl AsRef], - ) -> Result<(), CodeGeneratorError> { - let _ = Self::_generate_files(cwd, out_dir, simfs)?; - - Ok(()) - } - - pub fn generate_artifacts_mod( - out_dir_name: impl AsRef, - cwd: impl AsRef, - base_path: impl AsRef, - out_dir: impl AsRef, - simfs: &[impl AsRef], - ) -> Result<(), CodeGeneratorError> { - let out_dir = dbg!(out_dir.as_ref().join(out_dir_name.as_ref())); - - let tree = dbg!(Self::_build_directory_tree(simfs, &base_path)?); - Self::_generate_tree_file_structure(cwd.as_ref(), &out_dir, tree)?; - - Ok(()) - } -} - -impl<'b> CodeGenerator { - fn _generate_files( - cwd: impl AsRef, - out_dir: impl AsRef, - simfs: &[impl AsRef], - ) -> Result, CodeGeneratorError> { - let out_dir = out_dir.as_ref(); - let cwd = cwd.as_ref(); - - fs::create_dir_all(out_dir)?; - let mut module_files = Vec::with_capacity(simfs.len()); - - for simf_file_path in simfs { - let mod_name = Self::_generate_file(cwd, out_dir, simf_file_path)?; - module_files.push(mod_name); - } - Ok(module_files) - } - - fn _generate_tree_file_structure( - cwd: &Path, - out_dir: &Path, - path_tree: _TreeNode, - ) -> Result, CodeGeneratorError> { - let mut mod_filenames = Self::_generate_files(cwd, &out_dir, &path_tree.files)?; - for (folder_name, tree_node) in path_tree.folders.into_iter() { - Self::_generate_tree_file_structure(cwd, &out_dir.join(&folder_name), tree_node)?; - mod_filenames.push(folder_name); - } - Self::_generate_mod_rs(&out_dir, &mod_filenames)?; - Ok(mod_filenames) - } - - fn _generate_mod_rs( - out_dir: impl AsRef, - simfs_mod_name: &[_ContractModName], - ) -> Result<(), CodeGeneratorError> { - let out_dir = out_dir.as_ref(); - let output_file = out_dir.join("mod.rs"); - let mut file = fs::OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&output_file)?; - let simfs_mod_name = simfs_mod_name.iter().map(|x| format_ident!("{x}")).collect::>(); - let code = quote! { - #(pub mod #simfs_mod_name);*; - }; - Self::expand_file(code, &mut file)?; - Ok(()) - } - - fn _build_directory_tree( - paths: &[impl AsRef], - base: impl AsRef, - ) -> Result<_TreeNode, CodeGeneratorError> { - let mut root = _TreeNode::default(); - - for path in paths { - let path = path.as_ref(); - - let relative_path = path - .strip_prefix(base.as_ref()) - .map_err(CodeGeneratorError::NoBasePathForGeneration)?; - - let components: Vec<_> = relative_path - .components() - .filter_map(|c| { - if let Component::Normal(name) = c { - Some(name) - } else { - None - } - }) - .collect(); - - let mut current_node = &mut root; - let components_len = components.len(); - - for (i, name) in components.into_iter().enumerate() { - let is_file = i == components_len - 1; - if is_file { - current_node.files.push(path.to_path_buf()); - } else { - let folder_name = name.to_string_lossy().into_owned(); - current_node = current_node.folders.entry(folder_name).or_default(); - } - } - } - - Ok(root) - } - - fn _generate_file( - cwd: impl AsRef, - out_dir: impl AsRef, - simf_file_path: impl AsRef, - ) -> Result<_ContractModName, CodeGeneratorError> { - let path_buf = PathBuf::from(simf_file_path.as_ref()); - let simf_content = - SimfContent::extract_content_from_path(&path_buf).map_err(CodeGeneratorError::FailedToExtractContent)?; - - dbg!(cwd.as_ref()); - fs::create_dir_all(&out_dir)?; - let output_file = out_dir.as_ref().join(format!("{}.rs", &simf_content.contract_name)); - let contract_name = simf_content.contract_name.clone(); - - let mut file = fs::OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&output_file)?; - let code = Self::generate_simf_binding_code(FileDescriptor { - simf_content, - simf_file: path_buf, - cwd: cwd.as_ref().to_path_buf(), - })?; - Self::expand_file(code, &mut file)?; - Ok(contract_name) - } - - fn expand_file(code: TokenStream, buf: &mut dyn Write) -> Result<(), CodeGeneratorError> { - let file: syn::File = syn::parse2(code).map_err(|e| CodeGeneratorError::GenerationFailed(e.to_string()))?; - let prettystr = prettyplease::unparse(&file); - buf.write_all(prettystr.as_bytes())?; - buf.flush()?; - Ok(()) - } - - fn generate_simf_binding_code(file_descriptor: FileDescriptor) -> Result { - let contract_name = &file_descriptor.simf_content.contract_name; - let program_name = { - let base_name = convert_contract_name_to_struct_name(contract_name); - format_ident!("{base_name}Program") - }; - let include_simf_source_const = convert_contract_name_to_contract_source_const(contract_name); - let include_simf_module = convert_contract_name_to_contract_module(contract_name); - - let pathdiff = pathdiff::diff_paths(&file_descriptor.simf_file, &file_descriptor.cwd).ok_or( - CodeGeneratorError::FailedToFindCorrectRelativePath { - cwd: file_descriptor.cwd, - simf_file: file_descriptor.simf_file, - }, - )?; - let pathdiff = format!("{}", pathdiff.display()); - - let code = quote! { - use simplex::simplex_macros::include_simf; - use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; - use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; - - pub struct #program_name { - program: Program, - } - - impl #program_name { - pub const SOURCE: &'static str = #include_simf_module::#include_simf_source_const; - - pub fn new(public_key: XOnlyPublicKey, arguments: impl ArgumentsTrait + 'static) -> Self { - Self { - program: Program::new(Self::SOURCE, public_key, Box::new(arguments)), - } - } - - pub fn get_program(&self) -> &Program { - &self.program - } - - pub fn get_program_mut(&mut self) -> &mut Program { - &mut self.program - } - } - - include_simf!(#pathdiff); - }; - - Ok(code) - } -} diff --git a/crates/template-gen/src/lib.rs b/crates/template-gen/src/lib.rs deleted file mode 100644 index cc7e391..0000000 --- a/crates/template-gen/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![warn(clippy::all, clippy::pedantic)] - -mod env; -pub use env::CodeGeneratorError; - -pub fn expand_only_files( - cwd: impl AsRef, - outdir: impl AsRef, - simfs: &[impl AsRef], -) -> Result<(), CodeGeneratorError> { - env::CodeGenerator::generate_files(cwd, outdir, simfs) -} - -pub fn expand_files_with_nested_dirs( - cwd: impl AsRef, - base_dir: impl AsRef, - outdir: impl AsRef, - simfs: &[impl AsRef], -) -> Result<(), CodeGeneratorError> { - const ARTIFACTS_DIR_NAME: &str = "artifacts"; - - env::CodeGenerator::generate_artifacts_mod(ARTIFACTS_DIR_NAME, cwd, base_dir, outdir, simfs) -} diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index d93322b..a3795f1 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "simplex-test" version = "0.1.0" +description = "Simplex test command internal implementation" license.workspace = true edition.workspace = true @@ -9,9 +10,14 @@ workspace = true [dependencies] simplex-sdk = { workspace = true } +simplex-regtest = { workspace = true } thiserror = { workspace = true } simplicityhl = { workspace = true } electrsd = { workspace = true } serde = { workspace = true } toml = { workspace = true } + +syn = { version = "2.0.114", default-features = false, features = ["proc-macro", "full", "parsing", "derive", "clone-impls", "extra-traits", "printing"] } +proc-macro2 = { version = "1.0.106", features = ["span-locations"] } +quote = { version = "1.0.44" } diff --git a/crates/test/src/config.rs b/crates/test/src/config.rs index 2730675..301e6ac 100644 --- a/crates/test/src/config.rs +++ b/crates/test/src/config.rs @@ -9,20 +9,26 @@ use serde::{Deserialize, Serialize}; use super::error::TestError; pub const TEST_ENV_NAME: &str = "SIMPLEX_TEST_ENV"; +pub const TEST_MNEMONIC: &str = "exist carry drive collect lend cereal occur much tiger just involve mean"; -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TestConfig { - #[serde(default)] - pub esplora: Option, - #[serde(default)] + pub mnemonic: String, + pub esplora: Option, pub rpc: Option, } +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct EsploraConfig { + pub url: String, + pub network: String, +} + #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct RpcConfig { - url: String, - username: String, - password: String, + pub url: String, + pub username: String, + pub password: String, } impl TestConfig { @@ -45,6 +51,17 @@ impl TestConfig { file.read_to_string(&mut content)?; + // TODO: check that network name is correct Ok(toml::from_str(&content)?) } } + +impl Default for TestConfig { + fn default() -> Self { + Self { + mnemonic: TEST_MNEMONIC.to_string(), + esplora: None, + rpc: None, + } + } +} diff --git a/crates/test/src/context.rs b/crates/test/src/context.rs index fb30056..5ece65d 100644 --- a/crates/test/src/context.rs +++ b/crates/test/src/context.rs @@ -1,75 +1,142 @@ -// use crate::TestError; -// pub use config::*; -// use electrsd::bitcoind::bitcoincore_rpc::Auth; -// pub use rpc_provider::*; -// use std::path::PathBuf; - -// pub struct TestContext { -// config: ElementsDConf, -// rpc: TestRpcProvider, -// } - -// pub enum TestContextBuilder { -// Default, -// FromConfigPath(PathBuf), -// } - -// impl TestContextBuilder { -// pub fn build(self) -> Result { -// let context = match self { -// Self::Default => { -// let elementsd_path = ElementsDConf::obtain_default_elementsd_path(); -// let rpc = TestRpcProvider::init(ConfigOption::DefaultRegtest, &elementsd_path)?; -// TestContext { -// config: ElementsDConf { -// elemendsd_path: elementsd_path, -// rpc_creds: RpcCreds::None, -// }, -// rpc, -// } -// } -// Self::FromConfigPath(path) => { -// let config: ElementsDConf = ElementsDConf::from_file(&path)?; -// match &config.rpc_creds { -// RpcCreds::Auth { -// url, -// username, -// password, -// } => { -// let rpc = TestRpcProvider::init( -// ConfigOption::CustomRpcUrlRegtest { -// url: url.clone(), -// auth: Auth::UserPass(username.clone(), password.clone()), -// }, -// &config.elemendsd_path, -// )?; -// TestContext { config, rpc } -// } -// RpcCreds::None => { -// let rpc = TestRpcProvider::init(ConfigOption::DefaultRegtest, &config.elemendsd_path)?; -// TestContext { config, rpc } -// } -// } -// } -// }; -// Ok(context) -// } -// } - -// impl TestContext { -// pub fn get_config(&self) -> &ElementsDConf { -// &self.config -// } - -// pub fn get_rpc_provider(&self) -> &TestRpcProvider { -// &self.rpc -// } - -// pub fn default_rpc_setup(&self) -> Result<(), TestError> { -// self.rpc.generate_blocks(1)?; -// self.rpc.rescanblockchain(None, None)?; -// self.rpc.sweep_initialfreecoins()?; -// self.rpc.generate_blocks(100)?; -// Ok(()) -// } -// } +use std::path::PathBuf; +use std::time::Duration; + +use electrsd::bitcoind::bitcoincore_rpc::Auth; + +use simplex_regtest::TestClient; +use simplex_sdk::provider::ElementsRpc; +use simplex_sdk::provider::{EsploraProvider, ProviderTrait, SimplexProvider, SimplicityNetwork}; +use simplex_sdk::signer::Signer; + +use crate::config::TestConfig; +use crate::error::TestError; + +#[allow(dead_code)] +pub struct TestContext { + _client: Option, + config: TestConfig, + signer: Signer, +} + +impl TestContext { + pub fn new(config_path: PathBuf) -> Result { + let config = TestConfig::from_file(&config_path)?; + + let (provider, client) = Self::setup_provider(&config)?; + let signer = Self::setup_signer(provider, &client, &config.mnemonic)?; + + Ok(Self { + _client: client, + config: config, + signer: signer, + }) + } + + pub fn get_provider(&self) -> &Box { + &self.signer.get_provider() + } + + pub fn get_config(&self) -> &TestConfig { + &self.config + } + + pub fn get_network(&self) -> &SimplicityNetwork { + &self.signer.get_provider().get_network() + } + + pub fn get_signer(&self) -> &Signer { + &self.signer + } + + fn setup_provider(config: &TestConfig) -> Result<(Box, Option), TestError> { + let provider: Box; + let client: Option; + + match config.esplora.clone() { + Some(esplora) => match config.rpc.clone() { + Some(rpc) => { + // custom regtest case + let auth = Auth::UserPass(rpc.username, rpc.password); + + provider = Box::new(SimplexProvider::new( + esplora.url, + rpc.url, + auth, + SimplicityNetwork::default_regtest(), + )?); + client = None; + } + None => { + // external esplora network + let network = match esplora.network.as_str() { + "Liquid" => SimplicityNetwork::Liquid, + "LiquidTestnet" => SimplicityNetwork::LiquidTestnet, + _ => panic!("Impossible branch reached, please report a bug"), + }; + + provider = Box::new(EsploraProvider::new(esplora.url, network)); + client = None; + } + }, + None => { + // simplex inner network + let client_inner = TestClient::new(); + + provider = Box::new(SimplexProvider::new( + client_inner.esplora_url(), + client_inner.rpc_url(), + client_inner.auth(), + SimplicityNetwork::default_regtest(), + )?); + + // need to save the client so that rust doesn't kill it + client = Some(client_inner); + } + } + + Ok((provider, client)) + } + + fn setup_signer( + provider: Box, + client: &Option, + mnemonic: &String, + ) -> Result { + let signer = Signer::new(mnemonic, provider)?; + + match client { + // if client exists, we are using inner simplex network + Some(client_inner) => { + let rpc_provider = ElementsRpc::new(client_inner.rpc_url(), client_inner.auth())?; + + rpc_provider.generate_blocks(1)?; + rpc_provider.rescanblockchain(None, None)?; + rpc_provider.sweep_initialfreecoins()?; + rpc_provider.generate_blocks(100)?; + + // 20 million BTC + rpc_provider.sendtoaddress(&signer.get_wpkh_address()?, 20_000_000 * u64::pow(10, 8), None)?; + + // wait for electrs to index + let mut attempts = 0; + + loop { + if !(signer.get_wpkh_utxos()?).is_empty() { + break; + } + + attempts += 1; + + if attempts > 100 { + panic!("Electrs failed to index the sweep after 10 seconds"); + } + + std::thread::sleep(Duration::from_millis(100)); + } + } + None => {} + }; + + Ok(signer) + } +} diff --git a/crates/test/src/error.rs b/crates/test/src/error.rs index 2bb4ade..bcabf1e 100644 --- a/crates/test/src/error.rs +++ b/crates/test/src/error.rs @@ -1,12 +1,23 @@ use std::io; +use simplex_sdk::provider::ProviderError; +use simplex_sdk::provider::RpcError; +use simplex_sdk::signer::SignerError; + #[derive(thiserror::Error, Debug)] pub enum TestError { - /// Errors when io error occurred. #[error("Occurred io error: '{0}'")] Io(#[from] io::Error), - /// Errors when io error occurred. + #[error(transparent)] + Provider(#[from] ProviderError), + + #[error(transparent)] + Rpc(#[from] RpcError), + + #[error(transparent)] + Signer(#[from] SignerError), + #[error("Occurred config deserialization error: '{0}'")] ConfigDeserialize(#[from] toml::de::Error), } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index a2bc69c..21a3d3f 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -1,5 +1,6 @@ pub mod config; pub mod context; pub mod error; +pub mod macros; pub use config::{RpcConfig, TestConfig, TEST_ENV_NAME}; diff --git a/crates/test/src/macros/macros.rs b/crates/test/src/macros/macros.rs new file mode 100644 index 0000000..3b31fd8 --- /dev/null +++ b/crates/test/src/macros/macros.rs @@ -0,0 +1,50 @@ +use proc_macro2::TokenStream; +use syn::parse::Parser; + +use crate::TEST_ENV_NAME; + +type AttributeArgs = syn::punctuated::Punctuated; + +pub fn expand(args: TokenStream, input: syn::ItemFn) -> syn::Result { + let parser = AttributeArgs::parse_terminated; + let args = parser.parse2(args)?; + + expand_inner(&input, args) +} + +// TODO: args? +fn expand_inner(input: &syn::ItemFn, _args: AttributeArgs) -> syn::Result { + let ret = &input.sig.output; + let name = &input.sig.ident; + let inputs = &input.sig.inputs; + let body = &input.block; + let attrs = &input.attrs; + + let simplex_test_env = TEST_ENV_NAME; + + let expansion = quote::quote! { + #[::core::prelude::v1::test] + #(#attrs)* + fn #name() #ret { + use std::path::PathBuf; + use simplex::TestContext; + + fn #name(#inputs) #ret { + #body + } + + let test_context = match std::env::var(#simplex_test_env) { + Err(e) => { + panic!("Failed to run this test, required to use `simplex test`"); + }, + Ok(path) => { + TestContext::new(PathBuf::from(path)).unwrap() + } + }; + + #name(test_context) + } + }; + + Ok(expansion) +} diff --git a/crates/test/src/macros/mod.rs b/crates/test/src/macros/mod.rs new file mode 100644 index 0000000..eb7bb1d --- /dev/null +++ b/crates/test/src/macros/mod.rs @@ -0,0 +1,3 @@ +pub mod macros; + +pub use macros::expand; diff --git a/examples/basic/Cargo.lock b/examples/basic/Cargo.lock index ae5b5a9..c82e810 100644 --- a/examples/basic/Cargo.lock +++ b/examples/basic/Cargo.lock @@ -88,23 +88,6 @@ 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", -] - -[[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" @@ -133,12 +116,6 @@ 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" @@ -172,8 +149,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", - "rand_core 0.6.4", + "rand", + "rand_core", "serde", "unicode-normalization", ] @@ -297,6 +274,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -309,12 +296,6 @@ 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" @@ -331,12 +312,6 @@ 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" @@ -393,6 +368,31 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.7" @@ -413,17 +413,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "draft_example" version = "0.1.0" @@ -532,48 +521,6 @@ 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" @@ -597,20 +544,6 @@ dependencies = [ "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" @@ -630,6 +563,36 @@ 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 = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.11.0", + "ignore", + "walkdir", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -668,16 +631,6 @@ 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" @@ -693,187 +646,6 @@ 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.37", - "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" @@ -881,24 +653,19 @@ 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" +name = "ignore" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ - "icu_normalizer", - "icu_properties", + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.14", + "same-file", + "walkdir", + "winapi-util", ] [[package]] @@ -913,22 +680,6 @@ dependencies = [ "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" @@ -996,24 +747,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - [[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 = "memchr" version = "2.8.0" @@ -1045,22 +784,11 @@ 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", + "rustls", + "rustls-webpki", "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", + "webpki-roots", ] [[package]] @@ -1099,22 +827,10 @@ 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 = "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.17" +name = "pathdiff" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pin-utils" @@ -1122,15 +838,6 @@ 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" @@ -1169,61 +876,6 @@ dependencies = [ "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.37", - "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.37", - "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" @@ -1246,18 +898,8 @@ 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", + "rand_chacha", + "rand_core", ] [[package]] @@ -1267,37 +909,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] [[package]] -name = "rand_chacha" -version = "0.9.0" +name = "rand_core" +version = "0.6.4" 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" +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 = "regex" version = "1.12.3" @@ -1344,44 +967,6 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" -[[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.37", - "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" @@ -1396,12 +981,6 @@ dependencies = [ "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" @@ -1436,34 +1015,10 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki 0.101.7", + "rustls-webpki", "sct", ] -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -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" @@ -1474,17 +1029,6 @@ dependencies = [ "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" @@ -1492,10 +1036,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "ryu" -version = "1.0.23" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] name = "santiago" @@ -1523,7 +1070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand", "secp256k1-sys", "serde", ] @@ -1544,7 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" dependencies = [ "bitcoin-private", - "rand 0.8.5", + "rand", "secp256k1", "secp256k1-zkp-sys", "serde", @@ -1618,18 +1165,6 @@ 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" @@ -1655,46 +1190,43 @@ dependencies = [ "either", "serde", "simplex-macros", - "simplex-provider", "simplex-sdk", "simplex-test", "simplicityhl", - "tracing", ] [[package]] -name = "simplex-macros" +name = "simplex-build" version = "0.1.0" dependencies = [ - "simplex-macros-core", + "glob", + "globwalk", + "pathdiff", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "simplicityhl", "syn", + "thiserror", + "toml", ] [[package]] -name = "simplex-macros-core" +name = "simplex-macros" version = "0.1.0" dependencies = [ - "proc-macro2", - "quote", - "serde", + "simplex-build", "simplex-test", - "simplicityhl", "syn", ] [[package]] -name = "simplex-provider" +name = "simplex-regtest" version = "0.1.0" dependencies = [ - "async-trait", - "bitcoin_hashes", "electrsd", - "hex-simd", - "minreq", - "reqwest", - "serde", - "serde_json", - "simplicityhl", + "simplex-sdk", "thiserror", ] @@ -1702,15 +1234,16 @@ dependencies = [ name = "simplex-sdk" version = "0.1.0" dependencies = [ - "async-trait", "bip39", + "bitcoin_hashes", "dyn-clone", + "electrsd", "elements-miniscript", "hex", "minreq", "serde", + "serde_json", "sha2", - "simplex-provider", "simplicityhl", "thiserror", ] @@ -1720,10 +1253,13 @@ name = "simplex-test" version = "0.1.0" dependencies = [ "electrsd", + "proc-macro2", + "quote", "serde", - "simplex-provider", + "simplex-regtest", "simplex-sdk", "simplicityhl", + "syn", "thiserror", "toml", ] @@ -1773,34 +1309,6 @@ dependencies = [ "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" @@ -1820,12 +1328,6 @@ 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 = "2.0.117" @@ -1837,26 +1339,6 @@ dependencies = [ "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", -] - [[package]] name = "tempfile" version = "3.26.0" @@ -1890,16 +1372,6 @@ dependencies = [ "syn", ] -[[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" @@ -1915,30 +1387,6 @@ 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", - "pin-project-lite", - "socket2", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls 0.23.37", - "tokio", -] - [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -1978,88 +1426,6 @@ 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", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typenum" version = "1.19.0" @@ -2105,24 +1471,6 @@ 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" @@ -2142,18 +1490,13 @@ 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" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "try-lock", + "same-file", + "winapi-util", ] [[package]] @@ -2193,20 +1536,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.114" @@ -2273,41 +1602,12 @@ dependencies = [ "semver", ] -[[package]] -name = "web-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" -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" @@ -2320,6 +1620,15 @@ dependencies = [ "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" @@ -2332,7 +1641,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2341,16 +1650,7 @@ 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", + "windows-targets", ] [[package]] @@ -2368,31 +1668,14 @@ 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", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2401,96 +1684,48 @@ 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" @@ -2585,35 +1820,6 @@ dependencies = [ "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", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.8.40" @@ -2634,66 +1840,6 @@ dependencies = [ "syn", ] -[[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", - "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", -] - [[package]] name = "zmij" version = "1.0.21" diff --git a/examples/basic/Simplex.toml b/examples/basic/Simplex.toml index 30229db..b63c7a5 100644 --- a/examples/basic/Simplex.toml +++ b/examples/basic/Simplex.toml @@ -1,9 +1,18 @@ -network = "elementsregtest" +# TEST CONFIG -[build] -simf_files = ["*.simf"] -out_dir = "./src" -base_dir = "./simf" +# [build] +# simf_files = ["*.simf"] +# out_dir = "./src/artifacts" +# src_dir = "./simf" -[test] -elementsd_path = "../../assets/elementsd" +# [test] +# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" + +# [test.esplora] +# url = "https://blockstream.info/liquidtestnet/api" +# network = "LiquidTestnet" + +# [test.rpc] +# url = "" +# username = "" +# password = "" diff --git a/examples/basic/src/artifacts/another_dir/another_module/bytes32_tr_storage.rs b/examples/basic/src/artifacts/another_dir/another_module/bytes32_tr_storage.rs index 07978a1..70abfcf 100644 --- a/examples/basic/src/artifacts/another_dir/another_module/bytes32_tr_storage.rs +++ b/examples/basic/src/artifacts/another_dir/another_module/bytes32_tr_storage.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::include_simf; +use simplex::include_simf; use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; pub struct Bytes32TrStorageProgram { diff --git a/examples/basic/src/artifacts/another_dir/another_module/dual_currency_deposit.rs b/examples/basic/src/artifacts/another_dir/another_module/dual_currency_deposit.rs index 1cdc1ce..4a2c288 100644 --- a/examples/basic/src/artifacts/another_dir/another_module/dual_currency_deposit.rs +++ b/examples/basic/src/artifacts/another_dir/another_module/dual_currency_deposit.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::include_simf; +use simplex::include_simf; use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; pub struct DualCurrencyDepositProgram { diff --git a/examples/basic/src/artifacts/another_dir/array_tr_storage.rs b/examples/basic/src/artifacts/another_dir/array_tr_storage.rs index 6d5f8f0..373f444 100644 --- a/examples/basic/src/artifacts/another_dir/array_tr_storage.rs +++ b/examples/basic/src/artifacts/another_dir/array_tr_storage.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::include_simf; +use simplex::include_simf; use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; pub struct ArrayTrStorageProgram { diff --git a/examples/basic/src/artifacts/module/option_offer.rs b/examples/basic/src/artifacts/module/option_offer.rs index 17e639e..603e7be 100644 --- a/examples/basic/src/artifacts/module/option_offer.rs +++ b/examples/basic/src/artifacts/module/option_offer.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::include_simf; +use simplex::include_simf; use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; pub struct OptionOfferProgram { diff --git a/examples/basic/src/artifacts/options.rs b/examples/basic/src/artifacts/options.rs index a2546b2..9631c0a 100644 --- a/examples/basic/src/artifacts/options.rs +++ b/examples/basic/src/artifacts/options.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::include_simf; +use simplex::include_simf; use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; pub struct OptionsProgram { diff --git a/examples/basic/src/artifacts/p2pk.rs b/examples/basic/src/artifacts/p2pk.rs index 9f1b4a1..3cf11bf 100644 --- a/examples/basic/src/artifacts/p2pk.rs +++ b/examples/basic/src/artifacts/p2pk.rs @@ -1,4 +1,4 @@ -use simplex::simplex_macros::include_simf; +use simplex::include_simf; use simplex::simplex_sdk::program::{ArgumentsTrait, Program}; use simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; pub struct P2pkProgram { diff --git a/examples/basic/src/lib.rs b/examples/basic/src/lib.rs new file mode 100644 index 0000000..91946c7 --- /dev/null +++ b/examples/basic/src/lib.rs @@ -0,0 +1 @@ +pub mod artifacts; diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs deleted file mode 100644 index 094eaa2..0000000 --- a/examples/basic/src/main.rs +++ /dev/null @@ -1,102 +0,0 @@ -mod artifacts; - -use simplicityhl::elements::{Script, Txid}; - -use simplex::simplex_sdk::constants::DUMMY_SIGNATURE; -use simplex::simplex_sdk::provider::{EsploraProvider, ProviderTrait, SimplicityNetwork}; -use simplex::simplex_sdk::signer::Signer; -use simplex::simplex_sdk::transaction::{ - FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, -}; -use simplex::simplex_sdk::utils::tr_unspendable_key; - -use artifacts::p2pk::P2pkProgram; -use artifacts::p2pk::derived_p2pk::{P2pkArguments, P2pkWitness}; - -const ESPLORA_URL: &str = "https://blockstream.info/liquidtestnet/api"; - -fn get_p2pk(signer: &Signer) -> (P2pkProgram, Script) { - let arguments = P2pkArguments { - public_key: signer.get_schnorr_public_key().unwrap().serialize(), - }; - - let p2pk = P2pkProgram::new(tr_unspendable_key(), arguments); - let p2pk_script = p2pk - .get_program() - .get_script_pubkey(SimplicityNetwork::LiquidTestnet) - .unwrap(); - - (p2pk, p2pk_script) -} - -fn spend_p2wpkh(signer: &Signer, provider: &EsploraProvider) -> Txid { - let (_, p2pk_script) = get_p2pk(signer); - - let mut ft = FinalTransaction::new(SimplicityNetwork::LiquidTestnet); - - ft.add_output(PartialOutput::new( - p2pk_script.clone(), - 50, - SimplicityNetwork::LiquidTestnet.policy_asset(), - )); - - let (tx, _) = signer.finalize(&ft, 1).unwrap(); - let res = provider.broadcast_transaction(&tx).unwrap(); - - println!("Broadcast: {}", res); - - res -} - -fn spend_p2pk(signer: &Signer, provider: &EsploraProvider) -> Txid { - let (p2pk, p2pk_script) = get_p2pk(signer); - - let mut p2pk_utxos = provider.fetch_scripthash_utxos(&p2pk_script).unwrap(); - - p2pk_utxos.retain(|el| el.1.asset.explicit().unwrap() == SimplicityNetwork::LiquidTestnet.policy_asset()); - - let mut ft = FinalTransaction::new(SimplicityNetwork::LiquidTestnet); - - let witness = P2pkWitness { - signature: DUMMY_SIGNATURE, - }; - - ft.add_program_input( - PartialInput::new(p2pk_utxos[0].0, p2pk_utxos[0].1.clone()), - ProgramInput::new(Box::new(p2pk.get_program().clone()), Box::new(witness.clone())), - RequiredSignature::Witness("SIGNATURE".to_string()), - ) - .unwrap(); - - let (tx, _) = signer.finalize(&ft, 1).unwrap(); - let res = provider.broadcast_transaction(&tx).unwrap(); - - println!("Broadcast: {}", res); - - res -} - -fn main() -> anyhow::Result<()> { - let provider = EsploraProvider::new(ESPLORA_URL.to_string()); - let signer = Signer::new( - "exist carry drive collect lend cereal occur much tiger just involve mean", - provider.clone(), - SimplicityNetwork::LiquidTestnet, - )?; - - let tx = spend_p2wpkh(&signer, &provider); - - provider.wait(&tx)?; - - println!("Confirmed"); - - let tx = spend_p2pk(&signer, &provider); - - provider.wait(&tx)?; - - println!("Confirmed"); - - println!("OK"); - - Ok(()) -} diff --git a/examples/basic/tests/draft_test.rs b/examples/basic/tests/draft_test.rs index e4fa66c..6cefbcf 100644 --- a/examples/basic/tests/draft_test.rs +++ b/examples/basic/tests/draft_test.rs @@ -1,4 +1,92 @@ -#[simplex::simplex_macros::test] -fn test_invocation_tx_tracking(_context: simplex::simplex_test::TestContext) -> anyhow::Result<()> { +use simplicityhl::elements::{Script, Txid}; + +use simplex::simplex_sdk::constants::DUMMY_SIGNATURE; +use simplex::simplex_sdk::provider::ProviderTrait; +use simplex::simplex_sdk::signer::Signer; +use simplex::simplex_sdk::transaction::{ + FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, +}; +use simplex::simplex_sdk::utils::tr_unspendable_key; + +use draft_example::artifacts::p2pk::P2pkProgram; +use draft_example::artifacts::p2pk::derived_p2pk::{P2pkArguments, P2pkWitness}; + +fn get_p2pk(context: &simplex::TestContext, signer: &Signer) -> (P2pkProgram, Script) { + let arguments = P2pkArguments { + public_key: signer.get_schnorr_public_key().unwrap().serialize(), + }; + + let p2pk = P2pkProgram::new(tr_unspendable_key(), arguments); + let p2pk_script = p2pk.get_program().get_script_pubkey(*context.get_network()).unwrap(); + + (p2pk, p2pk_script) +} + +fn spend_p2wpkh(context: &simplex::TestContext, signer: &Signer, provider: &dyn ProviderTrait) -> Txid { + let (_, p2pk_script) = get_p2pk(context, signer); + + let mut ft = FinalTransaction::new(*context.get_network()); + + ft.add_output(PartialOutput::new( + p2pk_script.clone(), + 50, + context.get_network().policy_asset(), + )); + + let (tx, _) = signer.finalize(&ft, 1).unwrap(); + let res = provider.broadcast_transaction(&tx).unwrap(); + + println!("Broadcast: {}", res); + + res +} + +fn spend_p2pk(context: &simplex::TestContext, signer: &Signer, provider: &dyn ProviderTrait) -> Txid { + let (p2pk, p2pk_script) = get_p2pk(context, signer); + + let mut p2pk_utxos = provider.fetch_scripthash_utxos(&p2pk_script).unwrap(); + + p2pk_utxos.retain(|el| el.1.asset.explicit().unwrap() == context.get_network().policy_asset()); + + let mut ft = FinalTransaction::new(*context.get_network()); + + let witness = P2pkWitness { + signature: DUMMY_SIGNATURE, + }; + + ft.add_program_input( + PartialInput::new(p2pk_utxos[0].0, p2pk_utxos[0].1.clone()), + ProgramInput::new(Box::new(p2pk.get_program().clone()), Box::new(witness.clone())), + RequiredSignature::Witness("SIGNATURE".to_string()), + ) + .unwrap(); + + let (tx, _) = signer.finalize(&ft, 1).unwrap(); + let res = provider.broadcast_transaction(&tx).unwrap(); + + println!("Broadcast: {}", res); + + res +} + +#[simplex::test] +fn dummy_test(context: simplex::TestContext) -> anyhow::Result<()> { + let signer = context.get_signer(); + let provider = context.get_provider(); + + let tx = spend_p2wpkh(&context, &signer, provider.as_ref()); + + provider.wait(&tx)?; + + println!("Confirmed"); + + let tx = spend_p2pk(&context, &signer, provider.as_ref()); + + provider.wait(&tx)?; + + println!("Confirmed"); + + println!("OK"); + Ok(()) } diff --git a/rustfmt.toml b/rustfmt.toml index 5e4210e..74b8276 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -16,12 +16,10 @@ reorder_modules = true # format_code_in_doc_comments = true # imports_granularity = "Crate" # group_imports = "StdExternalCrate" - -#wrap_comments = true -#where_single_line = false -#blank_lines_upper_bound = 2 -#brace_style = "AlwaysNextLine" -#control_brace_style = "AlwaysNextLine" -#empty_item_single_line = true -#use_small_heuristics = "Off" - +# wrap_comments = true +# where_single_line = false +# blank_lines_upper_bound = 2 +# brace_style = "AlwaysNextLine" +# control_brace_style = "AlwaysNextLine" +# empty_item_single_line = true +# use_small_heuristics = "Off"