diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..8878aac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,30 @@ +name: Bug Report +description: File a bug report +labels: ['bug'] +assignees: + - +body: + - type: markdown + attributes: + value: Thanks for taking the time to fill out this bug report! + - type: input + id: version + attributes: + label: "Project version" + placeholder: "0.1.2" + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A brief description of what happened and what you expected to happen + validations: + required: true + - type: textarea + id: reproduction-steps + attributes: + label: "Minimal reproduction steps" + description: "The minimal steps needed to reproduce the bug" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..290a178 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,13 @@ +name: Feature request +description: Suggest a new feature +labels: ['feature'] +assignees: + - +body: + - type: textarea + id: feature-description + attributes: + label: "Describe the feature" + description: "A description of what you would like to see in the project" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/other-issue.md b/.github/ISSUE_TEMPLATE/other-issue.md new file mode 100644 index 0000000..7115534 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other-issue.md @@ -0,0 +1,4 @@ +--- +name: Other issue +about: Other kind of issue +--- diff --git a/.github/actions/setup-smplx/action.yml b/.github/actions/setup-smplx/action.yml new file mode 100644 index 0000000..5b2eff4 --- /dev/null +++ b/.github/actions/setup-smplx/action.yml @@ -0,0 +1,22 @@ +name: setup-smplx + +inputs: + commit-sha: + description: "Commit in repository to use" + required: false + default: "master" + owner-repo: + description: "Owner repo on Github to use" + required: false + default: "BlockstreamResearch/smplx" + +runs: + using: composite + steps: + - name: Install smplx-cli via simplexup + shell: bash + run: | + set -euo pipefail + REVISION="${{ inputs.commit-sha }}" + OWNER_REPO="${{ inputs.owner-repo }}" + bash "$GITHUB_WORKSPACE/.github/scripts/setup-smplx" "${REVISION}" "${OWNER_REPO}" diff --git a/.github/scripts/setup-smplx b/.github/scripts/setup-smplx new file mode 100644 index 0000000..0cfc330 --- /dev/null +++ b/.github/scripts/setup-smplx @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +set -euo pipefail + +say() { + printf "setup-smplx: %s\n" "$1" +} + +err() { + say "$1" >&2 + exit 1 +} + +main() { + # Parse and validate arguments + COMMIT_SHA="${1:-}" + OWNER_REPO="${2:-}" + FETCH_REF="${COMMIT_SHA:-master}" + FILE_PATH="simplexup/install" + + if [ "$#" -lt 2 ]; then + err "Usage: setup-smplx " + fi + + if [ -z "$OWNER_REPO" ]; then + err "no OWNER_REPO variable set" + fi + + # Download and execute the initial install script + RAW_URL="https://raw.githubusercontent.com/${OWNER_REPO}/${FETCH_REF}/${FILE_PATH}" + say "Fetching ${FILE_PATH} from ${RAW_URL}" + + curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 "${RAW_URL}" | bash + + # Download the simplexup executable + FILE_PATH="simplexup/simplexup" + RAW_URL="https://raw.githubusercontent.com/${OWNER_REPO}/${FETCH_REF}/${FILE_PATH}" + + BASE_DIR="${XDG_CONFIG_HOME:-$HOME}" + SIMPLEX_DIR="${SIMPLEX_DIR:-$BASE_DIR/.simplex}" + SIMPLEX_BIN_DIR="$SIMPLEX_DIR/bin" + BIN_PATH="$SIMPLEX_BIN_DIR/simplexup" + + # Ensure bin directory exists and download simplexup into it + mkdir -p "$SIMPLEX_BIN_DIR" + say "Fetching ${FILE_PATH} from ${RAW_URL}" + + curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 "${RAW_URL}" -o "$BIN_PATH" + chmod +x "$BIN_PATH" + + # Execute simplexup, using a specific commit if provided (and not master's default release) + if [ "$FETCH_REF" != "master" ]; then + say "Using simplexup and simplex from commit: ${FETCH_REF}" + "$BIN_PATH" --commit "${FETCH_REF}" + else + say "Using simplexup from master branch and installing latest simplex" + "$BIN_PATH" + fi + + # Add the Simplex binary directory to GitHub Actions PATH + echo "$SIMPLEX_BIN_DIR" >> "$GITHUB_PATH" + + # Verify installation + say "Verifying simplex installation..." + "${SIMPLEX_BIN_DIR}/simplex" --version +} + +main "$@" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..209790a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,61 @@ +name: Smplx-tests + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + - dev + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + +jobs: + simplex-tests: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + strategy: + fail-fast: false + matrix: + toolchain: [ "1.91.0" ] + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: true + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.toolchain }} + + - name: Use Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Install simplex-cli + uses: ./.github/actions/setup-smplx + + - name: Generate contract artifacts + shell: bash + run: | + simplex build + + - name: Build + run: | + cargo build --workspace --all-features --verbose + + - name: Test + run: | + simplex test --test-threads 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddafd0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Build output +target/ + +# Simplex output +src/artifacts + +# IDE/editors (optional but common minimal) +**/.DS_Store +.idea/ +.vscode/ +.cache +.env diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..924c45f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2020 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + +[[package]] +name = "ar_archive_writer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +dependencies = [ + "object", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "base58ck" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" +dependencies = [ + "bitcoin_hashes", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "rand", + "rand_core", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin" +version = "0.32.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed8ccb78a9ff7a6fbb90e2fb9b8588b4a9928d49d8af3cb789108a84ea6b0ce" +dependencies = [ + "base58ck", + "base64 0.21.7", + "bech32", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes", + "hex-conservative 0.2.2", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + +[[package]] +name = "bitcoin-io" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + +[[package]] +name = "bitcoin-units" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" +dependencies = [ + "bitcoin-consensus-encoding", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.2", + "serde", +] + +[[package]] +name = "bitcoincore-rpc" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" +dependencies = [ + "bitcoincore-rpc-json", + "jsonrpc", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoincore-rpc-json" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" +dependencies = [ + "bitcoin", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoind" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce6620b7c942dbe28cc49c21d95e792feb9ffd95a093205e7875ccfa69c2925" +dependencies = [ + "anyhow", + "bitcoincore-rpc", + "log", + "tempfile", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" +dependencies = [ + "memchr", + "serde_core", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chumsky" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc17a6284abccac6e50db35c1cee87f605474a72939b959a3a67d9371800efd" +dependencies = [ + "hashbrown 0.15.5", + "regex-automata 0.3.9", + "serde", + "stacker", + "unicode-ident", + "unicode-segmentation", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +dependencies = [ + "serde", +] + +[[package]] +name = "electrsd" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91435161fb2ad5098e7ac7a4b793bf9c34723b0208a3fcf6f33707489e771396" +dependencies = [ + "bitcoind", + "electrum-client", + "log", + "nix", + "which", +] + +[[package]] +name = "electrum-client" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0bd443023f9f5c4b7153053721939accc7113cbdf810a024434eed454b3db1" +dependencies = [ + "bitcoin", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "elements" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c63a04238f4b7f3564fe9705921452c2900efd00982545e5c634fac9024fe2" +dependencies = [ + "bech32", + "bitcoin", + "secp256k1-zkp", + "serde", + "serde_json", +] + +[[package]] +name = "elements-miniscript" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571fa105690f83c7833df2109eb2e14ca0e62d633d2624ffcb166ff18a3da870" +dependencies = [ + "bitcoin", + "elements", + "miniscript", + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "ghost-cell" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "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.15", + "regex-syntax 0.8.11", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.13.0", + "ignore", + "walkdir", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "ignore" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adf14691c72bcfc1058740436a35bdd3ae9c07d1a941ef00b749e9ea16aefa7" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.15", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "impl-codec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" +dependencies = [ + "base64 0.13.1", + "minreq 2.14.1", + "serde", + "serde_json", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniscript" +version = "12.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8343cc1ef1408bd9bdbf69f7aef47017dfab7e6349ec26fddf62e0e9fb5a4cf" +dependencies = [ + "bech32", + "bitcoin", +] + +[[package]] +name = "minreq" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "minreq" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "659579df697b372ef9e36f02fcbb41f6d6f157dcec7db9c9618fa0f23cf0fc20" +dependencies = [ + "rustls", + "rustls-webpki", + "serde", + "serde_json", + "webpki-roots", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primitive-types" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721a1da530b5a2633218dc9f75713394c983c352be88d2d7c9ee85e2c4c21794" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.11", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes", + "rand", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-zkp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" +dependencies = [ + "bitcoin-private", + "rand", + "secp256k1", + "secp256k1-zkp-sys", + "serde", +] + +[[package]] +name = "secp256k1-zkp-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f08b2d0b143a22e07f798ae4f0ab20d5590d7c68e0d090f2088a48a21d1654" +dependencies = [ + "cc", + "secp256k1-sys", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simplicity-lang" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13ed081e3046d66c146d7201bcbf3b655ca3436cb83f6efc26d7895bd2b79d06" +dependencies = [ + "bitcoin", + "bitcoin_hashes", + "byteorder", + "elements", + "getrandom 0.2.17", + "ghost-cell", + "hex-conservative 0.2.2", + "miniscript", + "simplicity-sys", +] + +[[package]] +name = "simplicity-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d1ec5477c7650b8ef511aa56dccb28f2e8cdb6e87f260ecffdaf0ebfef2d3b" +dependencies = [ + "bitcoin_hashes", + "cc", +] + +[[package]] +name = "simplicityhl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361316795ec753230c421d964ab60940d20d5252c2d39272452528832c9e0ec5" +dependencies = [ + "base64 0.21.7", + "chumsky", + "clap", + "either", + "getrandom 0.2.17", + "itertools", + "miniscript", + "serde", + "serde_json", + "simplicity-lang", +] + +[[package]] +name = "simplicityhl-std" +version = "0.1.0" +dependencies = [ + "anyhow", + "primitive-types", + "rand", + "smplx-std", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smplx-build" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a9a38dc0d803acdeed6217cdf9c1b3ded9220796315a458f47572269abaf2f" +dependencies = [ + "glob", + "globwalk", + "pathdiff", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "simplicityhl", + "syn", + "thiserror", + "toml", +] + +[[package]] +name = "smplx-macros" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e1091132a1f5157111920504e403fc8a4eb8a7814a376e4bd4c55a7fb6edff" +dependencies = [ + "smplx-build", + "smplx-test", + "syn", +] + +[[package]] +name = "smplx-regtest" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ccf7e3eade934e890bcee3558af658de46177dbd6a5dd0302bb1a64b7701bbe" +dependencies = [ + "electrsd", + "hex", + "hmac", + "serde", + "sha2", + "smplx-sdk", + "thiserror", + "toml", +] + +[[package]] +name = "smplx-sdk" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d06536b493296672867c1a1ea8d67538c233af72624c18694ecb50691fcb74" +dependencies = [ + "bip39", + "bitcoin_hashes", + "bitcoincore-rpc", + "dyn-clone", + "elements-miniscript", + "hex", + "minreq 3.0.0", + "serde", + "serde_json", + "sha2", + "simplicityhl", + "thiserror", +] + +[[package]] +name = "smplx-std" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "834fd437c9394610ece857755d5967124a0b97c6837bf1170ae8deed38ba4d32" +dependencies = [ + "either", + "serde", + "simplicityhl", + "smplx-macros", + "smplx-sdk", + "smplx-test", +] + +[[package]] +name = "smplx-test" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82bf119d89ca2b2ac992377912efe7dd1915e0aa6a193ca5383fb8a3da86594c" +dependencies = [ + "electrsd", + "proc-macro2", + "quote", + "serde", + "simplicityhl", + "smplx-regtest", + "smplx-sdk", + "syn", + "thiserror", + "toml", +] + +[[package]] +name = "stacker" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.61.2", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.4", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.4", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "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]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..36ad43c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "simplicityhl-std" +edition = "2024" +rust-version = "1.91.0" +version = "0.1.0" +description = "SimplicityHL Standard Library" +license = "MIT" +repository = "https://github.com/BlockstreamResearch/simplicityhl-std" +readme = "README.md" +keywords = ["simplicity", "liquid", "elements", "smart-contracts"] +categories = ["cryptography::cryptocurrencies"] + +[dependencies] +smplx-std = "0.0.8" + +[dev-dependencies] +anyhow = { version = "1.0.101" } +rand = { version = "0.8.6" } +primitive-types = { version = "0.14" } 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 f29ea14..5f858f7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,133 @@ # SimplicityHL Standard Library This repository contains the standard library for [SimplicityHL](https://github.com/BlockstreamResearch/SimplicityHL). + +> [!NOTE] +> The VS Code syntax-highlighting extension does not yet support modules and imports, so you may see spurious errors when editing files in this repo. + +## Modules: + +- asserts +- logical_operations +- op_return +- u8 +- u16 +- u32 +- u64 +- u128 + +--- +`Asserts` + +Generic assertion helpers with equality checks between uint values and validation of Option uint variants. + +--- +`Logical operations` + +Basic boolean logic operations: and, or, not. + +--- +`OP_RETURN` + +Utilities for detecting and enforcing OP_RETURN (null data) outputs. + +--- +`u8` + +Operations for the u8 type: +- overflow-checked arithmetic operations; +- comparison helpers. + +--- +`u16` + +Operations for the u16 type: +- overflow-checked arithmetic operations; +- comparison helpers. + +--- +`u32` + +Operations for the u32 type: +- overflow-checked arithmetic operations; +- comparison helpers. + +--- +`u64` + +Operations for the u64 type: +- overflow-checked arithmetic operations; +- comparison helpers. + +--- +`u128` + +Operations for the u128 type: +- overflow-checked arithmetic operations; +- comparison helpers; +- basic operations that are available as jets for `u8`-`u64` but are missing for `u128`. + +## Installation + +Install `simplexup`, then use it to install the pinned Simplex toolchain: + +```bash +curl -L https://smplx.simplicity-lang.org | bash +simplexup +``` + +```bash +simplex install simplicityhl-std +``` + +> [!NOTE] +> Library works with Simplex version 0.0.7 or higher. + +## Contributing + +### Build + +To compile the contracts, execute the following command: + +```bash +simplex build +``` + +To clean up generated artifacts: + +```bash +simplex clean +``` + +### Test + +To run the tests with logs (`-v` or `-vv` is available), execute the following command: + +```bash +simplex test -v +``` + +To run the tests using multiple threads: + +```bash +simplex test --test-threads 8 +``` + +To run a specific test module: + +```bash +simplex test u8_test +``` + +To run a specific test: + +```bash +simplex test test_name +``` + +### Lint & format + +```bash +cargo fmt +cargo clippy --workspace --all-targets --all-features -- -D warnings +``` diff --git a/Simplex.toml b/Simplex.toml new file mode 100644 index 0000000..c522f64 --- /dev/null +++ b/Simplex.toml @@ -0,0 +1,31 @@ +# DEFAULT CONFIG + +# [build] +# src_dir = "./simf" +# simf_files = ["*.simf"] +# out_dir = "./src/artifacts" + +# [dependencies] +# some_dep = { git = "", path = "" } + +# [regtest] +# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" +# bitcoins = 10_000_000 +# rpc_port = 18443 +# esplora_port = 3000 +# rpc_user = "user" +# rpc_password = "password" + +# [test] +# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" +# bitcoins = 10_000_000 +# verbosity = 0 + +# [test.esplora] +# url = "" +# network = "" + +# [test.rpc] +# url = "" +# username = "" +# password = "" diff --git a/simf/asserts_test.simf b/simf/asserts_test.simf new file mode 100644 index 0000000..bcaf598 --- /dev/null +++ b/simf/asserts_test.simf @@ -0,0 +1,40 @@ +use crate::lib::asserts::{assert_eq_8, assert_eq_16, assert_eq_32, assert_eq_64,assert_eq_128, assert_eq_256, assert_none_8, assert_none_16, assert_none_32, assert_none_64, assert_none_128, assert_none_256}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a_u8: Option = witness::FIRST_ARG_U8; + let b_u8: Option = witness::SECOND_ARG_U8; + + let a_u16: Option = witness::FIRST_ARG_U16; + let b_u16: Option = witness::SECOND_ARG_U16; + + let a_u32: Option = witness::FIRST_ARG_U32; + let b_u32: Option = witness::SECOND_ARG_U32; + + let a_u64: Option = witness::FIRST_ARG_U64; + let b_u64: Option = witness::SECOND_ARG_U64; + + let a_u128: Option = witness::FIRST_ARG_U128; + let b_u128: Option = witness::SECOND_ARG_U128; + + let a_u256: Option = witness::FIRST_ARG_U256; + let b_u256: Option = witness::SECOND_ARG_U256; + + // Assert Eq + match if_test_this_function(0, fn_idx) { true => { assert_eq_8(unwrap(a_u8), unwrap(b_u8)); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_eq_16(unwrap(a_u16), unwrap(b_u16)); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_eq_32(unwrap(a_u32), unwrap(b_u32)); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert_eq_64(unwrap(a_u64), unwrap(b_u64)); }, false => (), }; + match if_test_this_function(4, fn_idx) { true => { assert_eq_128(unwrap(a_u128), unwrap(b_u128)); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert_eq_256(unwrap(a_u256), unwrap(b_u256)); }, false => (), }; + + // Assert None + match if_test_this_function(6, fn_idx) { true => { assert_none_8(a_u8); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert_none_16(a_u16); }, false => (), }; + match if_test_this_function(8, fn_idx) { true => { assert_none_32(a_u32); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_none_64(a_u64); }, false => (), }; + match if_test_this_function(10, fn_idx) { true => { assert_none_128(a_u128); }, false => (), }; + match if_test_this_function(11, fn_idx) { true => { assert_none_256(a_u256); }, false => (), }; +} diff --git a/simf/helper.simf b/simf/helper.simf new file mode 100644 index 0000000..d718091 --- /dev/null +++ b/simf/helper.simf @@ -0,0 +1,13 @@ +use crate::lib::logical_operations::not; + +pub fn if_test_this_function(index: u8, flag: u8) -> bool { + jet::eq_8(index, flag) +} + +/// Asserts a result equals the expected bool value. +pub fn assert_bool(result: bool, expected_bool: bool) { + match expected_bool { + true => assert!(result), + false => assert!(not(result)), + } +} \ No newline at end of file diff --git a/simf/lib/asserts.simf b/simf/lib/asserts.simf new file mode 100644 index 0000000..6714e6d --- /dev/null +++ b/simf/lib/asserts.simf @@ -0,0 +1,61 @@ +use crate::lib::u128::{eq_128}; + +/// Asserts that two u8 are equal +pub fn assert_eq_8(a: u8, b: u8) { + assert!(jet::eq_8(a, b)); +} + +/// Asserts that two u16 are equal +pub fn assert_eq_16(a: u16, b: u16) { + assert!(jet::eq_16(a, b)); +} + +/// Asserts that two u32 are equal +pub fn assert_eq_32(a: u32, b: u32) { + assert!(jet::eq_32(a, b)); +} + +/// Asserts that two u64 are equal +pub fn assert_eq_64(a: u64, b: u64) { + assert!(jet::eq_64(a, b)); +} + +/// Asserts that two u128 are equal +pub fn assert_eq_128(a: u128, b: u128) { + assert!(eq_128(a, b)); +} + +/// Asserts that two u256 are equal +pub fn assert_eq_256(a: u256, b: u256) { + assert!(jet::eq_256(a, b)); +} + +/// Asserts that provided u8 Option value is a None +pub fn assert_none_8(val: Option) { + assert!(is_none::(val)); +} + +/// Asserts that provided u16 Option value is a None +pub fn assert_none_16(val: Option) { + assert!(is_none::(val)); +} + +/// Asserts that provided u32 Option value is a None +pub fn assert_none_32(val: Option) { + assert!(is_none::(val)); +} + +/// Asserts that provided u64 Option value is a None +pub fn assert_none_64(val: Option) { + assert!(is_none::(val)); +} + +/// Asserts that provided u128 Option value is a None +pub fn assert_none_128(val: Option) { + assert!(is_none::(val)); +} + +/// Asserts that provided u256 Option value is a None +pub fn assert_none_256(val: Option) { + assert!(is_none::(val)); +} diff --git a/simf/lib/logical_operations.simf b/simf/lib/logical_operations.simf new file mode 100644 index 0000000..63ea8e6 --- /dev/null +++ b/simf/lib/logical_operations.simf @@ -0,0 +1,14 @@ +/// Returns the result of the NOT operation on the provided value +pub fn not(bit: bool) -> bool { + ::into(jet::complement_1(::into(bit))) +} + +/// Returns the result of the OR operation on the provided values +pub fn or(a: bool, b: bool) -> bool { + ::into(jet::or_1(::into(a), ::into(b))) +} + +/// Returns the result of the AND operation on the provided values +pub fn and(a: bool, b: bool) -> bool { + ::into(jet::and_1(::into(a), ::into(b))) +} diff --git a/simf/lib/op_return.simf b/simf/lib/op_return.simf new file mode 100644 index 0000000..2f71087 --- /dev/null +++ b/simf/lib/op_return.simf @@ -0,0 +1,12 @@ +/// Returns true if the specified output is an OP_RETURN (null data) output +pub fn is_output_op_return(output_index: u32) -> bool { + match jet::output_null_datum(output_index, 0) { + Some(entry: Option>>) => true, + None => false, + } +} + +/// Asserts that the specified output is an OP_RETURN (null data) output +pub fn assert_output_is_op_return(output_index: u32) { + assert!(is_output_op_return(output_index)); +} diff --git a/simf/lib/u128.simf b/simf/lib/u128.simf new file mode 100644 index 0000000..4c80745 --- /dev/null +++ b/simf/lib/u128.simf @@ -0,0 +1,431 @@ +use crate::lib::logical_operations::{not, or, and}; + +/// Bit logic + +/// Bitwise AND of two 128-bit values +pub fn and_128(a: u128, b: u128) -> u128 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + <(u64, u64)>::into((jet::and_64(a_high, b_high), jet::and_64(a_low, b_low))) +} + +/// Bitwise OR of two 128-bit values +pub fn or_128(a: u128, b: u128) -> u128 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + <(u64, u64)>::into((jet::or_64(a_high, b_high), jet::or_64(a_low, b_low))) +} + +/// Checks if two 128-bit values are equal +pub fn eq_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + and(jet::eq_64(a_high, b_high), jet::eq_64(a_low, b_low)) +} + +/// Left-shift a 128-bit value by the given amount. Bits are filled with zeroes +pub fn left_shift_128(shift: u8, a: u128) -> u128 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u64, u64) = ::into(a); + + match jet::lt_8(shift, 64) { + true => { + let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + let shifted_bits: u64 = jet::right_shift_64(low_to_high_amount, a_low); + + let res_high: u64 = jet::or_64(jet::left_shift_64(shift, a_high), shifted_bits); + + <(u64, u64)>::into((res_high, jet::left_shift_64(shift, a_low))) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 + + <(u64, u64)>::into((jet::left_shift_64(shift, a_low), 0)) + } + } + } + } +} + +/// Right-shift a 128-bit value by the given amount. Bits are filled with zeroes +pub fn right_shift_128(shift: u8, a: u128) -> u128 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u64, u64) = ::into(a); + + match jet::lt_8(shift, 64) { + true => { + let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + let shifted_bits: u64 = jet::left_shift_64(high_to_low_amount, a_high); + + let res_low: u64 = jet::or_64(jet::right_shift_64(shift, a_low), shifted_bits); + + <(u64, u64)>::into((jet::right_shift_64(shift, a_high), res_low)) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 + + <(u64, u64)>::into((0, jet::right_shift_64(shift, a_high))) + } + } + } + } +} + +/// Arithmetic + +/// Checks if an integer is zero +pub fn is_zero_128(a: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + + and(jet::is_zero_64(a_high), jet::is_zero_64(a_low)) +} + +/// Checks if an integer is less than another integer +pub fn lt_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + match jet::lt_64(a_high, b_high) { + true => true, + false => { + match jet::eq_64(a_high, b_high) { + true => jet::lt_64(a_low, b_low), + false => false, + } + } + } +} + +/// Checks if an integer is less than or equal to another integer +pub fn le_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + match jet::lt_64(a_high, b_high) { + true => true, + false => { + match jet::eq_64(a_high, b_high) { + true => jet::le_64(a_low, b_low), + false => false, + } + } + } +} + +/// Check if an integer is greater than another integer +pub fn gt_128(a: u128, b: u128) -> bool { + lt_128(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_128(a: u128, b: u128) -> bool { + le_128(b, a) +} + +/// Adds two integers and returns the carry +pub fn add_128(a: u128, b: u128) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let (carry_low, sum_low): (bool, u64) = jet::add_64(a_low, b_low); + let (carry_high, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); + + let res: u128 = <(u64, u64)>::into((sum_high, sum_low)); + (carry_high, res) +} + +/// Adds the 128-bit integer with the 64-bit integer. Returns a tuple of the sum and the carry +pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + + let (carry_low, res_low): (bool, u64) = jet::add_64(a_low, b); + let (carry_high, res_high): (bool, u64) = jet::full_add_64(carry_low, a_high, 0); + + (carry_high, <(u64, u64)>::into((res_high, res_low))) +} + +/// Returns the sum of two u128 values wrapped in Some, or None if the result overflows u128 +pub fn checked_add_128(a: u128, b: u128) -> Option { + let (carry, sum): (bool, u128) = add_128(a, b); + + match carry { + true => None, + false => Some(sum), + } +} + +/// Returns the sum of two u128 values, panics if the result overflows u128 +pub fn safe_add_128(a: u128, b: u128) -> u128 { + unwrap(checked_add_128(a, b)) +} + +/// Subtracts the second integer from the first integer, and returns the borrow bit +pub fn sub_128(a: u128, b: u128) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let (borrow_low, diff_low): (bool, u64) = jet::subtract_64(a_low, b_low); + let (borrow_high, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); + + let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); + (borrow_high, res) +} + +/// Returns the difference of two u128 values wrapped in Some, or None if the result overflows u128 +pub fn checked_sub_128(a: u128, b: u128) -> Option { + let (borrow, diff): (bool, u128) = sub_128(a, b); + + match borrow { + true => None, + false => Some(diff), + } +} + +/// Returns the difference of two u128 values, panics if the result overflows u128 +pub fn safe_sub_128(a: u128, b: u128) -> u128 { + unwrap(checked_sub_128(a, b)) +} + +/// Multiply two integers. The output is a 256-bit integer +/// The idea is that u128-bit `a` divides into 64-bit `a_high` and `a_low`, +/// so a = a_high * 2^64 + a_low. +/// In the same way, b = b_high * 2^64 + b_low. +/// Therefore, a * b = 2^128 * a_high * b_high + 2^64(a_high * b_low + a_low * b_high) + a_low * b_low. +pub fn mul_128(a: u128, b: u128) -> u256 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let highest: u128 = jet::multiply_64(a_high, b_high); + let lowest: u128 = jet::multiply_64(a_low, b_low); + let (word_1, word_0): (u64, u64) = ::into(lowest); + let (word_3, word_2): (u64, u64) = ::into(highest); + + let product_1: u128 = jet::multiply_64(a_high, b_low); + let product_2: u128 = jet::multiply_64(b_high, a_low); + let (carry_3a, middle): (bool, u128) = add_128(product_1, product_2); + + let (middle_2, middle_1): (u64, u64) = ::into(middle); + + // fold the low-side carry directly into the word_2 addition via full_add_64, + // then propagate any resulting carry into word_3 + let (carry_2, res_1): (bool, u64) = jet::add_64(word_1, middle_1); + let (carry_3b, res_2): (bool, u64) = jet::full_add_64(carry_2, word_2, middle_2); + + // `word_3` is the upper half of a_high * b_high. It is at most `u64::MAX - 1` when + // either factor is `u64::MAX`, and even in the extreme case where + // a == b == u128::MAX, the total product still fits into u256. + // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `add_64` + // is used instead of `safe_add_64` to avoid the unnecessary overflow check + let (_, res_3a): (bool, u64) = jet::full_add_64(carry_3a, word_3, 0); + let (_, res_3): (bool, u64) = jet::full_add_64(carry_3b, res_3a, 0); + + let res_1_0: u128 = <(u64, u64)>::into((res_1, word_0)); + let res_3_2: u128 = <(u64, u64)>::into((res_3, res_2)); + + <(u128, u128)>::into((res_3_2, res_1_0)) +} + +/// Returns the product of two u128 values wrapped in Some, or None if the result overflows u128 +pub fn checked_mul_128(a: u128, b: u128) -> Option { + let result: u256 = mul_128(a, b); + let (high, low): (u128, u128) = ::into(result); + + match is_zero_128(high) { + true => Some(low), + false => None, + } +} + +/// Returns the product of two u128 values, panics if the result overflows u128 +pub fn safe_mul_128(a: u128, b: u128) -> u128 { + unwrap(checked_mul_128(a, b)) +} + +/// Splits the u256 integer into four u64 integers +pub fn split_256_into_64(a: u256) -> ((u64, u64), (u64, u64)) { + let (high, low): (u128, u128) = ::into(a); + + (::into(high), ::into(low)) +} + +/// Helper function, can be used with jet::div_mod_128_64. +/// Normalizes two u128 values by multiplying both by the same factor, +/// ensuring that the most significant non-zero word of `b` is at least 2^63. +/// +/// If `is_b_u128` is true, expects the upper half of `b` to be non-zero. +/// If `is_b_u128` is false, expects `b` to fit into u64. +/// +/// Division algorithms operate in base 2^64, so the normalization threshold is 2^63. +pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) { + // Compile-time constant: 2^63. Avoids a runtime jet::left_shift_64 call. + let threshold: u64 = 0x8000000000000000; + + let (b_high, b_low): (u64, u64) = ::into(b); + + let b_highest_word: u64 = match is_b_u128 { + true => b_high, + false => { + assert!(jet::is_zero_64(b_high)); + b_low + } + }; + assert!(not(jet::is_zero_64(b_highest_word))); + + let (norm, remainder): (u64, u64) = jet::div_mod_64(threshold, b_highest_word); + + let norm: u64 = match jet::is_zero_64(remainder) { + true => norm, + false => { + let (_, norm): (bool, u64) = jet::add_64(norm, 1); // norm <= 2^63, so norm + 1 can not overflow + norm + } + }; + let norm: u128 = <(u64, u64)>::into((0, norm)); + + match jet::lt_64(b_highest_word, threshold) { + true => (mul_128(a, norm), safe_mul_128(b, norm)), + false => (<(u128, u128)>::into((0, a)), b), + } +} + +/// Divides the first u128 integer by the second u128 integer, +/// returns the u64 quotient and the u128 remainder. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero. +pub fn algorithm_d(dividend: u128, divisor: u128) -> (u64, u128) { + let (norm_dividend, norm_divisor): (u256, u128) = normalize_to_threshold(dividend, divisor, true); + + // normalized dividend fits into 192 bits + let ((_, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend); + let (v1, v0): (u64, u64) = ::into(norm_divisor); + + let (q_hat, r_hat): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); + + let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); + + // correcting estimation: q_hat is off by at most 2. + let q: u64 = match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); + let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); + + match carry { + true => q_hat, + false => { + let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); + + match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); + + q_hat + } + false => q_hat, + } + } + + } + }, + false => q_hat, + }; + + let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, <(u64, u64)>::into((0, q)))); + (q, remainder) +} + +/// Divides the 128-bit integer by the 64-bit integer, +/// returns a tuple of the u128 quotient and the u64 remainder +pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { + let (a_high, a_low): (u64, u64) = ::into(a); + + // calculate the upper half of the quotient + let (q_high, remainder): (u64, u64) = jet::div_mod_64(a_high, b); + + // a' = remainder * 2^64 + a_low + let a_prime: u128 = <(u64, u64)>::into((remainder, a_low)); + + // we need to normalize here, because jet::div_mod_128_64 only accepts b >= 2^63 + let (a_normalized, b_normalized): (u256, u128) = normalize_to_threshold(a_prime, <(u64, u64)>::into((0, b)), false); + + // a_normalized fits into u128, because remainder < b and b_normalized fits into u64 + let (_, a_normalized): (u128, u128) = ::into(a_normalized); + let (_, b_normalized): (u64, u64) = ::into(b_normalized); + + // remainder < b, so (remainder * 2^64 + a_low) / b fits into u64 + let (q_low, _): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); // remainder is not valid here due to normalizing + + let (_, remainder): (u64, u64) = ::into(safe_sub_128(a_prime, jet::multiply_64(q_low, b))); + + (<(u64, u64)>::into((q_high, q_low)), remainder) +} + +/// Divides the first integer by the second integer, +/// returns the quotient and the remainder +pub fn div_mod_128(a: u128, b: u128) -> (u128, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + match lt_128(a, b) { + true => (0, a), + false => { + match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { + true => { + // if both a_high and b_high are zero, this narrows down to 64-bit division + let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); + (<(u64, u64)>::into((0, q)), <(u64, u64)>::into((0, r))) + }, + false => { + match jet::eq_64(a_high, b_high) { + true => { + // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, + // and the subtraction can not underflow + let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); + (1, <(u64, u64)>::into((0, diff))) + }, + false => { + match jet::is_zero_64(b_high) { + true => { + let (q, r): (u128, u64) = div_mod_128_64(a, b_low); + (q, <(u64, u64)>::into((0, r))) + }, + false => { + let (q, r): (u64, u128) = algorithm_d(a, b); + (<(u64, u64)>::into((0, q)), r) + } + } + } + } + } + } + } + } +} + +/// Divide the first integer by the second integer, returns the quotient +pub fn div_128(a: u128, b: u128) -> u128 { + let (q, _): (u128, u128) = div_mod_128(a, b); + + q +} + +/// Returns the quotient of two u128 values wrapped in Some, or None if the result overflows u128 +pub fn checked_div_128(a: u128, b: u128) -> Option { + match is_zero_128(b) { + true => None, + false => Some(div_128(a, b)), + } +} + +/// Returns the quotient of two u128 values, panics if the result overflows u128 +pub fn safe_div_128(a: u128, b: u128) -> u128 { + unwrap(checked_div_128(a, b)) +} diff --git a/simf/lib/u16.simf b/simf/lib/u16.simf new file mode 100644 index 0000000..e146253 --- /dev/null +++ b/simf/lib/u16.simf @@ -0,0 +1,69 @@ +/// Returns the sum of two u16 values wrapped in Some, or None if the result overflows u16 +pub fn checked_add_16(a: u16, b: u16) -> Option { + let (carry, sum): (bool, u16) = jet::add_16(a, b); + + match carry { + true => None, + false => Some(sum), + } +} + +/// Returns the sum of two u16 values, panics if the result overflows u16 +pub fn safe_add_16(a: u16, b: u16) -> u16 { + unwrap(checked_add_16(a, b)) +} + +/// Returns the difference of two u16 values wrapped in Some, or None if the result overflows u16 +pub fn checked_sub_16(a: u16, b: u16) -> Option { + let (borrow, diff): (bool, u16) = jet::subtract_16(a, b); + + match borrow { + true => None, + false => Some(diff), + } +} + +/// Returns the difference of two u16 values, panics if the result overflows u16 +pub fn safe_sub_16(a: u16, b: u16) -> u16 { + unwrap(checked_sub_16(a, b)) +} + +/// Returns the product of two u16 values wrapped in Some, or None if the result overflows u16 +pub fn checked_mul_16(a: u16, b: u16) -> Option { + let result: u32 = jet::multiply_16(a, b); + + let (high, low): (u16, u16) = ::into(result); + + match jet::is_zero_16(high) { + true => Some(low), + false => None, + } +} + +/// Returns the product of two u16 values, panics if the result overflows u16 +pub fn safe_mul_16(a: u16, b: u16) -> u16 { + unwrap(checked_mul_16(a, b)) +} + +/// Returns the quotient of two u16 values wrapped in Some, or None if the result overflows u16 +pub fn checked_div_16(a: u16, b: u16) -> Option { + match jet::is_zero_16(b) { + true => None, + false => Some(jet::divide_16(a, b)), + } +} + +/// Returns the quotient of two u16 values, panics if the result overflows u16 +pub fn safe_div_16(a: u16, b: u16) -> u16 { + unwrap(checked_div_16(a, b)) +} + +/// Check if an integer is greater than another integer +pub fn gt_16(a: u16, b: u16) -> bool { + jet::lt_16(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_16(a: u16, b: u16) -> bool { + jet::le_16(b, a) +} diff --git a/simf/lib/u32.simf b/simf/lib/u32.simf new file mode 100644 index 0000000..e85f5e4 --- /dev/null +++ b/simf/lib/u32.simf @@ -0,0 +1,69 @@ +/// Returns the sum of two u32 values wrapped in Some, or None if the result overflows u32 +pub fn checked_add_32(a: u32, b: u32) -> Option { + let (carry, sum): (bool, u32) = jet::add_32(a, b); + + match carry { + true => None, + false => Some(sum), + } +} + +/// Returns the sum of two u32 values, panics if the result overflows u32 +pub fn safe_add_32(a: u32, b: u32) -> u32 { + unwrap(checked_add_32(a, b)) +} + +/// Returns the difference of two u32 values wrapped in Some, or None if the result overflows u32 +pub fn checked_sub_32(a: u32, b: u32) -> Option { + let (borrow, diff): (bool, u32) = jet::subtract_32(a, b); + + match borrow { + true => None, + false => Some(diff), + } +} + +/// Returns the difference of two u32 values, panics if the result overflows u32 +pub fn safe_sub_32(a: u32, b: u32) -> u32 { + unwrap(checked_sub_32(a, b)) +} + +/// Returns the product of two u32 values wrapped in Some, or None if the result overflows u32 +pub fn checked_mul_32(a: u32, b: u32) -> Option { + let result: u64 = jet::multiply_32(a, b); + + let (high, low): (u32, u32) = ::into(result); + + match jet::is_zero_32(high) { + true => Some(low), + false => None, + } +} + +/// Returns the product of two u32 values, panics if the result overflows u32 +pub fn safe_mul_32(a: u32, b: u32) -> u32 { + unwrap(checked_mul_32(a, b)) +} + +/// Returns the quotient of two u32 values wrapped in Some, or None if the result overflows u32 +pub fn checked_div_32(a: u32, b: u32) -> Option { + match jet::is_zero_32(b) { + true => None, + false => Some(jet::divide_32(a, b)), + } +} + +/// Returns the quotient of two u32 values, panics if the result overflows u32 +pub fn safe_div_32(a: u32, b: u32) -> u32 { + unwrap(checked_div_32(a, b)) +} + +/// Check if an integer is greater than another integer +pub fn gt_32(a: u32, b: u32) -> bool { + jet::lt_32(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_32(a: u32, b: u32) -> bool { + jet::le_32(b, a) +} diff --git a/simf/lib/u64.simf b/simf/lib/u64.simf new file mode 100644 index 0000000..77fc4aa --- /dev/null +++ b/simf/lib/u64.simf @@ -0,0 +1,69 @@ +/// Returns the sum of two u64 values wrapped in Some, or None if the result overflows u64 +pub fn checked_add_64(a: u64, b: u64) -> Option { + let (carry, sum): (bool, u64) = jet::add_64(a, b); + + match carry { + true => None, + false => Some(sum), + } +} + +/// Returns the sum of two u64 values, panics if the result overflows u64 +pub fn safe_add_64(a: u64, b: u64) -> u64 { + unwrap(checked_add_64(a, b)) +} + +/// Returns the difference of two u64 values wrapped in Some, or None if the result overflows u64 +pub fn checked_sub_64(a: u64, b: u64) -> Option { + let (borrow, diff): (bool, u64) = jet::subtract_64(a, b); + + match borrow { + true => None, + false => Some(diff), + } +} + +/// Returns the difference of two u64 values, panics if the result overflows u64 +pub fn safe_sub_64(a: u64, b: u64) -> u64 { + unwrap(checked_sub_64(a, b)) +} + +/// Returns the product of two u64 values wrapped in Some, or None if the result overflows u64 +pub fn checked_mul_64(a: u64, b: u64) -> Option { + let result: u128 = jet::multiply_64(a, b); + + let (high, low): (u64, u64) = ::into(result); + + match jet::is_zero_64(high) { + true => Some(low), + false => None, + } +} + +/// Returns the product of two u64 values, panics if the result overflows u64 +pub fn safe_mul_64(a: u64, b: u64) -> u64 { + unwrap(checked_mul_64(a, b)) +} + +/// Returns the quotient of two u64 values wrapped in Some, or None if the result overflows u64 +pub fn checked_div_64(a: u64, b: u64) -> Option { + match jet::is_zero_64(b) { + true => None, + false => Some(jet::divide_64(a, b)), + } +} + +/// Returns the quotient of two u64 values, panics if the result overflows u64 +pub fn safe_div_64(a: u64, b: u64) -> u64 { + unwrap(checked_div_64(a, b)) +} + +/// Check if an integer is greater than another integer +pub fn gt_64(a: u64, b: u64) -> bool { + jet::lt_64(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_64(a: u64, b: u64) -> bool { + jet::le_64(b, a) +} diff --git a/simf/lib/u8.simf b/simf/lib/u8.simf new file mode 100644 index 0000000..9d50464 --- /dev/null +++ b/simf/lib/u8.simf @@ -0,0 +1,69 @@ +/// Returns the sum of two u8 values wrapped in Some, or None if the result overflows u8 +pub fn checked_add_8(a: u8, b: u8) -> Option { + let (carry, sum): (bool, u8) = jet::add_8(a, b); + + match carry { + true => None, + false => Some(sum), + } +} + +/// Returns the sum of two u8 values, panics if the result overflows u8 +pub fn safe_add_8(a: u8, b: u8) -> u8 { + unwrap(checked_add_8(a, b)) +} + +/// Returns the difference of two u8 values wrapped in Some, or None if the result overflows u8 +pub fn checked_sub_8(a: u8, b: u8) -> Option { + let (borrow, diff): (bool, u8) = jet::subtract_8(a, b); + + match borrow { + true => None, + false => Some(diff), + } +} + +/// Returns the difference of two u8 values, panics if the result overflows u8 +pub fn safe_sub_8(a: u8, b: u8) -> u8 { + unwrap(checked_sub_8(a, b)) +} + +/// Returns the product of two u8 values wrapped in Some, or None if the result overflows u8 +pub fn checked_mul_8(a: u8, b: u8) -> Option { + let result: u16 = jet::multiply_8(a, b); + + let (high, low): (u8, u8) = ::into(result); + + match jet::is_zero_8(high) { + true => Some(low), + false => None, + } +} + +/// Returns the product of two u8 values, panics if the result overflows u8 +pub fn safe_mul_8(a: u8, b: u8) -> u8 { + unwrap(checked_mul_8(a, b)) +} + +/// Returns the quotient of two u8 values wrapped in Some, or None if the result overflows u8 +pub fn checked_div_8(a: u8, b: u8) -> Option { + match jet::is_zero_8(b) { + true => None, + false => Some(jet::divide_8(a, b)), + } +} + +/// Returns the quotient of two u8 values, panics if the result overflows u8 +pub fn safe_div_8(a: u8, b: u8) -> u8 { + unwrap(checked_div_8(a, b)) +} + +/// Check if an integer is greater than another integer +pub fn gt_8(a: u8, b: u8) -> bool { + jet::lt_8(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_8(a: u8, b: u8) -> bool { + jet::le_8(b, a) +} diff --git a/simf/logical_ops_test.simf b/simf/logical_ops_test.simf new file mode 100644 index 0000000..45e48e4 --- /dev/null +++ b/simf/logical_ops_test.simf @@ -0,0 +1,16 @@ +use crate::lib::logical_operations::{not, or, and}; + +fn main() { + assert!(not(false)); + assert!(not(not(true))); + + assert!(or(true, false)); + assert!(or(false, true)); + assert!(or(true, true)); + assert!(not(or(false, false))); + + assert!(and(true, true)); + assert!(not(and(true, false))); + assert!(not(and(false, true))); + assert!(not(and(false, false))); +} diff --git a/simf/op_return_test.simf b/simf/op_return_test.simf new file mode 100644 index 0000000..d847d8e --- /dev/null +++ b/simf/op_return_test.simf @@ -0,0 +1,12 @@ +use crate::lib::op_return::{is_output_op_return, assert_output_is_op_return}; +use crate::helper::{if_test_this_function, assert_bool}; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let expected: bool = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_output_op_return(index), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_output_is_op_return(index); }, false => (), }; +} diff --git a/simf/u128_test.simf b/simf/u128_test.simf new file mode 100644 index 0000000..08c71f7 --- /dev/null +++ b/simf/u128_test.simf @@ -0,0 +1,53 @@ +use crate::lib::u128::{ checked_add_128, safe_add_128, checked_sub_128, safe_sub_128, checked_mul_128, safe_mul_128, checked_div_128, safe_div_128, eq_128, gt_128, ge_128 }; +use crate::lib::asserts::{assert_none_128, assert_eq_128}; +use crate::lib::logical_operations::not; +use crate::helper::if_test_this_function; + +/// Asserts a `checked_*` result equals the expected Option. +/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single +/// witness value carries both, removing the need for a separate overflow flag. +fn assert_eq_opt(result: Option, expected: Option) { + match expected { + None => assert_none_128(result), + Some(e: u128) => assert_eq_128(unwrap(result), e), + } +} + +/// Asserts a result equals the expected bool value. +/// `None` encodes `false`, and `Some(_)` encodes `true`. +fn assert_bool_by_opt(result: bool, expected: Option) { + match expected { + Some(_: u128) => assert!(result), + None => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + /// Safe functions + + // add + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_128(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(eq_128(safe_add_128(a, b), unwrap(expected))); }, false => (), }; + + // sub + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_128(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert!(eq_128(safe_sub_128(a, b), unwrap(expected))); }, false => (), }; + + // mul + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_128(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert!(eq_128(safe_mul_128(a, b), unwrap(expected))); }, false => (), }; + + // div + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_128(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert!(eq_128(safe_div_128(a, b), unwrap(expected))); }, false => (), }; + + // gt, ge + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_128(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_128(a, b), expected); }, false => (), }; +} diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_test_arithmetic.simf new file mode 100644 index 0000000..a95ee18 --- /dev/null +++ b/simf/u128_test_arithmetic.simf @@ -0,0 +1,112 @@ +use crate::lib::u128::{ eq_128, is_zero_128, lt_128, le_128, add_128, add_128_64, sub_128, mul_128, split_256_into_64, normalize_to_threshold, algorithm_d, div_mod_128_64, div_mod_128, div_128 }; +use crate::helper::{if_test_this_function, assert_bool}; + +/// Asserts a result equals expected u128 and bool values. +/// Used for functions that return carry or borrow bool value. +fn assert_eq_uint_bool(result: (bool, u128), expected: u128, expected_bool: bool) { + let (bool_res, uint_res): (bool, u128) = result; + + assert_bool(bool_res, expected_bool); + + assert!(eq_128(uint_res, expected)); +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + let second_expected: u128 = witness::SECOND_EXPECTED; // for cases where the result is a 256 bit value + let third_expected: u128 = witness::THIRD_EXPECTED; // for cases where the result is 256 bit + 128 bit values + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => (), }; + + match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(4, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + + match if_test_this_function(6, fn_idx) { + true => { + let result: u256 = mul_128(a, b); + + let (result_high, result_low): (u128, u128) = ::into(result); + + assert!(eq_128(result_high, unwrap(expected))); + assert!(eq_128(result_low, second_expected)); + }, + false => (), + }; + + match if_test_this_function(7, fn_idx) { + true => { + let input: u256 = <(u128, u128)>::into((a, b)); + let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(input); + + let (expected1, expected2): (u64, u64) = ::into(unwrap(expected)); + let (expected3, expected4): (u64, u64) = ::into(second_expected); + + assert!(jet::eq_64(res1, expected1)); + assert!(jet::eq_64(res2, expected2)); + assert!(jet::eq_64(res3, expected3)); + assert!(jet::eq_64(res4, expected4)); + }, + false => (), + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (result_a, result_b): (u256, u128) = normalize_to_threshold(a, b, expected_bool); + + let (result_a_high, result_a_low): (u128, u128) = ::into(result_a); + + assert!(eq_128(result_a_high, unwrap(expected))); + assert!(eq_128(result_a_low, second_expected)); + assert!(eq_128(result_b, third_expected)); + }, + false => (), + }; + + match if_test_this_function(9, fn_idx) { + true => { + let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); + + let (q, r): (u64, u128) = algorithm_d(a, b); + + assert!(jet::eq_64(q, expected_q)); + assert!(eq_128(r, second_expected)); + }, + false => (), + }; + + match if_test_this_function(10, fn_idx) { + true => { + let (_, b): (u64, u64) = ::into(b); + let (_, expected_r): (u64, u64) = ::into(second_expected); + + let (q, r): (u128, u64) = div_mod_128_64(a, b); + + assert!(eq_128(q, unwrap(expected))); + assert!(jet::eq_64(r, expected_r)); + }, + false => (), + }; + + match if_test_this_function(11, fn_idx) { + true => { + let (q, r): (u128, u128) = div_mod_128(a, b); + + assert!(eq_128(q, unwrap(expected))); + assert!(eq_128(r, second_expected)); + }, + false => (), + }; + + match if_test_this_function(12, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; +} diff --git a/simf/u128_test_bits.simf b/simf/u128_test_bits.simf new file mode 100644 index 0000000..725cad9 --- /dev/null +++ b/simf/u128_test_bits.simf @@ -0,0 +1,36 @@ +use crate::lib::u128::{ and_128, or_128, eq_128, left_shift_128, right_shift_128 }; +use crate::helper::{if_test_this_function, assert_bool}; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + + /// Bit logic + + match if_test_this_function(0, fn_idx) { true => { assert!(eq_128(and_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(eq_128(or_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(eq_128(a, b), expected_bool); }, false => (), }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, a): (u64, u64) = ::into(a); + let shift: u8 = jet::rightmost_64_8(a); + + assert!(eq_128(left_shift_128(shift, b), unwrap(expected))); + }, false => (), + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (_, a): (u64, u64) = ::into(a); + let shift: u8 = jet::rightmost_64_8(a); + + assert!(eq_128(right_shift_128(shift, b), unwrap(expected))); + }, false => (), + }; +} diff --git a/simf/u16_test.simf b/simf/u16_test.simf new file mode 100644 index 0000000..63d03e4 --- /dev/null +++ b/simf/u16_test.simf @@ -0,0 +1,51 @@ +use crate::lib::u16::{checked_add_16, safe_add_16, checked_sub_16, safe_sub_16, checked_mul_16, safe_mul_16, checked_div_16, safe_div_16, gt_16, ge_16}; +use crate::lib::asserts::{assert_none_16, assert_eq_16}; +use crate::lib::logical_operations::not; +use crate::helper::if_test_this_function; + +/// Asserts a `checked_*` result equals the expected Option. +/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single +/// witness value carries both, removing the need for a separate overflow flag. +fn assert_eq_opt(result: Option, expected: Option) { + match expected { + None => assert_none_16(result), + Some(e: u16) => assert_eq_16(unwrap(result), e), + } +} + +/// Asserts a result equals the expected bool value. +/// `None` encodes `false`, and `Some(_)` encodes `true`. +fn assert_bool_by_opt(result: bool, expected: Option) { + match expected { + Some(_: u16) => assert!(result), + None => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u16 = witness::FIRST_ARG; + let b: u16 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_16(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_16(safe_add_16(a, b), unwrap(expected))); }, false => (), }; + + // sub + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_16(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_16(safe_sub_16(a, b), unwrap(expected))); }, false => (), }; + + // mul + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_16(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_16(safe_mul_16(a, b), unwrap(expected))); }, false => (), }; + + // div + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_16(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_16(safe_div_16(a, b), unwrap(expected))); }, false => (), }; + + // gt, ge + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_16(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_16(a, b), expected); }, false => (), }; +} diff --git a/simf/u32_test.simf b/simf/u32_test.simf new file mode 100644 index 0000000..41e4f22 --- /dev/null +++ b/simf/u32_test.simf @@ -0,0 +1,51 @@ +use crate::lib::u32::{checked_add_32, safe_add_32, checked_sub_32, safe_sub_32, checked_mul_32, safe_mul_32, checked_div_32, safe_div_32, gt_32, ge_32}; +use crate::lib::asserts::{assert_none_32, assert_eq_32}; +use crate::lib::logical_operations::not; +use crate::helper::if_test_this_function; + +/// Asserts a `checked_*` result equals the expected Option. +/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single +/// witness value carries both, removing the need for a separate overflow flag. +fn assert_eq_opt(result: Option, expected: Option) { + match expected { + None => assert_none_32(result), + Some(e: u32) => assert_eq_32(unwrap(result), e), + } +} + +/// Asserts a result equals the expected bool value. +/// `None` encodes `false`, and `Some(_)` encodes `true`. +fn assert_bool_by_opt(result: bool, expected: Option) { + match expected { + Some(_: u32) => assert!(result), + None => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u32 = witness::FIRST_ARG; + let b: u32 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_32(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_32(safe_add_32(a, b), unwrap(expected))); }, false => (), }; + + // sub + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_32(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_32(safe_sub_32(a, b), unwrap(expected))); }, false => (), }; + + // mul + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_32(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_32(safe_mul_32(a, b), unwrap(expected))); }, false => (), }; + + // div + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_32(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_32(safe_div_32(a, b), unwrap(expected))); }, false => (), }; + + // gt, ge + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_32(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_32(a, b), expected); }, false => (), }; +} diff --git a/simf/u64_test.simf b/simf/u64_test.simf new file mode 100644 index 0000000..82c605b --- /dev/null +++ b/simf/u64_test.simf @@ -0,0 +1,51 @@ +use crate::lib::u64::{checked_add_64, safe_add_64, checked_sub_64, safe_sub_64, checked_mul_64, safe_mul_64, checked_div_64, safe_div_64, gt_64, ge_64}; +use crate::lib::asserts::{assert_none_64, assert_eq_64}; +use crate::lib::logical_operations::not; +use crate::helper::if_test_this_function; + +/// Asserts a `checked_*` result equals the expected Option. +/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single +/// witness value carries both, removing the need for a separate overflow flag. +fn assert_eq_opt(result: Option, expected: Option) { + match expected { + None => assert_none_64(result), + Some(e: u64) => assert_eq_64(unwrap(result), e), + } +} + +/// Asserts a result equals the expected bool value. +/// `None` encodes `false`, and `Some(_)` encodes `true`. +fn assert_bool_by_opt(result: bool, expected: Option) { + match expected { + Some(_: u64) => assert!(result), + None => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u64 = witness::FIRST_ARG; + let b: u64 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_64(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_64(safe_add_64(a, b), unwrap(expected))); }, false => (), }; + + // sub + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_64(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_64(safe_sub_64(a, b), unwrap(expected))); }, false => (), }; + + // mul + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_64(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_64(safe_mul_64(a, b), unwrap(expected))); }, false => (), }; + + // div + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_64(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_64(safe_div_64(a, b), unwrap(expected))); }, false => (), }; + + // gt, ge + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_64(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_64(a, b), expected); }, false => (), }; +} diff --git a/simf/u8_test.simf b/simf/u8_test.simf new file mode 100644 index 0000000..d77ee87 --- /dev/null +++ b/simf/u8_test.simf @@ -0,0 +1,51 @@ +use crate::lib::u8::{checked_add_8, safe_add_8, checked_sub_8, safe_sub_8, checked_mul_8, safe_mul_8, checked_div_8, safe_div_8, gt_8, ge_8}; +use crate::lib::asserts::{assert_none_8, assert_eq_8}; +use crate::lib::logical_operations::not; +use crate::helper::if_test_this_function; + +/// Asserts a `checked_*` result equals the expected Option. +/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single +/// witness value carries both, removing the need for a separate overflow flag. +fn assert_eq_opt(result: Option, expected: Option) { + match expected { + None => assert_none_8(result), + Some(e: u8) => assert_eq_8(unwrap(result), e), + } +} + +/// Asserts a result equals the expected bool value. +/// `None` encodes `false`, and `Some(_)` encodes `true`. +fn assert_bool_by_opt(result: bool, expected: Option) { + match expected { + Some(_: u8) => assert!(result), + None => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u8 = witness::FIRST_ARG; + let b: u8 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_8(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_8(safe_add_8(a, b), unwrap(expected))); }, false => (), }; + + // sub + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_8(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_8(safe_sub_8(a, b), unwrap(expected))); }, false => (), }; + + // mul + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_8(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_8(safe_mul_8(a, b), unwrap(expected))); }, false => (), }; + + // div + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_8(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_8(safe_div_8(a, b), unwrap(expected))); }, false => (), }; + + // gt, ge + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_8(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_8(a, b), expected); }, false => (), }; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..91946c7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod artifacts; diff --git a/tests/asserts_test.rs b/tests/asserts_test.rs new file mode 100644 index 0000000..ab8790c --- /dev/null +++ b/tests/asserts_test.rs @@ -0,0 +1,420 @@ +mod common; + +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::asserts_test::AssertsTestProgram; +use simplicityhl_std::artifacts::asserts_test::derived_asserts_test::{ + AssertsTestArguments, AssertsTestWitness, +}; + +// Dispatch indices — must match the `if_test_this_function(N, ..)` arms in +// simf/asserts_test.simf. +enum FunctionToTest { + AssertEq8, + AssertEq16, + AssertEq32, + AssertEq64, + AssertEq128, + AssertEq256, + AssertNone8, + AssertNone16, + AssertNone32, + AssertNone64, + AssertNone128, + AssertNone256, +} + +const DEFAULT_SOME_U8: Option = Some(0); +const DEFAULT_SOME_U16: Option = Some(0); +const DEFAULT_SOME_U32: Option = Some(0); +const DEFAULT_SOME_U64: Option = Some(0); +const DEFAULT_SOME_U128: Option = Some(0); +const DEFAULT_SOME_U256: Option<[u8; 32]> = Some([0; 32]); + +fn program() -> AssertsTestProgram { + AssertsTestProgram::new(AssertsTestArguments {}) +} + +/// Returns two values in `[min, max]` that are equal when `same`, distinct otherwise. +pub fn generate_uints_in_one_range(same: bool, min_val: u128, max_val: u128) -> (u128, u128) { + let some_u = rand::thread_rng().gen_range(min_val..=max_val); + + if same { + return (some_u, some_u); + } + + assert!( + min_val != max_val, + "cannot generate distinct values in a single-value range" + ); + + let mut other_u = rand::thread_rng().gen_range(min_val..=max_val); + + while other_u == some_u { + other_u = rand::thread_rng().gen_range(min_val..=max_val); + } + + (some_u, other_u) +} + +/// Builds the witness for one assert call. `same` controls the two `assert_eq` +/// args; `none` makes the single `assert_none` arg `None`. +fn build_witness(function: FunctionToTest, same: bool, none: bool) -> AssertsTestWitness { + let mut witness = AssertsTestWitness { + function_index: 0, + first_arg_u8: DEFAULT_SOME_U8, + second_arg_u8: DEFAULT_SOME_U8, + first_arg_u16: DEFAULT_SOME_U16, + second_arg_u16: DEFAULT_SOME_U16, + first_arg_u32: DEFAULT_SOME_U32, + second_arg_u32: DEFAULT_SOME_U32, + first_arg_u64: DEFAULT_SOME_U64, + second_arg_u64: DEFAULT_SOME_U64, + first_arg_u128: DEFAULT_SOME_U128, + second_arg_u128: DEFAULT_SOME_U128, + first_arg_u256: DEFAULT_SOME_U256, + second_arg_u256: DEFAULT_SOME_U256, + }; + + match function { + FunctionToTest::AssertEq8 => { + let (a, b) = generate_uints_in_one_range(same, 0, u8::MAX as u128); + (witness.first_arg_u8, witness.second_arg_u8) = (Some(a as u8), Some(b as u8)); + } + FunctionToTest::AssertEq16 => { + let (a, b) = generate_uints_in_one_range(same, 0, u16::MAX as u128); + (witness.first_arg_u16, witness.second_arg_u16) = (Some(a as u16), Some(b as u16)); + } + FunctionToTest::AssertEq32 => { + let (a, b) = generate_uints_in_one_range(same, 0, u32::MAX as u128); + (witness.first_arg_u32, witness.second_arg_u32) = (Some(a as u32), Some(b as u32)); + } + FunctionToTest::AssertEq64 => { + let (a, b) = generate_uints_in_one_range(same, 0, u64::MAX as u128); + (witness.first_arg_u64, witness.second_arg_u64) = (Some(a as u64), Some(b as u64)); + } + FunctionToTest::AssertEq128 => { + let (a, b) = generate_uints_in_one_range(same, 0, u128::MAX); + (witness.first_arg_u128, witness.second_arg_u128) = (Some(a), Some(b)); + } + FunctionToTest::AssertEq256 => { + let (a, b) = generate_uints_in_one_range(same, 0, u8::MAX as u128); + (witness.first_arg_u256, witness.second_arg_u256) = + (Some([a as u8; 32]), Some([b as u8; 32])); + } + FunctionToTest::AssertNone8 => { + if none { + witness.first_arg_u8 = None; + } + } + FunctionToTest::AssertNone16 => { + if none { + witness.first_arg_u16 = None; + } + } + FunctionToTest::AssertNone32 => { + if none { + witness.first_arg_u32 = None; + } + } + FunctionToTest::AssertNone64 => { + if none { + witness.first_arg_u64 = None; + } + } + FunctionToTest::AssertNone128 => { + if none { + witness.first_arg_u128 = None; + } + } + FunctionToTest::AssertNone256 => { + if none { + witness.first_arg_u256 = None; + } + } + } + + witness.function_index = function as u8; + witness +} + +fn run_assert( + context: &simplex::TestContext, + function: FunctionToTest, + same: bool, + none: bool, + expect: Expect, +) -> anyhow::Result<()> { + run( + context, + program(), + build_witness(function, same, none), + expect, + ) +} + +mod asserts_test { + use super::*; + + // ---------- assert_eq: happy = equal args, unhappy = distinct args ---------- + #[simplex::test] + fn assert_eq_8_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert(&context, FunctionToTest::AssertEq8, true, false, Expect::Ok) + } + + #[simplex::test] + fn assert_eq_8_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq8, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_eq_16_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq16, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_16_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq16, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_eq_32_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq32, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_32_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq32, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_eq_64_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq64, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_64_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq64, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_eq_128_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq128, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_128_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq128, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_eq_256_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq256, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_256_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEq256, + false, + false, + Expect::AssertFailed, + ) + } + + // ---------- assert_none: happy = None arg, unhappy = Some arg ---------- + #[simplex::test] + fn assert_none_8_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone8, + false, + true, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_none_8_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone8, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_none_16_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone16, + false, + true, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_none_16_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone16, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_none_32_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone32, + false, + true, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_none_32_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone32, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_none_64_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone64, + false, + true, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_none_64_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone64, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_none_128_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone128, + false, + true, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_none_128_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone128, + false, + false, + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn assert_none_256_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone256, + false, + true, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_none_256_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertNone256, + false, + false, + Expect::AssertFailed, + ) + } +} diff --git a/tests/common/core.rs b/tests/common/core.rs new file mode 100644 index 0000000..ada5e42 --- /dev/null +++ b/tests/common/core.rs @@ -0,0 +1,143 @@ +// Each `tests/*.rs` is a separate crate that mounts this module but uses only +// part of it, so per-crate dead-code analysis would warn about the rest. +#![allow(dead_code)] + +use simplex::program::{Program, WitnessTrait}; +use simplex::simplicityhl::elements::Script; +use simplex::transaction::{ + FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, +}; + +#[derive(Clone, Copy)] +pub enum Expect { + /// The spend succeeds. + Ok, + /// A failed `assert!` in the contract. + AssertFailed, + /// Execution reached a pruned branch (e.g. `unwrap(None)`, a `safe_*` overflow). + PrunedBranch, +} + +impl Expect { + /// The exact broadcast error message for a failing expectation (`None` for `Ok`). + fn error_message(self) -> Option<&'static str> { + match self { + Expect::Ok => None, + Expect::AssertFailed => Some("Failed to prune program: Jet failed during execution"), + Expect::PrunedBranch => { + Some("Failed to prune program: Execution reached a pruned branch") + } + } + } +} + +/// Send sats to the program's script so it has a UTXO to spend. +pub fn fund( + context: &simplex::TestContext, + program: &impl AsRef, +) -> anyhow::Result