Skip to content

cli/serviceability: validate CYOA/DIA and link bandwidth invariants#3778

Merged
juan-malbeclabs merged 5 commits into
mainfrom
jo/interface_bandwidth_checks
May 27, 2026
Merged

cli/serviceability: validate CYOA/DIA and link bandwidth invariants#3778
juan-malbeclabs merged 5 commits into
mainfrom
jo/interface_bandwidth_checks

Conversation

@juan-malbeclabs
Copy link
Copy Markdown
Contributor

Summary

Reject misconfigured device interfaces and links by enforcing two new bandwidth invariants in both the CLI (immediate, fee-free feedback) and the on-chain serviceability program (source-of-truth so the invariants hold regardless of the client):

  • CYOA/DIA device interfaces require a non-zero --bandwidth at device interface create and device interface update. On update the check fires only when the transaction is changing CYOA, DIA, or bandwidth, so legacy zero-bandwidth CYOA/DIA interfaces already onchain can still be updated for unrelated fields without first being repaired.
  • Link create requires interface[a|z].bandwidth >= link.bandwidth at link wan-create and link dzx-create (DZX only checks side A; side Z is external).

All on-chain rejections surface as DoubleZeroError::InvalidBandwidth (Custom(31)); CLI rejections surface as targeted eyre errors before submitting a transaction.

Base branch

Targets tve/cli-bandwidth-default-value because the CLI changes build on top of --bandwidth being optional again (default 0bps). When that branch lands on main, this branch can be retargeted with no replay required.

Testing Verification

  • CLI unit tests: 7 new tests covering each rejection path (CYOA-zero, DIA-zero on create; flip-to-CYOA without bandwidth and clear-bandwidth-on-CYOA on update; WAN side A / side Z insufficient; DZX side A insufficient) plus updates to 3 pre-existing tests that built links over zero-bandwidth interfaces. cargo test -p doublezero_cli --lib — 21 targeted tests pass.
  • Onchain integration tests: new tests/bandwidth_validation_test.rs exercises each rejection end-to-end through the program-test bank (4 tests, all assert Custom(31)). Pre-existing link_onchain_allocation_test.rs, link_wan_test.rs, and topology_test.rs updated where they built physical interfaces with bandwidth=0 next to non-zero link bandwidth.
  • make rust-fmt and make rust-lint clean on the full workspace; full cargo test -p doublezero-serviceability --tests green.

Notes for reviewers

  • The on-chain update check is deliberately conditional. Quote from the comment in processors/device/interface/update.rs:

    Only enforce when the transaction is changing CYOA, DIA, or bandwidth, so legacy zero-bandwidth CYOA/DIA interfaces created before this rule can still be updated for unrelated fields without first being repaired.

  • DZX links do not validate side Z bandwidth onchain or in the CLI because side Z is external (no Interface to inspect). This mirrors the existing CYOA/MTU treatment for DZX.

Base automatically changed from tve/cli-bandwidth-default-value to main May 27, 2026 20:15
@juan-malbeclabs juan-malbeclabs enabled auto-merge (squash) May 27, 2026 20:16
@thijsvanemmerik
Copy link
Copy Markdown
Contributor

Hey Juan, small thing on DZX: side Z's interface only gets set when contributor Z runs link accept, so the create-time bandwidth check only catches side A. A too-small side Z interface would still slip through. Worth adding the same check in link accept?

Reject CYOA/DIA device interfaces with --bandwidth=0 in
device interface create and device interface update, and reject
link wan-create / link dzx-create when interface bandwidth is less
than the link's bandwidth (DZX checks side A only; side Z is
external). These are client-side validations that fail before
submitting a transaction; matching onchain enforcement lands in a
follow-up commit so the same invariants hold regardless of the
client used.
…width onchain

Mirror the CLI-side bandwidth validations in the program so the
invariants hold regardless of the client.

- process_create_device_interface rejects CYOA/DIA with
  bandwidth == 0.
- process_update_device_interface applies the same rule, but only
  when the transaction is changing CYOA, DIA, or bandwidth, so
  legacy zero-bandwidth CYOA/DIA interfaces already onchain can
  still receive unrelated updates without first being repaired.
- process_create_link rejects when side_a_iface.bandwidth <
  link.bandwidth, and same for side_z_iface when present (WAN).
  DZX side Z is external and unchecked.

All rejections reuse DoubleZeroError::InvalidBandwidth (Custom(31)).

Tests:
- New tests/bandwidth_validation_test.rs covers each new rejection
  path end-to-end via the program-test bank.
- Existing link and topology integration tests that built
  interfaces with bandwidth=0 alongside non-zero link bandwidth
  now use 100 Gbps interfaces, matching the new invariant.
…ink >= interface rule

The shared LedgerHelper::create_interface in the telemetry integration
tests built interfaces with bandwidth=0 while the same tests created
links at 10 Gbps. After the new onchain check that link.bandwidth must
be <= side_a/side_z interface bandwidth, every telemetry test that
exercised create_link via this helper began failing with
DoubleZeroError::InvalidBandwidth (Custom(31)) during setup.

Raise the helper's interface bandwidth to 100 Gbps, matching the
serviceability test fixtures already updated in
0c14b62 (link_onchain_allocation_test, link_wan_test, topology_test).
The interface is created with interface_cyoa/interface_dia = None, so
this does not interact with the CYOA/DIA bandwidth==0 rejection; it
only satisfies the link <= interface invariant.
@juan-malbeclabs juan-malbeclabs force-pushed the jo/interface_bandwidth_checks branch from 4b2d054 to 55d5617 Compare May 27, 2026 20:27
@juan-malbeclabs juan-malbeclabs disabled auto-merge May 27, 2026 20:36
@juan-malbeclabs
Copy link
Copy Markdown
Contributor Author

@thijsvanemmerik Good catch. I added the same validation in link accept as well, so side Z now gets checked against the link bandwidth when the interface is assigned.

Mirror the existing wan-create / dzx-create bandwidth checks in
`doublezero link accept`. Side Z is validated against `link.bandwidth`
when it is first bound, and side A is re-validated in case it was
lowered via `device interface update` between create and accept. Both
checks run before submitting the transaction so misconfigured commands
fail fast with the same human-readable message style as the other
link create verbs; matching onchain enforcement lands in a follow-up
commit so the invariant holds regardless of the client.
Mirror the create-time `iface.bandwidth >= link.bandwidth` invariant
in `process_accept_link` so the same rule holds at the activation
boundary regardless of the client.

- Side Z is checked for the first time (DZX side Z is unknown at
  create).
- Side A is re-checked because a contributor can lower an interface's
  bandwidth via `process_update_device_interface` between link create
  and accept (the update only enforces the CYOA/DIA non-zero rule).

Both rejections reuse `DoubleZeroError::InvalidBandwidth` (Custom(31)),
matching the create-time error code.

Tests: new `test_link_accept_rejects_insufficient_side_z_bandwidth`
and `test_link_accept_rejects_insufficient_side_a_bandwidth` in
`tests/bandwidth_validation_test.rs` exercise both paths end-to-end
via the program-test bank.
@juan-malbeclabs juan-malbeclabs enabled auto-merge (squash) May 27, 2026 21:03
@juan-malbeclabs juan-malbeclabs merged commit bbc4737 into main May 27, 2026
33 checks passed
@juan-malbeclabs juan-malbeclabs deleted the jo/interface_bandwidth_checks branch May 27, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants