geolocation: add program scaffolding and ProgramConfig instructions#3083
geolocation: add program scaffolding and ProgramConfig instructions#3083
Conversation
faa0ca6 to
b909d19
Compare
| let (expected_pda, _) = get_program_config_pda(program_id); | ||
| if program_config_account.key != &expected_pda { | ||
| msg!("Invalid ProgramConfig Pubkey"); | ||
| return Err(ProgramError::InvalidSeeds); | ||
| } |
There was a problem hiding this comment.
This actually isn't needed when updating the program config since there is only one account that can be the program config (the one you initialized the program with). The discriminator defines this account, so the try_from below ensures that this account is the program config.
It doesn't hurt to have this check. But it doesn't add any safety, either
| let (expected_config_pda, _) = get_program_config_pda(program_id); | ||
| if program_config_account.key != &expected_config_pda { | ||
| msg!("Invalid ProgramConfig PDA"); | ||
| return Err(ProgramError::InvalidSeeds); | ||
| } |
| // Verify serviceability GlobalState PDA address | ||
| let (expected_gs_pda, _) = | ||
| doublezero_serviceability::pda::get_globalstate_pda(serviceability_program_id); | ||
| if serviceability_globalstate_account.key != &expected_gs_pda { | ||
| msg!("Invalid Serviceability GlobalState PDA"); | ||
| return Err(ProgramError::InvalidSeeds); | ||
| } |
There was a problem hiding this comment.
Same as your program config, there is only one global state on the Serviceability program
| use core::fmt; | ||
| use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError}; | ||
|
|
||
| #[derive(BorshSerialize, Debug, PartialEq, Clone)] |
There was a problem hiding this comment.
You should probably derive BorshDeserialize or BorshDeserializeIncremental
| let out = Self { | ||
| account_type: BorshDeserialize::deserialize(&mut data).unwrap_or_default(), | ||
| bump_seed: BorshDeserialize::deserialize(&mut data).unwrap_or_default(), | ||
| version: BorshDeserialize::deserialize(&mut data).unwrap_or_default(), | ||
| min_compatible_version: BorshDeserialize::deserialize(&mut data).unwrap_or_default(), | ||
| }; |
There was a problem hiding this comment.
If you derive either borsh deserialize traits, you wouldn't have to deserialize each element like this
| min_compatible_version: 1, | ||
| }; | ||
| let err = val.validate(); | ||
| assert!(err.is_err()); |
There was a problem hiding this comment.
unwrap_err below will panic if this line were false
| fn test_state_programconfig_try_from_invalid_account_type() { | ||
| let data = [AccountType::None as u8]; | ||
| let result = GeolocationProgramConfig::try_from(&data[..]); | ||
| assert!(result.is_err()); |
| impl GeolocationInstruction { | ||
| pub fn pack(&self) -> Vec<u8> { | ||
| borsh::to_vec(&self).unwrap() | ||
| } | ||
|
|
||
| pub fn unpack(data: &[u8]) -> Result<Self, ProgramError> { | ||
| borsh::from_slice(data).map_err(|_| ProgramError::InvalidInstructionData) | ||
| } | ||
| } |
There was a problem hiding this comment.
These don't seem necessary since they're light wrappers around the borsh methods
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| fn test_program_id() -> Pubkey { | ||
| Pubkey::new_unique() | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_program_config_pda_is_deterministic() { | ||
| let program_id = test_program_id(); | ||
| let (pda1, bump1) = get_program_config_pda(&program_id); | ||
| let (pda2, bump2) = get_program_config_pda(&program_id); | ||
| assert_eq!(pda1, pda2); | ||
| assert_eq!(bump1, bump2); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_program_config_pda_differs_by_program_id() { | ||
| let (pda1, _) = get_program_config_pda(&Pubkey::new_unique()); | ||
| let (pda2, _) = get_program_config_pda(&Pubkey::new_unique()); | ||
| assert_ne!(pda1, pda2); | ||
| } | ||
| } |
There was a problem hiding this comment.
You can probably do away with these tests since these seem to just exercise Pubkey::find_program_address
| let mut data = Vec::with_capacity(45); | ||
| data.extend_from_slice(&3u32.to_le_bytes()); | ||
| data.extend_from_slice(&0u64.to_le_bytes()); | ||
| data.push(1); // Some | ||
| data.extend_from_slice(upgrade_authority.as_ref()); |
There was a problem hiding this comment.
Like when you deserialize the upgradeable state, you should be able to serialize it with bincode, too
smartcontract/programs/doublezero-geolocation/tests/update_program_config_test.rs
Show resolved
Hide resolved
| ) | ||
| .await; | ||
| assert!( | ||
| result.is_err(), |
There was a problem hiding this comment.
Are you expecting a specific error? Or are you okay with just checking that there is any error?
| ) | ||
| .await; | ||
| assert!( | ||
| result.is_err(), |
| solana-bincode = "2.2.1" | ||
| solana-loader-v3-interface = { version = "3.0.0", features = ["serde"] } |
There was a problem hiding this comment.
Do you want to add these to the workspace Cargo.toml?
Add the doublezero-geolocation onchain program with InitProgramConfig and UpdateProgramConfig instructions, foundation allowlist integration via serviceability CPI, and build/lint/test integration. Part 1 of 3 for RFC16 geolocation verification.
…heck Move the min_compatible_version > version invariant check to run after both fields are applied, so a version-only downgrade cannot silently create an inconsistent state where min_compatible_version > version.
6917536 to
7176113
Compare
Summary of Changes
doublezero-geolocationonchain program withInitProgramConfigandUpdateProgramConfiginstructionsDiff Breakdown
Part 1 of 3 for RFC16 geolocation verification — program scaffolding and ProgramConfig management only; GeoProbe CRUD and parent device instructions follow in subsequent PRs.
Key files (click to expand)
smartcontract/programs/doublezero-geolocation/src/state/program_config.rs— ProgramConfig account state with Borsh serialization, field-by-field deserialization, and validationsmartcontract/programs/doublezero-geolocation/src/processors/program_config/update.rs— UpdateProgramConfig processor with upgrade authority verification and partial field updatessmartcontract/programs/doublezero-geolocation/src/processors/program_config/init.rs— InitProgramConfig processor with PDA creation and upgrade authority checksmartcontract/programs/doublezero-geolocation/src/serializer.rs— Generic account create/write helpers using doublezero-program-common utilitiessmartcontract/programs/doublezero-geolocation/src/instructions.rs— Borsh-derived GeolocationInstruction enum with 2 variantssmartcontract/programs/doublezero-geolocation/src/error.rs— GeolocationError enum with thiserror derives and ProgramError conversionsmartcontract/programs/doublezero-geolocation/src/processors/mod.rs—check_foundation_allowlistshared validation via serviceability CPITesting Verification
-Dclippy::all -Dwarnings