Skip to content
Closed
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
23 changes: 21 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ name = "mdp"
solana-program = { version = "=2.1.11" }
security-txt = { version = "1.1.1", package = "solana-security-txt", optional = true }

# serialization/deserialization
borsh = { version = "1.5.3", features = [ "derive" ] }
serde = { version = "1.0", features = [ "derive" ], optional = true }

# misc
[dependencies.derive_more]
version = "2.0"
features = [ "deref", "deref_mut", "from" ]

[dev-dependencies]
program-test = { package = "solana-program-test", version = "=2.1.11" }
Expand All @@ -32,7 +34,6 @@ tokio = { version = "1.0", features = [ "macros", "rt" ] }
[features]
entrypoint = ["security-txt"]
default = ["entrypoint"]
no-entrypoint = ["serde"]



16 changes: 16 additions & 0 deletions src/consts.rs
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
pub const VALIDATOR_INFO_SEED: &[u8] = b"validator-info";

pub mod tags {
pub type FieldTag = u8;

pub const IDENTITY_TAG: u8 = 0;
pub const BLOCK_TIME_MS_TAG: u8 = 1;
pub const FEES_TAG: u8 = 2;
pub const FEATURES_TAG: u8 = 3;
pub const ADDR_TAG: u8 = 4;
}

pub mod ix {
pub const REGISTER_IX: u8 = 0;
pub const SYNC_RECORD_IX: u8 = 1;
pub const UNREGISTER_IX: u8 = 2;
}
5 changes: 2 additions & 3 deletions src/entrypoint.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::{instructions::Instruction, processors::*};
use borsh::BorshDeserialize;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
pubkey::Pubkey,
Expand All @@ -15,11 +14,11 @@ pub fn process<'a>(
if *program_id != crate::ID {
return Err(ProgramError::IncorrectProgramId);
}
let ix = Instruction::try_from_slice(data)?;
let ix = Instruction::deserialize(data)?;
let accounts = accounts.iter();
match ix {
Instruction::Register(ix) => register::process_registration(accounts, ix),
Instruction::SyncInfo(ix) => sync::process_sync_info(accounts, ix),
Instruction::SyncRecord(ix) => sync::process_sync_record(accounts, ix),
Instruction::Unregister(ix) => unregister::process_unregistration(accounts, ix),
}
}
35 changes: 31 additions & 4 deletions src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
use borsh::{BorshDeserialize, BorshSerialize};
use derive_more::From;
use solana_program::program_error::ProgramError;

use register::RegisterInstruction;
use sync::SyncInfoInstruction;
use sync::SyncRecordInstruction;
use unregister::UnregisterInstruction;

use crate::consts::ix;

pub mod register;
pub mod sync;
pub mod unregister;

#[derive(BorshSerialize, BorshDeserialize)]
#[derive(From)]
pub enum Instruction {
Register(RegisterInstruction),
Unregister(UnregisterInstruction),
SyncInfo(SyncInfoInstruction),
SyncRecord(SyncRecordInstruction),
}

impl Instruction {
pub fn deserialize(data: &[u8]) -> Result<Self, ProgramError> {
(!data.is_empty())
.then_some(())
.ok_or(ProgramError::InvalidInstructionData)?;

match data[0] {
ix::REGISTER_IX => RegisterInstruction::deserialize(&data[1..]).map(Self::from),
ix::SYNC_RECORD_IX => SyncRecordInstruction::deserialize(&data[1..]).map(Self::from),
ix::UNREGISTER_IX => UnregisterInstruction::deserialize(&data[1..]).map(Self::from),
_ => Err(ProgramError::InvalidInstructionData),
}
}

pub fn serialize(&self) -> Vec<u8> {
match self {
Self::Register(ix) => ix.serialize(),
Self::SyncRecord(ix) => ix.serialize(),
Self::Unregister(ix) => ix.serialize(),
}
}
}
23 changes: 19 additions & 4 deletions src/instructions/register.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::program_error::ProgramError;

use crate::state::validator_info::ValidatorInfo;
use crate::{consts::ix, state::record::ErRecord};

#[derive(BorshSerialize, BorshDeserialize)]
pub struct RegisterInstruction(pub ValidatorInfo);
pub struct RegisterInstruction(pub ErRecord);

impl RegisterInstruction {
pub fn deserialize(data: &[u8]) -> Result<Self, ProgramError> {
ErRecord::deserialize(data).map(Self)
}

pub fn serialize(&self) -> Vec<u8> {
let len = self.0.serialized_size() + 1; // + 1 for ix discriminator
let mut buffer = vec![0; len];
buffer[0] = ix::REGISTER_IX;
self.0
.serialize(&mut buffer[1..])
.expect("should always serialize as we have allocated the exact size");
buffer
}
}
86 changes: 74 additions & 12 deletions src/instructions/sync.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,84 @@
use std::net::SocketAddrV4;

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::pubkey::Pubkey;
use solana_program::{log, program_error::ProgramError};

use crate::{consts::VALIDATOR_INFO_SEED, state::features::FeaturesSet, ID};
use crate::{
consts::{ix, VALIDATOR_INFO_SEED},
serde::FieldSerializer,
state::{
features::FeaturesSet,
field::{self, Field},
record::RecordBuilder,
},
};

#[derive(BorshSerialize, BorshDeserialize)]
pub struct SyncInfoInstruction {
pub identity: Pubkey,
pub addr: Option<SocketAddrV4>,
pub block_time_ms: Option<u16>,
pub fees: Option<u16>,
pub struct SyncRecordInstruction {
pub identity: field::Identity,
pub block_time_ms: Option<field::BlockTimeMs>,
pub fees: Option<field::Fees>,
pub features: Option<FeaturesSet>,
pub addr: Option<field::Addr>,
}

impl SyncInfoInstruction {
impl SyncRecordInstruction {
pub fn pda(&self) -> Pubkey {
let seeds = [VALIDATOR_INFO_SEED, self.identity.as_ref()];
Pubkey::find_program_address(&seeds, &ID).0
Pubkey::find_program_address(&seeds, &crate::ID).0
}

pub fn deserialize(data: &[u8]) -> Result<Self, ProgramError> {
let builder = RecordBuilder::populate(data, true)?;
let identity = builder
.identity
.ok_or(ProgramError::InvalidInstructionData)
.inspect_err(|_| log::msg!("failed to deserialize required identity field"))?;

Ok(Self {
identity,
block_time_ms: builder.block_time_ms,
features: builder.features,
fees: builder.fees,
addr: builder.addr,
})
}

fn serialized_size(&self) -> usize {
macro_rules! size {
($field: ident) => {
self.$field
.as_ref()
.map(|f| f.size() + 1)
.unwrap_or_default()
};
}
self.identity.size()
+ 1
+ size!(block_time_ms)
+ size!(fees)
+ size!(features)
+ size!(addr)
}

pub fn serialize(&self) -> Vec<u8> {
let len = self.serialized_size() + 1; // + 1 for ix discriminator
let mut buffer = vec![0; len];

buffer[0] = ix::SYNC_RECORD_IX;

let mut serializer = FieldSerializer::new(&mut buffer[1..]);
serializer.write_field(&self.identity);
if let Some(ref btms) = self.block_time_ms {
serializer.write_field(btms);
}
if let Some(ref fees) = self.fees {
serializer.write_field(fees);
}
if let Some(ref features) = self.features {
serializer.write_field(features);
}
if let Some(ref addr) = self.addr {
serializer.write_field(addr);
}

buffer
}
}
23 changes: 21 additions & 2 deletions src/instructions/unregister.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;

#[derive(BorshSerialize, BorshDeserialize)]
use crate::consts::ix;

pub struct UnregisterInstruction(pub Pubkey);

impl UnregisterInstruction {
pub fn deserialize(data: &[u8]) -> Result<Self, ProgramError> {
const LEN: usize = std::mem::size_of::<Pubkey>();
let array: [u8; LEN] = data
.get(..LEN)
.ok_or(ProgramError::InvalidInstructionData)?
.try_into()
.map_err(|_| ProgramError::InvalidInstructionData)?;
Ok(Self(Pubkey::new_from_array(array)))
}

pub fn serialize(&self) -> Vec<u8> {
let mut buffer = vec![ix::UNREGISTER_IX];
buffer.extend_from_slice(self.0.as_ref());
buffer
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use solana_program::declare_id;

pub mod consts;
pub mod instructions;
pub mod serde;
pub mod state;

#[cfg(feature = "entrypoint")]
Expand Down
9 changes: 3 additions & 6 deletions src/processors/register.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use borsh::BorshSerialize;
use solana_program::{
account_info::{next_account_info, AccountInfo},
program::invoke_signed,
Expand All @@ -8,9 +7,7 @@ use solana_program::{
sysvar::Sysvar,
};

use crate::{
instructions::register::RegisterInstruction, state::validator_info::ValidatorInfo, ID,
};
use crate::{instructions::register::RegisterInstruction, ID};

pub fn process_registration<'a>(
mut accounts: impl Iterator<Item = &'a AccountInfo<'a>>,
Expand All @@ -20,7 +17,7 @@ pub fn process_registration<'a>(
let pda_account = next_account_info(&mut accounts)?;
let system_program = next_account_info(&mut accounts)?;

if !(payer.is_signer && *payer.key == ix.0.identity) {
if !(payer.is_signer && *payer.key == *ix.0.identity) {
return Err(ProgramError::InvalidArgument);
}

Expand All @@ -34,7 +31,7 @@ pub fn process_registration<'a>(
return Err(ProgramError::InvalidArgument);
}

let mut data = Vec::with_capacity(std::mem::size_of::<ValidatorInfo>());
let mut data = vec![0; ix.0.serialized_size()];
ix.0.serialize(&mut data)?;

let space = data.len();
Expand Down
27 changes: 15 additions & 12 deletions src/processors/sync.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
program_error::ProgramError,
};

use crate::{instructions::sync::SyncInfoInstruction, state::validator_info::ValidatorInfo, ID};
use crate::{instructions::sync::SyncRecordInstruction, state::record::ErRecord, ID};

pub fn process_sync_info<'a>(
pub fn process_sync_record<'a>(
mut accounts: impl Iterator<Item = &'a AccountInfo<'a>>,
ix: SyncInfoInstruction,
ix: SyncRecordInstruction,
) -> Result<(), ProgramError> {
let payer = next_account_info(&mut accounts)?;
let pda_account = next_account_info(&mut accounts)?;
Expand All @@ -17,7 +16,7 @@ pub fn process_sync_info<'a>(
return Err(ProgramError::InvalidAccountOwner);
}

if !(payer.is_signer && *payer.key == ix.identity) {
if !(payer.is_signer && *payer.key == *ix.identity) {
return Err(ProgramError::InvalidArgument);
}

Expand All @@ -32,23 +31,27 @@ pub fn process_sync_info<'a>(
}

let mut data = pda_account.try_borrow_mut_data()?;
let mut info =
ValidatorInfo::try_from_slice(&data).map_err(|_| ProgramError::InvalidAccountData)?;
let mut record = ErRecord::deserialize(&data).map_err(|_| ProgramError::InvalidAccountData)?;

if let Some(addr) = ix.addr {
info.addr = addr;
record.addr = addr
}
if let Some(fees) = ix.fees {
info.fees = fees;
record.fees = fees
}
if let Some(block_time_ms) = ix.block_time_ms {
info.block_time_ms = block_time_ms;
record.block_time_ms = block_time_ms
}
if let Some(features) = ix.features {
info.features = features;
record.features = features
}
let new_len = record.serialized_size();
if new_len != data.len() {
pda_account.realloc(new_len, false)?;
data = pda_account.try_borrow_mut_data()?;
}

info.serialize(&mut *data)?;
record.serialize(*data)?;

Ok(())
}
Loading