From 9fe0e70473ff1f01eda93c012cc86c6d71c8aff3 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:47:18 +0530 Subject: [PATCH 01/23] sdk%chore: drop no longer needed `CLAUDE.md` symlink --- CLAUDE.md | 1 - 1 file changed, 1 deletion(-) delete mode 120000 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 120000 index 47dc3e3d..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -AGENTS.md \ No newline at end of file From 932ee5d5d9c9be63d0a53c38f35405f92988c459 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 03:00:50 +0530 Subject: [PATCH 02/23] sdk%doc: reap "Versioning and Platform Policy", thin down root README.md --- README.md | 91 +++++++++++++------------------------------------- docs/README.md | 48 +------------------------- 2 files changed, 24 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index a748cfa5..18e23094 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -![GitHub License](https://img.shields.io/github/license/dashpay/base-sdk) +[![GitHub License](https://img.shields.io/github/license/dashpay/base-sdk?style=flat&logo=readme&logoColor=white)](./LICENSE.md) ![Minimum Supported Rust Version](https://img.shields.io/badge/v1.85.0-msrv?style=flat&logo=rust&label=MSRV&color=orange) -[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/dashpay/base-sdk) > [!WARNING] > @@ -16,79 +15,35 @@ `base-sdk` is a parsing and stateless verification SDK for Dash's layer 1 blockchain. -## Crates - -| Crate | Description | CI (`develop`) | Coverage | -|-------|-------------|--------------|----------| -| [dash-num](./pkgs/num) | Hash blobs, 256-bit arithmetic, compact target encoding | [![dash-num](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_num.yml?style=flat&logo=github&logoColor=white&label=num)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_num.yml) | [![dash-num](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-num&style=flat&logo=codecov&logoColor=white&label=num)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fnum) | -| [dash-p2p-core](./pkgs/p2p_core) | P2P message types and wire format | [![dash-p2p-core](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_p2p_core.yml?style=flat&logo=github&logoColor=white&label=p2p-core)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_p2p_core.yml) | [![dash-p2p-core](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-p2p-core&style=flat&logo=codecov&logoColor=white&label=p2p-core)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fp2p_core) | -| [dash-params](./pkgs/params) | Chain parameters for `mainnet`, `testnet3`, and `regtest` | [![dash-params](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_params.yml?style=flat&logo=github&logoColor=white&label=params)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_params.yml) | [![dash-params](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-params&style=flat&logo=codecov&logoColor=white&label=params)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fparams) | -| [dash-pkc](./pkgs/pkc) | BLS (legacy + IETF) and secp256k1 operations | [![dash-pkc](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_pkc.yml?style=flat&logo=github&logoColor=white&label=pkc)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_pkc.yml) | [![dash-pkc](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-pkc&style=flat&logo=codecov&logoColor=white&label=pkc)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fpkc) | -| [dash-pow](./pkgs/pow) | Proof of work scheme | [![dash-pow](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_pow.yml?style=flat&logo=github&logoColor=white&label=pow)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_pow.yml) | [![dash-pow](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-pow&style=flat&logo=codecov&logoColor=white&label=pow)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fpow) | -| [dash-primitives](./pkgs/primitives) | Blocks, transactions, payloads, governance objects | [![dash-primitives](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_primitives.yml?style=flat&logo=github&logoColor=white&label=primitives)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_primitives.yml) | [![dash-primitives](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-primitives&style=flat&logo=codecov&logoColor=white&label=primitives)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fprimitives) | -| [dash-script](./pkgs/script) | Script opcodes, classification, and address derivation | [![dash-script](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_script.yml?style=flat&logo=github&logoColor=white&label=script)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_script.yml) | [![dash-script](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-script&style=flat&logo=codecov&logoColor=white&label=script)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fscript) | -| [dash-types](./pkgs/types) | Shared byte and integer newtypes, serde helpers | [![dash-types](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_types.yml?style=flat&logo=github&logoColor=white&label=types)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_types.yml) | [![dash-types](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-types&style=flat&logo=codecov&logoColor=white&label=types)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Ftypes) | - -## Dependencies - -> [!NOTE] -> Solid lines are build dependencies. Dotted lines are test dependencies. - - + -```mermaid -%%{init: { "flowchart": { "curve": "basis" } } }%% -graph LR - subgraph " " - types[dash-types] - num[dash-num] - pow[dash-pow] - end - subgraph " " - script[dash-script] - pkc[dash-pkc] - end - subgraph " " - primitives[dash-primitives] - params[dash-params] - p2p_core[dash-p2p-core] - end +| Crate | Description | CI | Coverage | Version | +| ----- | ----------- | -------------- | -------- | ------- | +| [dash-num](./pkgs/num) | Hash blobs, 256-bit arithmetic, compact target encoding | [![dash-num](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_num.yml?style=flat&logo=github&logoColor=white&label=num)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_num.yml) | [![dash-num](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-num&style=flat&logo=codecov&logoColor=white&label=num)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fnum) | [![dash-num](https://img.shields.io/crates/v/dash-num?style=flat&logo=hackthebox&logoColor=white&label=num&color=teal)](https://crates.io/crates/dash-num) | +| [dash-p2p-core](./pkgs/p2p_core) | P2P message types and wire format | [![dash-p2p-core](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_p2p_core.yml?style=flat&logo=github&logoColor=white&label=p2p-core)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_p2p_core.yml) | [![dash-p2p-core](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-p2p-core&style=flat&logo=codecov&logoColor=white&label=p2p-core)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fp2p_core) | _Unreleased._ | +| [dash-params](./pkgs/params) | Chain parameters for `mainnet`, `testnet3`, and `regtest` | [![dash-params](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_params.yml?style=flat&logo=github&logoColor=white&label=params)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_params.yml) | [![dash-params](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-params&style=flat&logo=codecov&logoColor=white&label=params)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fparams) | _Unreleased._ | +| [dash-pkc](./pkgs/pkc) | BLS (legacy + IETF) and secp256k1 operations | [![dash-pkc](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_pkc.yml?style=flat&logo=github&logoColor=white&label=pkc)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_pkc.yml) | [![dash-pkc](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-pkc&style=flat&logo=codecov&logoColor=white&label=pkc)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fpkc) | [![dash-pkc](https://img.shields.io/crates/v/dash-pkc?style=flat&logo=hackthebox&logoColor=white&label=pkc&color=teal)](https://crates.io/crates/dash-pkc) | +| [dash-pow](./pkgs/pow) | Proof of work scheme | [![dash-pow](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_pow.yml?style=flat&logo=github&logoColor=white&label=pow)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_pow.yml) | [![dash-pow](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-pow&style=flat&logo=codecov&logoColor=white&label=pow)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fpow) | _Unreleased._ | +| [dash-primitives](./pkgs/primitives) | Blocks, transactions, payloads, governance objects | [![dash-primitives](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_primitives.yml?style=flat&logo=github&logoColor=white&label=primitives)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_primitives.yml) | [![dash-primitives](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-primitives&style=flat&logo=codecov&logoColor=white&label=primitives)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fprimitives) | _Unreleased._ | +| [dash-script](./pkgs/script) | Script opcodes, classification, and address derivation | [![dash-script](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_script.yml?style=flat&logo=github&logoColor=white&label=script)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_script.yml) | [![dash-script](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-script&style=flat&logo=codecov&logoColor=white&label=script)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fscript) | _Unreleased._ | +| [dash-types](./pkgs/types) | Shared byte and integer newtypes, serde helpers | [![dash-types](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_types.yml?style=flat&logo=github&logoColor=white&label=types)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_types.yml) | [![dash-types](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-types&style=flat&logo=codecov&logoColor=white&label=types)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Ftypes) | [![dash-types](https://img.shields.io/crates/v/dash-types?style=flat&logo=hackthebox&logoColor=white&label=types&color=teal)](https://crates.io/crates/dash-types) | - types --> num - types --> script - num --> pkc - num --> primitives - script --> primitives - pkc --> p2p_core - pow --> primitives - primitives --> params - params --> p2p_core -``` + - +## Documentation -## Features - -All crates support these standard features: - -| Feature | Description | Crates | -|---------|-------------|--------| -| _(baseline)_ | `no_std` + `alloc`, always available | _All_ | -| `std` | Enable standard library support | _All_ | -| `serde` | Enable serde serialization (where applicable) | [num](./pkgs/num), [p2p-core](./pkgs/p2p_core), [pkc](./pkgs/pkc), [primitives](./pkgs/primitives), [script](./pkgs/script), [types](./pkgs/types) | -| `full` | Enables all non-conflicting features | _All_ | +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/dashpay/base-sdk) -Specific crates define additional features: + +Documentation is hosted at https://dashpay.github.io/base-sdk. To render it locally or learn its syntax, see +[getting started](https://dashpay.github.io/base-sdk/dev/about_docs). Release notes for published crates are listed +below. -| Feature | Description | Crates | -|---------|-------------|--------| -| `aes_hw` | Enable hardware-accelerated AES on supported platforms | [pow](./pkgs/pow) | -| `bls` | Enable standard and legacy BLS support | [pkc](./pkgs/pkc) | -| `codec` | Enable Bitcoin-compatible wire support | [types](./pkgs/types), [pkc](./pkgs/pkc) | -| `ecdsa` | Enable secp256k1 support | [pkc](./pkgs/pkc) | -| `simd` | Use SIMD backends (requires nightly) | [pow](./pkgs/pow) | +* [`dash-num`](./pkgs/num/CHANGELOG.md) +* [`dash-pkc`](./pkgs/pkc/CHANGELOG.md) +* [`dash-types`](./pkgs/types/CHANGELOG.md) -## License +## Licensing Copyright © 2026-present, The Dash Core developers. See the accompanying file [LICENSE](./LICENSE) or diff --git a/docs/README.md b/docs/README.md index 765886ec..efc05576 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,53 +7,7 @@ ecosystem to provide a framework that builds upon existing, familiar APIs and co To achieve this, state-independent portions of the consensus engine have been split into composable packages that can be imported as needed. -Some packages depend on other packages to expose their full functionality. Packages are primarily placed in three -buckets (see below). When upgrading downstream packages, it is recommended to version-match with the lowermost bucket -utilised by your packages. - -* Foundation packages. These packages provide unopinionated Bitcoin-compatible data manipulation. -* Base packages. These packages implement specific algorithms but without chain-distinguishing consensus logic. -* Protocol packages. These packages define the Dash protocol as deployed, blocks, transactions, chain parameters. - - - -*Note: Solid lines are build dependencies, dotted lines are test dependencies.* - -## Versioning and Platform Policy - -> [!WARNING] -> -> As the Base SDK is in early development, the public contract and API shape should be considered **unstable**. Releases -> are made sporadically on an *ad hoc* basis with `YYYY-MM-DD` [tags](https://github.com/dashpay/base-sdk/tags) and -> packages have a fixed version of `0.0.0`. Stable releases will be made in accordance with -> [Semantic Versioning](https://semver.org/). - -* The minimum supported Rust version has an upper cap at the version of Rust available with Debian - [`stable`](https://wiki.debian.org/DebianStable) (last updated, [`trixie`](https://wiki.debian.org/DebianTrixie)). - The current MSRV is 1.85.0 ([source](https://github.com/dashpay/base-sdk/blob/2026-04-22/.github/workflows/build_stable.yml#L34-L35)). - -* Some features rely on Rust `nightly` (notably affected is `core::simd`, see - [rust-lang/portable-simd#364](https://github.com/rust-lang/portable-simd/issues/364)), building packages with the - `full` feature will pull in these dependencies. Consuming packages built on Rust `stable` are recommended to manually - select the features desired from the package alongside `std`. - - * Packages are validated against the version of nightly pinned in - [`rust-toolchain.toml`](https://github.com/dashpay/base-sdk/blob/2026-04-22/rust-toolchain.toml), SDK functionality - is tested only against the pinned version. Regressions on succeeding versions are evaluated on a case-by-case basis. - -* `no_std` + `alloc` is the baseline target, the public contract is limited by what is feasible by this baseline target. - `std` is primarily a passthrough to `std` features provided by dependent crates, no additional functionality is - offered by the SDK *itself* through `std` enablement. - -Crates by default offer the following features, any additional features offered by crates are defined in their -respective sections. - -| Feature | Capabilities enabled | Defined by every package | -| ----------- | ------------------------------------------- | --------------------------------- | -| _(baseline)_ | `no_std` + `alloc`, always available. | Yes | -| `std` | Standard library support. | Yes | -| `full` | All non-conflicting features. | Yes | -| `serde` | Serialization using `serde`. | **No** (dependent on feature set) | + ## Is the SDK a full node? From 5a07dd067ec8943c62249276b1ec63f705e71c15 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:05:24 +0530 Subject: [PATCH 03/23] sdk%chore: pass `--locked` to `rust-analyzer` `cargo` invocations --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index b5238601..410e6ab0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -40,6 +40,7 @@ "**/Cargo.lock": true, "**/target": true, }, + "rust-analyzer.cargo.extraArgs": ["--locked"], "rust-analyzer.cargo.features": "all", "rust-analyzer.check.command": "clippy", "rust-analyzer.checkOnSave": true, From 49507bdee2c83c4786fb52820a68292e8861987d Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:05:36 +0530 Subject: [PATCH 04/23] sdk%fix(docs): build without the zensical page cache --- docs/build_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build_docs.py b/docs/build_docs.py index 70b97f85..6773817f 100755 --- a/docs/build_docs.py +++ b/docs/build_docs.py @@ -138,7 +138,7 @@ def _build_site(root: Path, zensical: str) -> None: reach = [str(DOCS_DIR), *([inherited] if inherited else [])] env = {**os.environ, "PYTHONPATH": os.pathsep.join(reach)} subprocess.run( # noqa: S603 - [zensical, "build", "--strict", "-f", str(CONFIG_FILE)], + [zensical, "build", "--clean", "--strict", "-f", str(CONFIG_FILE)], check=True, cwd=str(root), env=env, From 658ce47b45e031f40093a398e26ee742ccee604b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:47:21 +0530 Subject: [PATCH 05/23] pow%build: modify `aes_hw` to imply `simd` --- .github/workflows/build_msrv.yml | 4 ++-- pkgs/pow/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_msrv.yml b/.github/workflows/build_msrv.yml index 41f5ccab..9ffd62b7 100644 --- a/.github/workflows/build_msrv.yml +++ b/.github/workflows/build_msrv.yml @@ -143,11 +143,11 @@ jobs: export RUSTC="$TOOLCHAIN_MSRV/bin/rustc" export RUSTDOC="$TOOLCHAIN_MSRV/bin/rustdoc" "$TOOLCHAIN_MSRV/bin/cargo" build --workspace --exclude dash-pow --features full - "$TOOLCHAIN_MSRV/bin/cargo" build -p dash-pow --features std,aes_hw + "$TOOLCHAIN_MSRV/bin/cargo" build -p dash-pow --features std - name: Test workspace run: | export RUSTC="$TOOLCHAIN_MSRV/bin/rustc" export RUSTDOC="$TOOLCHAIN_MSRV/bin/rustdoc" "$TOOLCHAIN_MSRV/bin/cargo" test --workspace --exclude dash-pow --features full - "$TOOLCHAIN_MSRV/bin/cargo" test -p dash-pow --features std,aes_hw + "$TOOLCHAIN_MSRV/bin/cargo" test -p dash-pow --features std diff --git a/pkgs/pow/Cargo.toml b/pkgs/pow/Cargo.toml index e0ec224f..cfdf3f6b 100644 --- a/pkgs/pow/Cargo.toml +++ b/pkgs/pow/Cargo.toml @@ -13,7 +13,7 @@ rust-version.workspace = true default = [] std = [] full = ["std", "aes_hw", "simd"] -aes_hw = [] +aes_hw = ["simd"] simd = [] [dev-dependencies] From b98049437a5c1f0825756b2f88e711e9d72c3aa6 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:28:20 +0530 Subject: [PATCH 06/23] sdk%build!: bump MSRV to 1.89, refresh lockfiles --- .github/workflows/build_msrv.yml | 2 +- Cargo.lock | 378 +++++++++++++++--------------- Cargo.toml | 2 +- README.md | 4 +- contrib/nix/shell/common.nix | 2 +- docs/samples/Cargo.lock | 150 +++++++----- pkgs/pkc/src/aes_cbc.rs | 4 +- pkgs/primitives/src/types/util.rs | 2 +- 8 files changed, 283 insertions(+), 261 deletions(-) diff --git a/.github/workflows/build_msrv.yml b/.github/workflows/build_msrv.yml index 9ffd62b7..671c91a0 100644 --- a/.github/workflows/build_msrv.yml +++ b/.github/workflows/build_msrv.yml @@ -1,4 +1,4 @@ -name: MSRV (1.85) +name: MSRV (1.89) on: push: diff --git a/Cargo.lock b/Cargo.lock index 52f7812d..a316f0dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aes" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58" +checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" dependencies = [ "cipher", "cpubits", @@ -146,9 +146,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.13.1" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "block-buffer" @@ -202,9 +202,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" +checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" dependencies = [ "find-msvc-tools", "jobserver", @@ -214,9 +214,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "chrono" @@ -268,9 +268,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.5" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" dependencies = [ "clap_builder", "clap_derive", @@ -278,9 +278,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.5" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" dependencies = [ "anstyle", "clap_lex", @@ -289,21 +289,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.4" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.8", ] [[package]] name = "clap_lex" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" [[package]] name = "condtype" @@ -345,9 +345,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -355,18 +355,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.20" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crunchy" @@ -617,7 +617,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.8", ] [[package]] @@ -671,9 +671,9 @@ dependencies = [ [[package]] name = "either" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" [[package]] name = "encode_unicode" @@ -715,9 +715,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" [[package]] name = "fixedbitset" @@ -736,26 +736,26 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", ] [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-timer" @@ -765,9 +765,9 @@ checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-macro", @@ -843,9 +843,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" [[package]] name = "hex-conservative" @@ -900,21 +900,23 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", + "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -923,99 +925,62 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "idna" version = "1.1.0" @@ -1029,9 +994,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1039,9 +1004,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown", @@ -1094,9 +1059,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" dependencies = [ "cfg-if", "futures-util", @@ -1116,15 +1081,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.189" +version = "0.2.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" [[package]] name = "libgit2-sys" -version = "0.18.7+1.9.6" +version = "0.18.8+1.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c7391e4b9f4ffab1a624223cc1d7385ff9a678f490768add717de7ea2f4d89" +checksum = "7f7c568b25d7489bc3fb2988ed69ab111d2944d2f5fec3d5c987fe545ea97b50" dependencies = [ "cc", "libc", @@ -1158,15 +1123,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" @@ -1216,19 +1181,21 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.8" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" +checksum = "b568374ba38b33a6c627141f891faf16902b08d2db26b8ede1bcb0a15b1919fa" dependencies = [ "memchr", + "psm", + "stacker", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.8" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" +checksum = "b66e184b924cebaaff20ab2256ca52f12332d528a39aa76553b5d96f92aacf7f" dependencies = [ "pest", "pest_generator", @@ -1236,22 +1203,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.8" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" +checksum = "a87478d267e4de54a626af9754f2f0f58e927aac6ed0575fe89bc05ad6851694" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", ] [[package]] name = "pest_meta" -version = "2.8.8" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" +checksum = "4f986f248b4241ac359b831f6139aaa34e03b08a37b6caf7e201a33f95c869e1" dependencies = [ "pest", ] @@ -1274,15 +1241,24 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "portable-atomic" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] [[package]] name = "proc-macro-crate" @@ -1290,7 +1266,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.13+spec-1.1.0", + "toml_edit 0.25.17+spec-1.1.0", ] [[package]] @@ -1302,6 +1278,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.47" @@ -1425,9 +1410,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" dependencies = [ "bitflags", "errno", @@ -1497,14 +1482,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.8", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ "itoa", "memchr", @@ -1556,9 +1541,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" [[package]] name = "stable_deref_trait" @@ -1566,6 +1551,19 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stacker" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1585,9 +1583,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "01016da373cd8f7ef12624f796309f5c31ba8d646dd08856c02cd741d823c622" dependencies = [ "proc-macro2", "quote", @@ -1596,13 +1594,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", ] [[package]] @@ -1639,9 +1637,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -1670,9 +1668,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" dependencies = [ "serde_core", ] @@ -1693,21 +1691,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.13+spec-1.1.0" +version = "0.25.17+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" dependencies = [ "indexmap", - "toml_datetime 1.1.1+spec-1.1.0", + "toml_datetime 1.1.2+spec-1.1.0", "toml_parser", "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.3+spec-1.1.0" +version = "1.1.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" dependencies = [ "winnow 1.0.4", ] @@ -1732,9 +1730,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" [[package]] name = "unit-prefix" @@ -1754,12 +1752,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1774,9 +1766,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", @@ -1787,9 +1779,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1797,22 +1789,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] @@ -2042,31 +2034,24 @@ dependencies = [ "memchr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "xxhash-rust" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6" +checksum = "550a2b930b62486a393c52d5c3b84bff264b28aa437ed64694d31e93b1757af7" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -2074,30 +2059,30 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.55" +version = "0.8.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" +checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.55" +version = "0.8.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" +checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a" dependencies = [ "proc-macro2", "quote", @@ -2115,21 +2100,21 @@ dependencies = [ [[package]] name = "zerofrom-derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", "synstructure", ] [[package]] name = "zeroize" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" dependencies = [ "zeroize_derive", ] @@ -2145,11 +2130,22 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -2158,13 +2154,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3c6377872d72510393f688a555d7097b0f741995c7a00f0407f786dd486b2d" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.8", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 80e44813..1bb4eed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ edition = "2021" homepage = "https://dashpay.github.io/base-sdk" license = "MIT" repository = "https://github.com/dashpay/base-sdk" -rust-version = "1.85.0" +rust-version = "1.89.0" [profile.release] lto = true diff --git a/README.md b/README.md index 18e23094..2971d3c4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![GitHub License](https://img.shields.io/github/license/dashpay/base-sdk?style=flat&logo=readme&logoColor=white)](./LICENSE.md) -![Minimum Supported Rust Version](https://img.shields.io/badge/v1.85.0-msrv?style=flat&logo=rust&label=MSRV&color=orange) +[![GitHub License](https://img.shields.io/github/license/dashpay/base-sdk?style=flat&logo=readme&logoColor=white)](./LICENSE) +![Minimum Supported Rust Version](https://img.shields.io/badge/v1.89.0-msrv?style=flat&logo=rust&label=MSRV&color=orange) > [!WARNING] > diff --git a/contrib/nix/shell/common.nix b/contrib/nix/shell/common.nix index ff86f6c3..747130af 100644 --- a/contrib/nix/shell/common.nix +++ b/contrib/nix/shell/common.nix @@ -103,7 +103,7 @@ in toolchains = { nightly = nightlyWith crossTargets; # Must match `workspace.package.rust-version` in root Cargo.toml. - msrv = bare (pkgs.rust-bin.stable."1.85.0".minimal.override { extensions = rsComponents; }); + msrv = bare (pkgs.rust-bin.stable."1.89.0".minimal.override { extensions = rsComponents; }); }; }; }; diff --git a/docs/samples/Cargo.lock b/docs/samples/Cargo.lock index 8f007eee..6d505283 100644 --- a/docs/samples/Cargo.lock +++ b/docs/samples/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ambassador" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3d4da7fddbc6567cbd7b5c30364f91313701b1857ab5b925eb3acf06162eed" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "arrayvec" version = "0.7.8" @@ -10,30 +22,21 @@ checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "base58ck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6a87a8367e7a4248c8dfd783c37ef492bca1307cd4b21b4cfad9cfd15bf060" -dependencies = [ - "bitcoin-internals 0.5.0", - "bitcoin_hashes 0.20.0", -] - -[[package]] -name = "bitcoin-consensus-encoding" -version = "0.1.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d7ca3dc8ff835693ad73bf1596240c06f974a31eeb3f611aaedf855f1f2725" +checksum = "df540278d807651653f9b6f21e18903d790d7769eadfbc0dc4df0a141260318f" dependencies = [ - "bitcoin-internals 0.5.0", + "bitcoin-internals 0.6.0", + "bitcoin_hashes", ] [[package]] name = "bitcoin-consensus-encoding" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +checksum = "9daa31138eb443d5751b207f3f64154e2bb09cd59960562ccc7a7112be38147f" dependencies = [ - "bitcoin-internals 0.6.0", + "bitcoin-internals 0.7.0", "hex-conservative 1.3.0", "serde", ] @@ -56,16 +59,22 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8bea3a9f0cfece4564e37cb49a38cc245ca5184719e50d7d0dda3268722c4e2" + [[package]] name = "bitcoin-primitives" version = "0.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e12cb1814d037edbd667554c2c0e6b067db72c83ee935b645a6b5829aca908" dependencies = [ - "bitcoin-consensus-encoding 1.2.0", + "bitcoin-consensus-encoding", "bitcoin-internals 0.6.0", "bitcoin-units", - "bitcoin_hashes 1.2.0", + "bitcoin_hashes", "hex-conservative 1.3.0", "serde", ] @@ -76,29 +85,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ae6dcb008158c2bdbe698470280439844be5f640310e4f5d6ef0a0209e066c2" dependencies = [ - "bitcoin-consensus-encoding 1.2.0", + "bitcoin-consensus-encoding", "bitcoin-internals 0.5.0", "serde", ] -[[package]] -name = "bitcoin_hashes" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a45c2b41c457a9a9e4670422fcbdf109afb3b22bc920b4045e8bdfd788a3d" -dependencies = [ - "bitcoin-consensus-encoding 0.1.0", - "bitcoin-internals 0.5.0", - "hex-conservative 0.3.2", -] - [[package]] name = "bitcoin_hashes" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" dependencies = [ - "bitcoin-consensus-encoding 1.2.0", + "bitcoin-consensus-encoding", "bitcoin-internals 0.6.0", "hex-conservative 1.3.0", "serde", @@ -112,16 +110,19 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "dash-num" version = "0.1.0-beta" dependencies = [ - "bitcoin-consensus-encoding 1.2.0", + "ambassador", + "bitcoin-consensus-encoding", + "bitcoin-internals 0.7.0", "dash-types", + "delegate", "hex-conservative 1.3.0", "serde", ] @@ -131,7 +132,7 @@ name = "dash-pkc" version = "0.1.0-beta" dependencies = [ "base58ck", - "bitcoin_hashes 1.2.0", + "bitcoin_hashes", "cfg-if", "dash-num", "dash-types", @@ -149,11 +150,10 @@ version = "0.0.0" name = "dash-primitives" version = "0.0.0" dependencies = [ - "bitcoin-consensus-encoding 1.2.0", - "bitcoin-internals 0.6.0", + "bitcoin-consensus-encoding", "bitcoin-primitives", "bitcoin-units", - "bitcoin_hashes 1.2.0", + "bitcoin_hashes", "cfg-if", "dash-num", "dash-pkc", @@ -180,7 +180,7 @@ dependencies = [ name = "dash-sample-solver" version = "0.0.0" dependencies = [ - "bitcoin-consensus-encoding 1.2.0", + "bitcoin-consensus-encoding", "bitcoin-primitives", "bitcoin-units", "dash-num", @@ -199,7 +199,7 @@ name = "dash-script" version = "0.0.0" dependencies = [ "base58ck", - "bitcoin-consensus-encoding 1.2.0", + "bitcoin-consensus-encoding", "dash-num", "dash-pkc", "dash-types", @@ -211,9 +211,9 @@ name = "dash-types" version = "0.1.0-beta" dependencies = [ "base58ck", - "bitcoin-consensus-encoding 1.2.0", + "bitcoin-consensus-encoding", "bitcoin-primitives", - "bitcoin_hashes 1.2.0", + "bitcoin_hashes", "cfg-if", "dash-types-marker", "hex-conservative 1.3.0", @@ -231,6 +231,23 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + [[package]] name = "hex-conservative" version = "0.3.2" @@ -249,6 +266,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -324,14 +350,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.8", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ "itoa", "memchr", @@ -359,9 +385,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.5" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +checksum = "01016da373cd8f7ef12624f796309f5c31ba8d646dd08856c02cd741d823c622" dependencies = [ "proc-macro2", "quote", @@ -370,15 +396,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" [[package]] name = "wasm-bindgen" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", @@ -389,9 +415,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -399,37 +425,37 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.8", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] [[package]] name = "xxhash-rust" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6" +checksum = "550a2b930b62486a393c52d5c3b84bff264b28aa437ed64694d31e93b1757af7" [[package]] name = "zeroize" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" dependencies = [ "zeroize_derive", ] diff --git a/pkgs/pkc/src/aes_cbc.rs b/pkgs/pkc/src/aes_cbc.rs index 7a2db5c0..7db61b50 100644 --- a/pkgs/pkc/src/aes_cbc.rs +++ b/pkgs/pkc/src/aes_cbc.rs @@ -21,7 +21,7 @@ pub(crate) const AES_KEY_LEN: usize = 32; /// Encrypts under unpadded AES-256-CBC, or `None` when the input is not a /// whole number of blocks. pub(crate) fn encrypt(key: &[u8; AES_KEY_LEN], iv: &[u8; AES_BLOCK_LEN], plaintext: &[u8]) -> Option> { - if plaintext.len() % AES_BLOCK_LEN != 0 { + if !plaintext.len().is_multiple_of(AES_BLOCK_LEN) { return None; } @@ -49,7 +49,7 @@ pub(crate) fn decrypt( iv: &[u8; AES_BLOCK_LEN], ciphertext: &[u8], ) -> Option>> { - if ciphertext.len() % AES_BLOCK_LEN != 0 { + if !ciphertext.len().is_multiple_of(AES_BLOCK_LEN) { return None; } diff --git a/pkgs/primitives/src/types/util.rs b/pkgs/primitives/src/types/util.rs index 91b49f5e..6b583138 100644 --- a/pkgs/primitives/src/types/util.rs +++ b/pkgs/primitives/src/types/util.rs @@ -101,7 +101,7 @@ fn hex_nibble(b: u8) -> Result { /// input. pub(super) fn base16_dec(s: &str) -> Result, NetAddrError> { let bytes = s.as_bytes(); - if bytes.len() % 2 != 0 { + if !bytes.len().is_multiple_of(2) { return Err(NetAddrError::BadLen { expected: bytes.len() + 1, actual: bytes.len(), From 5ab8fb1ceef6d0818a55bb9255cba382a7db485c Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:23:39 +0530 Subject: [PATCH 07/23] sdk%feat: add SDK-wide `portable` feature, forward `blst/portable` --- AGENTS.md | 6 ++---- Justfile | 1 + pkgs/p2p_core/Cargo.toml | 6 ++++++ pkgs/params/Cargo.toml | 1 + pkgs/pkc/Cargo.toml | 1 + pkgs/pkc/README.md | 1 + pkgs/pow/Cargo.toml | 1 + pkgs/primitives/Cargo.toml | 1 + pkgs/script/Cargo.toml | 1 + 9 files changed, 15 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bbddced9..e83df997 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,13 +48,11 @@ Every crate must follow this feature layout: [features] default = [] std = [...] -full = ["std"] # add "serde" here only if the crate implements Serialize/Deserialize +full = ["std"] # add "serde" here if the crate implements Serialize/Deserialize, do not include "portable" +portable = [...] # only if the crate or a dependency defines platform acceleration serde = ["dep:serde"] # only if the crate has serde impls ``` -No other features should exist unless there is a pressing justification. The `serde` feature is only added to crates -that actually derive or implement `Serialize`/`Deserialize` on public types. - ### Module conventions Any shim code that mediates between `alloc` and `std` (e.g. `pub(crate) use alloc::vec::Vec;`) belongs in diff --git a/Justfile b/Justfile index d788cd6c..0c9bbe85 100644 --- a/Justfile +++ b/Justfile @@ -38,6 +38,7 @@ preview: test: cargo clippy --all-targets --no-default-features cargo clippy --all-targets --features full + cargo clippy --all-targets --features full,portable {{ nextest }} --fail-fast --workspace --features full cargo test --doc --workspace --features full cargo doc --workspace --no-deps --features full diff --git a/pkgs/p2p_core/Cargo.toml b/pkgs/p2p_core/Cargo.toml index e6a9cb67..62153ed6 100644 --- a/pkgs/p2p_core/Cargo.toml +++ b/pkgs/p2p_core/Cargo.toml @@ -26,6 +26,12 @@ serde = [ "dash-types/serde", ] full = ["std", "serde"] +portable = [ + "dash-params/portable", + "dash-pkc/portable", + "dash-primitives/portable", + "dash-script/portable", +] [dependencies] bitcoin-consensus-encoding = { workspace = true, features = ["alloc"] } diff --git a/pkgs/params/Cargo.toml b/pkgs/params/Cargo.toml index 3dc8ba48..2f219673 100644 --- a/pkgs/params/Cargo.toml +++ b/pkgs/params/Cargo.toml @@ -13,6 +13,7 @@ rust-version.workspace = true default = [] std = ["bitcoin-primitives/std", "dash-primitives/std", "dash-script/std"] full = ["std"] +portable = ["dash-primitives/portable", "dash-script/portable"] [dependencies] bitcoin-primitives = { workspace = true, features = ["alloc"] } diff --git a/pkgs/pkc/Cargo.toml b/pkgs/pkc/Cargo.toml index 8f1739e7..2c36c103 100644 --- a/pkgs/pkc/Cargo.toml +++ b/pkgs/pkc/Cargo.toml @@ -103,6 +103,7 @@ full = [ "serde", "std", ] +portable = ["blst?/portable"] serde = [ "codec", "dash-num/serde", diff --git a/pkgs/pkc/README.md b/pkgs/pkc/README.md index 80e69c52..9d712fb7 100644 --- a/pkgs/pkc/README.md +++ b/pkgs/pkc/README.md @@ -55,6 +55,7 @@ This crate is `#![no_std]` + `alloc` by default. | `ecdsa` | | Enables secp256k1 operations backed by [`rust-secp256k1`](https://docs.rs/secp256k1). Byte types are available without enabling this feature. | | `eddsa` | | Enables ed25519 operations backed by [`ed25519-dalek`](https://docs.rs/ed25519-dalek). Byte types are available without enabling this feature. | | `full` | | Enables all features and `std` support. | +| `portable` | | Disables optimizations that constrain CPU support for the target architecture. | | `serde` | | Enables support for encoding and decoding `codec`-backed types using [`serde`](https://github.com/serde-rs/serde). _Implies `codec`._ | ## Documentation diff --git a/pkgs/pow/Cargo.toml b/pkgs/pow/Cargo.toml index cfdf3f6b..4c10c7f3 100644 --- a/pkgs/pow/Cargo.toml +++ b/pkgs/pow/Cargo.toml @@ -14,6 +14,7 @@ default = [] std = [] full = ["std", "aes_hw", "simd"] aes_hw = ["simd"] +portable = [] simd = [] [dev-dependencies] diff --git a/pkgs/primitives/Cargo.toml b/pkgs/primitives/Cargo.toml index 73a3a5aa..6827fd35 100644 --- a/pkgs/primitives/Cargo.toml +++ b/pkgs/primitives/Cargo.toml @@ -32,6 +32,7 @@ serde = [ "dash-types/serde", ] full = ["std", "serde"] +portable = ["dash-pkc/portable", "dash-pow/portable", "dash-script/portable"] [dependencies] bitcoin-consensus-encoding = { workspace = true, features = ["alloc"] } diff --git a/pkgs/script/Cargo.toml b/pkgs/script/Cargo.toml index 4802e1b6..4d6985ec 100644 --- a/pkgs/script/Cargo.toml +++ b/pkgs/script/Cargo.toml @@ -18,6 +18,7 @@ std = [ "dash-types/std", ] full = ["std", "serde"] +portable = ["dash-pkc/portable"] serde = ["dep:serde", "dash-pkc/serde", "dash-types/serde"] [dependencies] From f36faf1a2b94c37787381211b0110e113db72106 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:00:08 +0530 Subject: [PATCH 08/23] sdk%build!: enforce platform minimums, set flags for in-tree builds --- .cargo/config.toml | 9 ++++++++ .github/workflows/build_nightly.yml | 1 - Justfile | 7 +++--- README.md | 11 ++++++++++ contrib/git_filter.py | 4 ++-- docs/build_docs.py | 3 +-- pkgs/p2p_core/Cargo.toml | 3 +++ pkgs/params/Cargo.toml | 3 +++ pkgs/pkc/src/lib.rs | 8 +++++++ pkgs/pow/Cargo.toml | 3 +++ pkgs/pow/src/lib.rs | 33 +++++++++++++++++++++++++++++ pkgs/primitives/Cargo.toml | 3 +++ 12 files changed, 79 insertions(+), 9 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index c91c3f38..6f9b8ea9 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,11 @@ [net] git-fetch-with-cli = true + +[target.'cfg(target_arch = "aarch64")'] +rustflags = ["-C", "target-feature=+aes"] + +[target.'cfg(target_arch = "x86_64")'] +rustflags = ["-C", "target-cpu=x86-64-v3", "-C", "target-feature=+adx,+aes"] + +[target.'cfg(target_arch = "wasm32")'] +rustflags = ["-C", "target-feature=+simd128"] diff --git a/.github/workflows/build_nightly.yml b/.github/workflows/build_nightly.yml index 8a3cad98..8aec2f5a 100644 --- a/.github/workflows/build_nightly.yml +++ b/.github/workflows/build_nightly.yml @@ -25,7 +25,6 @@ jobs: name: ubuntu-24.04-arm (nightly, ${{ matrix.config.name }}) runs-on: ubuntu-24.04-arm env: - RUSTFLAGS: -D warnings HAS_CODECOV: ${{ secrets.CODECOV_TOKEN != '' }} strategy: fail-fast: false diff --git a/Justfile b/Justfile index 0c9bbe85..b20ef31e 100644 --- a/Justfile +++ b/Justfile @@ -5,7 +5,6 @@ python := if os() == "windows" { "python" } else { "python3" } # `[env]` attribute unavailable on just 1.40 packaged with Debian trixie # (see https://packages.debian.org/trixie/just) export RUSTDOCFLAGS := "-D warnings" -export RUSTFLAGS := "-D warnings" nextest := "cargo nextest run --config-file maint/nextest.toml --profile=ci" @@ -36,9 +35,9 @@ preview: {{ python }} docs/build_docs.py preview test: - cargo clippy --all-targets --no-default-features - cargo clippy --all-targets --features full - cargo clippy --all-targets --features full,portable + cargo clippy --all-targets --no-default-features -- -D warnings + cargo clippy --all-targets --features full -- -D warnings + cargo clippy --all-targets --features full,portable -- -D warnings {{ nextest }} --fail-fast --workspace --features full cargo test --doc --workspace --features full cargo doc --workspace --no-deps --features full diff --git a/README.md b/README.md index 2971d3c4..4bd3e5a1 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,17 @@ | [dash-script](./pkgs/script) | Script opcodes, classification, and address derivation | [![dash-script](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_script.yml?style=flat&logo=github&logoColor=white&label=script)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_script.yml) | [![dash-script](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-script&style=flat&logo=codecov&logoColor=white&label=script)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Fscript) | _Unreleased._ | | [dash-types](./pkgs/types) | Shared byte and integer newtypes, serde helpers | [![dash-types](https://img.shields.io/github/actions/workflow/status/dashpay/base-sdk/pkg_types.yml?style=flat&logo=github&logoColor=white&label=types)](https://github.com/dashpay/base-sdk/actions/workflows/pkg_types.yml) | [![dash-types](https://img.shields.io/codecov/c/github/dashpay/base-sdk/develop?flag=dash-types&style=flat&logo=codecov&logoColor=white&label=types)](https://app.codecov.io/github/dashpay/base-sdk/tree/develop/pkgs%2Ftypes) | [![dash-types](https://img.shields.io/crates/v/dash-types?style=flat&logo=hackthebox&logoColor=white&label=types&color=teal)](https://crates.io/crates/dash-types) | +## System Requirements + +Some crates rely on instruction set extensions that set hardware minimums for the architectures listed below. Builds +for these architectures fail unless the build flags for them are [defined](.cargo/config.toml) or the `portable` feature +is enabled. Builds for other architectures run without hardware acceleration. + +* (x86_64) Intel Broadwell, AMD Zen, or newer with AES-NI and ADX enabled. +* (AArch64) ARMv8-A or newer with + [cryptographic extensions](https://support.arm.com/documentation/109697/2026_06/Feature-descriptions/The-Armv8-0-architecture-extension?lang=en#md445-the-armv80-architecture-extension__feat_FEAT_Crypto). +* (WebAssembly) Wasm 2.0-capable runtime with support for [vector instructions](https://www.w3.org/TR/2024/CR-wasm-core-2-20241217/#vector-instructions). + ## Documentation diff --git a/contrib/git_filter.py b/contrib/git_filter.py index 69f5a59c..65564e96 100755 --- a/contrib/git_filter.py +++ b/contrib/git_filter.py @@ -18,8 +18,8 @@ ./contrib/git_filter.py feature -- cargo test -F full ./contrib/git_filter.py base_branch feature -- cargo test ./contrib/git_filter.py --fast-fail -- cargo clippy - ./contrib/git_filter.py --env RUSTFLAGS=-Dwarnings -- cargo check -""" + ./contrib/git_filter.py --env RUSTUP_TOOLCHAIN=nightly-2026-02-01 -- cargo check +""" # noqa: E501 from __future__ import annotations diff --git a/docs/build_docs.py b/docs/build_docs.py index 6773817f..53cf9d25 100755 --- a/docs/build_docs.py +++ b/docs/build_docs.py @@ -106,9 +106,8 @@ def _build_wasm_samples(root: Path, wasm_pack: str, cfg: Config) -> None: env = { **os.environ, "CARGO_TARGET_DIR": str(root / "target" / "samples"), - "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS": - "-C target-feature=+simd128", } + env.pop("RUSTFLAGS", None) env.setdefault("RUSTUP_TOOLCHAIN", channel) for cargo_toml in samples: diff --git a/pkgs/p2p_core/Cargo.toml b/pkgs/p2p_core/Cargo.toml index 62153ed6..f3cac245 100644 --- a/pkgs/p2p_core/Cargo.toml +++ b/pkgs/p2p_core/Cargo.toml @@ -56,3 +56,6 @@ rstest = { workspace = true } [lints] workspace = true + +[package.metadata.docs.rs] +features = ["portable"] diff --git a/pkgs/params/Cargo.toml b/pkgs/params/Cargo.toml index 2f219673..b8666a27 100644 --- a/pkgs/params/Cargo.toml +++ b/pkgs/params/Cargo.toml @@ -32,3 +32,6 @@ rstest = { workspace = true } [lints] workspace = true + +[package.metadata.docs.rs] +features = ["portable"] diff --git a/pkgs/pkc/src/lib.rs b/pkgs/pkc/src/lib.rs index 066a3b73..7c6761ab 100644 --- a/pkgs/pkc/src/lib.rs +++ b/pkgs/pkc/src/lib.rs @@ -13,6 +13,14 @@ extern crate self as dash_pkc; #[cfg(feature = "std")] extern crate std; +#[cfg(all( + not(any(doc, feature = "portable")), + feature = "bls", + target_arch = "x86_64", + not(target_feature = "adx") +))] +compile_error!("dash-pkc requires ADX with `bls`, build with `-C target-feature=+adx` or enable `portable`"); + #[cfg(feature = "bls")] mod aes_cbc; #[allow(unused_imports, reason = "ergonomic shim, exports may be unused")] diff --git a/pkgs/pow/Cargo.toml b/pkgs/pow/Cargo.toml index 4c10c7f3..8ac92519 100644 --- a/pkgs/pow/Cargo.toml +++ b/pkgs/pow/Cargo.toml @@ -53,3 +53,6 @@ required-features = ["simd"] [lints] workspace = true + +[package.metadata.docs.rs] +features = ["portable"] diff --git a/pkgs/pow/src/lib.rs b/pkgs/pow/src/lib.rs index ff0394b4..942bff88 100644 --- a/pkgs/pow/src/lib.rs +++ b/pkgs/pow/src/lib.rs @@ -17,6 +17,39 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +#[cfg(all( + not(any(doc, feature = "portable")), + target_arch = "x86_64", + not(all( + target_feature = "aes", + target_feature = "avx", + target_feature = "avx2", + target_feature = "bmi1", + target_feature = "bmi2", + target_feature = "f16c", + target_feature = "fma", + target_feature = "lzcnt", + target_feature = "movbe", + target_feature = "xsave", + )) +))] +compile_error!(concat!( + "dash-pow requires x86-64-v3 and AES, build with ", + "`-C target-cpu=x86-64-v3 -C target-feature=+aes` or enable `portable`", +)); +#[cfg(all( + not(any(doc, feature = "portable")), + target_arch = "aarch64", + not(all(target_feature = "neon", target_feature = "aes")) +))] +compile_error!("dash-pow requires NEON and AES, build with `-C target-feature=+neon,+aes` or enable `portable`"); +#[cfg(all( + not(any(doc, feature = "portable")), + target_arch = "wasm32", + not(target_feature = "simd128") +))] +compile_error!("dash-pow requires 128-bit SIMD, build with `-C target-feature=+simd128` or enable `portable`"); + mod blake; mod bmw; mod cubehash; diff --git a/pkgs/primitives/Cargo.toml b/pkgs/primitives/Cargo.toml index 6827fd35..e4b774f2 100644 --- a/pkgs/primitives/Cargo.toml +++ b/pkgs/primitives/Cargo.toml @@ -66,3 +66,6 @@ serde = { workspace = true, features = ["derive", "std"] } [lints] workspace = true + +[package.metadata.docs.rs] +features = ["portable"] From 0126dc07797a2aa272deb64de52d86b7b2325694 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:16:51 +0530 Subject: [PATCH 09/23] types%feat!: decouple `serde` from `codec` --- pkgs/types/Cargo.toml | 1 - pkgs/types/README.md | 2 +- pkgs/types/src/numeric.rs | 73 +++++++++++++++++++++------------------ 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/pkgs/types/Cargo.toml b/pkgs/types/Cargo.toml index c9ca54d0..758d2c52 100644 --- a/pkgs/types/Cargo.toml +++ b/pkgs/types/Cargo.toml @@ -47,7 +47,6 @@ full = [ "std", ] serde = [ - "codec", "dep:serde", "hex-conservative/alloc", ] diff --git a/pkgs/types/README.md b/pkgs/types/README.md index 88b74d6f..306efdd1 100644 --- a/pkgs/types/README.md +++ b/pkgs/types/README.md @@ -44,7 +44,7 @@ This crate is `#![no_std]` + `alloc` by default. | `bitcoin-primitives` | | Enables [adapters](https://github.com/dashpay/base-sdk/blob/develop/pkgs/types/src/adapters.rs) for types defined in [`bitcoin-primitives`](https://docs.rs/bitcoin-primitives). _Implies `codec`._ | | `codec` | :white_check_mark: | Enables support for encoding and decoding types using [`bitcoin-consensus-encoding`](https://docs.rs/bitcoin-consensus-encoding) | | `full` | | Enables all features and `std` support. | -| `serde` | | Enables support for encoding and decoding `codec`-backed types using [`serde`](https://github.com/serde-rs/serde). _Implies `codec`._ | +| `serde` | | Enables support for encoding and decoding types using [`serde`](https://github.com/serde-rs/serde). | ## Documentation diff --git a/pkgs/types/src/numeric.rs b/pkgs/types/src/numeric.rs index 7401503e..cf64b403 100644 --- a/pkgs/types/src/numeric.rs +++ b/pkgs/types/src/numeric.rs @@ -119,21 +119,29 @@ numeric_prim! { i128, 16; u128, 16; } -/// Generates `BaseCodec` + `Encode` + `Decode` + serde for a type -/// that already implements [`Numeric`] over `$uint`. -#[cfg(feature = "codec")] +/// Generates `BaseCodec`, `Encode` and `Decode` with `codec`, and serde with +/// `serde`, for a type that already implements [`Numeric`] over `$uint`. #[macro_export] macro_rules! impl_num { - ($name:tt, i8) => { $crate::impl_num!(@codec $name, i8, 1); }; - ($name:tt, u8) => { $crate::impl_num!(@codec $name, u8, 1); }; - ($name:tt, i16) => { $crate::impl_num!(@codec $name, i16, 2); }; - ($name:tt, u16) => { $crate::impl_num!(@codec $name, u16, 2); }; - ($name:tt, i32) => { $crate::impl_num!(@codec $name, i32, 4); }; - ($name:tt, u32) => { $crate::impl_num!(@codec $name, u32, 4); }; - ($name:tt, i64) => { $crate::impl_num!(@codec $name, i64, 8); }; - ($name:tt, u64) => { $crate::impl_num!(@codec $name, u64, 8); }; - ($name:tt, i128) => { $crate::impl_num!(@codec $name, i128, 16); }; - ($name:tt, u128) => { $crate::impl_num!(@codec $name, u128, 16); }; + ($name:tt, i8) => { $crate::impl_num!(@both $name, i8, 1); }; + ($name:tt, u8) => { $crate::impl_num!(@both $name, u8, 1); }; + ($name:tt, i16) => { $crate::impl_num!(@both $name, i16, 2); }; + ($name:tt, u16) => { $crate::impl_num!(@both $name, u16, 2); }; + ($name:tt, i32) => { $crate::impl_num!(@both $name, i32, 4); }; + ($name:tt, u32) => { $crate::impl_num!(@both $name, u32, 4); }; + ($name:tt, i64) => { $crate::impl_num!(@both $name, i64, 8); }; + ($name:tt, u64) => { $crate::impl_num!(@both $name, u64, 8); }; + ($name:tt, i128) => { $crate::impl_num!(@both $name, i128, 16); }; + ($name:tt, u128) => { $crate::impl_num!(@both $name, u128, 16); }; + (@both $name:ty, $uint:ty, $n:literal) => { + $crate::cfg_codec! { + $crate::impl_num!(@codec $name, $uint, $n); + } + + $crate::cfg_serde! { + $crate::impl_num!(@serde $name, $uint); + } + }; (@codec $name:ty, $uint:ty, $n:literal) => { impl $crate::codec::BaseCodec for $name { fn decode( @@ -155,26 +163,25 @@ macro_rules! impl_num { } $crate::impl_type!($name); - - $crate::cfg_serde! { - impl $crate::__private::serde::Serialize for $name { - fn serialize( - &self, serializer: S, - ) -> Result { - $crate::__private::serde::Serialize::serialize( - &::to_base(self), - serializer, - ) - } + }; + (@serde $name:ty, $uint:ty) => { + impl $crate::__private::serde::Serialize for $name { + fn serialize( + &self, serializer: S, + ) -> Result { + $crate::__private::serde::Serialize::serialize( + &::to_base(self), + serializer, + ) } + } - impl<'de> $crate::__private::serde::Deserialize<'de> for $name { - fn deserialize>( - deserializer: D, - ) -> Result { - <$uint as $crate::__private::serde::Deserialize>::deserialize(deserializer) - .map(::from_base) - } + impl<'de> $crate::__private::serde::Deserialize<'de> for $name { + fn deserialize>( + deserializer: D, + ) -> Result { + <$uint as $crate::__private::serde::Deserialize>::deserialize(deserializer) + .map(::from_base) } } }; @@ -199,12 +206,12 @@ macro_rules! make_num { $crate::make_num!( @struct $attrs #[derive($crate::type_id::TypeId)] $name, $uint ); - - $crate::impl_num!($name, $uint); } else { $crate::make_num!(@struct $attrs $name, $uint); } } + + $crate::impl_num!($name, $uint); }; ( $(#[$attr:meta])* From db5680dfebf1cf65efae75737d1283d0f8e81736 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:01:09 +0530 Subject: [PATCH 10/23] num%feat!: decouple `serde` from `codec` --- pkgs/num/Cargo.toml | 1 - pkgs/num/README.md | 2 +- pkgs/num/src/compact.rs | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pkgs/num/Cargo.toml b/pkgs/num/Cargo.toml index 0775c9cd..4220421e 100644 --- a/pkgs/num/Cargo.toml +++ b/pkgs/num/Cargo.toml @@ -41,7 +41,6 @@ full = [ "std", ] serde = [ - "codec", "dash-types/serde", "dep:serde", ] diff --git a/pkgs/num/README.md b/pkgs/num/README.md index a285bafb..1ed5204c 100644 --- a/pkgs/num/README.md +++ b/pkgs/num/README.md @@ -39,7 +39,7 @@ This crate is `#![no_std]` + `alloc` by default. | ------- | ------- | ----------- | | `codec` | :white_check_mark: | Enables support for encoding and decoding types using [`bitcoin-consensus-encoding`](https://docs.rs/bitcoin-consensus-encoding) | | `full` | | Enables all features and `std` support. | -| `serde` | | Enables support for encoding and decoding `codec`-backed types using [`serde`](https://github.com/serde-rs/serde). _Implies `codec`._ | +| `serde` | | Enables support for encoding and decoding types using [`serde`](https://github.com/serde-rs/serde). | ## Documentation diff --git a/pkgs/num/src/compact.rs b/pkgs/num/src/compact.rs index 81f3646c..ca97df2a 100644 --- a/pkgs/num/src/compact.rs +++ b/pkgs/num/src/compact.rs @@ -9,7 +9,6 @@ use crate::Arith256; use dash_types::__private::__read_hex as read_hex; -#[cfg(feature = "codec")] use dash_types::impl_num; use dash_types::{Numeric, ParseHexError}; @@ -64,7 +63,6 @@ impl Numeric for CompactTarget { } } -#[cfg(feature = "codec")] impl_num!(CompactTarget, u32); impl CompactTarget { From d53e4779091102f93b51aeac581615ecd4c9ec07 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:01:10 +0530 Subject: [PATCH 11/23] pkc%feat!: decouple `serde` from `codec` --- pkgs/pkc/Cargo.toml | 1 - pkgs/pkc/README.md | 2 +- pkgs/pkc/src/bls/ies_ops.rs | 4 ++-- pkgs/pkc/src/ecdsa/sig_rec_bytes.rs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pkgs/pkc/Cargo.toml b/pkgs/pkc/Cargo.toml index 2c36c103..353986b1 100644 --- a/pkgs/pkc/Cargo.toml +++ b/pkgs/pkc/Cargo.toml @@ -105,7 +105,6 @@ full = [ ] portable = ["blst?/portable"] serde = [ - "codec", "dash-num/serde", "dash-types/serde", "dep:serde", diff --git a/pkgs/pkc/README.md b/pkgs/pkc/README.md index 9d712fb7..929c3a6e 100644 --- a/pkgs/pkc/README.md +++ b/pkgs/pkc/README.md @@ -56,7 +56,7 @@ This crate is `#![no_std]` + `alloc` by default. | `eddsa` | | Enables ed25519 operations backed by [`ed25519-dalek`](https://docs.rs/ed25519-dalek). Byte types are available without enabling this feature. | | `full` | | Enables all features and `std` support. | | `portable` | | Disables optimizations that constrain CPU support for the target architecture. | -| `serde` | | Enables support for encoding and decoding `codec`-backed types using [`serde`](https://github.com/serde-rs/serde). _Implies `codec`._ | +| `serde` | | Enables support for encoding and decoding types using [`serde`](https://github.com/serde-rs/serde). Types serialized as their wire image also need `codec`. | ## Documentation diff --git a/pkgs/pkc/src/bls/ies_ops.rs b/pkgs/pkc/src/bls/ies_ops.rs index 1868e45c..fbc82787 100644 --- a/pkgs/pkc/src/bls/ies_ops.rs +++ b/pkgs/pkc/src/bls/ies_ops.rs @@ -79,7 +79,7 @@ fn ephemeral(rng: &mut impl CryptoRng) -> Result<(BlsSecretKey, /// A BLS-IES encrypted blob under an ephemeral key. #[cfg_attr( - feature = "serde", + all(feature = "codec", feature = "serde"), derive(::serde::Serialize, ::serde::Deserialize), serde(into = "BlsIesBlobBytes", try_from = "BlsIesBlobBytes"), serde(bound(serialize = "", deserialize = "")) @@ -185,7 +185,7 @@ type_cvrt!(for[S: BlsScheme] TryFrom> for BlsIesBlob, BlsE /// One ciphertext per recipient under an ephemeral key. #[cfg_attr( - feature = "serde", + all(feature = "codec", feature = "serde"), derive(::serde::Serialize, ::serde::Deserialize), serde(into = "BlsIesMultiBytes", try_from = "BlsIesMultiBytes"), serde(bound(serialize = "", deserialize = "")) diff --git a/pkgs/pkc/src/ecdsa/sig_rec_bytes.rs b/pkgs/pkc/src/ecdsa/sig_rec_bytes.rs index 2b11f98a..86b7cb0c 100644 --- a/pkgs/pkc/src/ecdsa/sig_rec_bytes.rs +++ b/pkgs/pkc/src/ecdsa/sig_rec_bytes.rs @@ -8,7 +8,7 @@ use super::sig_bytes::{EcdsaSigBytes, ECDSA_SIG_LEN}; use super::Compression; -#[cfg(feature = "codec")] +#[cfg(any(feature = "codec", feature = "serde"))] use crate::prelude::*; use bitcoin_hashes::sha256d; From a02faf20cce4081b7e46fb6005fafe94cb1d9d7e Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:29:11 +0530 Subject: [PATCH 12/23] p2p_core%refac: derive error structs and enums with `thiserror` --- AGENTS.md | 2 +- Cargo.lock | 21 ++++++++++++++ Cargo.toml | 1 + docs/dev/guide_rust.md | 31 ++++---------------- pkgs/p2p_core/Cargo.toml | 1 + pkgs/p2p_core/src/error.rs | 50 ++++++++++---------------------- pkgs/p2p_core/src/msg/version.rs | 9 ++---- 7 files changed, 48 insertions(+), 67 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e83df997..acb864f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,7 @@ The full guide is at [`docs/dev/guide_rust.md`](./docs/dev/guide_rust.md). Key p ### Environment - `no_std` + `alloc` is mandatory for all crates. Every crate uses `#![no_std]` with `extern crate alloc`. -- `std` is an optional feature that downstream consumers enable when they need stdlib I/O, `Error` impls, etc. +- `std` is an optional feature that downstream consumers enable when they need standard library I/O, primitives, etc. - No networking code in any crate. The SDK provides encoding, decoding, and data types only. ### Feature flags diff --git a/Cargo.lock b/Cargo.lock index a316f0dc..0267eed0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -471,6 +471,7 @@ dependencies = [ "hex-conservative 1.3.0", "rstest", "serde", + "thiserror", ] [[package]] @@ -1626,6 +1627,26 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.8", +] + [[package]] name = "threadpool" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index 1bb4eed4..88a8784a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ rand_core = { version = "0.10.1", default-features = false } rstest = "0.25" serde = { version = "1", default-features = false } subtle = { version = "2.6.1", default-features = false } +thiserror = { version = "2", default-features = false } zeroize = { version = "1.9.0", default-features = false } [workspace.lints.clippy] diff --git a/docs/dev/guide_rust.md b/docs/dev/guide_rust.md index 2c5eccbf..5f389aaa 100644 --- a/docs/dev/guide_rust.md +++ b/docs/dev/guide_rust.md @@ -129,8 +129,8 @@ impl core::fmt::Debug for SecretKey { > Propagate errors with `?` or handle them explicitly with `match`. Both `clippy::unwrap_used` and > `clippy::expect_used` are denied at the workspace level. -- Define domain-specific error enums with a manual `Display` implementation; gate `std::error::Error` - behind the `std` feature so the error type remains usable in `no_std` contexts +- Define domain-specific error enums and derive `thiserror::Error`, which generates `Display` from `#[error]` + attributes - Error messages are lowercase and carry no trailing punctuation; they may be embedded in larger messages by callers, so capitalization and periods would read awkwardly in the middle of a sentence - Keep `?` chains short; if a function contains more than a few `?` calls on unrelated operations, each @@ -145,38 +145,19 @@ impl core::fmt::Debug for SecretKey { Example code: ```rust -use core::fmt; - /// Errors produced by BLS operations. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum Error { /// secret key bytes are not a valid scalar + #[error("invalid secret key bytes")] InvalidSecretKey, /// public key bytes are not a valid G1 point + #[error("invalid public key bytes")] InvalidPublicKey, /// signature verification failed + #[error("signature verification failed")] VerifyFailed, } - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InvalidSecretKey => { - write!(f, "invalid secret key bytes") - } - Self::InvalidPublicKey => { - write!(f, "invalid public key bytes") - } - Self::VerifyFailed => { - write!(f, "signature verification failed") - } - } - } -} - -// Only available when the downstream crate enables `std`. -#[cfg(feature = "std")] -impl std::error::Error for Error {} ``` ```rust diff --git a/pkgs/p2p_core/Cargo.toml b/pkgs/p2p_core/Cargo.toml index f3cac245..573d11e1 100644 --- a/pkgs/p2p_core/Cargo.toml +++ b/pkgs/p2p_core/Cargo.toml @@ -48,6 +48,7 @@ serde = { workspace = true, features = [ "alloc", "derive", ], optional = true } +thiserror = { workspace = true } [dev-dependencies] dash-dev = { version = "0.0.0", path = "../dev", features = ["full"] } diff --git a/pkgs/p2p_core/src/error.rs b/pkgs/p2p_core/src/error.rs index 19120f8c..be821dfc 100644 --- a/pkgs/p2p_core/src/error.rs +++ b/pkgs/p2p_core/src/error.rs @@ -14,21 +14,25 @@ use dash_types::type_id::Unencodable; use core::fmt; /// An error encountered during P2P message decoding. -#[derive(Clone, Debug, Eq, Hash, PartialEq, Unencodable)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, thiserror::Error, Unencodable)] pub enum P2pDecodeError { /// Underlying consensus decoding error. + #[error("{0}")] Consensus(String), /// Unrecognised 12-byte command string. + #[error(fmt = fmt_unknown_command)] UnknownCommand { /// The raw command bytes. bytes: [u8; 12], }, /// V2 short ID does not map to a known message. + #[error("unknown v2 short id: {id}")] UnknownShortId { /// The short ID byte. id: u8, }, /// Message payload exceeds the allowed size. + #[error("{command} payload too large: {size} bytes, max {max}")] PayloadTooLarge { /// Wire command name. command: &'static str, @@ -38,6 +42,7 @@ pub enum P2pDecodeError { max: usize, }, /// Message carries a payload where none is permitted. + #[error("{command} must have an empty payload, got {size} bytes")] PayloadNotEmpty { /// Wire command name. command: &'static str, @@ -45,6 +50,7 @@ pub enum P2pDecodeError { size: usize, }, /// A field value is outside the valid range. + #[error("invalid value for {field}")] InvalidValue { /// Brief description of what was invalid. field: &'static str, @@ -57,39 +63,15 @@ impl From for P2pDecodeError { } } -impl fmt::Display for P2pDecodeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Consensus(e) => write!(f, "{e}"), - Self::UnknownCommand { bytes } => { - write!(f, "unknown command: ")?; - for &b in bytes { - if b == 0 { - break; - } - if b.is_ascii_graphic() || b == b' ' { - write!(f, "{}", b as char)?; - } else { - write!(f, "\\x{b:02x}")?; - } - } - Ok(()) - } - Self::UnknownShortId { id } => { - write!(f, "unknown v2 short id: {id}") - } - Self::PayloadTooLarge { command, size, max } => { - write!(f, "{command} payload too large: {size} bytes, max {max}") - } - Self::PayloadNotEmpty { command, size } => { - write!(f, "{command} must have an empty payload, got {size} bytes") - } - Self::InvalidValue { field } => { - write!(f, "invalid value for {field}") - } +/// Formats `P2pDecodeError::UnknownCommand`, escaping non-printable bytes. +fn fmt_unknown_command(bytes: &[u8; 12], f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("unknown command: ")?; + for &b in bytes.iter().take_while(|&&b| b != 0) { + if b.is_ascii_graphic() || b == b' ' { + write!(f, "{}", b as char)?; + } else { + write!(f, "\\x{b:02x}")?; } } + Ok(()) } - -#[cfg(feature = "std")] -impl std::error::Error for P2pDecodeError {} diff --git a/pkgs/p2p_core/src/msg/version.rs b/pkgs/p2p_core/src/msg/version.rs index 72a46ee4..8d871c47 100644 --- a/pkgs/p2p_core/src/msg/version.rs +++ b/pkgs/p2p_core/src/msg/version.rs @@ -72,18 +72,13 @@ impl BitOrAssign for ServiceFlags { } /// The user agent exceeds the 256-byte limit. -#[derive(Debug, Clone, PartialEq, Eq, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Unencodable)] +#[error("user agent too long: {len} bytes, max {MAX_USER_AGENT}")] pub struct UserAgentTooLong { /// Actual length in bytes. pub len: usize, } -impl fmt::Display for UserAgentTooLong { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "user agent too long: {} bytes, max {MAX_USER_AGENT}", self.len) - } -} - /// CompactSize-prefixed user agent bytestring. #[derive(Clone, Debug, Eq, Hash, PartialEq, TypeId)] #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] From eacbff732aa9e0e60ff785f3aa278ab59f264f79 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 11:16:48 +0530 Subject: [PATCH 13/23] primitives%refac: derive error enums with `thiserror` --- Cargo.lock | 1 + docs/samples/Cargo.lock | 21 +++++++ pkgs/primitives/Cargo.toml | 1 + pkgs/primitives/src/block.rs | 23 +++---- pkgs/primitives/src/gov.rs | 25 +++----- pkgs/primitives/src/payload/assetlock.rs | 17 ++---- pkgs/primitives/src/payload/assetunlock.rs | 13 +--- pkgs/primitives/src/payload/cbtx.rs | 15 ++--- pkgs/primitives/src/payload/mnhftx.rs | 13 +--- pkgs/primitives/src/payload/mod.rs | 71 ++++++++-------------- pkgs/primitives/src/payload/quorum.rs | 11 +--- pkgs/primitives/src/transaction.rs | 33 ++++------ pkgs/primitives/src/types/netaddr.rs | 53 ++++------------ pkgs/primitives/src/types/netinfo.rs | 28 +++------ 14 files changed, 113 insertions(+), 212 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0267eed0..9aefdef8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -546,6 +546,7 @@ dependencies = [ "libm", "rstest", "serde", + "thiserror", ] [[package]] diff --git a/docs/samples/Cargo.lock b/docs/samples/Cargo.lock index 6d505283..4108bb74 100644 --- a/docs/samples/Cargo.lock +++ b/docs/samples/Cargo.lock @@ -163,6 +163,7 @@ dependencies = [ "hex-conservative 1.3.0", "libm", "serde", + "thiserror", ] [[package]] @@ -394,6 +395,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.8", +] + [[package]] name = "unicode-ident" version = "1.0.26" diff --git a/pkgs/primitives/Cargo.toml b/pkgs/primitives/Cargo.toml index e4b774f2..5cdcffb0 100644 --- a/pkgs/primitives/Cargo.toml +++ b/pkgs/primitives/Cargo.toml @@ -58,6 +58,7 @@ serde = { workspace = true, features = [ "alloc", "derive", ], optional = true } +thiserror = { workspace = true } [dev-dependencies] dash-dev = { version = "0.0.0", path = "../dev", features = ["full"] } diff --git a/pkgs/primitives/src/block.rs b/pkgs/primitives/src/block.rs index d3c810d8..b1a2df5e 100644 --- a/pkgs/primitives/src/block.rs +++ b/pkgs/primitives/src/block.rs @@ -88,38 +88,31 @@ impl fmt::Display for BlockHeader { } /// Block validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum BlockInvalid { /// Decoded target is negative, zero, overflows, or the hash exceeds it. + #[error("bad-proof-of-work")] BadProofOfWork, /// Computed merkle root does not match the header. + #[error("bad-merkle-root")] BadMerkleRoot, /// `bad-blk-length` + #[error("bad-blk-length: {size} bytes")] BadBlockLength { size: usize }, /// `bad-cb-missing` + #[error("bad-cb-missing")] MissingCoinbase, /// `bad-cb-multiple` + #[error("bad-cb-multiple: tx {index}")] MultipleCoinbases { index: usize }, /// `bad-blk-sigops` + #[error("bad-blk-sigops: {count} > {limit}")] TooManySigops { count: usize, limit: usize }, /// A contained transaction failed validation. + #[error("tx {index}: {error}")] Transaction { index: usize, error: TxInvalid }, } -impl fmt::Display for BlockInvalid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadProofOfWork => write!(f, "bad-proof-of-work"), - Self::BadMerkleRoot => write!(f, "bad-merkle-root"), - Self::BadBlockLength { size } => write!(f, "bad-blk-length: {size} bytes"), - Self::MissingCoinbase => write!(f, "bad-cb-missing"), - Self::MultipleCoinbases { index } => write!(f, "bad-cb-multiple: tx {index}"), - Self::TooManySigops { count, limit } => write!(f, "bad-blk-sigops: {count} > {limit}"), - Self::Transaction { index, error } => write!(f, "tx {index}: {error}"), - } - } -} - /// A Dash block: header followed by a vector of transactions. #[derive(Clone, Debug, Eq, Hash, PartialEq, TypeId)] #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] diff --git a/pkgs/primitives/src/gov.rs b/pkgs/primitives/src/gov.rs index 6d3020d3..fb729f1f 100644 --- a/pkgs/primitives/src/gov.rs +++ b/pkgs/primitives/src/gov.rs @@ -18,8 +18,6 @@ use dash_types::type_id::{TypeId, Unencodable}; use dash_types::{enum_map, impl_num, ArrayBuf, Checkable, Hashable, Numeric}; use hex_conservative::DisplayHex; -use core::fmt; - /// Maximum allowed name length for governance proposals. const MAX_PROPOSAL_NAME_LEN: usize = 40; @@ -292,38 +290,31 @@ impl Hashable for GovVote { } /// Governance proposal validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum ProposalInvalid { /// Name is empty. + #[error("invalid name: empty")] NameEmpty, /// Name exceeds maximum length. + #[error("invalid name: {len} chars exceeds {}", MAX_PROPOSAL_NAME_LEN)] NameTooLong { len: usize }, /// Name contains invalid characters. + #[error("invalid name: disallowed characters")] NameInvalidChars, /// `end_epoch` is not after `start_epoch`. + #[error("invalid start:end range")] BadEpochRange, /// Payment amount is not positive. + #[error("invalid payment amount")] BadPaymentAmount, /// URL is too short. + #[error("url too short: {len} chars")] UrlTooShort { len: usize }, /// URL contains whitespace. + #[error("url has whitespace")] UrlWhitespace, } -impl fmt::Display for ProposalInvalid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::NameEmpty => write!(f, "invalid name: empty"), - Self::NameTooLong { len } => write!(f, "invalid name: {len} chars exceeds {MAX_PROPOSAL_NAME_LEN}"), - Self::NameInvalidChars => write!(f, "invalid name: disallowed characters"), - Self::BadEpochRange => write!(f, "invalid start:end range"), - Self::BadPaymentAmount => write!(f, "invalid payment amount"), - Self::UrlTooShort { len } => write!(f, "url too short: {len} chars"), - Self::UrlWhitespace => write!(f, "url has whitespace"), - } - } -} - impl Checkable for Proposal { type Error = ProposalInvalid; diff --git a/pkgs/primitives/src/payload/assetlock.rs b/pkgs/primitives/src/payload/assetlock.rs index 682baf2e..e284aecc 100644 --- a/pkgs/primitives/src/payload/assetlock.rs +++ b/pkgs/primitives/src/payload/assetlock.rs @@ -33,29 +33,22 @@ codec_payload!(AssetLock { }); /// Asset lock validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum AssetLockInvalid { /// `bad-assetlocktx-version` + #[error("bad-assetlocktx-version: {version}")] BadVersion { version: u8 }, /// `bad-assetlocktx-emptycreditoutputs` + #[error("bad-assetlocktx-emptycreditoutputs")] EmptyCreditOutputs, /// `bad-assetlocktx-credit-outofrange` + #[error("bad-assetlocktx-credit-outofrange: output {index}")] CreditOutOfRange { index: usize }, /// `bad-assetlocktx-pubKeyHash` + #[error("bad-assetlocktx-pubKeyHash: output {index}")] CreditNotP2pkh { index: usize }, } -impl core::fmt::Display for AssetLockInvalid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::BadVersion { version } => write!(f, "bad-assetlocktx-version: {version}"), - Self::EmptyCreditOutputs => write!(f, "bad-assetlocktx-emptycreditoutputs"), - Self::CreditOutOfRange { index } => write!(f, "bad-assetlocktx-credit-outofrange: output {index}"), - Self::CreditNotP2pkh { index } => write!(f, "bad-assetlocktx-pubKeyHash: output {index}"), - } - } -} - impl Checkable for AssetLock { type Error = AssetLockInvalid; diff --git a/pkgs/primitives/src/payload/assetunlock.rs b/pkgs/primitives/src/payload/assetunlock.rs index b2063822..295981ef 100644 --- a/pkgs/primitives/src/payload/assetunlock.rs +++ b/pkgs/primitives/src/payload/assetunlock.rs @@ -44,23 +44,16 @@ codec_payload!(AssetUnlock { }); /// Asset unlock validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum AssetUnlockInvalid { /// `bad-assetunlocktx-version` + #[error("bad-assetunlocktx-version: {version}")] BadVersion { version: u8 }, /// `bad-txns-assetunlock-fee-outofrange` + #[error("bad-txns-assetunlock-fee-outofrange: {fee}")] FeeOutOfRange { fee: u32 }, } -impl core::fmt::Display for AssetUnlockInvalid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::BadVersion { version } => write!(f, "bad-assetunlocktx-version: {version}"), - Self::FeeOutOfRange { fee } => write!(f, "bad-txns-assetunlock-fee-outofrange: {fee}"), - } - } -} - impl Checkable for AssetUnlock { type Error = AssetUnlockInvalid; diff --git a/pkgs/primitives/src/payload/cbtx.rs b/pkgs/primitives/src/payload/cbtx.rs index a4e72a81..4c8ba9d3 100644 --- a/pkgs/primitives/src/payload/cbtx.rs +++ b/pkgs/primitives/src/payload/cbtx.rs @@ -95,26 +95,19 @@ impl BaseCodec for CoinbaseCommitment { } /// Coinbase commitment validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum CbTxInvalid { /// `bad-cbtx-version` + #[error("bad-cbtx-version: {version}")] BadVersion { version: u16 }, /// `bad-cbtx-missing-field` + #[error("bad-cbtx-missing-field")] MissingField, /// `bad-cbtx-unexpected-field` + #[error("bad-cbtx-unexpected-field")] UnexpectedField, } -impl core::fmt::Display for CbTxInvalid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::BadVersion { version } => write!(f, "bad-cbtx-version: {version}"), - Self::MissingField => write!(f, "bad-cbtx-missing-field"), - Self::UnexpectedField => write!(f, "bad-cbtx-unexpected-field"), - } - } -} - impl Checkable for CoinbaseCommitment { type Error = CbTxInvalid; diff --git a/pkgs/primitives/src/payload/mnhftx.rs b/pkgs/primitives/src/payload/mnhftx.rs index 7717e6c5..bb30736b 100644 --- a/pkgs/primitives/src/payload/mnhftx.rs +++ b/pkgs/primitives/src/payload/mnhftx.rs @@ -41,23 +41,16 @@ codec_payload!(MnHardFork { }); /// MNHF signal validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum MnHardForkInvalid { /// `bad-mnhf-version` + #[error("bad-mnhf-version: {version}")] BadVersion { version: u8 }, /// `bad-mnhf-nbit-out-of-bounds` + #[error("bad-mnhf-nbit-out-of-bounds: {bit}")] VersionBitOutOfBounds { bit: u8 }, } -impl core::fmt::Display for MnHardForkInvalid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::BadVersion { version } => write!(f, "bad-mnhf-version: {version}"), - Self::VersionBitOutOfBounds { bit } => write!(f, "bad-mnhf-nbit-out-of-bounds: {bit}"), - } - } -} - impl Checkable for MnHardFork { type Error = MnHardForkInvalid; diff --git a/pkgs/primitives/src/payload/mod.rs b/pkgs/primitives/src/payload/mod.rs index a2780c70..3c4ca57a 100644 --- a/pkgs/primitives/src/payload/mod.rs +++ b/pkgs/primitives/src/payload/mod.rs @@ -27,8 +27,6 @@ use dash_num::make_hash; use dash_types::type_id::{TypeId, Unencodable}; use dash_types::{enum_map, impl_num, Checkable}; -use core::fmt; - pub use self::assetlock::{AssetLock, AssetLockInvalid}; pub use self::assetunlock::{AssetUnlock, AssetUnlockInvalid}; pub use self::cbtx::{CbTxInvalid, CoinbaseCommitment}; @@ -115,62 +113,55 @@ impl_num!(MnType, u16); hash_impl!(MnType); /// Provider transaction validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum ProTxInvalid { /// `bad-protx-version` + #[error("bad-protx-version: {version}")] BadVersion { version: u16 }, /// `bad-protx-evo-version` + #[error("bad-protx-evo-version: {version}")] EvoVersionTooLow { version: u16 }, /// `bad-protx-type` + #[error("bad-protx-type: {mn_type}")] BadMnType { mn_type: MnType }, /// `bad-protx-mode` + #[error("bad-protx-mode: {mode}")] BadMode { mode: u16 }, /// `bad-protx-key-null` + #[error("bad-protx-key-null")] NullKey, /// `bad-protx-operator-pubkey` + #[error("bad-protx-operator-pubkey")] OperatorKeyMismatch, /// `bad-protx-payee` + #[error("bad-protx-payee")] BadPayoutScript, /// `bad-protx-netinfo-version` + #[error("bad-protx-netinfo-version")] NetInfoVersionMismatch, /// `bad-protx-netinfo-empty` + #[error("bad-protx-netinfo-empty")] NetInfoEmpty, /// `bad-protx-netinfo-bad` + #[error("bad-protx-netinfo-bad: {error}")] NetInfoInvalid { /// The underlying error. error: NIError, }, /// `bad-protx-payee-reuse` + #[error("bad-protx-payee-reuse")] PayoutKeyReuse, /// `bad-protx-operator-reward` + #[error("bad-protx-operator-reward: {reward}")] OperatorRewardTooHigh { reward: u16 }, /// `bad-protx-reason` + #[error("bad-protx-reason: {reason}")] BadReason { reason: crate::support::RevocationReason }, /// `bad-protx-platform-fields` + #[error("bad-protx-platform-fields")] BadPlatformFields, } -impl fmt::Display for ProTxInvalid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadVersion { version } => write!(f, "bad-protx-version: {version}"), - Self::EvoVersionTooLow { version } => write!(f, "bad-protx-evo-version: {version}"), - Self::BadMnType { mn_type } => write!(f, "bad-protx-type: {mn_type}"), - Self::BadMode { mode } => write!(f, "bad-protx-mode: {mode}"), - Self::NullKey => write!(f, "bad-protx-key-null"), - Self::OperatorKeyMismatch => write!(f, "bad-protx-operator-pubkey"), - Self::BadPayoutScript => write!(f, "bad-protx-payee"), - Self::NetInfoVersionMismatch => write!(f, "bad-protx-netinfo-version"), - Self::NetInfoEmpty => write!(f, "bad-protx-netinfo-empty"), - Self::NetInfoInvalid { error } => write!(f, "bad-protx-netinfo-bad: {error}"), - Self::PayoutKeyReuse => write!(f, "bad-protx-payee-reuse"), - Self::OperatorRewardTooHigh { reward } => write!(f, "bad-protx-operator-reward: {reward}"), - Self::BadReason { reason } => write!(f, "bad-protx-reason: {reason}"), - Self::BadPlatformFields => write!(f, "bad-protx-platform-fields"), - } - } -} - /// Checks that an extended net info payload is trivially valid. pub(crate) fn check_sptx_netinfo(ext: &NetInfoV2, version: u16, mn_type: MnType) -> Option { if let Some(error) = ext.check() { @@ -230,7 +221,8 @@ pub enum SpecialPayload { } /// Error decoding a special payload. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Unencodable)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, thiserror::Error, Unencodable)] +#[error("{tx_type} payload: {message}")] pub struct PayloadError { /// Which transaction type was being decoded. pub tx_type: TxType, @@ -238,45 +230,32 @@ pub struct PayloadError { pub message: String, } -impl fmt::Display for PayloadError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} payload: {}", self.tx_type, self.message) - } -} - /// Structural check failure for a special payload. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Unencodable)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, thiserror::Error, Unencodable)] pub enum PayloadInvalid { /// Provider transaction check failed. + #[error(transparent)] ProTx(ProTxInvalid), /// Coinbase commitment check failed. + #[error(transparent)] CbTx(CbTxInvalid), /// Hard-fork signal check failed. + #[error(transparent)] MnHardFork(MnHardForkInvalid), /// Asset lock check failed. + #[error(transparent)] AssetLock(AssetLockInvalid), /// Asset unlock check failed. + #[error(transparent)] AssetUnlock(AssetUnlockInvalid), /// Quorum commitment check failed. + #[error(transparent)] Commitment(CommitmentInvalid), /// Unrecognized special transaction. + #[error("bad-txns-type: {0}")] UnknownType(TxType), } -impl fmt::Display for PayloadInvalid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ProTx(e) => e.fmt(f), - Self::CbTx(e) => e.fmt(f), - Self::MnHardFork(e) => e.fmt(f), - Self::AssetLock(e) => e.fmt(f), - Self::AssetUnlock(e) => e.fmt(f), - Self::Commitment(e) => e.fmt(f), - Self::UnknownType(t) => write!(f, "bad-txns-type: {t}"), - } - } -} - impl Checkable for SpecialPayload { type Error = PayloadInvalid; diff --git a/pkgs/primitives/src/payload/quorum.rs b/pkgs/primitives/src/payload/quorum.rs index 05c93383..4e9f8d38 100644 --- a/pkgs/primitives/src/payload/quorum.rs +++ b/pkgs/primitives/src/payload/quorum.rs @@ -149,20 +149,13 @@ impl BaseCodec for FinalCommitment { } /// Final commitment validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum CommitmentInvalid { /// `bad-qc-quorum-index` + #[error("bad-qc-quorum-index")] BadQuorumIndex, } -impl fmt::Display for CommitmentInvalid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadQuorumIndex => write!(f, "bad-qc-quorum-index"), - } - } -} - impl Checkable for FinalCommitment { type Error = CommitmentInvalid; diff --git a/pkgs/primitives/src/transaction.rs b/pkgs/primitives/src/transaction.rs index 02fa3d25..a2d51b65 100644 --- a/pkgs/primitives/src/transaction.rs +++ b/pkgs/primitives/src/transaction.rs @@ -128,53 +128,46 @@ impl fmt::Display for TxOut { } /// Transaction validation failure. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Unencodable)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Unencodable)] pub enum TxInvalid { /// `bad-txns-vin-empty` + #[error("bad-txns-vin-empty")] EmptyInputs, /// `bad-txns-vout-empty` + #[error("bad-txns-vout-empty")] EmptyOutputs, /// `bad-txns-oversize` + #[error("bad-txns-oversize: {size} bytes")] Oversize { size: usize }, /// `bad-txns-payload-oversize` + #[error("bad-txns-payload-oversize: {size} bytes")] PayloadOversize { size: usize }, /// `bad-txns-vout-toolarge` + #[error("bad-txns-vout-toolarge: output {index} value {value}")] OutputTooLarge { index: usize, value: u64 }, /// `bad-txns-txouttotal-toolarge` + #[error("bad-txns-txouttotal-toolarge: {total}")] OutputTotalTooLarge { total: u64 }, /// `bad-txns-inputs-duplicate` + #[error("bad-txns-inputs-duplicate: {outpoint}")] DuplicateInputs { outpoint: OutPoint }, /// `bad-cb-length` + #[error("bad-cb-length: {len}")] BadCoinbaseScriptLength { len: usize }, /// `bad-txns-prevout-null` + #[error("bad-txns-prevout-null: input {index}")] NullPrevout { index: usize }, /// `bad-txns-payload-not-allowed` + #[error("bad-txns-payload-not-allowed")] PayloadNotAllowed, /// `bad-txns-payload-decode` + #[error("bad-txns-payload-decode: {0}")] PayloadDecode(PayloadError), /// `bad-txns-payload-check` + #[error("bad-txns-payload-check: {0}")] PayloadCheck(PayloadInvalid), } -impl fmt::Display for TxInvalid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::EmptyInputs => write!(f, "bad-txns-vin-empty"), - Self::EmptyOutputs => write!(f, "bad-txns-vout-empty"), - Self::Oversize { size } => write!(f, "bad-txns-oversize: {size} bytes"), - Self::PayloadOversize { size } => write!(f, "bad-txns-payload-oversize: {size} bytes"), - Self::OutputTooLarge { index, value } => write!(f, "bad-txns-vout-toolarge: output {index} value {value}"), - Self::OutputTotalTooLarge { total } => write!(f, "bad-txns-txouttotal-toolarge: {total}"), - Self::DuplicateInputs { outpoint } => write!(f, "bad-txns-inputs-duplicate: {outpoint}"), - Self::BadCoinbaseScriptLength { len } => write!(f, "bad-cb-length: {len}"), - Self::NullPrevout { index } => write!(f, "bad-txns-prevout-null: input {index}"), - Self::PayloadNotAllowed => write!(f, "bad-txns-payload-not-allowed"), - Self::PayloadDecode(e) => write!(f, "bad-txns-payload-decode: {e}"), - Self::PayloadCheck(e) => write!(f, "bad-txns-payload-check: {e}"), - } - } -} - /// A Dash transaction. /// /// The wire format packs `version` (i16) and `tx_type` (u16) into a single diff --git a/pkgs/primitives/src/types/netaddr.rs b/pkgs/primitives/src/types/netaddr.rs index 29b9b9c0..17d662d6 100644 --- a/pkgs/primitives/src/types/netaddr.rs +++ b/pkgs/primitives/src/types/netaddr.rs @@ -11,8 +11,6 @@ use crate::hash_impl; use dash_types::type_id::TypeId; use dash_types::{enum_map, impl_num}; -use core::fmt; - enum_map! { /// Network address type (BIP155). #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, TypeId)] @@ -50,19 +48,25 @@ impl NetworkType { } /// Network address validation error. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, thiserror::Error)] pub enum NetAddrError { /// Numeric value outside structural bounds. + #[error("value 0x{value:02x} out of range")] BadRange { /// The out-of-range value. value: u8, }, /// Invalid character in encoded address. + #[error("invalid character 0x{byte:02x}")] BadChar { /// The offending byte. byte: u8, }, /// Checksum mismatch. + #[error( + "checksum mismatch: expected {:02x}{:02x}, got {:02x}{:02x}", + .expected[0], .expected[1], .actual[0], .actual[1] + )] BadChecksum { /// Expected checksum bytes. expected: [u8; 2], @@ -70,11 +74,13 @@ pub enum NetAddrError { actual: [u8; 2], }, /// Encoding error at a byte position. + #[error("encoding error at position {pos}")] BadEncode { /// Byte offset of the error. pos: usize, }, /// Unexpected address length. + #[error("expected {expected} bytes, got {actual}")] BadLen { /// Expected byte count. expected: usize, @@ -82,60 +88,25 @@ pub enum NetAddrError { actual: usize, }, /// Unsupported address version byte. + #[error("unsupported version {version}")] BadVersion { /// The version byte. version: u8, }, /// Port outside u16 range or zero. + #[error("invalid port {port}")] BadPort { /// The invalid port value. port: u16, }, /// Address type not representable in target format. + #[error("{network} address type not supported")] AddrTooNew { /// The incompatible network type. network: NetworkType, }, } -impl fmt::Display for NetAddrError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadRange { value } => { - write!(f, "value 0x{value:02x} out of range") - } - Self::BadChar { byte } => { - write!(f, "invalid character 0x{byte:02x}") - } - Self::BadChecksum { expected, actual } => { - write!( - f, - "checksum mismatch: expected {:02x}{:02x}, got {:02x}{:02x}", - expected[0], expected[1], actual[0], actual[1] - ) - } - Self::BadEncode { pos } => { - write!(f, "encoding error at position {pos}") - } - Self::BadLen { expected, actual } => { - write!(f, "expected {expected} bytes, got {actual}") - } - Self::BadVersion { version } => { - write!(f, "unsupported version {version}") - } - Self::BadPort { port } => { - write!(f, "invalid port {port}") - } - Self::AddrTooNew { network } => { - write!(f, "{network} address type not supported") - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for NetAddrError {} - /// Returns `true` when the port is on the blocklist. pub fn is_bad_port(port: u16) -> bool { if (1..=1023).contains(&port) { diff --git a/pkgs/primitives/src/types/netinfo.rs b/pkgs/primitives/src/types/netinfo.rs index 27730abf..e2776d7f 100644 --- a/pkgs/primitives/src/types/netinfo.rs +++ b/pkgs/primitives/src/types/netinfo.rs @@ -84,26 +84,31 @@ impl_num!(NIEntryCode, u8); hash_impl!(NIEntryCode); /// Network info validation error. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, thiserror::Error)] pub enum NIError { /// Address failed validation. + #[error("invalid address: {error}")] BadAddr { /// The underlying address error. error: NetAddrError, }, /// Port is zero or invalid for context. + #[error("invalid port {port}")] BadPort { /// The invalid port value. port: u16, }, /// Entry or address type not valid for this purpose. + #[error("unsupported entry type {entry_type}")] BadType { /// The offending entry type byte. entry_type: u8, }, /// Duplicate address:port within the structure. + #[error("duplicate entry")] Duplicate, /// Too many entries or purpose groups. + #[error("too many entries: {count} exceeds limit {max}")] MaxLimit { /// Actual count. count: usize, @@ -111,29 +116,10 @@ pub enum NIError { max: usize, }, /// Structural integrity violation. + #[error("malformed structure")] Malformed, } -impl fmt::Display for NIError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadAddr { error } => write!(f, "invalid address: {error}"), - Self::BadPort { port } => write!(f, "invalid port {port}"), - Self::BadType { entry_type } => { - write!(f, "unsupported entry type {entry_type}") - } - Self::Duplicate => f.write_str("duplicate entry"), - Self::MaxLimit { count, max } => { - write!(f, "too many entries: {count} exceeds limit {max}") - } - Self::Malformed => f.write_str("malformed structure"), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for NIError {} - /// A single network info entry within a purpose group. #[derive(Clone, Debug, Eq, Hash, PartialEq, TypeId)] #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] From 45f138d9298dde08f19002904a08500d4d1f2244 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 11 Oct 2026 19:47:53 +0530 Subject: [PATCH 14/23] pkc%refac: derive error enums with `thiserror` --- Cargo.lock | 1 + docs/samples/Cargo.lock | 1 + pkgs/pkc/Cargo.toml | 1 + pkgs/pkc/src/bls/error.rs | 52 ++++++++++++++----------------------- pkgs/pkc/src/ecdsa/error.rs | 30 +++++++-------------- pkgs/pkc/src/eddsa/error.rs | 19 +++----------- 6 files changed, 35 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9aefdef8..2f538cbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,6 +514,7 @@ dependencies = [ "serde", "sha2", "subtle", + "thiserror", "zeroize", ] diff --git a/docs/samples/Cargo.lock b/docs/samples/Cargo.lock index 4108bb74..70618304 100644 --- a/docs/samples/Cargo.lock +++ b/docs/samples/Cargo.lock @@ -139,6 +139,7 @@ dependencies = [ "hex-conservative 1.3.0", "serde", "subtle", + "thiserror", "zeroize", ] diff --git a/pkgs/pkc/Cargo.toml b/pkgs/pkc/Cargo.toml index 353986b1..cfa707ca 100644 --- a/pkgs/pkc/Cargo.toml +++ b/pkgs/pkc/Cargo.toml @@ -54,6 +54,7 @@ serde = { workspace = true, features = [ ], optional = true } sha2 = { version = "0.11", default-features = false, optional = true } subtle = { workspace = true } +thiserror = { workspace = true } zeroize = { workspace = true, features = ["alloc", "derive"] } [dev-dependencies] diff --git a/pkgs/pkc/src/bls/error.rs b/pkgs/pkc/src/bls/error.rs index 7a22277e..9576ff5e 100644 --- a/pkgs/pkc/src/bls/error.rs +++ b/pkgs/pkc/src/bls/error.rs @@ -6,76 +6,64 @@ //! Error type for BLS operations. -use core::fmt; - /// Errors produced by BLS operations. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)] pub enum BlsError { /// paired input counts do not match + #[error("paired input counts differ")] CountMismatch, /// repeated message in a distinct-message aggregate + #[error("repeated message in a distinct-message aggregate")] DuplicateMessage, /// duplicate share id in recovery set + #[error("duplicate share id in recovery set")] DuplicateShareId, /// no items provided for aggregation + #[error("no items provided for aggregation")] EmptyAggregation, /// recipient index past the end of the message + #[error("recipient index past the end of the message")] IndexOutOfRange, /// recipient index above the supported maximum + #[error("recipient index above the supported maximum")] IndexTooLarge, /// fewer than two master keys to evaluate a share + #[error("fewer than two master keys to evaluate a share")] InsufficientCoefficients, /// not enough shares to recover + #[error("not enough shares to recover")] InsufficientShares, /// ciphertext is empty or not a whole number of cipher blocks + #[error("ciphertext is empty or not a whole number of cipher blocks")] InvalidCiphertextLength, /// initialisation vector seed is all zeroes + #[error("initialisation vector seed is all zeroes")] InvalidIvSeed, /// input keying material is too short (need >= 32 bytes) + #[error("input keying material too short")] InvalidKeyMaterial, /// plaintext is empty or not a whole number of cipher blocks + #[error("plaintext is empty or not a whole number of cipher blocks")] InvalidPlaintextLength, /// public key bytes are not a valid G1 point + #[error("invalid public key bytes")] InvalidPublicKey, /// secret key bytes are not a valid scalar + #[error("invalid secret key bytes")] InvalidSecretKey, /// signature bytes are not a valid G2 point + #[error("invalid signature bytes")] InvalidSignature, /// threshold is below 2, exceeds id count or no ids supplied + #[error("threshold is below 2, exceeds id count or no ids supplied")] InvalidThreshold, /// tweak is not below the group order, or the result is the identity + #[error("tweak is not below the group order, or the result is the identity")] InvalidTweak, /// signature verification failed + #[error("signature verification failed")] VerifyFailed, /// input reduces to zero in the scalar field + #[error("input reduces to zero in the scalar field")] ZeroScalar, } - -impl fmt::Display for BlsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::CountMismatch => write!(f, "paired input counts differ"), - Self::DuplicateMessage => write!(f, "repeated message in a distinct-message aggregate"), - Self::DuplicateShareId => write!(f, "duplicate share id in recovery set"), - Self::EmptyAggregation => write!(f, "no items provided for aggregation"), - Self::IndexOutOfRange => write!(f, "recipient index past the end of the message"), - Self::IndexTooLarge => write!(f, "recipient index above the supported maximum"), - Self::InsufficientCoefficients => write!(f, "fewer than two master keys to evaluate a share"), - Self::InsufficientShares => write!(f, "not enough shares to recover"), - Self::InvalidCiphertextLength => write!(f, "ciphertext is empty or not a whole number of cipher blocks"), - Self::InvalidIvSeed => write!(f, "initialisation vector seed is all zeroes"), - Self::InvalidKeyMaterial => write!(f, "input keying material too short"), - Self::InvalidPlaintextLength => write!(f, "plaintext is empty or not a whole number of cipher blocks"), - Self::InvalidPublicKey => write!(f, "invalid public key bytes"), - Self::InvalidSecretKey => write!(f, "invalid secret key bytes"), - Self::InvalidSignature => write!(f, "invalid signature bytes"), - Self::InvalidThreshold => write!(f, "threshold is below 2, exceeds id count or no ids supplied"), - Self::InvalidTweak => write!(f, "tweak is not below the group order, or the result is the identity"), - Self::VerifyFailed => write!(f, "signature verification failed"), - Self::ZeroScalar => write!(f, "input reduces to zero in the scalar field"), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for BlsError {} diff --git a/pkgs/pkc/src/ecdsa/error.rs b/pkgs/pkc/src/ecdsa/error.rs index d516ee1a..6f0c773d 100644 --- a/pkgs/pkc/src/ecdsa/error.rs +++ b/pkgs/pkc/src/ecdsa/error.rs @@ -6,43 +6,31 @@ //! Error types for secp256k1 operations. -use core::fmt; - /// Errors produced by secp256k1 operations. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, thiserror::Error)] pub enum EcdsaError { /// public key bytes are not a valid curve point + #[error("public key bytes are not a valid curve point")] InvalidPublicKey, /// recovery id is out of range (must be 0..4) + #[error("recovery id out of range (must be 0..4)")] InvalidRecoveryId, /// secret key bytes are not a valid scalar + #[error("secret key bytes are not a valid scalar")] InvalidSecretKey, /// signature bytes are malformed + #[error("signature bytes are malformed")] InvalidSignature, /// tweak is not below the order, or the result is zero or infinity + #[error("tweak is not below the order, or the result is zero or infinity")] InvalidTweak, /// DER-encoded private key has invalid structure + #[error("DER-encoded private key has invalid structure")] MalformedDer, /// recovery failed; no valid public key for this signature and message + #[error("recovery failed; no valid public key")] RecoveryFailed, /// signature verification failed + #[error("signature verification failed")] VerifyFailed, } - -impl fmt::Display for EcdsaError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InvalidPublicKey => write!(f, "public key bytes are not a valid curve point"), - Self::InvalidRecoveryId => write!(f, "recovery id out of range (must be 0..4)"), - Self::InvalidSecretKey => write!(f, "secret key bytes are not a valid scalar"), - Self::InvalidSignature => write!(f, "signature bytes are malformed"), - Self::InvalidTweak => write!(f, "tweak is not below the order, or the result is zero or infinity"), - Self::MalformedDer => write!(f, "DER-encoded private key has invalid structure"), - Self::RecoveryFailed => write!(f, "recovery failed; no valid public key"), - Self::VerifyFailed => write!(f, "signature verification failed"), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for EcdsaError {} diff --git a/pkgs/pkc/src/eddsa/error.rs b/pkgs/pkc/src/eddsa/error.rs index 2b340add..b4cba18c 100644 --- a/pkgs/pkc/src/eddsa/error.rs +++ b/pkgs/pkc/src/eddsa/error.rs @@ -6,26 +6,13 @@ //! Errors types for Ed25519 operations. -use core::fmt; - /// Errors produced by Ed25519 operations. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, thiserror::Error)] pub enum EddsaError { /// public key bytes are not a usable curve point + #[error("public key bytes are not a usable curve point")] InvalidPublicKey, /// signature verification failed + #[error("signature verification failed")] VerifyFailed, } - -impl fmt::Display for EddsaError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let text = match self { - EddsaError::InvalidPublicKey => "public key bytes are not a usable curve point", - EddsaError::VerifyFailed => "signature verification failed", - }; - f.write_str(text) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for EddsaError {} From a7fcb6a341a32ad1d22fd31b6b0d50cbe25edbe2 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:55:51 +0530 Subject: [PATCH 15/23] types%refac: derive error enums with `thiserror` --- Cargo.lock | 1 + docs/samples/Cargo.lock | 1 + pkgs/types/Cargo.toml | 1 + pkgs/types/src/codec.rs | 61 ++++++++--------------------------------- pkgs/types/src/hex.rs | 21 +++----------- 5 files changed, 19 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f538cbd..e1de6a45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -579,6 +579,7 @@ dependencies = [ "rstest", "serde", "subtle", + "thiserror", "zeroize", ] diff --git a/docs/samples/Cargo.lock b/docs/samples/Cargo.lock index 70618304..8f133b7f 100644 --- a/docs/samples/Cargo.lock +++ b/docs/samples/Cargo.lock @@ -221,6 +221,7 @@ dependencies = [ "hex-conservative 1.3.0", "serde", "subtle", + "thiserror", "zeroize", ] diff --git a/pkgs/types/Cargo.toml b/pkgs/types/Cargo.toml index 758d2c52..27062d12 100644 --- a/pkgs/types/Cargo.toml +++ b/pkgs/types/Cargo.toml @@ -60,6 +60,7 @@ cfg-if = "1" dash-types-marker = { version = "0.1.0-beta", path = "marker", optional = true } hex-conservative = { workspace = true } subtle = { workspace = true } +thiserror = { workspace = true } serde = { workspace = true, features = [ "derive", "alloc", diff --git a/pkgs/types/src/codec.rs b/pkgs/types/src/codec.rs index 3cbe0426..16e9e00f 100644 --- a/pkgs/types/src/codec.rs +++ b/pkgs/types/src/codec.rs @@ -11,15 +11,15 @@ use crate::type_id::TypeId; use crate::{CompactSize, Hashable}; use core::convert::Infallible; -use core::fmt; /// Maximum bytes to pre-allocate per batch when deserializing vectors. const MAX_VECTOR_ALLOCATE: usize = 5_000_000; /// An error encountered during consensus decoding. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)] pub enum DecodeError { /// A decoded field has an invalid byte length. + #[error("invalid length: expected one of {expected:?}, got {actual}")] BadLen { /// Acceptable lengths. expected: Vec, @@ -27,8 +27,10 @@ pub enum DecodeError { actual: usize, }, /// Decode validation error. - DecError(E), + #[error("decode validation: {0}")] + DecError(#[source] E), /// CompactSize value exceeds the allowed limit. + #[error("compact size value {value} exceeds limit {limit}")] CompactSizeExceedsLimit { /// The configured limit. limit: usize, @@ -36,6 +38,7 @@ pub enum DecodeError { value: u64, }, /// Not enough bytes remaining in the cursor. + #[error("unexpected eof: needed {needed} bytes, {remaining} remaining")] Eof { /// Bytes needed for the read. needed: usize, @@ -43,8 +46,10 @@ pub enum DecodeError { remaining: usize, }, /// Decoded bytes are not valid UTF-8. + #[error("invalid utf-8 in string")] InvalidUtf8, /// A decoded value does not match any expected value. + #[error("invalid value: expected one of {expected:?}, got {actual}")] InvalidValue { /// Acceptable values. expected: Vec, @@ -52,11 +57,13 @@ pub enum DecodeError { actual: u64, }, /// CompactSize encoding is not minimal. + #[error("non-minimal compact size encoding for value {value}")] NonMinimalCompactSize { /// The decoded value that was not minimally encoded. value: u64, }, /// Unconsumed bytes remain after decoding. + #[error("{remaining} trailing bytes after decode")] TrailingBytes { /// Number of bytes left over. remaining: usize, @@ -79,43 +86,6 @@ impl DecodeError { } } -impl fmt::Display for DecodeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadLen { expected, actual } => { - write!(f, "invalid length: expected one of {expected:?}, got {actual}") - } - Self::DecError(e) => write!(f, "decode validation: {e}"), - Self::CompactSizeExceedsLimit { limit, value } => { - write!(f, "compact size value {value} exceeds limit {limit}",) - } - Self::Eof { needed, remaining } => { - write!(f, "unexpected eof: needed {needed} bytes, {remaining} remaining",) - } - Self::InvalidUtf8 => write!(f, "invalid utf-8 in string"), - Self::InvalidValue { expected, actual } => { - write!(f, "invalid value: expected one of {expected:?}, got {actual}") - } - Self::NonMinimalCompactSize { value } => { - write!(f, "non-minimal compact size encoding for value {value}",) - } - Self::TrailingBytes { remaining } => { - write!(f, "{remaining} trailing bytes after decode") - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for DecodeError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::DecError(e) => Some(e), - _ => None, - } - } -} - /// Checks that `data` has at least `n` bytes remaining. /// /// # Errors @@ -382,17 +352,10 @@ mod tests { use rstest::*; - use core::fmt; - - #[derive(Clone, Debug, Eq, PartialEq)] + #[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)] + #[error("sample text")] struct SampleError; - impl fmt::Display for SampleError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "sample text") - } - } - #[rstest] fn dec_error_displays_the_inner_error() { let err: DecodeError = DecodeError::DecError(SampleError); diff --git a/pkgs/types/src/hex.rs b/pkgs/types/src/hex.rs index d7d2a212..914dae6b 100644 --- a/pkgs/types/src/hex.rs +++ b/pkgs/types/src/hex.rs @@ -11,12 +11,14 @@ use hex_conservative::{BytesToHexIter, Case, HexSliceToBytesIter}; use core::fmt::{self, Write as _}; /// Error returned when parsing a hex string fails. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] #[non_exhaustive] pub enum ParseHexError { /// The hex string has an odd number of characters. + #[error("hex string has odd length")] OddLength, /// The hex character count does not match the expected length. + #[error("expected {expected} hex chars, got {got}")] InvalidLength { /// Hex characters the target type accepts, twice its byte width. expected: usize, @@ -24,25 +26,10 @@ pub enum ParseHexError { got: usize, }, /// A non-hex character was encountered. + #[error("invalid hex character: {0:#04x}")] InvalidChar(u8), } -impl fmt::Display for ParseHexError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::OddLength => write!(f, "hex string has odd length"), - Self::InvalidLength { expected, got } => { - write!(f, "expected {expected} hex chars, got {got}") - } - Self::InvalidChar(c) => { - write!(f, "invalid hex character: {:#04x}", c) - } - } - } -} - -impl core::error::Error for ParseHexError {} - /// Decodes `2 * N` hex digits, reversing the byte order with `rev` enabled. /// /// # Errors From 1c4063775f186b439a6dda19f72ae3b9e17358ff Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 02:56:26 +0530 Subject: [PATCH 16/23] sdk%build: move verb configs to `.cargo`, tune `release`, `nextest` opts --- deny.toml => .cargo/deny.toml | 0 .cargo/nextest.toml | 8 ++++++++ .github/workflows/build_nightly.yml | 8 ++++---- Cargo.toml | 7 +++++-- Justfile | 6 +++--- maint/README.md | 2 +- maint/nextest.toml | 5 ----- 7 files changed, 21 insertions(+), 15 deletions(-) rename deny.toml => .cargo/deny.toml (100%) create mode 100644 .cargo/nextest.toml delete mode 100644 maint/nextest.toml diff --git a/deny.toml b/.cargo/deny.toml similarity index 100% rename from deny.toml rename to .cargo/deny.toml diff --git a/.cargo/nextest.toml b/.cargo/nextest.toml new file mode 100644 index 00000000..a5a52594 --- /dev/null +++ b/.cargo/nextest.toml @@ -0,0 +1,8 @@ +[profile.default] +fail-fast = false + +[profile.default.junit] +path = "junit.xml" + +[profile.fail-fast] +fail-fast = true diff --git a/.github/workflows/build_nightly.yml b/.github/workflows/build_nightly.yml index 8aec2f5a..4ee9b7f4 100644 --- a/.github/workflows/build_nightly.yml +++ b/.github/workflows/build_nightly.yml @@ -97,7 +97,7 @@ jobs: - name: Test package if: matrix.config.name != 'full' run: | - cargo nextest run --config-file maint/nextest.toml --profile=ci -p ${{ inputs.package }} ${{ matrix.config.args }} + cargo nextest run --config-file .cargo/nextest.toml -p ${{ inputs.package }} ${{ matrix.config.args }} cargo test --doc -p ${{ inputs.package }} ${{ matrix.config.args }} - name: Check formatting @@ -108,7 +108,7 @@ jobs: if: matrix.config.name == 'full' run: | cargo llvm-cov clean --workspace - cargo llvm-cov --no-report nextest --config-file maint/nextest.toml --profile=ci ${{ matrix.config.args }} --package ${{ inputs.package }} + cargo llvm-cov --no-report nextest --config-file .cargo/nextest.toml ${{ matrix.config.args }} --package ${{ inputs.package }} cargo llvm-cov report --lcov --output-path lcov.info cargo test --doc -p ${{ inputs.package }} ${{ matrix.config.args }} @@ -121,10 +121,10 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Upload test results to Codecov - if: ${{ !cancelled() && matrix.config.name == 'full' && env.HAS_CODECOV == 'true' && hashFiles('target/nextest/ci/junit.xml') != '' }} + if: ${{ !cancelled() && matrix.config.name == 'full' && env.HAS_CODECOV == 'true' && hashFiles('target/nextest/default/junit.xml') != '' }} uses: codecov/codecov-action@v5 with: - files: target/nextest/ci/junit.xml + files: target/nextest/default/junit.xml report_type: test_results flags: ${{ inputs.package }} token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index 88a8784a..0186a061 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,10 +57,13 @@ repository = "https://github.com/dashpay/base-sdk" rust-version = "1.89.0" [profile.release] -lto = true +codegen-units = 1 +debug = false +incremental = false +lto = "fat" opt-level = 3 panic = "abort" -strip = true +strip = "symbols" [profile.dev] codegen-units = 1 diff --git a/Justfile b/Justfile index b20ef31e..72e3ddf2 100644 --- a/Justfile +++ b/Justfile @@ -6,7 +6,7 @@ python := if os() == "windows" { "python" } else { "python3" } # (see https://packages.debian.org/trixie/just) export RUSTDOCFLAGS := "-D warnings" -nextest := "cargo nextest run --config-file maint/nextest.toml --profile=ci" +nextest := "cargo nextest run --config-file .cargo/nextest.toml" [private] default: @@ -20,7 +20,7 @@ build: cargo build --workspace --no-default-features comb: - {{ python }} contrib/git_filter.py --fast-fail develop HEAD -- just test lint + {{ python }} contrib/git_filter.py --fast-fail --env NEXTEST_PROFILE=fail-fast develop HEAD -- just test lint lint: @just --fmt --check @@ -38,7 +38,7 @@ test: cargo clippy --all-targets --no-default-features -- -D warnings cargo clippy --all-targets --features full -- -D warnings cargo clippy --all-targets --features full,portable -- -D warnings - {{ nextest }} --fail-fast --workspace --features full + {{ nextest }} --workspace --features full cargo test --doc --workspace --features full cargo doc --workspace --no-deps --features full cargo bench --workspace --features full --no-run diff --git a/maint/README.md b/maint/README.md index 94a5fc9c..0fa50691 100644 --- a/maint/README.md +++ b/maint/README.md @@ -15,7 +15,7 @@ use [`lint_all.py`](./lint_all.py). | Name | Purpose | Verbs | Depends on | | ---- | ------- | ---------- | ---------- | | [`lint_commit.py`](./lint/lint_commit.py) | Lint commit names against [`unconv.toml`](./unconv.toml) | `run` | `git` | -| [`lint_cargo.py`](./lint/lint_cargo.py) | Deny dependencies per [`deny.toml`](../deny.toml), check/format TOML files against [`taplo.toml`](./taplo.toml) | `check` , `apply`, `apply-all` | `cargo-deny` (deny dependencies), `taplo` (TOML formatting) | +| [`lint_cargo.py`](./lint/lint_cargo.py) | Deny dependencies per [`deny.toml`](../.cargo/deny.toml), check/format TOML files against [`taplo.toml`](./taplo.toml) | `check` , `apply`, `apply-all` | `cargo-deny` (deny dependencies), `taplo` (TOML formatting) | | [`lint_codeql.py`](./lint/lint_codeql.py) | Query Rust sources against [`maint/codeql/rust/*.ql`](./codeql/rust) | `check`, `apply`, `apply-all`, `run`, `run-all` | `codeql`, `rustc` | | [`lint_javascript.py`](./lint/lint_javascript.py) | Lint Javascript sources against [`eslint.config.mjs`](js/eslint.config.mjs) | *None* | `npx` (part of Node.js), `eslint` (auto-retrieved by script) | | [`lint_markdown.py`](./lint/lint_markdown.py) | Lint Markdown [documentation](../docs/dev/about_docs.md) | *None* | `pymarkdownlnt` | diff --git a/maint/nextest.toml b/maint/nextest.toml deleted file mode 100644 index 6a109e17..00000000 --- a/maint/nextest.toml +++ /dev/null @@ -1,5 +0,0 @@ -[profile.ci] -fail-fast = false - -[profile.ci.junit] -path = "junit.xml" From 4aa63863a4245d7879b21699a576457207ddff85 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:31:21 +0530 Subject: [PATCH 17/23] sdk%feat(lint): add `cargo mutants` to stress unit test coverage --- .cargo/mutants.toml | 12 ++++++++++++ Cargo.toml | 8 ++++++++ Justfile | 9 +++++++++ contrib/README.md | 7 ++++--- contrib/nix/mods/nixpkgs.nix | 1 + 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 .cargo/mutants.toml diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml new file mode 100644 index 00000000..3cea5700 --- /dev/null +++ b/.cargo/mutants.toml @@ -0,0 +1,12 @@ +additional_cargo_test_args = [ + "--config-file", + ".cargo/nextest.toml", + "--profile", + "fail-fast", +] +build_timeout_multiplier = 5.0 +exclude_globs = ["pkgs/dev/**", "pkgs/pow/**"] +features = ["full"] +gitignore = true +profile = "mutants" +test_tool = "nextest" diff --git a/Cargo.toml b/Cargo.toml index 0186a061..9947874f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,14 @@ opt-level = 3 panic = "abort" strip = "symbols" +[profile.mutants] +inherits = "release" +codegen-units = 16 +debug-assertions = true +incremental = true +lto = false +overflow-checks = true + [profile.dev] codegen-units = 1 overflow-checks = true diff --git a/Justfile b/Justfile index 72e3ddf2..bbb851b9 100644 --- a/Justfile +++ b/Justfile @@ -28,6 +28,15 @@ lint: {{ python }} maint/lint/lint_codeql.py check {{ python }} maint/lint/lint_codeql.py run --lang=rust --with-suite=rust-security-and-quality +[positional-arguments] +[unix] +mutants *args: + cargo mutants "$@" + +[windows] +mutants *args: + cargo mutants {{ args }} + sh: nix develop ./contrib/nix#dev diff --git a/contrib/README.md b/contrib/README.md index 05327e54..8631180d 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -33,6 +33,7 @@ source .venv/bin/activate packages need to be additionally sourced. * [cargo-deny](https://github.com/EmbarkStudios/cargo-deny) +* [cargo-mutants](https://mutants.rs) * [cargo-nextest](https://github.com/nextest-rs/nextest) * [CodeQL 2.27 or higher](https://github.com/github/codeql-cli-binaries/releases) * [Git](https://git-scm.com/install/) @@ -49,7 +50,7 @@ packages need to be additionally sourced. > ([source](https://docs.brew.sh/FAQ#what-does-keg-only-mean)). ```bash -brew install cargo-deny cargo-nextest codeql git just just-lsp node@24 +brew install cargo-deny cargo-mutants cargo-nextest codeql git just just-lsp node@24 ``` ### Linux/WSL @@ -60,10 +61,10 @@ you may need to update your shell to add your installation path to `PATH` so tha lint script. Neither CodeQL nor taplo are available in official Debian or Fedora repositories and must be sourced per vendor -guidance. `cargo-nextest` and `just-lsp` are likewise best installed as Rust binary crates. +guidance. `cargo-mutants`, `cargo-nextest` and `just-lsp` are likewise best installed as Rust binary crates. ```bash -cargo install --locked cargo-nextest just-lsp +cargo install --locked cargo-mutants cargo-nextest just-lsp ``` #### Installing `taplo` diff --git a/contrib/nix/mods/nixpkgs.nix b/contrib/nix/mods/nixpkgs.nix index da28cda9..2f55fd44 100644 --- a/contrib/nix/mods/nixpkgs.nix +++ b/contrib/nix/mods/nixpkgs.nix @@ -26,6 +26,7 @@ in packages = [ pkgs.cargo-deny pkgs.cargo-llvm-cov + pkgs.cargo-mutants pkgs.cargo-nextest pkgs.git pkgs.just From 7d59e04dcfe915dce4db310504b485556cd24aaf Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:45:27 +0530 Subject: [PATCH 18/23] sdk%lint(deny): track advisories from `rustsec/advisory-db` --- .cargo/deny.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cargo/deny.toml b/.cargo/deny.toml index 48a924c7..68402d42 100644 --- a/.cargo/deny.toml +++ b/.cargo/deny.toml @@ -2,6 +2,9 @@ all-features = true [advisories] +db-path = "$CARGO_HOME/advisory-dbs" +db-urls = ["https://github.com/rustsec/advisory-db"] +version = 2 yanked = "deny" [bans] From 15dc12f21a119af92d95a40b22917fa0e385e1f4 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:49:01 +0530 Subject: [PATCH 19/23] sdk%build: bump dev `codegen-units`, avoid `cargo bench` to skip fat LTO --- .github/workflows/build_nightly.yml | 2 +- Cargo.toml | 2 +- Justfile | 2 +- pkgs/dev/Cargo.toml | 4 ++++ pkgs/num/Cargo.toml | 3 +++ pkgs/p2p_core/Cargo.toml | 3 +++ pkgs/params/Cargo.toml | 3 +++ pkgs/pkc/Cargo.toml | 3 +++ pkgs/pow/Cargo.toml | 3 +++ pkgs/primitives/Cargo.toml | 3 +++ pkgs/script/Cargo.toml | 3 +++ pkgs/types/Cargo.toml | 3 +++ pkgs/types/marker/Cargo.toml | 1 + 13 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_nightly.yml b/.github/workflows/build_nightly.yml index 4ee9b7f4..8ae09fb2 100644 --- a/.github/workflows/build_nightly.yml +++ b/.github/workflows/build_nightly.yml @@ -130,4 +130,4 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Sanity check benchmarks - run: cargo bench ${{ matrix.config.args }} --package ${{ inputs.package }} --no-run + run: cargo test ${{ matrix.config.args }} --package ${{ inputs.package }} --benches diff --git a/Cargo.toml b/Cargo.toml index 9947874f..4d288953 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,5 +74,5 @@ lto = false overflow-checks = true [profile.dev] -codegen-units = 1 +codegen-units = 256 overflow-checks = true diff --git a/Justfile b/Justfile index bbb851b9..9b203ff9 100644 --- a/Justfile +++ b/Justfile @@ -50,4 +50,4 @@ test: {{ nextest }} --workspace --features full cargo test --doc --workspace --features full cargo doc --workspace --no-deps --features full - cargo bench --workspace --features full --no-run + cargo test --workspace --features full --benches diff --git a/pkgs/dev/Cargo.toml b/pkgs/dev/Cargo.toml index ed20b4e8..78e2c6b9 100644 --- a/pkgs/dev/Cargo.toml +++ b/pkgs/dev/Cargo.toml @@ -9,9 +9,13 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [[bin]] name = "bsdk-util" path = "src/bin/bsdk_util/main.rs" +bench = false required-features = ["bin"] [features] diff --git a/pkgs/num/Cargo.toml b/pkgs/num/Cargo.toml index 4220421e..963fe70b 100644 --- a/pkgs/num/Cargo.toml +++ b/pkgs/num/Cargo.toml @@ -22,6 +22,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = ["codec"] std = [ diff --git a/pkgs/p2p_core/Cargo.toml b/pkgs/p2p_core/Cargo.toml index 573d11e1..9abb8933 100644 --- a/pkgs/p2p_core/Cargo.toml +++ b/pkgs/p2p_core/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = [] std = [ diff --git a/pkgs/params/Cargo.toml b/pkgs/params/Cargo.toml index b8666a27..3caef4c8 100644 --- a/pkgs/params/Cargo.toml +++ b/pkgs/params/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = [] std = ["bitcoin-primitives/std", "dash-primitives/std", "dash-script/std"] diff --git a/pkgs/pkc/Cargo.toml b/pkgs/pkc/Cargo.toml index cfa707ca..2ee32183 100644 --- a/pkgs/pkc/Cargo.toml +++ b/pkgs/pkc/Cargo.toml @@ -22,6 +22,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [dependencies] aes = { version = "0.9", default-features = false, features = [ "zeroize", diff --git a/pkgs/pow/Cargo.toml b/pkgs/pow/Cargo.toml index 8ac92519..e7704a49 100644 --- a/pkgs/pow/Cargo.toml +++ b/pkgs/pow/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = [] std = [] diff --git a/pkgs/primitives/Cargo.toml b/pkgs/primitives/Cargo.toml index 5cdcffb0..616a2614 100644 --- a/pkgs/primitives/Cargo.toml +++ b/pkgs/primitives/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = [] std = [ diff --git a/pkgs/script/Cargo.toml b/pkgs/script/Cargo.toml index 4d6985ec..391469c6 100644 --- a/pkgs/script/Cargo.toml +++ b/pkgs/script/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = [] std = [ diff --git a/pkgs/types/Cargo.toml b/pkgs/types/Cargo.toml index 27062d12..a30e2ee2 100644 --- a/pkgs/types/Cargo.toml +++ b/pkgs/types/Cargo.toml @@ -22,6 +22,9 @@ license.workspace = true repository.workspace = true rust-version.workspace = true +[lib] +bench = false + [features] default = ["codec"] std = [ diff --git a/pkgs/types/marker/Cargo.toml b/pkgs/types/marker/Cargo.toml index b8dfb2b8..eee07ace 100644 --- a/pkgs/types/marker/Cargo.toml +++ b/pkgs/types/marker/Cargo.toml @@ -18,6 +18,7 @@ repository.workspace = true rust-version.workspace = true [lib] +bench = false proc-macro = true [dependencies] From 9924a19fd7ec1967579752268d20b24d65c38d77 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:16:45 +0530 Subject: [PATCH 20/23] sdk%lint(commit): flesh out `lint_commit`, add AI disclosure to template --- .github/PULL_REQUEST_TEMPLATE.md | 9 ++ docs/dev/about_docs.md | 2 +- docs/preprocess.py | 14 ++-- maint/README.md | 2 +- maint/commit.toml | 61 ++++++++++++++ maint/lint/lint_commit.py | 138 ++++++++++++++++++++----------- maint/unconv.toml | 34 -------- 7 files changed, 168 insertions(+), 92 deletions(-) create mode 100644 maint/commit.toml delete mode 100644 maint/unconv.toml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7d839747..301e725f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -22,4 +22,13 @@ - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional tests - [ ] I have made corresponding changes to the documentation +- [ ] I did not use agentic assistance (i.e. LLMs, AI) except as [disclosed](#agent-disclosure) and can answer questions, + comments and concerns without said assistance. I assume authorship of the code submitted. - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ + +### Agent disclosure + + diff --git a/docs/dev/about_docs.md b/docs/dev/about_docs.md index 5d1c619a..6f8824e0 100644 --- a/docs/dev/about_docs.md +++ b/docs/dev/about_docs.md @@ -130,7 +130,7 @@ This is avoided by using segments from the live codebase, as shown below for inl ````text ```toml - + ``` ```` diff --git a/docs/preprocess.py b/docs/preprocess.py index a8889e02..bd7b6e8e 100644 --- a/docs/preprocess.py +++ b/docs/preprocess.py @@ -505,7 +505,7 @@ def test_section_refuses_an_unclosed_fence(self) -> None: def test_a_literal_fence_shows_the_directive_it_holds(self) -> None: held = [ - "", + "", "", "", "", @@ -527,11 +527,11 @@ def test_a_section_marker_is_a_known_directive(self) -> None: def test_include_nests(self) -> None: with self._scratch( - outer='```toml\n\n```\n', + outer='```toml\n\n```\n', ) as home: out = self._render(f'\n') assert "[include:" not in out - assert "global" in out + assert "namespace" in out def test_include_refuses_a_cycle(self) -> None: import pytest @@ -639,7 +639,7 @@ def test_a_link_is_rebased_in_every_form_it_is_written(self) -> None: ('[a](../README.md "root")', (f'href="{blob}/README.md"', 'title="root"')), ("[a](<../README.md>)", (f'href="{blob}/README.md"',)), - ("[a](../maint/unconv.toml)", (f'href="{blob}/maint/unconv.toml"',)), + ("[a](../maint/commit.toml)", (f'href="{blob}/maint/commit.toml"',)), ) for source, expected in cases: with self._scratch(page=f"{source}\n") as home: @@ -697,10 +697,10 @@ def test_spelling_check_matches_the_stored_name(self) -> None: def test_include_survives_a_fenced_info_string(self) -> None: out = self._render( - '```text\n```markdown\n\n```\n' + '```text\n```markdown\n\n```\n' ) - assert "[include:maint/unconv.toml]" in out - assert "[global]" not in out + assert "[include:maint/commit.toml]" in out + assert "[default]" not in out def test_alert_survives_a_fenced_info_string(self) -> None: out = self._render("```\n```text\n> [!NOTE]\n> Shown.\n```\n") diff --git a/maint/README.md b/maint/README.md index 0fa50691..e2ae40bc 100644 --- a/maint/README.md +++ b/maint/README.md @@ -14,7 +14,7 @@ use [`lint_all.py`](./lint_all.py). | Name | Purpose | Verbs | Depends on | | ---- | ------- | ---------- | ---------- | -| [`lint_commit.py`](./lint/lint_commit.py) | Lint commit names against [`unconv.toml`](./unconv.toml) | `run` | `git` | +| [`lint_commit.py`](./lint/lint_commit.py) | Lint commit names against [`commit.toml`](./commit.toml) | `run` | `git` | | [`lint_cargo.py`](./lint/lint_cargo.py) | Deny dependencies per [`deny.toml`](../.cargo/deny.toml), check/format TOML files against [`taplo.toml`](./taplo.toml) | `check` , `apply`, `apply-all` | `cargo-deny` (deny dependencies), `taplo` (TOML formatting) | | [`lint_codeql.py`](./lint/lint_codeql.py) | Query Rust sources against [`maint/codeql/rust/*.ql`](./codeql/rust) | `check`, `apply`, `apply-all`, `run`, `run-all` | `codeql`, `rustc` | | [`lint_javascript.py`](./lint/lint_javascript.py) | Lint Javascript sources against [`eslint.config.mjs`](js/eslint.config.mjs) | *None* | `npx` (part of Node.js), `eslint` (auto-retrieved by script) | diff --git a/maint/commit.toml b/maint/commit.toml new file mode 100644 index 00000000..7abf1183 --- /dev/null +++ b/maint/commit.toml @@ -0,0 +1,61 @@ +block_authors = [ + # https://github.com/commit-check/commit-check/pull/606 + "138933559+factory-droid[bot]@users.noreply.github.com", + # https://github.com/PrefectHQ/prefect/releases/tag/prefect-github-0.4.3 + "158243242+devin-ai-integration[bot]@users.noreply.github.com", + # https://github.com/arran4/goa4web/pull/3146 + "161369871+google-labs-jules[bot]@users.noreply.github.com", + # https://github.com/github/docs/pull/38568 + "175728472+Copilot@users.noreply.github.com", + # https://github.com/github/docs/pull/38568 + "198982749+Copilot@users.noreply.github.com", + # https://github.com/microsoft/mssql-rs/commit/b758050e + "223556219+Copilot@users.noreply.github.com", + # https://github.com/Aider-AI/aider/blob/5dc9490b/aider/repo.py#L252 + "aider@aider.chat", + # https://ampcode.com/docs/cli/settings + "amp@ampcode.com", + # https://github.com/openai/codex/issues/18095 + "codex@example.com", + # https://github.com/openai/codex/discussions/2807 + "codex@openai.com", + # https://github.com/microsoft/vscode/blob/ff00e788/extensions/git/src/repository.ts#L1505 + "copilot@github.com", + # https://forum.cursor.com/t/co-author-added-without-consent-and-cant-be-turned-off/150096 + "cursoragent@cursor.com", + # https://github.com/Aider-AI/aider/issues/4226 + "noreply@aider.chat", + # https://code.claude.com/docs/en/settings-reference.md + "noreply@anthropic.com", + # https://github.com/openai/codex/blob/a9b08151/codex-rs/ext/git-attribution/src/world_state.rs#L19 + "noreply@openai.com", + # https://docs.openhands.dev/openhands/usage/settings/application-settings + "openhands@all-hands.dev", +] +max_height = 5 +max_width = 73 + +[default] +nouns = [ + "build", + "chore", + "ci", + "doc", + "feat", + "fix", + "lint", + "misc", + "perf", + "refac", + "test", +] + +[namespace.num] +[namespace.p2p_core] +[namespace.params] +[namespace.pkc] +[namespace.pow] +[namespace.primitives] +[namespace.script] +[namespace.sdk] +[namespace.types] diff --git a/maint/lint/lint_commit.py b/maint/lint/lint_commit.py index 42a14e9b..fca246e3 100755 --- a/maint/lint/lint_commit.py +++ b/maint/lint/lint_commit.py @@ -24,14 +24,13 @@ root_dir, ) -# File in the repository root the accepted vocabulary is read from. -CONFIG_FILENAME = "unconv.toml" +# Vocabulary file, read from the nearest directory above this script. +CONFIG_FILENAME = "commit.toml" -# Maximum line width for a commit. -MAX_WIDTH = 73 - -# Maximum number of lines for a commit, subject inclusive. -MAX_HEIGHT = 5 +# Matches the address in a 'Co-authored-by' trailer. +_CO_AUTHOR = re.compile( + r"^co-authored-by:.*<(?P[^>]+)>\s*$", re.IGNORECASE | re.MULTILINE +) # Matches a commit subject, 'namespace%type[(scope)][!]: description'. _PATTERN = re.compile( @@ -42,20 +41,27 @@ r": (?P.+)$" ) -# Config keys that name a setting rather than a namespace. -_RESERVED: frozenset[str] = frozenset({"global"}) + +@dataclass(frozen=True) +class Commit: + message: str + author: str | None = None + committer: str | None = None @dataclass(frozen=True) class NamespaceConfig: - ignore_global: bool = False - types: frozenset[str] = field(default_factory=frozenset) + ignore_default: bool = False + nouns: frozenset[str] = field(default_factory=frozenset) scopes: frozenset[str] = field(default_factory=frozenset) @dataclass(frozen=True) class Config: - global_types: frozenset[str] = field(default_factory=frozenset) + max_width: int + max_height: int + block_authors: frozenset[str] = field(default_factory=frozenset) + default_nouns: frozenset[str] = field(default_factory=frozenset) namespaces: dict[str, NamespaceConfig] = field(default_factory=dict) @classmethod @@ -63,52 +69,75 @@ def load(cls, path: Path) -> Config: with path.open("rb") as f: raw = tomllib.load(f) - global_types = frozenset(raw.get("global", {}).get("types", [])) - namespaces: dict[str, NamespaceConfig] = {} - - for key, value in raw.items(): - if key == "global" or not isinstance(value, dict): - continue - namespaces[key] = NamespaceConfig( - ignore_global=value.get("ignore_global", False), - types=frozenset(value.get("types", [])), + for key in ("max_width", "max_height"): + if not isinstance(raw.get(key), int): + raise ValueError(f"{path}: {key} must be an integer") + default_nouns = frozenset(raw.get("default", {}).get("nouns", [])) + namespaces = { + key: NamespaceConfig( + ignore_default=value.get("ignore_default", False), + nouns=frozenset(value.get("nouns", [])), scopes=frozenset(value.get("scopes", [])), ) - - return cls(global_types=global_types, namespaces=namespaces) + for key, value in raw.get("namespace", {}).items() + } + + return cls( + max_width=raw["max_width"], + max_height=raw["max_height"], + block_authors=frozenset(a.lower() for a in raw.get("block_authors", [])), + default_nouns=default_nouns, + namespaces=namespaces, + ) -def _allowed_types(namespace: str, config: Config) -> frozenset[str]: +def _allowed_nouns(namespace: str, config: Config) -> frozenset[str]: ns = config.namespaces.get(namespace) if ns is None: - return config.global_types - base = frozenset() if ns.ignore_global else config.global_types - return base | ns.types + return config.default_nouns + base = frozenset() if ns.ignore_default else config.default_nouns + return base | ns.nouns -def _lint_shape(lines: list[str]) -> list[str]: +def _lint_shape(lines: list[str], config: Config) -> list[str]: """Return error strings for the width and height of a commit message.""" errors: list[str] = [] - if len(lines) > MAX_HEIGHT: - errors.append(f"{len(lines)} lines tall, over {MAX_HEIGHT}") - wide = [n for n, line in enumerate(lines, 1) if len(line) > MAX_WIDTH] + if len(lines) > config.max_height: + errors.append(f"{len(lines)} lines tall, over {config.max_height}") + wide = [n for n, line in enumerate(lines, 1) if len(line) > config.max_width] if wide: where = ", ".join(str(n) for n in wide) - errors.append(f"line {where} wider than {MAX_WIDTH} characters") + errors.append(f"line {where} wider than {config.max_width} characters") + return errors + + +def _lint_authors(commit: Commit, config: Config) -> list[str]: + """Return error strings for a blocked author, committer or co-author.""" + errors: list[str] = [] + blocked = config.block_authors + roles = (("author", commit.author), ("committer", commit.committer)) + for role, email in roles: + if email is not None and email.lower() in blocked: + errors.append(f"{role} {email!r} is blocked") + for m in _CO_AUTHOR.finditer(commit.message): + email = m.group("email").strip() + if email.lower() in blocked: + errors.append(f"co-author {email!r} is blocked") return errors def _lint_message( - message: str, config: Config, *, title: bool = False + commit: Commit, config: Config, *, title: bool = False ) -> list[str]: """Return error strings for a commit message or, if *title*, a PR title""" - lines = message.strip().splitlines() + lines = commit.message.strip().splitlines() subject = lines[0].strip() if lines else "" if title: return _lint_subject(subject, config) + errors = _lint_authors(commit, config) if not subject or subject.startswith("#") or subject.startswith("Merge "): - return [] - return _lint_subject(subject, config) + _lint_shape(lines) + return errors + return errors + _lint_subject(subject, config) + _lint_shape(lines, config) def _lint_subject(subject: str, config: Config) -> list[str]: @@ -126,12 +155,10 @@ def _lint_subject(subject: str, config: Config) -> list[str]: if not namespace.isascii(): errors.append(f"namespace {namespace!r} contains non-ASCII characters") - if namespace in _RESERVED: - errors.append(f"namespace {namespace!r} is reserved") - elif namespace not in config.namespaces: + if namespace not in config.namespaces: errors.append(f"unknown namespace {namespace!r}") - allowed = _allowed_types(namespace, config) + allowed = _allowed_nouns(namespace, config) if allowed and commit_type.lower() not in allowed: errors.append( f"unknown type {commit_type!r} for namespace {namespace!r}" @@ -171,19 +198,27 @@ def _default_range() -> str: return "HEAD~1..HEAD" -def _messages(*args: str) -> list[str]: - out = git_out(root_dir(), "log", "--no-merges", "--format=%B%x00", *args) - return [m.strip() for m in out.split("\0") if m.strip()] +def _messages(*args: str) -> list[Commit]: + out = git_out( + root_dir(), "log", "--no-merges", "--format=%ae%x00%ce%x00%B%x1e", *args + ) + commits: list[Commit] = [] + for record in out.split("\x1e"): + author, _, rest = record.lstrip("\n").partition("\0") + committer, _, message = rest.partition("\0") + if message.strip(): + commits.append(Commit(message.strip(), author, committer)) + return commits -def _messages_from_commit(ref: str) -> list[str]: +def _messages_from_commit(ref: str) -> list[Commit]: parents = git_out(root_dir(), "rev-list", "--parents", "-n", "1", ref) if len(parents.split()) > 2: return [] return _messages("-1", ref) -def _messages_from_range(git_range: str) -> list[str]: +def _messages_from_range(git_range: str) -> list[Commit]: return _messages(git_range) @@ -231,10 +266,14 @@ def main() -> int: print(f"error: {config_path} is not a file", file=sys.stderr) return 2 - config = Config.load(config_path) + try: + config = Config.load(config_path) + except ValueError as exc: + print(f"error: {exc}", file=sys.stderr) + return 2 if args.m is not None: - messages = [args.m] + messages = [Commit(args.m)] elif args.r is not None: messages = _messages_from_range(args.r) elif args.c is not None: @@ -246,8 +285,9 @@ def main() -> int: results = [(m, _lint_message(m, config, title=title)) for m in messages] failed = False - for message, errors in results: - subject = message.strip().splitlines()[0] if message.strip() else "" + for commit, errors in results: + lines = commit.message.strip().splitlines() + subject = lines[0] if lines else "" if errors: print(f"[FAIL] {subject} (reason: {'; '.join(errors)})") failed = True diff --git a/maint/unconv.toml b/maint/unconv.toml deleted file mode 100644 index 2d5a8cd3..00000000 --- a/maint/unconv.toml +++ /dev/null @@ -1,34 +0,0 @@ -# Permitted regardless of namespace unless overridden -[global] -types = [ - "build", - "chore", - "ci", - "doc", - "feat", - "fix", - "lint", - "misc", - "perf", - "refac", - "test", -] - -# Defined namespaces -[num] - -[p2p_core] - -[params] - -[pkc] - -[pow] - -[primitives] - -[script] - -[sdk] - -[types] From b6454cef93eaf7a25f3720a5188d646f4fa4f42e Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 11:16:03 +0530 Subject: [PATCH 21/23] sdk%feat(contrib): add merge script to preserve PR descriptions --- contrib/git_merge.py | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100755 contrib/git_merge.py diff --git a/contrib/git_merge.py b/contrib/git_merge.py new file mode 100755 index 00000000..fab40a97 --- /dev/null +++ b/contrib/git_merge.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# coding: latin-1 + +# +# Copyright (c) 2026-present, The Dash Core developers +# SPDX-License-Identifier: MIT +# See the accompanying file LICENSE or https://opensource.org/license/MIT +# + +"""Merge a pull request with its commits and description baked in. + +Fetches the pull request from GitHub and merges its head into the checked out +base branch. + +Usage: + + ./contrib/git_merge.py 57 + ./contrib/git_merge.py -R dashpay/base-sdk 57 + ./contrib/git_merge.py -B v0.1.x 57 +""" + +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import tempfile + +from common import ( + RETCODE_ERR, + RETCODE_PASS, + git_out, + git_run, + require_bin, + root_dir, +) + +# Fields read from `gh pr view`. +_PR_FIELDS = "number,title,body,state,isDraft,baseRefName,headRefOid" + +# Fence around the description. +_FENCE = "`````" + + +def _gh_json(root: str, *args: str) -> dict[str, object]: + result = subprocess.run( # noqa: S603 + [require_bin("gh"), *args], + capture_output=True, + check=False, + cwd=root, + encoding="utf-8", + ) + if result.returncode != 0: + raise RuntimeError(f"gh {args[0]}: {result.stderr.strip()}") + return json.loads(result.stdout) + + +def _fetch(root: str, url: str, ref: str) -> str: + """Fetch *ref* from *url* and return the commit it points to.""" + git_out(root, "fetch", "--quiet", url, ref) + return git_out(root, "rev-parse", "FETCH_HEAD^{commit}") + + +def _message(pr: dict[str, object], commits: list[tuple[str, ...]]) -> str: + """Build the merge commit message for *pr*.""" + lines = [f"{pr['title']} (#{pr['number']})", ""] + lines += [ + f"* [{h[:8]}] {subject} ({author})" for h, subject, author in commits + ] + body = str(pr["body"] or "").replace("\r\n", "\n").strip("\n") + if body: + lines += ["", "Description:", "", _FENCE, body, _FENCE] + return "\n".join(lines) + "\n" + + +def _parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Merge a pull request with its description baked in.", + ) + parser.add_argument( + "-R", + metavar="OWNER/REPO", + help="repository on GitHub (default: as resolved by gh)", + ) + parser.add_argument( + "-B", + "--base", + metavar="BRANCH", + help="branch to merge into (default: the pull request's base)", + ) + parser.add_argument("pr", type=int, help="pull request number") + return parser.parse_args() + + +def main() -> int: + args = _parse_args() + root = str(root_dir()) + repo = str( + args.R + or _gh_json(root, "repo", "view", "--json", "nameWithOwner")[ + "nameWithOwner" + ] + ) + + pr = _gh_json( + root, "pr", "view", str(args.pr), "-R", repo, "--json", _PR_FIELDS + ) + if pr["state"] != "OPEN": + raise RuntimeError(f"#{args.pr} is {str(pr['state']).lower()}") + if pr["isDraft"]: + raise RuntimeError(f"#{args.pr} is a draft") + + base = args.base or str(pr["baseRefName"]) + branch = git_out(root, "rev-parse", "--abbrev-ref", "HEAD") + if branch != base: + raise RuntimeError(f"on {branch!r}, check out {base!r} to merge into") + if git_out(root, "status", "--porcelain", "--untracked-files=no"): + raise RuntimeError("working tree has uncommitted changes") + + url = f"https://github.com/{repo}.git" + # A custom base is the merger's own branch, so only the pull request's base + # is held to its copy on GitHub. + if base == pr["baseRefName"]: + base_tip = _fetch(root, url, f"refs/heads/{base}") + if git_run( + root, "merge-base", "--is-ancestor", base_tip, "HEAD" + ).returncode: + raise RuntimeError(f"{base} is behind {repo}, pull it first") + head = _fetch(root, url, f"refs/pull/{args.pr}/head") + # A push between the two reads would merge commits nobody saw listed. + if head != pr["headRefOid"]: + raise RuntimeError(f"#{args.pr} changed while fetching, retry") + + out = git_out( + root, "log", "--reverse", "--format=%H%x00%s%x00%an", f"HEAD..{head}" + ) + commits = [tuple(line.split("\0")) for line in out.splitlines()] + if not commits: + raise RuntimeError(f"#{args.pr} has nothing to merge into {base}") + + with tempfile.NamedTemporaryFile( + "w", encoding="utf-8", suffix=".txt", delete=False + ) as f: + f.write(_message(pr, commits)) + try: + # Verbatim cleanup keeps the description's blank lines and '#' lines. + merge = subprocess.run( # noqa: S603 + [ + require_bin("git"), + "merge", + "--no-ff", + "--no-edit", + "--cleanup=verbatim", + "-F", + f.name, + head, + ], + check=False, + cwd=root, + ) + finally: + os.unlink(f.name) + if merge.returncode != 0: + abort = git_run(root, "merge", "--abort") + if abort.returncode != 0: + raise RuntimeError( + f"merging #{args.pr} failed, cancellation failed, recovering manually: " + f"{abort.stderr.strip()}" + ) + raise RuntimeError(f"merging #{args.pr} failed, merge aborted") + + print(git_out(root, "log", "-1", "--format=%B")) + return RETCODE_PASS + + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as exc: # noqa: BLE001 + print(f"error: {exc}", file=sys.stderr) + sys.exit(RETCODE_ERR) From 087fa60a1fba09ec7954d764ff84763ac0b027df Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:48:34 +0530 Subject: [PATCH 22/23] sdk%lint: clippy every feature combination with `cargo hack` --- .github/workflows/build_nightly.yml | 10 +++++++++- Justfile | 1 + contrib/README.md | 8 +++++--- contrib/nix/mods/nixpkgs.nix | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_nightly.yml b/.github/workflows/build_nightly.yml index 8ae09fb2..aeb20825 100644 --- a/.github/workflows/build_nightly.yml +++ b/.github/workflows/build_nightly.yml @@ -33,6 +33,7 @@ jobs: - { name: full, args: "--features full" } - { name: min, args: "--no-default-features" } - { name: std, args: "--features std" } + - { name: powerset, args: "" } defaults: run: @@ -89,13 +90,19 @@ jobs: cargo-build-nightly-${{ runner.os }}-${{ runner.arch }}-${{ inputs.package }}-${{ matrix.config.name }}- - name: Lint package + if: matrix.config.name == 'full' || matrix.config.name == 'min' run: cargo clippy -p ${{ inputs.package }} ${{ matrix.config.args }} --tests --benches -- -D warnings + - name: Lint every feature combination + if: matrix.config.name == 'powerset' + run: cargo hack clippy -p ${{ inputs.package }} --feature-powerset --exclude-features default -- -D warnings + - name: Build package + if: matrix.config.name == 'full' || matrix.config.name == 'min' run: cargo build -p ${{ inputs.package }} ${{ matrix.config.args }} - name: Test package - if: matrix.config.name != 'full' + if: matrix.config.name == 'min' || matrix.config.name == 'std' run: | cargo nextest run --config-file .cargo/nextest.toml -p ${{ inputs.package }} ${{ matrix.config.args }} cargo test --doc -p ${{ inputs.package }} ${{ matrix.config.args }} @@ -130,4 +137,5 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Sanity check benchmarks + if: matrix.config.name != 'powerset' run: cargo test ${{ matrix.config.args }} --package ${{ inputs.package }} --benches diff --git a/Justfile b/Justfile index 9b203ff9..48d35ebc 100644 --- a/Justfile +++ b/Justfile @@ -47,6 +47,7 @@ test: cargo clippy --all-targets --no-default-features -- -D warnings cargo clippy --all-targets --features full -- -D warnings cargo clippy --all-targets --features full,portable -- -D warnings + cargo hack clippy --ignore-private --feature-powerset --exclude-features default -- -D warnings {{ nextest }} --workspace --features full cargo test --doc --workspace --features full cargo doc --workspace --no-deps --features full diff --git a/contrib/README.md b/contrib/README.md index 8631180d..1e43c618 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -33,6 +33,7 @@ source .venv/bin/activate packages need to be additionally sourced. * [cargo-deny](https://github.com/EmbarkStudios/cargo-deny) +* [cargo-hack](https://github.com/taiki-e/cargo-hack) * [cargo-mutants](https://mutants.rs) * [cargo-nextest](https://github.com/nextest-rs/nextest) * [CodeQL 2.27 or higher](https://github.com/github/codeql-cli-binaries/releases) @@ -50,7 +51,7 @@ packages need to be additionally sourced. > ([source](https://docs.brew.sh/FAQ#what-does-keg-only-mean)). ```bash -brew install cargo-deny cargo-mutants cargo-nextest codeql git just just-lsp node@24 +brew install cargo-deny cargo-hack cargo-mutants cargo-nextest codeql git just just-lsp node@24 ``` ### Linux/WSL @@ -61,10 +62,11 @@ you may need to update your shell to add your installation path to `PATH` so tha lint script. Neither CodeQL nor taplo are available in official Debian or Fedora repositories and must be sourced per vendor -guidance. `cargo-mutants`, `cargo-nextest` and `just-lsp` are likewise best installed as Rust binary crates. +guidance. `cargo-hack`, `cargo-mutants`, `cargo-nextest` and `just-lsp` are likewise best installed as Rust binary +crates. ```bash -cargo install --locked cargo-mutants cargo-nextest just-lsp +cargo install --locked cargo-hack cargo-mutants cargo-nextest just-lsp ``` #### Installing `taplo` diff --git a/contrib/nix/mods/nixpkgs.nix b/contrib/nix/mods/nixpkgs.nix index 2f55fd44..4c9f5505 100644 --- a/contrib/nix/mods/nixpkgs.nix +++ b/contrib/nix/mods/nixpkgs.nix @@ -25,6 +25,7 @@ in { packages = [ pkgs.cargo-deny + pkgs.cargo-hack pkgs.cargo-llvm-cov pkgs.cargo-mutants pkgs.cargo-nextest From e90924e703adb4949169315b86ceee53eeefd1dc Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:18:57 +0530 Subject: [PATCH 23/23] sdk%doc: update changelogs --- pkgs/num/CHANGELOG.md | 3 +++ pkgs/pkc/CHANGELOG.md | 6 ++++++ pkgs/types/CHANGELOG.md | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/pkgs/num/CHANGELOG.md b/pkgs/num/CHANGELOG.md index bd886186..3c540add 100644 --- a/pkgs/num/CHANGELOG.md +++ b/pkgs/num/CHANGELOG.md @@ -20,10 +20,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Changed +- **The minimum supported Rust version is raised from 1.85 to 1.89.** - `HashBlob`, `make_hash!` types and `Arith256` serialize as their little-endian storage bytes for machine-readable formats, JSON retains hex encoded big-endian string encoding. - `CompactTarget::from_str` requires exactly 8 hex digits, optionally `0x`/`0X`-prefixed. Shorter input is now rejected with `ParseHexError::OddLength` or `ParseHexError::InvalidLength`. +- The `serde` feature no longer implies `codec`. `CompactTarget` implements `Serialize` and `Deserialize` without + `codec`. Callers that relied on `serde` to enable `codec` must enable it explicitly. ### Removed diff --git a/pkgs/pkc/CHANGELOG.md b/pkgs/pkc/CHANGELOG.md index fc18b114..963d827c 100644 --- a/pkgs/pkc/CHANGELOG.md +++ b/pkgs/pkc/CHANGELOG.md @@ -10,9 +10,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Added - `Bls{Public,Secret}Key::negate`, `BlsSecretKey::mul_tweak`, `EcdsaPublicKey::negate` and `EcdsaSecretKey::mul_tweak`. +- `portable` feature, forwarding `blst/portable`, for binaries that run on any CPU of the target architecture. ### Changed +- **The minimum supported Rust version is raised from 1.85 to 1.89.** +- **`bls` requires ADX on x86_64 unless `portable` is enabled.** - `BlsScheme::sk_to_bytes` returns `Zeroizing<[u8; 32]>` instead of a bare array. Code that binds the result as `[u8; 32]` must dereference it. - `EcdsaSecretKey::negate` takes `&self` and returns the negated key as `Result` instead of negating @@ -25,6 +28,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), `Bls{Pk,Sig}Share`, `BlsIes{Blob,Multi}`, `EcdsaPkBytes`, `EcdsaSigBytes`, `EcdsaRecSigBytes`, `EcdsaPkHash`, `EcdsaPublicKey`, `EcdsaSignature`, `EcdsaRecSignature`, `EddsaPkBytes`, `EddsaSigBytes`, `EddsaPkHash`, `EddsaPublicKey`, `EddsaSignature`. +- The `serde` feature no longer implies `codec`. `BlsIes{Blob,Multi}` and `BlsIes{Blob,Multi}Bytes` serialize as + their wire image and implement `Serialize` and `Deserialize` only with both features. Callers that relied on `serde` + to enable `codec` must enable it explicitly. ## [0.1.0-beta] - 2026-09-15 diff --git a/pkgs/types/CHANGELOG.md b/pkgs/types/CHANGELOG.md index 3440a445..e7fd3059 100644 --- a/pkgs/types/CHANGELOG.md +++ b/pkgs/types/CHANGELOG.md @@ -17,12 +17,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Changed +- **The minimum supported Rust version is raised from 1.85 to 1.89.** - `{derive,make}_bytes!`-defined types serialize as their raw storage bytes for machine-readable formats. JSON and other human-readable formats retain hex in storage order for `fwd` and in reverse storage order for `rev`. - `derive_bytes!` emits `From<[u8; N]>` in place of `impl_bytes!`, and `make_sbytes!` emits the implementation itself. Callers of `impl_bytes!` without `derive_bytes!` must now write the conversion manually. - As a result, both `make_bytes!` and `make_sbytes!` carry it with `nocodec` tag and without the `codec` feature and callers that wrote the conversion manually for those configurations must drop it. +- The `serde` feature no longer implies `codec`. `impl_num!` is available without `codec` and emits its `serde` + implementation independently, as does `make_num!`. Callers that relied on `serde` to enable `codec` must enable it + explicitly. - The `zeroize` export is moved from the crate root to `__deps`. Callers must switch to `dash_types::__deps::zeroize`. - Types using `derive_bytes!` that implemented `From<&[u8; N]>`, `TryFrom<&[u8]>`, `FromStr`, `LowerHex` or `UpperHex` manually must drop those implementations, as `derive_bytes!` now emits them.