Skip to content

feat(smartcontract, activator): remove multiple tunnel restriction (#2725)#2728

Open
bgm-malbeclabs wants to merge 9 commits intomainfrom
bgm/simultaneous_tunnels_cli
Open

feat(smartcontract, activator): remove multiple tunnel restriction (#2725)#2728
bgm-malbeclabs wants to merge 9 commits intomainfrom
bgm/simultaneous_tunnels_cli

Conversation

@bgm-malbeclabs
Copy link
Contributor

@bgm-malbeclabs bgm-malbeclabs commented Jan 26, 2026

Summary of Changes

This PR adds the ability from the CLI and the smart contract to add simultaneous tunnels: a single unicast and a single mulitcast tunnel (subscriber or publisher only).

Closes #2735

Testing Verification

  • E2E tests were extended to include creating multiple tunnels for a single user
  • The coexistence tests that were skipped are now enabled and pass
  • This also adds tests to verify that pubkey is always the last deserialized field in both go and rust
  • Tests to validate the default ip and edge cases
  • Claude helped extend the tests

@bgm-malbeclabs bgm-malbeclabs marked this pull request as draft January 26, 2026 22:58
@bgm-malbeclabs bgm-malbeclabs force-pushed the bgm/simultaneous_tunnels_cli branch from 14a2136 to 231d212 Compare January 27, 2026 01:06
@bgm-malbeclabs bgm-malbeclabs self-assigned this Jan 27, 2026
@bgm-malbeclabs bgm-malbeclabs force-pushed the bgm/simultaneous_tunnels_cli branch 2 times, most recently from 10c95c2 to 4bda09f Compare January 28, 2026 20:18
@bgm-malbeclabs bgm-malbeclabs force-pushed the bgm/simultaneous_tunnels_cli branch 2 times, most recently from 1efa5cf to 3b7754c Compare January 29, 2026 22:09
@bgm-malbeclabs bgm-malbeclabs force-pushed the bgm/simultaneous_tunnels_cli branch from 3b7754c to 42711d2 Compare January 29, 2026 22:39
@bgm-malbeclabs bgm-malbeclabs changed the title DNM: feat(cli): remove multiple tunnel restriction (#2725) feat(cli, smartcontract, activator): remove multiple tunnel restriction (#2725) Jan 29, 2026
@bgm-malbeclabs bgm-malbeclabs marked this pull request as ready for review January 29, 2026 22:46
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the restriction that allowed only a single tunnel per user, enabling users to maintain both a unicast (IBRL) tunnel and a multicast tunnel simultaneously. The implementation creates separate user accounts per tunnel type while sharing the same client IP, with each tunnel connecting to potentially different devices.

Changes:

  • Added tunnel_endpoint field to User state to track device-side GRE endpoints for multiple concurrent tunnels
  • Modified CLI and smart contract to support creating multiple user accounts (one per UserType) for the same client IP
  • Extended E2E tests to validate concurrent IBRL+multicast tunnel scenarios

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
smartcontract/programs/doublezero-serviceability/src/state/user.rs Added tunnel_endpoint field and capability helper methods to User struct
smartcontract/programs/doublezero-serviceability/src/processors/user/activate.rs Updated activate processor to handle tunnel_endpoint parameter
smartcontract/sdk/rs/src/commands/user/activate.rs Added tunnel_endpoint parameter to ActivateUserCommand
client/doublezero/src/command/connect.rs Modified connection logic to support multiple user types per IP and device selection
client/doublezero/src/dzd_latency.rs Added exclude_ips parameter to prevent selecting same device for concurrent tunnels
e2e/ibrl_multicast_coexistence_test.go Added comprehensive tests for concurrent IBRL+multicast tunnels
activator/src/process/user.rs Updated to use new capability helper methods and set tunnel_endpoint

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// For single-client scenarios where the same user has both IBRL and multicast, both tunnels
// use sequential addresses in the 169.254.0.x range (e.g., IBRL on .0/.1, multicast on .2/.3).
func verifyConcurrentMulticastPIMAdjacency(t *testing.T, log *slog.Logger, device *devnet.Device) {
// log.Info("==> Verifying concurrent multicast PIM adjacency", "deviceCode", device.Code)
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented-out log statement.

Suggested change
// log.Info("==> Verifying concurrent multicast PIM adjacency", "deviceCode", device.Code)

Copilot uses AI. Check for mistakes.

// Give extra time for agent config to be applied to device before verification
log.Info("==> Waiting for agent config to be applied to device")
time.Sleep(15 * time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to use a poll/eventually check here instead of the 15s sleep?


// TestE2E_SingleClient_IBRL_Then_Multicast_Publisher tests a single client connecting
// to IBRL first, then adding multicast publisher capability.
func TestE2E_SingleClient_IBRL_Then_Multicast_Publisher(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to combine the subscriber and publisher part of these (1 DZD, 2 clients)?

Copy link
Contributor

@elitegreg elitegreg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Did you verify that doublezero status with two tunnels connected to separate devices still shows that the user is connect to the "best" devices?

UserType::Multicast => !user.publishers.is_empty(),
};
// Use device public_ip as tunnel_endpoint
let tunnel_endpoint = device_state.device.public_ip;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elitegreg is this correct or should it use the first ip from device_state.device.dz_prefixes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct. He's setting the tunnel endpoint address (what device to connect to).

@vihu
Copy link
Contributor

vihu commented Jan 30, 2026

Not included in this PR but potentially relevant:

const AIRDROP_USER_RENT_LAMPORTS_BYTES: usize = 236 * 3; // 236 bytes per User account x 3 accounts = 708 bytes

I think this should be changed to:

  const AIRDROP_USER_RENT_LAMPORTS_BYTES: usize = 240 * 3; // 240 bytes per User account

To account for the 4 bytes for the newly added tunnel_endpoint.

@elitegreg
Copy link
Contributor

  const AIRDROP_USER_RENT_LAMPORTS_BYTES: usize = 240 * 3; // 240 bytes per User account

To account for the 4 bytes for the newly added tunnel_endpoint.

Maybe we can add a test for this so it doesn't get forgotten is User grows?

@bgm-malbeclabs
Copy link
Contributor Author

  const AIRDROP_USER_RENT_LAMPORTS_BYTES: usize = 240 * 3; // 240 bytes per User account

To account for the 4 bytes for the newly added tunnel_endpoint.

Maybe we can add a test for this so it doesn't get forgotten is User grows?

added a test - good call out. Also added tests to validate go and rust deserialization given the requirement for pubkey to be the last field


// Give time for agent to apply the configuration
log.Info("==> Waiting for agent to apply configuration")
time.Sleep(10 * time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this sleep if we're waiting for client tunnels to be up below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea probably not

)
.unwrap();

// Use device public_ip as tunnel_endpoint (preserve existing if set)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we were separating out the 2nd tunnel endpoint work. If this lands as is, what else is needed to enable that, at least on the serviceability/client front? I'm worried about fracturing support for it across multiple PRs where some are multi-feature.

@bgm-malbeclabs bgm-malbeclabs changed the title feat(cli, smartcontract, activator): remove multiple tunnel restriction (#2725) feat(smartcontract, activator): remove multiple tunnel restriction (#2725) Jan 31, 2026
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.

Update smart contract to allow for a single unicast and a single multicast tunnel

5 participants