Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use crate::{
instructions::GeolocationInstruction,
processors::{
geo_probe::{
create::process_create_geo_probe, delete::process_delete_geo_probe,
add_parent_device::process_add_parent_device, create::process_create_geo_probe,
delete::process_delete_geo_probe, remove_parent_device::process_remove_parent_device,
update::process_update_geo_probe,
},
program_config::{
Expand Down Expand Up @@ -43,6 +44,10 @@ pub fn process_instruction(
process_update_geo_probe(program_id, accounts, &args)?
}
GeolocationInstruction::DeleteGeoProbe => process_delete_geo_probe(program_id, accounts)?,
GeolocationInstruction::AddParentDevice => process_add_parent_device(program_id, accounts)?,
GeolocationInstruction::RemoveParentDevice(args) => {
process_remove_parent_device(program_id, accounts, &args)?
}
};

Ok(())
Expand Down
6 changes: 6 additions & 0 deletions smartcontract/programs/doublezero-geolocation/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub enum GeolocationError {
InvalidIpAddress = 5,
#[error("Maximum parent devices reached")]
MaxParentDevicesReached = 6,
#[error("Parent device already exists in probe")]
ParentDeviceAlreadyExists = 7,
#[error("Parent device not found in probe")]
ParentDeviceNotFound = 8,
#[error("Invalid serviceability program ID")]
InvalidServiceabilityProgramId = 11,
#[error("Invalid account code")]
Expand Down Expand Up @@ -43,6 +47,8 @@ mod tests {
(GeolocationError::InvalidCodeLength, 4),
(GeolocationError::InvalidIpAddress, 5),
(GeolocationError::MaxParentDevicesReached, 6),
(GeolocationError::ParentDeviceAlreadyExists, 7),
(GeolocationError::ParentDeviceNotFound, 8),
(GeolocationError::InvalidServiceabilityProgramId, 11),
(GeolocationError::InvalidAccountCode, 12),
(GeolocationError::ReferenceCountNotZero, 15),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use borsh::{BorshDeserialize, BorshSerialize};

pub use crate::processors::{
geo_probe::{create::CreateGeoProbeArgs, update::UpdateGeoProbeArgs},
geo_probe::{
create::CreateGeoProbeArgs, remove_parent_device::RemoveParentDeviceArgs,
update::UpdateGeoProbeArgs,
},
program_config::{init::InitProgramConfigArgs, update::UpdateProgramConfigArgs},
};

Expand All @@ -12,6 +15,8 @@ pub enum GeolocationInstruction {
CreateGeoProbe(CreateGeoProbeArgs),
UpdateGeoProbe(UpdateGeoProbeArgs),
DeleteGeoProbe,
AddParentDevice,
RemoveParentDevice(RemoveParentDeviceArgs),
}

#[cfg(test)]
Expand Down Expand Up @@ -55,6 +60,12 @@ mod tests {
metrics_publisher_pk: None,
}));
test_instruction(GeolocationInstruction::DeleteGeoProbe);
test_instruction(GeolocationInstruction::AddParentDevice);
test_instruction(GeolocationInstruction::RemoveParentDevice(
RemoveParentDeviceArgs {
device_pk: Pubkey::new_unique(),
},
));
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::{
error::GeolocationError, processors::check_foundation_allowlist, serializer::try_acc_write,
state::geo_probe::{GeoProbe, MAX_PARENT_DEVICES},
};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};

pub fn process_add_parent_device(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let accounts_iter = &mut accounts.iter();

let probe_account = next_account_info(accounts_iter)?;
let device_account = next_account_info(accounts_iter)?;
let program_config_account = next_account_info(accounts_iter)?;
let serviceability_globalstate_account = next_account_info(accounts_iter)?;
let payer_account = next_account_info(accounts_iter)?;
let _system_program = next_account_info(accounts_iter)?;

if !payer_account.is_signer {
msg!("Payer must be a signer");
return Err(ProgramError::MissingRequiredSignature);
}

check_foundation_allowlist(
program_config_account,
serviceability_globalstate_account,
payer_account,
program_id,
)?;

if probe_account.owner != program_id {
msg!("Invalid GeoProbe Account Owner");
return Err(ProgramError::IllegalOwner);
}
if !probe_account.is_writable {
msg!("GeoProbe account must be writable");
return Err(ProgramError::InvalidAccountData);
}

// Validate device_account belongs to the Serviceability program
let serviceability_program_id = crate::serviceability_program_id();
if *device_account.owner != serviceability_program_id {
msg!(
"Device account owner {} does not match serviceability program {}",
device_account.owner,
serviceability_program_id
);
return Err(GeolocationError::InvalidServiceabilityProgramId.into());
}

// Verify it's a valid, activated Device
let device = doublezero_serviceability::state::device::Device::try_from(device_account)?;
if device.status != doublezero_serviceability::state::device::DeviceStatus::Activated {
msg!(
"Device {} is not activated (status: {:?})",
device_account.key,
device.status
);
return Err(ProgramError::InvalidAccountData);
}

let mut probe = GeoProbe::try_from(probe_account)?;

if probe.parent_devices.contains(device_account.key) {
msg!("Device {} is already a parent device", device_account.key);
return Err(GeolocationError::ParentDeviceAlreadyExists.into());
}

if probe.parent_devices.len() >= MAX_PARENT_DEVICES {
msg!(
"Cannot add parent device: already at maximum of {}",
MAX_PARENT_DEVICES
);
return Err(GeolocationError::MaxParentDevicesReached.into());
}

probe.parent_devices.push(*device_account.key);

try_acc_write(&probe, probe_account, payer_account, accounts)?;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod add_parent_device;
pub mod create;
pub mod delete;
pub mod remove_parent_device;
pub mod update;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::{
error::GeolocationError, processors::check_foundation_allowlist, serializer::try_acc_write,
state::geo_probe::GeoProbe,
};
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};

#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq, Clone)]
pub struct RemoveParentDeviceArgs {
pub device_pk: Pubkey,
}

pub fn process_remove_parent_device(
program_id: &Pubkey,
accounts: &[AccountInfo],
args: &RemoveParentDeviceArgs,
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();

let probe_account = next_account_info(accounts_iter)?;
let program_config_account = next_account_info(accounts_iter)?;
let serviceability_globalstate_account = next_account_info(accounts_iter)?;
let payer_account = next_account_info(accounts_iter)?;
let _system_program = next_account_info(accounts_iter)?;

if !payer_account.is_signer {
msg!("Payer must be a signer");
return Err(ProgramError::MissingRequiredSignature);
}

check_foundation_allowlist(
program_config_account,
serviceability_globalstate_account,
payer_account,
program_id,
)?;

if probe_account.owner != program_id {
msg!("Invalid GeoProbe Account Owner");
return Err(ProgramError::IllegalOwner);
}
if !probe_account.is_writable {
msg!("GeoProbe account must be writable");
return Err(ProgramError::InvalidAccountData);
}

let mut probe = GeoProbe::try_from(probe_account)?;

if !probe.parent_devices.contains(&args.device_pk) {
msg!("Device {} is not a parent device", args.device_pk);
return Err(GeolocationError::ParentDeviceNotFound.into());
}

probe.parent_devices.retain(|&pk| pk != args.device_pk);

try_acc_write(&probe, probe_account, payer_account, accounts)?;

Ok(())
}
Loading