Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
- Smartcontract
- Serviceability: add `Reservation` account and `ReserveConnection`/`CloseReservation` instructions for pre-reserving connection seats on devices, with `reserved_seats` factored into capacity checks on both reservation and user creation
- Allow sentinel authority to add/remove multicast publisher and subscriber allowlist entries
- Multicast group PDA derivation now uses `code` instead of `account_index`, preventing duplicate codes at the PDA level; code-based lookups fall back to `getProgramAccounts` scan for legacy index-based accounts
- Telemetry
- Fix global monitor crash when IBRL and multicast users share the same client IP but are on different devices, by preferring non-multicast users in client IP lookups to match status device selection
- E2E tests
Expand Down
6 changes: 3 additions & 3 deletions activator/src/process/multicastgroup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ mod tests {
metrics::with_local_recorder(&recorder, || {
let mut client = create_test_client();

let (_, bump_seed) = get_multicastgroup_pda(&client.get_program_id(), 1);
let (_, bump_seed) = get_multicastgroup_pda(&client.get_program_id(), "test");
client
.expect_execute_transaction()
.with(
Expand Down Expand Up @@ -308,7 +308,7 @@ mod tests {
metrics::with_local_recorder(&recorder, || {
let mut client = create_test_client();

let (_, bump_seed) = get_multicastgroup_pda(&client.get_program_id(), 1);
let (_, bump_seed) = get_multicastgroup_pda(&client.get_program_id(), "test");

// Stateless mode: multicast_ip=UNSPECIFIED (onchain will allocate)
client
Expand Down Expand Up @@ -376,7 +376,7 @@ mod tests {
metrics::with_local_recorder(&recorder, || {
let mut client = create_test_client();

let (_, bump_seed) = get_multicastgroup_pda(&client.get_program_id(), 1);
let (_, bump_seed) = get_multicastgroup_pda(&client.get_program_id(), "test");

let mut multicastgroups = HashMap::new();
let pubkey = Pubkey::new_unique();
Expand Down
31 changes: 14 additions & 17 deletions e2e/compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,22 @@ type knownIncompat struct {
}

var knownIncompatibilities = map[string]knownIncompat{
// multicast_group_create: The MulticastGroupCreateArgs Borsh struct changed in v0.8.1.
// The index and bump_seed fields were removed. Older CLIs send the old format which
// causes Borsh deserialization failure in the current program.
"write/multicast_group_create": {minVersion: "0.8.1"},
// multicast_group_create: v0.9.1 changed PDA derivation from account_index to code.
// Older CLIs compute the wrong PDA, causing an assertion failure in the onchain program.
// testnet v0.9.0 was released before this change, so it's incompatible there too.
"write/multicast_group_create": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},

// All multicast operations that depend on multicast_group_create. When the group
// can't be created (< 0.8.1), these all fail with "MulticastGroup not found".
"write/multicast_group_wait_activated": {minVersion: "0.8.1"},
// multicast_group_update: In addition to the dependency above, v0.8.1-v0.8.8 parsed
// --max-bandwidth as a plain integer. v0.8.9 added validate_parse_bandwidth (a855ca7a)
// which accepts unit strings like "200Mbps".
"write/multicast_group_update": {minVersion: "0.8.9"},
"write/multicast_group_pub_allowlist_add": {minVersion: "0.8.1"},
"write/multicast_group_pub_allowlist_remove": {minVersion: "0.8.1"},
"write/multicast_group_sub_allowlist_add": {minVersion: "0.8.1"},
"write/user_subscribe": {minVersion: "0.8.1"},
"write/multicast_group_sub_allowlist_remove": {minVersion: "0.8.1"},
"write/multicast_group_get": {minVersion: "0.8.1"},
"write/multicast_group_delete": {minVersion: "0.8.1"},
// can't be created, these all fail with "MulticastGroup not found".
"write/multicast_group_wait_activated": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_update": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_pub_allowlist_add": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_pub_allowlist_remove": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_sub_allowlist_add": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/user_subscribe": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_sub_allowlist_remove": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_get": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},
"write/multicast_group_delete": {minVersion: "0.9.0", envOverride: map[string]string{"testnet": "0.9.1"}},

// set-health commands: The CLI subcommand was added in commit eb7ea308 (Jan 16).
// mainnet-beta v0.8.2 was built Jan 13 (before set-health) → doesn't have it.
Expand Down
2 changes: 1 addition & 1 deletion smartcontract/cli/src/multicastgroup/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ mod tests {
fn test_cli_multicastgroup_delete() {
let mut client = create_test_client();

let (mgroup_pubkey, _bump_seed) = get_multicastgroup_pda(&client.get_program_id(), 1);
let (mgroup_pubkey, _bump_seed) = get_multicastgroup_pda(&client.get_program_id(), "test");
let signature = Signature::from([
120, 138, 162, 185, 59, 209, 241, 157, 71, 157, 74, 131, 4, 87, 54, 28, 38, 180, 222,
82, 64, 62, 61, 62, 22, 46, 17, 203, 187, 136, 62, 43, 11, 38, 235, 17, 239, 82, 240,
Expand Down
6 changes: 3 additions & 3 deletions smartcontract/cli/src/multicastgroup/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ mod tests {
Ok(devices)
});

let (mgroup_pubkey, _bump_seed) = get_multicastgroup_pda(&client.get_program_id(), 1);
let (mgroup_pubkey, _bump_seed) = get_multicastgroup_pda(&client.get_program_id(), "test");

let user1_pk = Pubkey::from_str_const("11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1");
let user1 = User {
Expand Down Expand Up @@ -358,7 +358,7 @@ mod tests {
.execute(&client, &mut output);
assert!(res.is_ok(), "I should find a item by pubkey");
let output_str = String::from_utf8(output).unwrap();
assert_eq!(output_str, "account: G4DjGHreV54t5yeNuSHi5iVcT5Qkykuj43pWWdSsP3dj\r\ncode: test\r\nmulticast_ip: 10.0.0.1\r\nmax_bandwidth: 1Gbps\r\nstatus: activated\r\nowner: G4DjGHreV54t5yeNuSHi5iVcT5Qkykuj43pWWdSsP3dj\n\r\nallowlist:\r\n account | mode | client_ip | user_payer \n G4DjGHreV54t5yeNuSHi5iVcT5Qkykuj43pWWdSsP3dj | P+S | 192.168.1.1 | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n\r\nusers:\r\n account | multicast_mode | device | location | cyoa_type | client_ip | tunnel_id | tunnel_net | dz_ip | status | owner \n 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 | P | 11111111111111111111111111111111 | | GREOverDIA | 192.168.1.1 | 12345 | 10.0.0.0/32 | 10.0.0.2 | activated | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n");
assert_eq!(output_str, "account: 8BPMVhpGxZrAowXyBUtkATgAFB2dz7sKAq5yKgospWPp\r\ncode: test\r\nmulticast_ip: 10.0.0.1\r\nmax_bandwidth: 1Gbps\r\nstatus: activated\r\nowner: 8BPMVhpGxZrAowXyBUtkATgAFB2dz7sKAq5yKgospWPp\n\r\nallowlist:\r\n account | mode | client_ip | user_payer \n 8BPMVhpGxZrAowXyBUtkATgAFB2dz7sKAq5yKgospWPp | P+S | 192.168.1.1 | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n\r\nusers:\r\n account | multicast_mode | device | location | cyoa_type | client_ip | tunnel_id | tunnel_net | dz_ip | status | owner \n 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 | P | 11111111111111111111111111111111 | | GREOverDIA | 192.168.1.1 | 12345 | 10.0.0.0/32 | 10.0.0.2 | activated | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n");

// Expected success
let mut output = Vec::new();
Expand All @@ -368,6 +368,6 @@ mod tests {
.execute(&client, &mut output);
assert!(res.is_ok(), "I should find a item by code");
let output_str = String::from_utf8(output).unwrap();
assert_eq!(output_str, "account: G4DjGHreV54t5yeNuSHi5iVcT5Qkykuj43pWWdSsP3dj\r\ncode: test\r\nmulticast_ip: 10.0.0.1\r\nmax_bandwidth: 1Gbps\r\nstatus: activated\r\nowner: G4DjGHreV54t5yeNuSHi5iVcT5Qkykuj43pWWdSsP3dj\n\r\nallowlist:\r\n account | mode | client_ip | user_payer \n G4DjGHreV54t5yeNuSHi5iVcT5Qkykuj43pWWdSsP3dj | P+S | 192.168.1.1 | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n\r\nusers:\r\n account | multicast_mode | device | location | cyoa_type | client_ip | tunnel_id | tunnel_net | dz_ip | status | owner \n 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 | P | 11111111111111111111111111111111 | | GREOverDIA | 192.168.1.1 | 12345 | 10.0.0.0/32 | 10.0.0.2 | activated | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n");
assert_eq!(output_str, "account: 8BPMVhpGxZrAowXyBUtkATgAFB2dz7sKAq5yKgospWPp\r\ncode: test\r\nmulticast_ip: 10.0.0.1\r\nmax_bandwidth: 1Gbps\r\nstatus: activated\r\nowner: 8BPMVhpGxZrAowXyBUtkATgAFB2dz7sKAq5yKgospWPp\n\r\nallowlist:\r\n account | mode | client_ip | user_payer \n 8BPMVhpGxZrAowXyBUtkATgAFB2dz7sKAq5yKgospWPp | P+S | 192.168.1.1 | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n\r\nusers:\r\n account | multicast_mode | device | location | cyoa_type | client_ip | tunnel_id | tunnel_net | dz_ip | status | owner \n 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 | P | 11111111111111111111111111111111 | | GREOverDIA | 192.168.1.1 | 12345 | 10.0.0.0/32 | 10.0.0.2 | activated | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo1 \n");
}
}
2 changes: 1 addition & 1 deletion smartcontract/cli/src/multicastgroup/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ mod tests {
fn test_cli_multicastgroup_update() {
let mut client = create_test_client();

let (pda_pubkey, _bump_seed) = get_multicastgroup_pda(&client.get_program_id(), 1);
let (pda_pubkey, _bump_seed) = get_multicastgroup_pda(&client.get_program_id(), "test");
let signature = Signature::from([
120, 138, 162, 185, 59, 209, 241, 157, 71, 157, 74, 131, 4, 87, 54, 28, 38, 180, 222,
82, 64, 62, 61, 62, 22, 46, 17, 203, 187, 136, 62, 43, 11, 38, 235, 17, 239, 82, 240,
Expand Down
4 changes: 2 additions & 2 deletions smartcontract/programs/doublezero-serviceability/src/pda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ pub fn get_user_pda(program_id: &Pubkey, ip: &Ipv4Addr, user_type: UserType) ->
)
}

pub fn get_multicastgroup_pda(program_id: &Pubkey, index: u128) -> (Pubkey, u8) {
pub fn get_multicastgroup_pda(program_id: &Pubkey, code: &str) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[SEED_PREFIX, SEED_MULTICAST_GROUP, &index.to_le_bytes()],
&[SEED_PREFIX, SEED_MULTICAST_GROUP, code.as_bytes()],
program_id,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
error::DoubleZeroError,
pda::get_multicastgroup_pda,
seeds::{SEED_MULTICAST_GROUP, SEED_PREFIX},
serializer::{try_acc_create, try_acc_write},
serializer::try_acc_create,
state::{accounttype::AccountType, globalstate::GlobalState, multicastgroup::*},
};
use borsh::BorshSerialize;
Expand Down Expand Up @@ -72,12 +72,10 @@ pub fn process_create_multicastgroup(
assert!(mgroup_account.is_writable, "PDA Account is not writable");

// Parse the global state account & check if the payer is in the allowlist
let mut globalstate = GlobalState::try_from(globalstate_account)?;
globalstate.account_index += 1;
let globalstate = GlobalState::try_from(globalstate_account)?;

// Get the PDA pubkey and bump seed for the account multicastgroup & check if it matches the account
let (expected_pda_account, bump_seed) =
get_multicastgroup_pda(program_id, globalstate.account_index);
let (expected_pda_account, bump_seed) = get_multicastgroup_pda(program_id, &code);
assert_eq!(
mgroup_account.key, &expected_pda_account,
"Invalid MulticastGroup Pubkey"
Expand All @@ -94,7 +92,7 @@ pub fn process_create_multicastgroup(
let multicastgroup = MulticastGroup {
account_type: AccountType::MulticastGroup,
owner: value.owner,
index: globalstate.account_index,
index: 0,
bump_seed,
tenant_pk: Pubkey::default(),
code,
Expand All @@ -114,11 +112,10 @@ pub fn process_create_multicastgroup(
&[
SEED_PREFIX,
SEED_MULTICAST_GROUP,
&globalstate.account_index.to_le_bytes(),
multicastgroup.code.as_bytes(),
&[bump_seed],
],
)?;
try_acc_write(&globalstate, globalstate_account, payer_account, accounts)?;

Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,7 @@ async fn test_multicast_publisher_allowlist() {
/*****************************************************************************************************************************************************/
println!("🟢 2. Create MulticastGroup...");

let globalstate = get_account_data(&mut banks_client, globalstate_pubkey)
.await
.expect("Unable to get Account")
.get_global_state()
.unwrap();

let (multicastgroup_pubkey, _) =
get_multicastgroup_pda(&program_id, globalstate.account_index + 1);
let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, "test");

execute_transaction(
&mut banks_client,
Expand Down Expand Up @@ -94,8 +87,6 @@ async fn test_multicast_publisher_allowlist() {
/*****************************************************************************************************************************************************/
println!("🟢 3. Activate MulticastGroup...");

let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, 1);

execute_transaction(
&mut banks_client,
recent_blockhash,
Expand Down Expand Up @@ -181,7 +172,7 @@ async fn test_multicast_publisher_allowlist() {
/*****************************************************************************************************************************************************/
println!("🟢 6. Remove Allowlist ...");

let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, 1);
let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, "test");

execute_transaction(
&mut banks_client,
Expand Down Expand Up @@ -264,14 +255,7 @@ async fn test_multicast_publisher_allowlist_sentinel_authority() {
.await;

// 3. Create and activate a multicast group (owned by payer, NOT sentinel)
let globalstate = get_account_data(&mut banks_client, globalstate_pubkey)
.await
.expect("Unable to get Account")
.get_global_state()
.unwrap();

let (multicastgroup_pubkey, _) =
get_multicastgroup_pda(&program_id, globalstate.account_index + 1);
let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, "sentinel-test");

execute_transaction(
&mut banks_client,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,7 @@ async fn test_multicast_subscriber_allowlist() {
/*****************************************************************************************************************************************************/
println!("🟢 2. Create MulticastGroup...");

let globalstate = get_account_data(&mut banks_client, globalstate_pubkey)
.await
.expect("Unable to get Account")
.get_global_state()
.unwrap();

let (multicastgroup_pubkey, _) =
get_multicastgroup_pda(&program_id, globalstate.account_index + 1);
let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, "test");

execute_transaction(
&mut banks_client,
Expand Down Expand Up @@ -94,8 +87,6 @@ async fn test_multicast_subscriber_allowlist() {
/*****************************************************************************************************************************************************/
println!("🟢 3. Activate MulticastGroup...");

let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, 1);

execute_transaction(
&mut banks_client,
recent_blockhash,
Expand Down Expand Up @@ -264,14 +255,7 @@ async fn test_multicast_subscriber_allowlist_sentinel_authority() {
.await;

// 3. Create and activate a multicast group (owned by payer, NOT sentinel)
let globalstate = get_account_data(&mut banks_client, globalstate_pubkey)
.await
.expect("Unable to get Account")
.get_global_state()
.unwrap();

let (multicastgroup_pubkey, _) =
get_multicastgroup_pda(&program_id, globalstate.account_index + 1);
let (multicastgroup_pubkey, _) = get_multicastgroup_pda(&program_id, "sentinel-test");

execute_transaction(
&mut banks_client,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,7 @@ async fn setup_fixture() -> TestFixture {
.await;

// 7. Create two multicast groups and activate them
let gs = get_globalstate(&mut banks_client, globalstate_pubkey).await;
let (mgroup1_pubkey, _) = get_multicastgroup_pda(&program_id, gs.account_index + 1);
let (mgroup1_pubkey, _) = get_multicastgroup_pda(&program_id, "group1");
execute_transaction(
&mut banks_client,
recent_blockhash,
Expand Down Expand Up @@ -285,8 +284,7 @@ async fn setup_fixture() -> TestFixture {
)
.await;

let gs = get_globalstate(&mut banks_client, globalstate_pubkey).await;
let (mgroup2_pubkey, _) = get_multicastgroup_pda(&program_id, gs.account_index + 1);
let (mgroup2_pubkey, _) = get_multicastgroup_pda(&program_id, "group2");
execute_transaction(
&mut banks_client,
recent_blockhash,
Expand Down
Loading
Loading