Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ All notable changes to this project will be documented in this file.
- Serviceability: require DeactivateMulticastGroup to only close multicast group accounts when both `publisher_count` and `subscriber_count` are zero, preventing deletion of groups that still have active publishers or subscribers.
- Deprecated the user suspend status, as it is no longer used.
- Serviceability: enforce that CloseAccountUser instructions verify the target user has no multicast publishers or subscribers (both `publishers` and `subscribers` are empty) before closing, and add regression coverage for this behavior.
- Removed device and user allowlist functionality, updating the global state, initialization flow, tests, and processors accordingly, and cleaning up unused account checks.
- SetGlobalConfig, ActivateDevice, UpdateDevice and CloseAccountDevice instructions updated to manage resource accounts.
- Telemetry
- Add gNMI tunnel client for state collection
- Activator
- fix(activator): ip_to_index fn honors ip range #2658
- E2E tests
- Add influxdb and device-health-oracle containers
- SDK
- Commands for setting global config, activating devices, updating devices, and closing device accounts now manage resource accounts.

### Breaking

Expand Down
82 changes: 79 additions & 3 deletions activator/src/process/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,19 @@ mod tests {
use doublezero_program_common::types::NetworkV4;
use doublezero_sdk::{
AccountData, AccountType, CurrentInterfaceVersion, DeviceType, InterfaceStatus,
InterfaceType, LoopbackType,
InterfaceType, LoopbackType, ResourceExtensionOwned, ResourceType,
};
use doublezero_serviceability::{
id_allocator::IdAllocator,
instructions::DoubleZeroInstruction,
ip_allocator::IpAllocator,
pda::get_resource_extension_pda,
processors::device::{
activate::DeviceActivateArgs,
closeaccount::DeviceCloseAccountArgs,
interface::{activate::DeviceInterfaceActivateArgs, unlink::DeviceInterfaceUnlinkArgs},
},
state::resource_extension::Allocator,
};
use metrics_util::debugging::DebuggingRecorder;
use mockall::{predicate, Sequence};
Expand Down Expand Up @@ -207,12 +211,22 @@ mod tests {
expected_interfaces[1].ip_net = "1.1.1.1/32".parse().unwrap();
expected_interfaces[1].node_segment_idx = 1;

let device_clone = device.clone();
client
.expect_get()
.times(1)
.in_sequence(&mut seq)
.with(predicate::eq(device_pubkey))
.returning(move |_| Ok(AccountData::Device(device_clone.clone())));

client
.expect_execute_transaction()
.times(1)
.in_sequence(&mut seq)
.with(
predicate::eq(DoubleZeroInstruction::ActivateDevice(DeviceActivateArgs {})),
predicate::eq(DoubleZeroInstruction::ActivateDevice(DeviceActivateArgs {
resource_count: 3,
})),
predicate::always(),
)
.returning(|_, _| Ok(Signature::new_unique()));
Expand Down Expand Up @@ -291,12 +305,74 @@ mod tests {
.in_sequence(&mut seq)
.with(
predicate::eq(DoubleZeroInstruction::CloseAccountDevice(
DeviceCloseAccountArgs {},
DeviceCloseAccountArgs { resource_count: 3 },
)),
predicate::always(),
)
.returning(|_, _| Ok(Signature::new_unique()));

let (resource1_pk, _, _) = get_resource_extension_pda(
&client.get_program_id(),
ResourceType::TunnelIds(device_pubkey, 0),
);
let resource1 = ResourceExtensionOwned {
account_type: AccountType::ResourceExtension,
owner: Pubkey::default(),
bump_seed: 0,
associated_with: device_pubkey,
allocator: Allocator::Id(IdAllocator::new((1, 100)).unwrap()),
storage: vec![],
};

let (resource2_pk, _, _) = get_resource_extension_pda(
&client.get_program_id(),
ResourceType::DzPrefixBlock(device_pubkey, 0),
);
let resource2 = ResourceExtensionOwned {
account_type: AccountType::ResourceExtension,
owner: Pubkey::default(),
bump_seed: 0,
associated_with: device_pubkey,
allocator: Allocator::Ip(
IpAllocator::new("0.0.0.0/0".parse().unwrap(), 1).unwrap(),
),
storage: vec![],
};

let (resource3_pk, _, _) = get_resource_extension_pda(
&client.get_program_id(),
ResourceType::DzPrefixBlock(device_pubkey, 1),
);
let resource3 = ResourceExtensionOwned {
account_type: AccountType::ResourceExtension,
owner: Pubkey::default(),
bump_seed: 0,
associated_with: device_pubkey,
allocator: Allocator::Ip(
IpAllocator::new("0.0.0.0/0".parse().unwrap(), 1).unwrap(),
),
storage: vec![],
};

client
.expect_get()
.with(predicate::in_hash(vec![
resource1_pk,
resource2_pk,
resource3_pk,
]))
.returning(move |pk| {
if pk == resource1_pk {
Ok(AccountData::ResourceExtension(resource1.clone()))
} else if pk == resource2_pk {
Ok(AccountData::ResourceExtension(resource2.clone()))
} else if pk == resource3_pk {
Ok(AccountData::ResourceExtension(resource3.clone()))
} else {
Err(eyre::eyre!("Unexpected resource pk"))
}
});

process_device_event(
&client,
&device_pubkey,
Expand Down
25 changes: 23 additions & 2 deletions activator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
pub mod utils {
use doublezero_sdk::{AccountData, AccountType, DoubleZeroClient, MockDoubleZeroClient};
use doublezero_serviceability::{
pda::{get_device_pda, get_globalstate_pda, get_link_pda, get_user_old_pda},
state::globalstate::GlobalState,
pda::{
get_device_pda, get_globalconfig_pda, get_globalstate_pda, get_link_pda,
get_user_old_pda,
},
state::{globalconfig::GlobalConfig, globalstate::GlobalState},
};
use mockall::predicate;
use solana_sdk::pubkey::Pubkey;
Expand Down Expand Up @@ -33,13 +36,31 @@ pub mod utils {
health_oracle_pk: payer,
};

let (globalconfig_pubkey, _) = get_globalconfig_pda(&program_id);
let globalconfig = GlobalConfig {
account_type: AccountType::GlobalConfig,
owner: payer,
bump_seed: 0,
local_asn: 0,
remote_asn: 0,
device_tunnel_block: "1.0.0.0/24".parse().unwrap(),
user_tunnel_block: "2.0.0.0/24".parse().unwrap(),
multicastgroup_block: "224.0.0.0/24".parse().unwrap(),
next_bgp_community: 0,
};

client.expect_get_payer().returning(move || payer);

client
.expect_get()
.with(predicate::eq(globalstate_pubkey))
.returning(move |_| Ok(AccountData::GlobalState(globalstate.clone())));

client
.expect_get()
.with(predicate::eq(globalconfig_pubkey))
.returning(move |_| Ok(AccountData::GlobalConfig(globalconfig.clone())));

client
}

Expand Down
8 changes: 6 additions & 2 deletions client/doublezero/src/cli/resource.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use clap::{Args, Subcommand};
use doublezero_cli::resource::{
allocate::AllocateResourceCliCommand, create::CreateResourceCliCommand,
deallocate::DeallocateResourceCliCommand, get::GetResourceCliCommand,
allocate::AllocateResourceCliCommand, close::CloseResourceCliCommand,
create::CreateResourceCliCommand, deallocate::DeallocateResourceCliCommand,
get::GetResourceCliCommand,
};

#[derive(Args, Debug)]
Expand All @@ -24,4 +25,7 @@ pub enum ResourceCommands {
/// Get a resource
#[clap()]
Get(GetResourceCliCommand),
/// Close a resource
#[clap()]
Close(CloseResourceCliCommand),
}
1 change: 1 addition & 0 deletions client/doublezero/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ async fn main() -> eyre::Result<()> {
cli::resource::ResourceCommands::Create(args) => args.execute(&client, &mut handle),
cli::resource::ResourceCommands::Deallocate(args) => args.execute(&client, &mut handle),
cli::resource::ResourceCommands::Get(args) => args.execute(&client, &mut handle),
cli::resource::ResourceCommands::Close(args) => args.execute(&client, &mut handle),
},

Command::Export(args) => args.execute(&client, &mut handle),
Expand Down
9 changes: 7 additions & 2 deletions smartcontract/cli/src/doublezerocommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ use doublezero_sdk::{
},
programconfig::get::GetProgramConfigCommand,
resource::{
allocate::AllocateResourceCommand, create::CreateResourceCommand,
deallocate::DeallocateResourceCommand, get::GetResourceCommand,
allocate::AllocateResourceCommand, closeaccount::CloseResourceCommand,
create::CreateResourceCommand, deallocate::DeallocateResourceCommand,
get::GetResourceCommand,
},
user::{
create::CreateUserCommand, create_subscribe::CreateSubscribeUserCommand,
Expand Down Expand Up @@ -275,6 +276,7 @@ pub trait CliCommand {
&self,
cmd: GetResourceCommand,
) -> eyre::Result<(Pubkey, ResourceExtensionOwned)>;
fn close_resource(&self, cmd: CloseResourceCommand) -> eyre::Result<Signature>;
}

pub struct CliCommandImpl<'a> {
Expand Down Expand Up @@ -648,4 +650,7 @@ impl CliCommand for CliCommandImpl<'_> {
) -> eyre::Result<(Pubkey, ResourceExtensionOwned)> {
cmd.execute(self.client)
}
fn close_resource(&self, cmd: CloseResourceCommand) -> eyre::Result<Signature> {
cmd.execute(self.client)
}
}
120 changes: 120 additions & 0 deletions smartcontract/cli/src/resource/close.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use super::ResourceType;
use crate::{
doublezerocommand::CliCommand,
requirements::{CHECK_BALANCE, CHECK_ID_JSON},
};
use clap::Args;
use doublezero_sdk::commands::resource::{
closeaccount::CloseResourceCommand, get::GetResourceCommand,
};
use std::io::Write;

#[derive(Args, Debug)]
pub struct CloseResourceCliCommand {
// Type of resource extension to allocate
#[arg(long)]
pub resource_type: ResourceType,
// Associated public key (only for DzPrefixBlock)
#[arg(long)]
pub associated_pubkey: Option<String>,
// Index (only for DzPrefixBlock)
#[arg(long)]
pub index: Option<usize>,
}

impl CloseResourceCliCommand {
pub fn execute<C: CliCommand, W: Write>(self, client: &C, out: &mut W) -> eyre::Result<()> {
// Check requirements
client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?;

let resource_type = super::resource_type_from(
self.resource_type,
self.associated_pubkey.as_ref().and_then(|s| s.parse().ok()),
self.index,
);

let (_, _) = client
.get_resource(GetResourceCommand { resource_type })
.map_err(|_| eyre::eyre!("Resource not found"))?;

let signature = client.close_resource(CloseResourceCommand { resource_type })?;
writeln!(out, "Signature: {signature}",)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::tests::utils::create_test_client;
use doublezero_sdk::{
commands::resource::{closeaccount::CloseResourceCommand, get::GetResourceCommand},
get_resource_extension_pda, AccountType, ResourceExtensionOwned,
};
use mockall::predicate;
use solana_sdk::{pubkey::Pubkey, signature::Signature};

#[test]
fn test_cli_resource_close() {
let mut client = create_test_client();

let (pda_pubkey, _, _) = get_resource_extension_pda(
&client.get_program_id(),
doublezero_sdk::ResourceType::DeviceTunnelBlock,
);

let owner = Pubkey::new_unique();

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,
139, 130, 217, 227, 214, 9, 242, 141, 223, 94, 29, 184, 110, 62, 32, 87, 137, 63, 139,
100, 221, 20, 137, 4, 5,
]);

let resource = ResourceExtensionOwned {
account_type: AccountType::ResourceExtension,
owner,
bump_seed: 0,
associated_with: Pubkey::default(),
allocator: doublezero_serviceability::state::resource_extension::Allocator::Ip(
doublezero_serviceability::ip_allocator::IpAllocator::new(
"10.0.0.0/24".parse().unwrap(),
1,
)
.unwrap(),
),
storage: vec![0x0; 8],
};

let resource_type = doublezero_sdk::ResourceType::DeviceTunnelBlock;

client
.expect_check_requirements()
.with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE))
.returning(|_| Ok(()));
client
.expect_get_resource()
.with(predicate::eq(GetResourceCommand { resource_type }))
.returning(move |_| Ok((pda_pubkey, resource.clone())));

client
.expect_close_resource()
.with(predicate::eq(CloseResourceCommand { resource_type }))
.returning(move |_| Ok(signature));

let mut output = Vec::new();
let res = CloseResourceCliCommand {
resource_type: ResourceType::DeviceTunnelBlock,
associated_pubkey: None,
index: None,
}
.execute(&client, &mut output);
assert!(res.is_ok());
let output_str = String::from_utf8(output).unwrap();
assert_eq!(
output_str,"Signature: 3QnHBSdd4doEF6FgpLCejqEw42UQjfvNhQJwoYDSpoBszpCCqVft4cGoneDCnZ6Ez3ujzavzUu85u6F79WtLhcsv\n"
);
}
}
8 changes: 4 additions & 4 deletions smartcontract/cli/src/resource/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use tabled::{Table, Tabled};
pub struct GetResourceCliCommand {
// Type of resource extension to allocate
#[arg(long)]
pub resource_extension_type: ResourceType,
pub resource_type: ResourceType,
// Associated public key (only for DzPrefixBlock)
#[arg(long)]
pub associated_pubkey: Option<String>,
Expand All @@ -21,7 +21,7 @@ pub struct GetResourceCliCommand {
impl From<GetResourceCliCommand> for GetResourceCommand {
fn from(cmd: GetResourceCliCommand) -> Self {
let resource_type = super::resource_type_from(
cmd.resource_extension_type,
cmd.resource_type,
cmd.associated_pubkey.as_ref().and_then(|s| s.parse().ok()),
cmd.index,
);
Expand Down Expand Up @@ -70,7 +70,7 @@ mod tests {
fn test_from_cli_to_command() {
let pk = Pubkey::new_unique();
let cli_cmd = GetResourceCliCommand {
resource_extension_type: ResourceType::DzPrefixBlock,
resource_type: ResourceType::DzPrefixBlock,
associated_pubkey: Some(pk.to_string()),
index: Some(1),
};
Expand All @@ -81,7 +81,7 @@ mod tests {
#[test]
fn test_execute_prints_table() {
let cli_cmd = GetResourceCliCommand {
resource_extension_type: ResourceType::LinkIds,
resource_type: ResourceType::LinkIds,
associated_pubkey: None,
index: None,
};
Expand Down
Loading
Loading