From 2d0fc75411615e7fe10a83730f583bd593e9be95 Mon Sep 17 00:00:00 2001 From: Ben Blier Date: Thu, 30 Jul 2026 10:58:51 -0400 Subject: [PATCH 1/4] build: bump rust toolchain to 1.97.1 1.90.0 was seven releases behind and below rust-analyzer's supported floor of 1.94.0, so editors warned on every workspace load. Bumps rust-toolchain.toml, the twelve dtolnay/rust-toolchain CI pins, and the devcontainer rust feature. Also fixes the clippy sites the newer lints flag. The serviceability changes are all redundant '&' removals inside Display/Debug impls, where format_args! auto-references, so formatted output is unchanged. The onchain programs keep their own toolchain file, and cargo build-sbf still supplies its own rustc via platform-tools v1.54, so program codegen is unaffected. --- .devcontainer/devcontainer.json | 2 +- .github/workflows/release.client.yml | 2 +- .github/workflows/release.daily.yml | 2 +- .../release.devnet.smartcontract.daily.yml | 2 +- .../workflows/release.pipeline.validation.yml | 2 +- .github/workflows/release.testnet.yml | 4 ++-- .github/workflows/rust.yml | 10 +++++----- .github/workflows/sdk.yml | 2 +- CLAUDE.md | 2 +- client/doublezero/src/cli/sentinel.rs | 2 +- .../doublezero-daemon-cli/src/disconnect.rs | 20 ++++++++----------- .../src/user/delete.rs | 2 +- rust-toolchain.toml | 2 +- smartcontract/cli/src/accesspass/list.rs | 2 +- smartcontract/cli/src/account.rs | 2 +- smartcontract/cli/src/address.rs | 2 +- smartcontract/cli/src/contributor/list.rs | 2 +- .../cli/src/device/interface/list.rs | 4 ++-- .../src/processors/globalconfig/set.rs | 8 ++++---- .../src/processors/user/create.rs | 4 ++-- .../src/processors/user/create_subscribe.rs | 4 ++-- .../src/state/device.rs | 2 +- .../src/state/globalconfig.rs | 8 ++++---- .../src/state/link.rs | 2 +- .../src/state/user.rs | 8 ++++---- 25 files changed, 49 insertions(+), 53 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e41ef001a0..7b3c98ae5b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,7 +14,7 @@ // Go is installed directly in the Dockerfile at a pinned version to avoid // stdlib conflicts from layering the Go feature on the base image's Go. "ghcr.io/devcontainers/features/rust:1": { - "version": "1.90.0" + "version": "1.97.1" }, }, "initializeCommand": "mkdir -p \"${DZ_WORKTREES_DIR:-/tmp/worktrees}\"", diff --git a/.github/workflows/release.client.yml b/.github/workflows/release.client.yml index e677b0d726..7c57887ce9 100644 --- a/.github/workflows/release.client.yml +++ b/.github/workflows/release.client.yml @@ -31,7 +31,7 @@ jobs: - name: Set env vars run: ./scripts/env.sh >> $GITHUB_ENV - name: Install rust for cli - uses: dtolnay/rust-toolchain@1.90.0 + uses: dtolnay/rust-toolchain@1.97.1 with: targets: x86_64-unknown-linux-musl - name: Install dependencies for rpm packaging and musl static build diff --git a/.github/workflows/release.daily.yml b/.github/workflows/release.daily.yml index 1063f8917d..9fc8f5933a 100644 --- a/.github/workflows/release.daily.yml +++ b/.github/workflows/release.daily.yml @@ -57,7 +57,7 @@ jobs: - name: Set env vars run: ./scripts/env.sh >> $GITHUB_ENV - name: Install rust for cli - uses: dtolnay/rust-toolchain@1.90.0 + uses: dtolnay/rust-toolchain@1.97.1 with: targets: x86_64-unknown-linux-musl - name: Install dependencies for rpm packaging diff --git a/.github/workflows/release.devnet.smartcontract.daily.yml b/.github/workflows/release.devnet.smartcontract.daily.yml index 44dce90e48..f1c3f28195 100644 --- a/.github/workflows/release.devnet.smartcontract.daily.yml +++ b/.github/workflows/release.devnet.smartcontract.daily.yml @@ -27,7 +27,7 @@ jobs: binary: doublezero_geolocation.so steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 - name: Install agave solana tools run: | diff --git a/.github/workflows/release.pipeline.validation.yml b/.github/workflows/release.pipeline.validation.yml index 3935de08c5..c66d958cb6 100644 --- a/.github/workflows/release.pipeline.validation.yml +++ b/.github/workflows/release.pipeline.validation.yml @@ -54,7 +54,7 @@ jobs: target/x86_64-unknown-linux-gnu/release - name: Install rust toolchain - uses: dtolnay/rust-toolchain@1.90.0 + uses: dtolnay/rust-toolchain@1.97.1 if: contains(matrix.languages, 'rust') with: targets: x86_64-unknown-linux-musl diff --git a/.github/workflows/release.testnet.yml b/.github/workflows/release.testnet.yml index 9e3fa39add..3a411c23d0 100644 --- a/.github/workflows/release.testnet.yml +++ b/.github/workflows/release.testnet.yml @@ -91,7 +91,7 @@ jobs: - uses: actions/checkout@v4 with: token: ${{ steps.app.outputs.token }} - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - name: Open doublezero version-bump PR id: dzpr env: @@ -371,7 +371,7 @@ jobs: - uses: actions/checkout@v4 with: ref: main # the merged version-bump commit - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 # Keep the agave version and --tools-version in sync with # release.devnet.smartcontract.daily.yml (platform-tools >= v1.54 is diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8a711d612f..ff93dda07f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-24.04-16c-64gb steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 - name: Install agave solana tools run: | @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-24.04-16c-64gb steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 with: components: clippy, rustfmt - uses: Swatinem/rust-cache@v2 @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-24.04-16c-64gb steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 - name: Install agave solana tools run: | @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-24.04-16c-64gb steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 - name: Install agave solana tools run: | @@ -54,7 +54,7 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 with: targets: x86_64-unknown-linux-musl - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 37a2da5b18..3e77cc861b 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -57,7 +57,7 @@ jobs: timeout-minutes: 15 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.90.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 - name: Check fixture drift run: make check-fixtures diff --git a/CLAUDE.md b/CLAUDE.md index 7faffacca5..0c0ae926fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ cargo test -p Always run `make rust-fmt` before committing Rust changes. -Rust toolchain: 1.90.0 (via `rust-toolchain.toml`). Solana SDK: 2.2.x. +Rust toolchain: 1.97.1 (via `rust-toolchain.toml`); the onchain programs pin their own in `smartcontract/programs/rust-toolchain.toml`. Solana SDK: 3.0. ### Go diff --git a/client/doublezero/src/cli/sentinel.rs b/client/doublezero/src/cli/sentinel.rs index 2d5b3e4324..905e0bca34 100644 --- a/client/doublezero/src/cli/sentinel.rs +++ b/client/doublezero/src/cli/sentinel.rs @@ -389,7 +389,7 @@ impl FindValidatorMulticastPublishersCommand { not_publishers: on_dz - pubs, }) .collect(); - summary_rows.sort_by(|a, b| b.validators.cmp(&a.validators)); + summary_rows.sort_by_key(|r| std::cmp::Reverse(r.validators)); summary_rows.push(SummaryRow { client: "TOTAL".to_string(), validators: total, diff --git a/crates/doublezero-daemon-cli/src/disconnect.rs b/crates/doublezero-daemon-cli/src/disconnect.rs index 5cc594fb70..ad4f0eaa01 100644 --- a/crates/doublezero-daemon-cli/src/disconnect.rs +++ b/crates/doublezero-daemon-cli/src/disconnect.rs @@ -131,20 +131,16 @@ impl Disconnect { let payer = ledger.get_payer(); for (pubkey, user) in users.iter().filter(|(_, u)| u.client_ip == client_ip) { - match self.dz_mode { + let mode_mismatch = match self.dz_mode { Some(DzMode::IBRL) => { - if user.user_type != UserType::IBRL + user.user_type != UserType::IBRL && user.user_type != UserType::IBRLWithAllocatedIP - { - continue; - } - } - Some(DzMode::Multicast) => { - if user.user_type != UserType::Multicast { - continue; - } } - None => {} + Some(DzMode::Multicast) => user.user_type != UserType::Multicast, + None => false, + }; + if mode_mismatch { + continue; } // Skip users owned by a different keypair — only the owner can delete them. @@ -250,7 +246,7 @@ impl Disconnect { let get_user = || { match ledger.get_user(*user_pubkey) { - Ok(user) => Err(user), // User still exists, keep retrying + Ok(_) => Err(()), // User still exists, keep retrying Err(e) => { Ok(if e.to_string().contains("User not found") { Ok(()) // User deleted, stop retrying diff --git a/crates/doublezero-geolocation-cli/src/user/delete.rs b/crates/doublezero-geolocation-cli/src/user/delete.rs index 6c264de015..448eb341f6 100644 --- a/crates/doublezero-geolocation-cli/src/user/delete.rs +++ b/crates/doublezero-geolocation-cli/src/user/delete.rs @@ -34,7 +34,7 @@ impl DeleteGeolocationUserCliCommand { if !std::io::stdin().is_terminal() { eyre::bail!("stdin is not a terminal — pass --yes to skip confirmation"); } - write!(out, "Delete user '{}'? [y/N]: ", &code)?; + write!(out, "Delete user '{}'? [y/N]: ", code)?; out.flush()?; let mut input = String::new(); std::io::stdin().read_line(&mut input)?; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ff100edcbb..7243604323 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.90.0" +channel = "1.97.1" diff --git a/smartcontract/cli/src/accesspass/list.rs b/smartcontract/cli/src/accesspass/list.rs index 1a38cf5689..3eb0ccc2e2 100644 --- a/smartcontract/cli/src/accesspass/list.rs +++ b/smartcontract/cli/src/accesspass/list.rs @@ -361,7 +361,7 @@ impl ListAccessPassCliCommand { }) .collect(); - access_pass_displays.sort_by(|a, b| a.client_ip.cmp(&b.client_ip)); + access_pass_displays.sort_by_key(|a| a.client_ip); let res = if self.json { serde_json::to_string_pretty(&access_pass_displays)? diff --git a/smartcontract/cli/src/account.rs b/smartcontract/cli/src/account.rs index f93f7c9007..d669dc326b 100644 --- a/smartcontract/cli/src/account.rs +++ b/smartcontract/cli/src/account.rs @@ -93,7 +93,7 @@ impl GetAccountCliCommand { writeln!( out, "{}: {} ({})\n\t\t\tpubkey: {}, signature: {}", - &tran.time.to_string(), + tran.time, tran.instruction.get_name(), tran.instruction.get_args(), tran.account, diff --git a/smartcontract/cli/src/address.rs b/smartcontract/cli/src/address.rs index c128aded71..d52a099de3 100644 --- a/smartcontract/cli/src/address.rs +++ b/smartcontract/cli/src/address.rs @@ -15,7 +15,7 @@ impl AddressCliCommand { ) -> eyre::Result<()> { require!(client, RequirementCheck::KEYPAIR); - writeln!(out, "{}", &client.get_payer())?; + writeln!(out, "{}", client.get_payer())?; Ok(()) } diff --git a/smartcontract/cli/src/contributor/list.rs b/smartcontract/cli/src/contributor/list.rs index f82aa28fdd..19d4bab590 100644 --- a/smartcontract/cli/src/contributor/list.rs +++ b/smartcontract/cli/src/contributor/list.rs @@ -41,7 +41,7 @@ impl ListContributorCliCommand { let mut contributors: Vec<(Pubkey, Contributor)> = contributors.into_iter().collect(); - contributors.sort_by(|(_, a), (_, b)| a.owner.cmp(&b.owner)); + contributors.sort_by_key(|(_, a)| a.owner); let contributor_displays: Vec = contributors .into_iter() diff --git a/smartcontract/cli/src/device/interface/list.rs b/smartcontract/cli/src/device/interface/list.rs index 9ca3ca8095..55de0f71ec 100644 --- a/smartcontract/cli/src/device/interface/list.rs +++ b/smartcontract/cli/src/device/interface/list.rs @@ -70,8 +70,8 @@ impl ListDeviceInterfaceCliCommand { let devices = client.list_device(ListDeviceCommand {})?; devices - .iter() - .flat_map(|(_, device)| { + .values() + .flat_map(|device| { device .interfaces .iter() diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/globalconfig/set.rs b/smartcontract/programs/doublezero-serviceability/src/processors/globalconfig/set.rs index 79f09b74e0..0fa8c9d668 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/globalconfig/set.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/globalconfig/set.rs @@ -41,11 +41,11 @@ impl fmt::Debug for SetGlobalConfigArgs { "local_asn: {}, remote_asn: {}, tunnel_block: {}, user _block: {}, multicastgroup_block: {}, next_bgp_community: {:?}, multicast_publisher_block: {}", self.local_asn, self.remote_asn, - &self.device_tunnel_block, - &self.user_tunnel_block, - &self.multicastgroup_block, + self.device_tunnel_block, + self.user_tunnel_block, + self.multicastgroup_block, self.next_bgp_community, - &self.multicast_publisher_block, + self.multicast_publisher_block, ) } } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/user/create.rs b/smartcontract/programs/doublezero-serviceability/src/processors/user/create.rs index 2a980ca892..5b3312df26 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/user/create.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/user/create.rs @@ -41,8 +41,8 @@ impl fmt::Debug for UserCreateArgs { "user_type: {}, cyoa_type: {}, client_ip: {}, tunnel_endpoint: {}, dz_prefix_count: {}", self.user_type, self.cyoa_type, - &self.client_ip, - &self.tunnel_endpoint, + self.client_ip, + self.tunnel_endpoint, self.dz_prefix_count, ) } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/user/create_subscribe.rs b/smartcontract/programs/doublezero-serviceability/src/processors/user/create_subscribe.rs index 2e721bfb76..9842aa066c 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/user/create_subscribe.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/user/create_subscribe.rs @@ -56,8 +56,8 @@ impl fmt::Debug for UserCreateSubscribeArgs { "user_type: {}, cyoa_type: {}, client_ip: {}, tunnel_endpoint: {}, dz_prefix_count: {}, owner: {}", self.user_type, self.cyoa_type, - &self.client_ip, - &self.tunnel_endpoint, + self.client_ip, + self.tunnel_endpoint, self.dz_prefix_count, self.owner, ) diff --git a/smartcontract/programs/doublezero-serviceability/src/state/device.rs b/smartcontract/programs/doublezero-serviceability/src/state/device.rs index bc0c573c8c..eba5137384 100644 --- a/smartcontract/programs/doublezero-serviceability/src/state/device.rs +++ b/smartcontract/programs/doublezero-serviceability/src/state/device.rs @@ -498,7 +498,7 @@ impl fmt::Display for Device { unicast_users_count: {}, multicast_subscribers_count: {}, max_unicast_users: {}, max_multicast_subscribers: {}, reserved_seats: {}, \ multicast_publishers_count: {}, max_multicast_publishers: {}", self.account_type, self.owner, self.index, self.contributor_pk, self.location_pk, self.exchange_pk, self.device_type, - &self.public_ip, &self.dz_prefixes, self.status, self.code, self.metrics_publisher_pk, self.mgmt_vrf, self.interfaces, + self.public_ip, self.dz_prefixes, self.status, self.code, self.metrics_publisher_pk, self.mgmt_vrf, self.interfaces, self.reference_count, self.users_count, self.max_users, self.device_health, self.desired_status, self.unicast_users_count, self.multicast_subscribers_count, self.max_unicast_users, self.max_multicast_subscribers, self.reserved_seats, self.multicast_publishers_count, self.max_multicast_publishers diff --git a/smartcontract/programs/doublezero-serviceability/src/state/globalconfig.rs b/smartcontract/programs/doublezero-serviceability/src/state/globalconfig.rs index 48c5aafd70..86c45331e9 100644 --- a/smartcontract/programs/doublezero-serviceability/src/state/globalconfig.rs +++ b/smartcontract/programs/doublezero-serviceability/src/state/globalconfig.rs @@ -28,11 +28,11 @@ impl fmt::Display for GlobalConfig { f, "account_type: {}, owner: {}, local_asn: {}, remote_asn: {}, device_tunnel_block: {}, user_tunnel_block: {}, multicastgroup_block: {}, next_bgp_community: {}, multicast_publisher_block: {}", self.account_type, self.owner, self.local_asn, self.remote_asn, - &self.device_tunnel_block, - &self.user_tunnel_block, - &self.multicastgroup_block, + self.device_tunnel_block, + self.user_tunnel_block, + self.multicastgroup_block, self.next_bgp_community, - &self.multicast_publisher_block, + self.multicast_publisher_block, ) } } diff --git a/smartcontract/programs/doublezero-serviceability/src/state/link.rs b/smartcontract/programs/doublezero-serviceability/src/state/link.rs index 5c4b11c575..041083942d 100644 --- a/smartcontract/programs/doublezero-serviceability/src/state/link.rs +++ b/smartcontract/programs/doublezero-serviceability/src/state/link.rs @@ -277,7 +277,7 @@ impl fmt::Display for Link { write!( f, "account_type: {}, owner: {}, index: {}, side_a_pk: {}, side_z_pk: {}, tunnel_type: {}, bandwidth: {}, mtu: {}, delay_ns: {}, jitter_ns: {}, tunnel_id: {}, tunnel_net: {}, status: {}, code: {}, contributor_pk: {}, link_health: {}, desired_status: {}, link_topologies: {:?}, link_flags: {:#010x}", - self.account_type, self.owner, self.index, self.side_a_pk, self.side_z_pk, self.link_type, self.bandwidth, self.mtu, self.delay_ns, self.jitter_ns, self.tunnel_id, &self.tunnel_net, self.status, self.code, self.contributor_pk, self.link_health, self.desired_status, self.link_topologies, self.link_flags + self.account_type, self.owner, self.index, self.side_a_pk, self.side_z_pk, self.link_type, self.bandwidth, self.mtu, self.delay_ns, self.jitter_ns, self.tunnel_id, self.tunnel_net, self.status, self.code, self.contributor_pk, self.link_health, self.desired_status, self.link_topologies, self.link_flags ) } } diff --git a/smartcontract/programs/doublezero-serviceability/src/state/user.rs b/smartcontract/programs/doublezero-serviceability/src/state/user.rs index 169498df8f..13cddd6846 100644 --- a/smartcontract/programs/doublezero-serviceability/src/state/user.rs +++ b/smartcontract/programs/doublezero-serviceability/src/state/user.rs @@ -337,12 +337,12 @@ impl fmt::Display for User { self.user_type, self.device_pk, self.cyoa_type, - &self.client_ip, - &self.dz_ip, + self.client_ip, + self.dz_ip, self.tunnel_id, - &self.tunnel_net, + self.tunnel_net, self.status, - &self.tunnel_endpoint + self.tunnel_endpoint ) } } From 9ab7fa386d23ef1bb4dc6663dde7a68923f64845 Mon Sep 17 00:00:00 2001 From: Ben Blier Date: Thu, 30 Jul 2026 11:02:38 -0400 Subject: [PATCH 2/4] docs: add changelog entry for the rust 1.97.1 bump --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d7f8b2aa..509264f013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ All notable changes to this project will be documented in this file. - Serviceability - New `SubscribeFeed` and `UnsubscribeFeed` instructions join or leave whole feeds on an EdgeSeat access pass in a single atomic transaction, charging one seat per feed rather than per group. A feed is all-or-nothing: the caller names feeds, the processor derives which groups change and rejects a group list that does not match, so two feeds carrying the same group stay unambiguous. `UpdateMulticastGroupRoles` now enforces the multicast-group allowlists for every access-pass type, EdgeSeat included, so purchased groups go through the feed instructions and individually comped groups through the allowlist. `CreateSubscribeUser` skips the allowlist only for the case its feed gate actually covers, closing two paths that could join a group with no check. `MAX_FEED_GROUPS` drops from 64 to 20, bounded by what one transaction can carry: because joining passes every group a feed holds, a larger feed could never be joined. No feed onchain is affected. For the same reason a user may hold at most 6 feeds, and a held feed the pass no longer carries is pruned on leave instead of blocking it. New errors: `EdgeSeatRequired` (101), `UserDeviceMismatch` (102), `UserFeedLimitExceeded` (103), `EdgeSeatIsMulticastOnly` (104). (#4109) - `CreateUser` can now create a bare multicast user (no group, no feed) under an EdgeSeat pass: the feed gate moved to `CreateSubscribeUser`, its only caller, and feed seats are otherwise charged by `SubscribeFeed`. The pass-level `max_multicast_users` cap is enforced again (it had been vestigial under the feed-seat model) so bare users stay bounded; the oracle already provisions it as the total purchased seats. `CreateUser` is also idempotent: re-running it for an existing user matching the requested owner, device, user type, and tenant succeeds without changing state, so a client can retry safely; a mismatch still fails with `AccountAlreadyInitialized`, and a banned user with `InvalidStatus`. This applies to the current client_ip-keyed user PDA; the legacy index-keyed PDA cannot be re-run, since its index has moved on. Deploy note: `max_multicast_users` becomes load-bearing on existing EdgeSeat passes; before deploying to a cluster, confirm each pass's value covers its feed seats and backfill via `SetAccessPass` where short. Checked 2026-07-30: mainnet-beta and devnet hold no EdgeSeat pass; testnet's single one (`5x9DTsWC…`) has `max_multicast_users` 1, zero users, zero connections. (#4110) +- CI + - The Rust toolchain moves from 1.90.0 to 1.97.1, seven releases forward. 1.90.0 sat below the supported floor of the rust-analyzer builds shipping with current editors (1.94.0), so the language server warned on every workspace load; that floor tracks stable at roughly minus-three releases, so the gap widens with each Rust release rather than holding steady. `rust-toolchain.toml`, the twelve `dtolnay/rust-toolchain` CI pins across seven workflows, and the devcontainer Rust feature move together — **contributors must rebuild their devcontainer after this merges**. The accompanying source changes are only the clippy lints the newer toolchain adds (`useless_borrows_in_formatting`, `unnecessary_sort_by`, `iter_kv_map`, `to_string_in_format_args`, `collapsible_match`, `result_large_err`). The serviceability ones are redundant `&` removals inside `Display`/`Debug` impls, where `format_args!` auto-references, so formatted output is byte-identical; they were required rather than optional because `lint-programs` runs from `smartcontract/` and so picks up the root toolchain, not `smartcontract/programs/rust-toolchain.toml`. Onchain codegen is unaffected either way: `cargo build-sbf --tools-version v1.54` supplies its own rustc. (#4118) ## [v0.32.0](https://github.com/malbeclabs/doublezero/compare/client/v0.31.0...client/v0.32.0) - 2026-07-29 From 2de40b5b0bfc7c744ff7f302cd102f183767560e Mon Sep 17 00:00:00 2001 From: Ben Blier Date: Thu, 30 Jul 2026 13:29:28 -0400 Subject: [PATCH 3/4] release: bump the release image rust pin to 1.97.1 Fourth place the toolchain version lives. Left at 1.90.0 the cached release image drifts from rust-toolchain.toml, and because build-snapshot.sh only rebuilds the image when the Dockerfile hash changes, goreleaser would re-download a full 1.97.1 toolchain on every snapshot build -- RUSTUP_HOME is not a named volume and the container runs --rm, so nothing caches it. --- release/Dockerfile.release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/Dockerfile.release b/release/Dockerfile.release index 948d5a7240..8052e57443 100644 --- a/release/Dockerfile.release +++ b/release/Dockerfile.release @@ -5,7 +5,7 @@ ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ PATH=/usr/local/cargo/bin:/go/bin:/usr/local/go/bin:$PATH RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain 1.90.0 \ + | sh -s -- -y --default-toolchain 1.97.1 \ && rustup target add x86_64-unknown-linux-gnu # Cross-compiler for Rust linux builds + rpm packaging From 7304f109f2568589123a589ff742581d4d5e7aca Mon Sep 17 00:00:00 2001 From: Ben Blier Date: Thu, 30 Jul 2026 13:30:31 -0400 Subject: [PATCH 4/4] docs: note the release image pin in the changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 509264f013..3bacd192f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ All notable changes to this project will be documented in this file. - New `SubscribeFeed` and `UnsubscribeFeed` instructions join or leave whole feeds on an EdgeSeat access pass in a single atomic transaction, charging one seat per feed rather than per group. A feed is all-or-nothing: the caller names feeds, the processor derives which groups change and rejects a group list that does not match, so two feeds carrying the same group stay unambiguous. `UpdateMulticastGroupRoles` now enforces the multicast-group allowlists for every access-pass type, EdgeSeat included, so purchased groups go through the feed instructions and individually comped groups through the allowlist. `CreateSubscribeUser` skips the allowlist only for the case its feed gate actually covers, closing two paths that could join a group with no check. `MAX_FEED_GROUPS` drops from 64 to 20, bounded by what one transaction can carry: because joining passes every group a feed holds, a larger feed could never be joined. No feed onchain is affected. For the same reason a user may hold at most 6 feeds, and a held feed the pass no longer carries is pruned on leave instead of blocking it. New errors: `EdgeSeatRequired` (101), `UserDeviceMismatch` (102), `UserFeedLimitExceeded` (103), `EdgeSeatIsMulticastOnly` (104). (#4109) - `CreateUser` can now create a bare multicast user (no group, no feed) under an EdgeSeat pass: the feed gate moved to `CreateSubscribeUser`, its only caller, and feed seats are otherwise charged by `SubscribeFeed`. The pass-level `max_multicast_users` cap is enforced again (it had been vestigial under the feed-seat model) so bare users stay bounded; the oracle already provisions it as the total purchased seats. `CreateUser` is also idempotent: re-running it for an existing user matching the requested owner, device, user type, and tenant succeeds without changing state, so a client can retry safely; a mismatch still fails with `AccountAlreadyInitialized`, and a banned user with `InvalidStatus`. This applies to the current client_ip-keyed user PDA; the legacy index-keyed PDA cannot be re-run, since its index has moved on. Deploy note: `max_multicast_users` becomes load-bearing on existing EdgeSeat passes; before deploying to a cluster, confirm each pass's value covers its feed seats and backfill via `SetAccessPass` where short. Checked 2026-07-30: mainnet-beta and devnet hold no EdgeSeat pass; testnet's single one (`5x9DTsWC…`) has `max_multicast_users` 1, zero users, zero connections. (#4110) - CI - - The Rust toolchain moves from 1.90.0 to 1.97.1, seven releases forward. 1.90.0 sat below the supported floor of the rust-analyzer builds shipping with current editors (1.94.0), so the language server warned on every workspace load; that floor tracks stable at roughly minus-three releases, so the gap widens with each Rust release rather than holding steady. `rust-toolchain.toml`, the twelve `dtolnay/rust-toolchain` CI pins across seven workflows, and the devcontainer Rust feature move together — **contributors must rebuild their devcontainer after this merges**. The accompanying source changes are only the clippy lints the newer toolchain adds (`useless_borrows_in_formatting`, `unnecessary_sort_by`, `iter_kv_map`, `to_string_in_format_args`, `collapsible_match`, `result_large_err`). The serviceability ones are redundant `&` removals inside `Display`/`Debug` impls, where `format_args!` auto-references, so formatted output is byte-identical; they were required rather than optional because `lint-programs` runs from `smartcontract/` and so picks up the root toolchain, not `smartcontract/programs/rust-toolchain.toml`. Onchain codegen is unaffected either way: `cargo build-sbf --tools-version v1.54` supplies its own rustc. (#4118) + - The Rust toolchain moves from 1.90.0 to 1.97.1, seven releases forward. 1.90.0 sat below the supported floor of the rust-analyzer builds shipping with current editors (1.94.0), so the language server warned on every workspace load; that floor tracks stable at roughly minus-three releases, so the gap widens with each Rust release rather than holding steady. `rust-toolchain.toml`, the twelve `dtolnay/rust-toolchain` CI pins across seven workflows, the devcontainer Rust feature, and the rustup pin baked into `release/Dockerfile.release` move together — **contributors must rebuild their devcontainer after this merges**. The release image needs no manual step: `scripts/build-snapshot.sh` keys its rebuild on the Dockerfile's hash, so changing the pin is what triggers it. Left stale, that image would have kept 1.90.0 while goreleaser ran against a 1.97.1 `rust-toolchain.toml`, re-downloading a full toolchain on every snapshot build (`RUSTUP_HOME` is not one of the named volumes and the container runs `--rm`). The accompanying source changes are only the clippy lints the newer toolchain adds (`useless_borrows_in_formatting`, `unnecessary_sort_by`, `iter_kv_map`, `to_string_in_format_args`, `collapsible_match`, `result_large_err`). The serviceability ones are redundant `&` removals inside `Display`/`Debug` impls, where `format_args!` auto-references, so formatted output is byte-identical; they were required rather than optional because `lint-programs` runs from `smartcontract/` and so picks up the root toolchain, not `smartcontract/programs/rust-toolchain.toml`. Onchain codegen is unaffected either way: `cargo build-sbf --tools-version v1.54` supplies its own rustc. (#4118) ## [v0.32.0](https://github.com/malbeclabs/doublezero/compare/client/v0.31.0...client/v0.32.0) - 2026-07-29