diff --git a/Cargo.toml b/Cargo.toml index ba5f10e..43f0a06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -solana-program = "=1.10.29" -borsh = "0.9.3" -thiserror = "1.0.31" +solana-program = "2.0.10" +borsh = "1.5.1" +thiserror = "1.0.63" [lib] crate-type = ["cdylib", "lib"] \ No newline at end of file diff --git a/src/instruction.rs b/src/instruction.rs index ff4c2fd..96b8be2 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -20,31 +20,36 @@ struct ReplyPayload { impl IntroInstruction { pub fn unpack(input: &[u8]) -> Result { - let (&variant, rest) = input + let (&discriminator, rest) = input .split_first() .ok_or(ProgramError::InvalidInstructionData)?; - Ok(match variant { + + match discriminator { 0 => { - let payload = StudentIntroPayload::try_from_slice(rest).unwrap(); - Self::InitUserInput { + let payload = StudentIntroPayload::try_from_slice(rest) + .map_err(|_| ProgramError::InvalidInstructionData)?; + + Ok(Self::InitUserInput { name: payload.name, message: payload.message, - } + }) } 1 => { - let payload = StudentIntroPayload::try_from_slice(rest).unwrap(); - Self::UpdateStudentIntro { + let payload = StudentIntroPayload::try_from_slice(rest) + .map_err(|_| ProgramError::InvalidInstructionData)?; + Ok(Self::UpdateStudentIntro { name: payload.name, message: payload.message, - } + }) } 2 => { - let payload = ReplyPayload::try_from_slice(rest).unwrap(); - Self::AddReply { + let payload = ReplyPayload::try_from_slice(rest) + .map_err(|_| ProgramError::InvalidInstructionData)?; + Ok(Self::AddReply { reply: payload.reply, - } + }) } _ => return Err(ProgramError::InvalidInstructionData), - }) + } } } diff --git a/src/processor.rs b/src/processor.rs index 014cf98..5604723 100644 --- a/src/processor.rs +++ b/src/processor.rs @@ -1,10 +1,9 @@ use crate::error::StudentIntroError; use crate::instruction::IntroInstruction; use crate::state::{Reply, ReplyCounter, StudentInfo}; -use borsh::BorshSerialize; +use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{ account_info::{next_account_info, AccountInfo}, - borsh::try_from_slice_unchecked, entrypoint::ProgramResult, msg, program::invoke_signed, @@ -39,14 +38,15 @@ pub fn add_student_intro( name: String, message: String, ) -> ProgramResult { - msg!("Adding student intro..."); - msg!("Name: {}", name); - msg!("Message: {}", message); + msg!( + "Adding student intro... Name: {}, Message: {}", + name, + message + ); let account_info_iter = &mut accounts.iter(); let initializer = next_account_info(account_info_iter)?; let user_account = next_account_info(account_info_iter)?; - let reply_counter = next_account_info(account_info_iter)?; let system_program = next_account_info(account_info_iter)?; let (pda, bump_seed) = Pubkey::find_program_address(&[initializer.key.as_ref()], program_id); @@ -54,6 +54,7 @@ pub fn add_student_intro( msg!("Invalid seeds for PDA"); return Err(StudentIntroError::InvalidPDA.into()); } + let studentinfo_discriminator = "studentinfo"; let total_len: usize = (4 + studentinfo_discriminator.len()) + 1 + (4 + name.len()) + (4 + message.len()); @@ -61,8 +62,8 @@ pub fn add_student_intro( msg!("Data length is larger than 1000 bytes"); return Err(StudentIntroError::InvalidDataLength.into()); } - let account_len: usize = 1000; + let account_len: usize = 1000; let rent = Rent::get()?; let rent_lamports = rent.minimum_balance(account_len); @@ -84,12 +85,10 @@ pub fn add_student_intro( msg!("PDA created: {}", pda); - msg!("unpacking state account"); - let mut account_data = - try_from_slice_unchecked::(&user_account.data.borrow()).unwrap(); - msg!("borrowed account data"); + msg!("Unpacking state account"); + let mut account_data = StudentInfo::try_from_slice(&user_account.data.borrow())?; - msg!("checking if studentinfo account is already initialized"); + msg!("Checking if studentinfo account is already initialized"); if account_data.is_initialized() { msg!("Account already initialized"); return Err(ProgramError::AccountAlreadyInitialized); @@ -98,54 +97,12 @@ pub fn add_student_intro( account_data.name = name; account_data.msg = message; account_data.is_initialized = true; - msg!("serializing account"); - account_data.serialize(&mut &mut user_account.data.borrow_mut()[..])?; - msg!("state account serialized"); - msg!("create reply counter"); - let counter_discriminator = "counter"; - let counter_len: usize = (4 + counter_discriminator.len()) + 1 + 1; - - let rent = Rent::get()?; - let counter_rent_lamports = rent.minimum_balance(counter_len); - - let (counter, counter_bump) = - Pubkey::find_program_address(&[pda.as_ref(), "reply".as_ref()], program_id); - if counter != *reply_counter.key { - msg!("Invalid seeds for PDA"); - return Err(ProgramError::InvalidArgument); - } - - invoke_signed( - &system_instruction::create_account( - initializer.key, - reply_counter.key, - counter_rent_lamports, - counter_len.try_into().unwrap(), - program_id, - ), - &[ - initializer.clone(), - reply_counter.clone(), - system_program.clone(), - ], - &[&[pda.as_ref(), "reply".as_ref(), &[counter_bump]]], - )?; - msg!("reply counter created"); - - let mut counter_data = - try_from_slice_unchecked::(&reply_counter.data.borrow()).unwrap(); + msg!("Serializing account"); + account_data.serialize(&mut &mut user_account.data.borrow_mut()[..])?; + msg!("State account serialized"); - msg!("checking if counter account is already initialized"); - if counter_data.is_initialized() { - msg!("Account already initialized"); - return Err(ProgramError::AccountAlreadyInitialized); - } - counter_data.discriminator = counter_discriminator.to_string(); - counter_data.counter = 0; - counter_data.is_initialized = true; - msg!("reply count: {}", counter_data.counter); - counter_data.serialize(&mut &mut reply_counter.data.borrow_mut()[..])?; + create_reply_counter(program_id, accounts, pda)?; Ok(()) } @@ -156,20 +113,20 @@ pub fn update_student_intro( name: String, message: String, ) -> ProgramResult { - msg!("Updating student intro..."); - msg!("Name: {}", name); - msg!("Message: {}", message); + msg!( + "Updating student intro... Name: {}, Message: {}", + name, + message + ); let account_info_iter = &mut accounts.iter(); let initializer = next_account_info(account_info_iter)?; let user_account = next_account_info(account_info_iter)?; - msg!("unpacking state account"); - let mut account_data = - try_from_slice_unchecked::(&user_account.data.borrow()).unwrap(); - msg!("borrowed account data"); + msg!("Unpacking state account"); + let mut account_data = StudentInfo::try_from_slice(&user_account.data.borrow())?; - msg!("checking if movie account is initialized"); + msg!("Checking if account is initialized"); if !account_data.is_initialized() { msg!("Account is not initialized"); return Err(StudentIntroError::UninitializedAccount.into()); @@ -184,6 +141,7 @@ pub fn update_student_intro( msg!("Invalid seeds for PDA"); return Err(StudentIntroError::InvalidPDA.into()); } + let update_len: usize = 1 + (4 + account_data.name.len()) + (4 + message.len()); if update_len > 1000 { msg!("Data length is larger than 1000 bytes"); @@ -192,27 +150,24 @@ pub fn update_student_intro( account_data.name = account_data.name; account_data.msg = message; - msg!("serializing account"); + msg!("Serializing account"); account_data.serialize(&mut &mut user_account.data.borrow_mut()[..])?; - msg!("state account serialized"); + msg!("State account serialized"); Ok(()) } pub fn add_reply(program_id: &Pubkey, accounts: &[AccountInfo], reply: String) -> ProgramResult { - msg!("Adding Reply..."); - msg!("Reply: {}", reply); + msg!("Adding Reply: {}", reply); let account_info_iter = &mut accounts.iter(); - let replier = next_account_info(account_info_iter)?; let user_account = next_account_info(account_info_iter)?; let reply_counter = next_account_info(account_info_iter)?; let reply_account = next_account_info(account_info_iter)?; let system_program = next_account_info(account_info_iter)?; - let mut counter_data = - try_from_slice_unchecked::(&reply_counter.data.borrow()).unwrap(); + let mut counter_data = ReplyCounter::try_from_slice(&reply_counter.data.borrow())?; let reply_discriminator = "reply"; let account_len: usize = (4 + reply_discriminator.len()) + 1 + 32 + (4 + reply.len()); @@ -253,20 +208,82 @@ pub fn add_reply(program_id: &Pubkey, accounts: &[AccountInfo], reply: String) - )?; msg!("Created Reply Account"); - let mut reply_data = try_from_slice_unchecked::(&reply_account.data.borrow()).unwrap(); + let mut reply_data = Reply::try_from_slice(&reply_account.data.borrow())?; - msg!("checking if comment account is already initialized"); + msg!("Checking if reply account is already initialized"); if reply_data.is_initialized() { msg!("Account already initialized"); return Err(ProgramError::AccountAlreadyInitialized); } + reply_data.discriminator = reply_discriminator.to_string(); reply_data.studentinfo = *user_account.key; reply_data.reply = reply; reply_data.is_initialized = true; reply_data.serialize(&mut &mut reply_account.data.borrow_mut()[..])?; + msg!("Reply Count: {}", counter_data.counter); counter_data.counter += 1; counter_data.serialize(&mut &mut reply_counter.data.borrow_mut()[..])?; + + Ok(()) +} + +fn create_reply_counter( + program_id: &Pubkey, + accounts: &[AccountInfo], + pda: Pubkey, +) -> ProgramResult { + let account_info_iterator = &mut accounts.iter(); + let initializer = next_account_info(account_info_iterator)?; + let reply_counter = next_account_info(account_info_iterator)?; + let system_program = next_account_info(account_info_iterator)?; + + msg!("Creating reply counter"); + let counter_discriminator = "counter"; + let counter_len: usize = (4 + counter_discriminator.len()) + 1 + 1; + + let rent = Rent::get()?; + let counter_rent_lamports = rent.minimum_balance(counter_len); + + let (counter, counter_bump) = + Pubkey::find_program_address(&[pda.as_ref(), "reply".as_ref()], program_id); + if counter != *reply_counter.key { + msg!("Invalid seeds for PDA"); + return Err(ProgramError::InvalidArgument); + } + + invoke_signed( + &system_instruction::create_account( + initializer.key, + reply_counter.key, + counter_rent_lamports, + counter_len.try_into().unwrap(), + program_id, + ), + &[ + initializer.clone(), + reply_counter.clone(), + system_program.clone(), + ], + &[&[pda.as_ref(), "reply".as_ref(), &[counter_bump]]], + )?; + + msg!("Reply counter created"); + + let mut counter_data = ReplyCounter::try_from_slice(&reply_counter.data.borrow())?; + + msg!("Checking if counter account is already initialized"); + if counter_data.is_initialized() { + msg!("Account already initialized"); + return Err(ProgramError::AccountAlreadyInitialized); + } + + counter_data.discriminator = counter_discriminator.to_string(); + counter_data.counter = 0; + counter_data.is_initialized = true; + msg!("Reply count: {}", counter_data.counter); + counter_data.serialize(&mut &mut reply_counter.data.borrow_mut()[..])?; + Ok(()) }