This repository has been archived by the owner on Sep 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Abstract SNS, ANS and GPL Nameservice
- Loading branch information
1 parent
16006e6
commit cfba4f0
Showing
7 changed files
with
383 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use crate::nameservice::NameServiceParser; | ||
use crate::NameServiceError; | ||
use anchor_lang::prelude::*; | ||
use borsh::{BorshDeserialize, BorshSerialize}; | ||
|
||
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, PartialEq)] | ||
pub struct ANSNameRecord { | ||
pub parent_name: Pubkey, | ||
|
||
// The owner of this name | ||
pub owner: Pubkey, | ||
|
||
// If `Pubkey::default()` the data is unspecified. | ||
pub class: Pubkey, | ||
|
||
//TODO: Check with ANS team for the right type here. | ||
pub expires_at: u64, | ||
} | ||
|
||
impl ANSNameRecord { | ||
pub const LEN: usize = 104; | ||
} | ||
|
||
pub struct ANSNameService; | ||
|
||
impl NameServiceParser for ANSNameService { | ||
type ServiceName = ANSNameRecord; | ||
|
||
fn id_str() -> &'static str { | ||
"ALTNSZ46uaAUU7XUV6awvdorLGqAsPwa9shm7h4uP2FK" | ||
} | ||
|
||
fn unpack(data: &[u8]) -> Result<ANSNameRecord> { | ||
let record = ANSNameRecord::try_from_slice(&data)?; | ||
Ok(record) | ||
} | ||
|
||
fn from_program_id(program_id: &Pubkey) -> Option<Self> | ||
where | ||
Self: Sized, | ||
{ | ||
if program_id == &Self::id() { | ||
Some(Self) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn validate(accounts: &[AccountInfo]) -> Result<bool> { | ||
let accounts_iter = &mut accounts.iter(); | ||
|
||
let record = next_account_info(accounts_iter)?; | ||
|
||
let authority = next_account_info(accounts_iter)?; | ||
|
||
// Validate the owner | ||
Self::validate_owner(record)?; | ||
|
||
if record.data_len() != ANSNameRecord::LEN { | ||
return Err(NameServiceError::InvalidDataLength.into()); | ||
} | ||
|
||
let record_data = &record.try_borrow_data()?; | ||
let name_record = Self::unpack(&record_data)?; | ||
|
||
// The authority should be the same as the owner in the record | ||
if authority.key != &name_record.owner { | ||
return Err(ProgramError::InvalidAccountData.into()); | ||
} | ||
|
||
Ok(true) | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
programs/gpl_nameservice/src/nameservice/gpl_nameservice.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use anchor_lang::Discriminator; | ||
|
||
use crate::nameservice::NameServiceParser; | ||
use crate::NameRecord; | ||
use anchor_lang::prelude::*; | ||
|
||
pub struct GplNameService; | ||
impl NameServiceParser for GplNameService { | ||
type ServiceName = NameRecord; | ||
|
||
fn id_str() -> &'static str { | ||
"7LEuQxAEegasvBSq7dDrMregc3mrDtTyHiytNK9pU68u" | ||
} | ||
|
||
fn unpack(data: &[u8]) -> Result<NameRecord> { | ||
let record = NameRecord::try_from_slice(&data[8..])?; | ||
Ok(record) | ||
} | ||
|
||
fn from_program_id(program_id: &Pubkey) -> Option<Self> | ||
where | ||
Self: Sized, | ||
{ | ||
if program_id == &Self::id() { | ||
Some(Self) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn validate(accounts: &[AccountInfo]) -> Result<bool> { | ||
let accounts_iter = &mut accounts.iter(); | ||
|
||
let record = next_account_info(accounts_iter)?; | ||
|
||
let authority = next_account_info(accounts_iter)?; | ||
|
||
// Validate the owner | ||
Self::validate_owner(record)?; | ||
|
||
let record_data = &record.try_borrow_data()?; | ||
|
||
// The first 8 bytes should be same as the discriminator | ||
if record_data[0..8] != NameRecord::DISCRIMINATOR { | ||
return Err(ProgramError::InvalidAccountData.into()); | ||
} | ||
|
||
let name_record = Self::unpack(&record_data)?; | ||
|
||
// record.key should be the same as the PDA generated from NameRecord::SEED_PREFIX, hash of | ||
// the name, domain key and the bump seed | ||
|
||
let (expected_pda, _) = Pubkey::find_program_address( | ||
&[ | ||
NameRecord::SEED_PREFIX.as_bytes(), | ||
&NameRecord::hash(&name_record.name), | ||
&name_record.domain.to_bytes(), | ||
], | ||
&Self::id(), | ||
); | ||
|
||
if record.key != &expected_pda { | ||
return Err(ProgramError::InvalidSeeds.into()); | ||
} | ||
|
||
// The authority should be the same as the one in the record | ||
if authority.key != &name_record.authority { | ||
return Err(ProgramError::InvalidAccountData.into()); | ||
} | ||
|
||
Ok(true) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use crate::nameservice::{ | ||
ans::ANSNameService, gpl_nameservice::GplNameService, sns::SNSNameService, | ||
}; | ||
|
||
use crate::NameServiceError; | ||
|
||
use anchor_lang::prelude::*; | ||
|
||
mod ans; | ||
mod gpl_nameservice; | ||
mod nameservice_parser; | ||
mod sns; | ||
|
||
pub use nameservice_parser::*; | ||
|
||
enum NameService { | ||
GplNameService, | ||
SNSNameService, | ||
ANSNameService, | ||
} | ||
|
||
impl NameService { | ||
pub fn from_program_id(program_id: &Pubkey) -> Option<Self> { | ||
if program_id == &GplNameService::id() { | ||
Some(Self::GplNameService) | ||
} else if program_id == &SNSNameService::id() { | ||
Some(Self::SNSNameService) | ||
} else if program_id == &ANSNameService::id() { | ||
Some(Self::ANSNameService) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
pub fn validate(&self, accounts: &[AccountInfo]) -> Result<bool> { | ||
match self { | ||
Self::GplNameService => GplNameService::validate(accounts), | ||
Self::SNSNameService => SNSNameService::validate(accounts), | ||
Self::ANSNameService => ANSNameService::validate(accounts), | ||
} | ||
} | ||
} | ||
|
||
pub fn validate(accounts: &[AccountInfo]) -> Result<bool> { | ||
let record = &accounts[0]; | ||
let name_service = | ||
NameService::from_program_id(&record.owner).ok_or(NameServiceError::InvalidNameService)?; | ||
name_service.validate(accounts) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::NameRecord; | ||
use anchor_lang::system_program::System; | ||
use anchor_lang::Discriminator; | ||
|
||
// test validate_record | ||
#[test] | ||
fn test_validate_gpl_namerecord() { | ||
let program_id = crate::id(); | ||
let authority = Pubkey::new_unique(); | ||
|
||
let gum_tld = NameRecord { | ||
name: "gum".to_string(), | ||
authority, | ||
domain: Pubkey::default(), | ||
}; | ||
|
||
// Create the NameRecord PDA | ||
let (gum_tld_key, _bump_seed) = Pubkey::find_program_address( | ||
&[ | ||
NameRecord::SEED_PREFIX.as_bytes(), | ||
&NameRecord::hash(&gum_tld.name), | ||
&gum_tld.domain.to_bytes(), | ||
], | ||
&crate::id(), | ||
); | ||
|
||
let mut lamports = 10u64; | ||
|
||
let mut account_data: Vec<u8> = vec![]; | ||
account_data.append(&mut NameRecord::DISCRIMINATOR.to_vec()); | ||
account_data.append(&mut gum_tld.try_to_vec().unwrap()); | ||
|
||
// Create the NameRecord account | ||
let gum_tld_account = AccountInfo::new( | ||
&gum_tld_key, | ||
false, | ||
false, | ||
&mut lamports, | ||
&mut account_data, | ||
&program_id, | ||
false, | ||
0, | ||
); | ||
|
||
let mut authority_lamports = 10u64; | ||
|
||
let mut authority_account_data: Vec<u8> = vec![]; | ||
|
||
let system_program_id = System::id(); | ||
|
||
let record_authority_account = AccountInfo::new( | ||
&gum_tld.authority, | ||
true, | ||
false, | ||
&mut authority_lamports, | ||
&mut authority_account_data, | ||
&system_program_id, | ||
false, | ||
0, | ||
); | ||
|
||
// Validate the NameRecord | ||
let result = validate(&[gum_tld_account, record_authority_account]); | ||
assert!(result.is_ok()); | ||
assert_eq!(result.unwrap(), true); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
programs/gpl_nameservice/src/nameservice/nameservice_parser.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use anchor_lang::prelude::*; | ||
use std::str::FromStr; | ||
|
||
pub trait NameServiceParser { | ||
type ServiceName; | ||
|
||
fn id_str() -> &'static str; | ||
|
||
fn id() -> Pubkey { | ||
// Unwrap because this is infallible | ||
Pubkey::from_str(Self::id_str()).unwrap() | ||
} | ||
fn unpack(data: &[u8]) -> Result<Self::ServiceName>; | ||
|
||
fn from_program_id(program_id: &Pubkey) -> Option<Self> | ||
where | ||
Self: Sized; | ||
|
||
fn validate(accounts: &[AccountInfo]) -> Result<bool>; | ||
|
||
fn validate_owner(record: &AccountInfo) -> Result<bool> { | ||
if record.owner != &Self::id() { | ||
return Err(ProgramError::IncorrectProgramId.into()); | ||
} | ||
Ok(true) | ||
} | ||
} |
Oops, something went wrong.