diff --git a/Cargo.lock b/Cargo.lock index 703d79790e3..b0ef54fb5fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7111,12 +7111,13 @@ dependencies = [ name = "spl-record" version = "0.1.0" dependencies = [ - "borsh 0.10.3", + "bytemuck", "num-derive 0.4.1", "num-traits", "solana-program", "solana-program-test", "solana-sdk", + "spl-pod 0.1.0", "thiserror", ] diff --git a/record/program/Cargo.toml b/record/program/Cargo.toml index 4b8c6a0800c..8cfb90a44ee 100644 --- a/record/program/Cargo.toml +++ b/record/program/Cargo.toml @@ -12,11 +12,12 @@ no-entrypoint = [] test-sbf = [] [dependencies] -borsh = "0.10" +bytemuck = { version = "1.14.0", features = ["derive"] } num-derive = "0.4" num-traits = "0.2" solana-program = "1.17.6" thiserror = "1.0" +spl-pod = { version = "0.1", path = "../../libraries/pod" } [dev-dependencies] solana-program-test = "1.17.6" diff --git a/record/program/src/instruction.rs b/record/program/src/instruction.rs index 045af3184b0..6df52d3518b 100644 --- a/record/program/src/instruction.rs +++ b/record/program/src/instruction.rs @@ -2,16 +2,17 @@ use { crate::id, - borsh::{BorshDeserialize, BorshSerialize}, solana_program::{ instruction::{AccountMeta, Instruction}, + program_error::ProgramError, pubkey::Pubkey, }, + std::mem::size_of, }; /// Instructions supported by the program -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, PartialEq)] -pub enum RecordInstruction { +#[derive(Clone, Debug, PartialEq)] +pub enum RecordInstruction<'a> { /// Create a new record /// /// Accounts expected by this instruction: @@ -30,7 +31,7 @@ pub enum RecordInstruction { /// Offset to start writing record, expressed as `u64`. offset: u64, /// Data to replace the existing record data - data: Vec, + data: &'a [u8], }, /// Update the authority of the provided record account @@ -53,28 +54,80 @@ pub enum RecordInstruction { CloseAccount, } +impl<'a> RecordInstruction<'a> { + /// Unpacks a byte buffer into a [RecordInstruction]. + pub fn unpack(input: &'a [u8]) -> Result { + let (&tag, rest) = input + .split_first() + .ok_or(ProgramError::InvalidInstructionData)?; + Ok(match tag { + 0 => Self::Initialize, + 1 => { + const U32_BYTES: usize = 4; + const U64_BYTES: usize = 8; + let offset = rest + .get(..U64_BYTES) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .ok_or(ProgramError::InvalidInstructionData)?; + let (length, data) = rest[U64_BYTES..].split_at(U32_BYTES); + let length = u32::from_le_bytes( + length + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ) as usize; + + Self::Write { + offset, + data: &data[..length], + } + } + 2 => Self::SetAuthority, + 3 => Self::CloseAccount, + _ => return Err(ProgramError::InvalidInstructionData), + }) + } + + /// Packs a [RecordInstruction] into a byte buffer. + pub fn pack(&self) -> Vec { + let mut buf = Vec::with_capacity(size_of::()); + match self { + Self::Initialize => buf.push(0), + Self::Write { offset, data } => { + buf.push(1); + buf.extend_from_slice(&offset.to_le_bytes()); + buf.extend_from_slice(&(data.len() as u32).to_le_bytes()); + buf.extend_from_slice(data); + } + Self::SetAuthority => buf.push(2), + Self::CloseAccount => buf.push(3), + }; + buf + } +} + /// Create a `RecordInstruction::Initialize` instruction pub fn initialize(record_account: &Pubkey, authority: &Pubkey) -> Instruction { - Instruction::new_with_borsh( - id(), - &RecordInstruction::Initialize, - vec![ + Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(*record_account, false), AccountMeta::new_readonly(*authority, false), ], - ) + data: RecordInstruction::Initialize.pack(), + } } /// Create a `RecordInstruction::Write` instruction -pub fn write(record_account: &Pubkey, signer: &Pubkey, offset: u64, data: Vec) -> Instruction { - Instruction::new_with_borsh( - id(), - &RecordInstruction::Write { offset, data }, - vec![ +pub fn write(record_account: &Pubkey, signer: &Pubkey, offset: u64, data: &[u8]) -> Instruction { + Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(*record_account, false), AccountMeta::new_readonly(*signer, true), ], - ) + data: RecordInstruction::Write { offset, data }.pack(), + } } /// Create a `RecordInstruction::SetAuthority` instruction @@ -83,92 +136,79 @@ pub fn set_authority( signer: &Pubkey, new_authority: &Pubkey, ) -> Instruction { - Instruction::new_with_borsh( - id(), - &RecordInstruction::SetAuthority, - vec![ + Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(*record_account, false), AccountMeta::new_readonly(*signer, true), AccountMeta::new_readonly(*new_authority, false), ], - ) + data: RecordInstruction::SetAuthority.pack(), + } } /// Create a `RecordInstruction::CloseAccount` instruction pub fn close_account(record_account: &Pubkey, signer: &Pubkey, receiver: &Pubkey) -> Instruction { - Instruction::new_with_borsh( - id(), - &RecordInstruction::CloseAccount, - vec![ + Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(*record_account, false), AccountMeta::new_readonly(*signer, true), AccountMeta::new(*receiver, false), ], - ) + data: RecordInstruction::CloseAccount.pack(), + } } #[cfg(test)] mod tests { - use {super::*, crate::state::tests::TEST_DATA, solana_program::program_error::ProgramError}; + use { + super::*, crate::state::tests::TEST_DATA, solana_program::program_error::ProgramError, + spl_pod::bytemuck::pod_bytes_of, + }; #[test] fn serialize_initialize() { let instruction = RecordInstruction::Initialize; let expected = vec![0]; - assert_eq!(instruction.try_to_vec().unwrap(), expected); - assert_eq!( - RecordInstruction::try_from_slice(&expected).unwrap(), - instruction - ); + assert_eq!(instruction.pack(), expected); + assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction); } #[test] fn serialize_write() { - let data = TEST_DATA.try_to_vec().unwrap(); + let data = pod_bytes_of(&TEST_DATA); let offset = 0u64; - let instruction = RecordInstruction::Write { - offset: 0, - data: data.clone(), - }; + let instruction = RecordInstruction::Write { offset: 0, data }; let mut expected = vec![1]; expected.extend_from_slice(&offset.to_le_bytes()); - expected.append(&mut data.try_to_vec().unwrap()); - assert_eq!(instruction.try_to_vec().unwrap(), expected); - assert_eq!( - RecordInstruction::try_from_slice(&expected).unwrap(), - instruction - ); + expected.extend_from_slice(&(data.len() as u32).to_le_bytes()); + expected.extend_from_slice(data); + assert_eq!(instruction.pack(), expected); + assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction); } #[test] fn serialize_set_authority() { let instruction = RecordInstruction::SetAuthority; let expected = vec![2]; - assert_eq!(instruction.try_to_vec().unwrap(), expected); - assert_eq!( - RecordInstruction::try_from_slice(&expected).unwrap(), - instruction - ); + assert_eq!(instruction.pack(), expected); + assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction); } #[test] fn serialize_close_account() { let instruction = RecordInstruction::CloseAccount; let expected = vec![3]; - assert_eq!(instruction.try_to_vec().unwrap(), expected); - assert_eq!( - RecordInstruction::try_from_slice(&expected).unwrap(), - instruction - ); + assert_eq!(instruction.pack(), expected); + assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction); } #[test] fn deserialize_invalid_instruction() { let mut expected = vec![12]; - expected.append(&mut TEST_DATA.try_to_vec().unwrap()); - let err: ProgramError = RecordInstruction::try_from_slice(&expected) - .unwrap_err() - .into(); - assert!(matches!(err, ProgramError::BorshIoError(_))); + expected.append(&mut pod_bytes_of(&TEST_DATA).to_vec()); + let err: ProgramError = RecordInstruction::unpack(&expected).unwrap_err(); + assert_eq!(err, ProgramError::InvalidInstructionData); } } diff --git a/record/program/src/processor.rs b/record/program/src/processor.rs index 451e2312b67..472740cb245 100644 --- a/record/program/src/processor.rs +++ b/record/program/src/processor.rs @@ -6,7 +6,6 @@ use { instruction::RecordInstruction, state::{Data, RecordData}, }, - borsh::BorshDeserialize, solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, @@ -15,6 +14,7 @@ use { program_pack::IsInitialized, pubkey::Pubkey, }, + spl_pod::bytemuck::{pod_from_bytes, pod_from_bytes_mut}, }; fn check_authority(authority_info: &AccountInfo, expected_authority: &Pubkey) -> ProgramResult { @@ -35,7 +35,7 @@ pub fn process_instruction( accounts: &[AccountInfo], input: &[u8], ) -> ProgramResult { - let instruction = RecordInstruction::try_from_slice(input)?; + let instruction = RecordInstruction::unpack(input)?; let account_info_iter = &mut accounts.iter(); match instruction { @@ -45,7 +45,8 @@ pub fn process_instruction( let data_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - let mut account_data = RecordData::try_from_slice(*data_info.data.borrow())?; + let raw_data = &mut data_info.data.borrow_mut(); + let account_data = pod_from_bytes_mut::(raw_data)?; if account_data.is_initialized() { msg!("Record account already initialized"); return Err(ProgramError::AccountAlreadyInitialized); @@ -53,26 +54,28 @@ pub fn process_instruction( account_data.authority = *authority_info.key; account_data.version = RecordData::CURRENT_VERSION; - borsh::to_writer(&mut data_info.data.borrow_mut()[..], &account_data) - .map_err(|e| e.into()) + Ok(()) } RecordInstruction::Write { offset, data } => { msg!("RecordInstruction::Write"); let data_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - let account_data = RecordData::try_from_slice(&data_info.data.borrow())?; - if !account_data.is_initialized() { - msg!("Record account not initialized"); - return Err(ProgramError::UninitializedAccount); + { + let raw_data = &data_info.data.borrow(); + let account_data = pod_from_bytes::(raw_data)?; + if !account_data.is_initialized() { + msg!("Record account not initialized"); + return Err(ProgramError::UninitializedAccount); + } + check_authority(authority_info, &account_data.authority)?; } - check_authority(authority_info, &account_data.authority)?; let start = RecordData::WRITABLE_START_INDEX.saturating_add(offset as usize); let end = start.saturating_add(data.len()); if end > data_info.data.borrow().len() { Err(ProgramError::AccountDataTooSmall) } else { - data_info.data.borrow_mut()[start..end].copy_from_slice(&data); + data_info.data.borrow_mut()[start..end].copy_from_slice(data); Ok(()) } } @@ -82,15 +85,15 @@ pub fn process_instruction( let data_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; let new_authority_info = next_account_info(account_info_iter)?; - let mut account_data = RecordData::try_from_slice(&data_info.data.borrow())?; + let raw_data = &mut data_info.data.borrow_mut(); + let account_data = pod_from_bytes_mut::(raw_data)?; if !account_data.is_initialized() { msg!("Record account not initialized"); return Err(ProgramError::UninitializedAccount); } check_authority(authority_info, &account_data.authority)?; account_data.authority = *new_authority_info.key; - borsh::to_writer(&mut data_info.data.borrow_mut()[..], &account_data) - .map_err(|e| e.into()) + Ok(()) } RecordInstruction::CloseAccount => { @@ -98,7 +101,8 @@ pub fn process_instruction( let data_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; let destination_info = next_account_info(account_info_iter)?; - let mut account_data = RecordData::try_from_slice(&data_info.data.borrow())?; + let raw_data = &mut data_info.data.borrow_mut(); + let account_data = pod_from_bytes_mut::(raw_data)?; if !account_data.is_initialized() { msg!("Record not initialized"); return Err(ProgramError::UninitializedAccount); @@ -111,8 +115,7 @@ pub fn process_instruction( .checked_add(data_lamports) .ok_or(RecordError::Overflow)?; account_data.data = Data::default(); - borsh::to_writer(&mut data_info.data.borrow_mut()[..], &account_data) - .map_err(|e| e.into()) + Ok(()) } } } diff --git a/record/program/src/state.rs b/record/program/src/state.rs index b88b698255f..2dfec2e0bf7 100644 --- a/record/program/src/state.rs +++ b/record/program/src/state.rs @@ -1,11 +1,12 @@ //! Program state use { - borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, + bytemuck::{Pod, Zeroable}, solana_program::{program_pack::IsInitialized, pubkey::Pubkey}, }; /// Struct wrapping data and providing metadata -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq)] +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct RecordData { /// Struct version, allows for upgrades to the program pub version: u8, @@ -17,11 +18,15 @@ pub struct RecordData { pub data: Data, } +/// The length of the data contained in the account for testing. +const DATA_SIZE: usize = 8; + /// Struct just for data -#[derive(Clone, Debug, Default, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq)] +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct Data { /// The data contained by the account, could be anything or serializable - pub bytes: [u8; Self::DATA_SIZE], + pub bytes: [u8; DATA_SIZE], } impl Data { @@ -46,7 +51,11 @@ impl IsInitialized for RecordData { #[cfg(test)] pub mod tests { - use {super::*, solana_program::program_error::ProgramError}; + use { + super::*, + solana_program::program_error::ProgramError, + spl_pod::bytemuck::{pod_bytes_of, pod_from_bytes}, + }; /// Version for tests pub const TEST_VERSION: u8 = 1; @@ -68,10 +77,10 @@ pub mod tests { let mut expected = vec![TEST_VERSION]; expected.extend_from_slice(&TEST_PUBKEY.to_bytes()); expected.extend_from_slice(&TEST_DATA.bytes); - assert_eq!(TEST_RECORD_DATA.try_to_vec().unwrap(), expected); + assert_eq!(pod_bytes_of(&TEST_RECORD_DATA), expected); assert_eq!( - RecordData::try_from_slice(&expected).unwrap(), - TEST_RECORD_DATA + *pod_from_bytes::(&expected).unwrap(), + TEST_RECORD_DATA, ); } @@ -81,7 +90,7 @@ pub mod tests { let mut expected = vec![TEST_VERSION]; expected.extend_from_slice(&TEST_PUBKEY.to_bytes()); expected.extend_from_slice(&data); - let err: ProgramError = RecordData::try_from_slice(&expected).unwrap_err().into(); - assert!(matches!(err, ProgramError::BorshIoError(_))); + let err: ProgramError = pod_from_bytes::(&expected).unwrap_err(); + assert_eq!(err, ProgramError::InvalidArgument); } } diff --git a/record/program/tests/functional.rs b/record/program/tests/functional.rs index 3582d13cbf8..64e068f6604 100644 --- a/record/program/tests/functional.rs +++ b/record/program/tests/functional.rs @@ -1,9 +1,7 @@ #![cfg(feature = "test-sbf")] use { - borsh::BorshSerialize, solana_program::{ - borsh0_10::get_packed_len, instruction::{AccountMeta, Instruction, InstructionError}, pubkey::Pubkey, rent::Rent, @@ -14,6 +12,7 @@ use { signature::{Keypair, Signer}, transaction::{Transaction, TransactionError}, }, + spl_pod::bytemuck::{pod_bytes_of, pod_from_bytes, pod_get_packed_len}, spl_record::{ error::RecordError, id, instruction, @@ -37,8 +36,8 @@ async fn initialize_storage_account( system_instruction::create_account( &context.payer.pubkey(), &account.pubkey(), - 1.max(Rent::default().minimum_balance(get_packed_len::())), - get_packed_len::() as u64, + 1.max(Rent::default().minimum_balance(pod_get_packed_len::())), + pod_get_packed_len::() as u64, &id(), ), instruction::initialize(&account.pubkey(), &authority.pubkey()), @@ -46,7 +45,7 @@ async fn initialize_storage_account( &account.pubkey(), &authority.pubkey(), 0, - data.try_to_vec().unwrap(), + pod_bytes_of(&data), ), ], Some(&context.payer.pubkey()), @@ -69,12 +68,15 @@ async fn initialize_success() { let data = Data { bytes: [111u8; Data::DATA_SIZE], }; - initialize_storage_account(&mut context, &authority, &account, data.clone()).await; - let account_data = context + initialize_storage_account(&mut context, &authority, &account, data).await; + + let account = context .banks_client - .get_account_data_with_borsh::(account.pubkey()) + .get_account(account.pubkey()) .await + .unwrap() .unwrap(); + let account_data = pod_from_bytes::(&account.data).unwrap(); assert_eq!(account_data.data, data); assert_eq!(account_data.authority, authority.pubkey()); assert_eq!(account_data.version, RecordData::CURRENT_VERSION); @@ -97,12 +99,12 @@ async fn initialize_with_seed_success() { &account, &authority.pubkey(), seed, - 1.max(Rent::default().minimum_balance(get_packed_len::())), - get_packed_len::() as u64, + 1.max(Rent::default().minimum_balance(pod_get_packed_len::())), + pod_get_packed_len::() as u64, &id(), ), instruction::initialize(&account, &authority.pubkey()), - instruction::write(&account, &authority.pubkey(), 0, data.try_to_vec().unwrap()), + instruction::write(&account, &authority.pubkey(), 0, pod_bytes_of(&data)), ], Some(&context.payer.pubkey()), &[&context.payer, &authority], @@ -113,11 +115,13 @@ async fn initialize_with_seed_success() { .process_transaction(transaction) .await .unwrap(); - let account_data = context + let account = context .banks_client - .get_account_data_with_borsh::(account) + .get_account(account) .await + .unwrap() .unwrap(); + let account_data = pod_from_bytes::(&account.data).unwrap(); assert_eq!(account_data.data, data); assert_eq!(account_data.authority, authority.pubkey()); assert_eq!(account_data.version, RecordData::CURRENT_VERSION); @@ -172,7 +176,7 @@ async fn write_success() { &account.pubkey(), &authority.pubkey(), 0, - new_data.try_to_vec().unwrap(), + pod_bytes_of(&new_data), )], Some(&context.payer.pubkey()), &[&context.payer, &authority], @@ -184,11 +188,13 @@ async fn write_success() { .await .unwrap(); - let account_data = context + let account = context .banks_client - .get_account_data_with_borsh::(account.pubkey()) + .get_account(account.pubkey()) .await + .unwrap() .unwrap(); + let account_data = pod_from_bytes::(&account.data).unwrap(); assert_eq!(account_data.data, new_data); assert_eq!(account_data.authority, authority.pubkey()); assert_eq!(account_data.version, RecordData::CURRENT_VERSION); @@ -214,7 +220,7 @@ async fn write_fail_wrong_authority() { &account.pubkey(), &wrong_authority.pubkey(), 0, - new_data.try_to_vec().unwrap(), + pod_bytes_of(&new_data), )], Some(&context.payer.pubkey()), &[&context.payer, &wrong_authority], @@ -245,20 +251,19 @@ async fn write_fail_unsigned() { }; initialize_storage_account(&mut context, &authority, &account, data).await; - let data = Data { + let data = pod_bytes_of(&Data { bytes: [200u8; Data::DATA_SIZE], - } - .try_to_vec() - .unwrap(); + }); + let transaction = Transaction::new_signed_with_payer( - &[Instruction::new_with_borsh( - id(), - &instruction::RecordInstruction::Write { offset: 0, data }, - vec![ + &[Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(account.pubkey(), false), AccountMeta::new_readonly(authority.pubkey(), false), ], - )], + data: instruction::RecordInstruction::Write { offset: 0, data }.pack(), + }], Some(&context.payer.pubkey()), &[&context.payer], context.last_blockhash, @@ -310,7 +315,7 @@ async fn close_account_success() { .unwrap(); assert_eq!( account.lamports, - 1.max(Rent::default().minimum_balance(get_packed_len::())) + 1.max(Rent::default().minimum_balance(pod_get_packed_len::())) ); } @@ -327,15 +332,15 @@ async fn close_account_fail_wrong_authority() { let wrong_authority = Keypair::new(); let transaction = Transaction::new_signed_with_payer( - &[Instruction::new_with_borsh( - id(), - &instruction::RecordInstruction::CloseAccount, - vec![ + &[Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(account.pubkey(), false), AccountMeta::new_readonly(wrong_authority.pubkey(), true), AccountMeta::new(Pubkey::new_unique(), false), ], - )], + data: instruction::RecordInstruction::CloseAccount.pack(), + }], Some(&context.payer.pubkey()), &[&context.payer, &wrong_authority], context.last_blockhash, @@ -366,15 +371,15 @@ async fn close_account_fail_unsigned() { initialize_storage_account(&mut context, &authority, &account, data).await; let transaction = Transaction::new_signed_with_payer( - &[Instruction::new_with_borsh( - id(), - &instruction::RecordInstruction::CloseAccount, - vec![ + &[Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(account.pubkey(), false), AccountMeta::new_readonly(authority.pubkey(), false), AccountMeta::new(Pubkey::new_unique(), false), ], - )], + data: instruction::RecordInstruction::CloseAccount.pack(), + }], Some(&context.payer.pubkey()), &[&context.payer], context.last_blockhash, @@ -418,11 +423,13 @@ async fn set_authority_success() { .await .unwrap(); - let account_data = context + let account_handle = context .banks_client - .get_account_data_with_borsh::(account.pubkey()) + .get_account(account.pubkey()) .await + .unwrap() .unwrap(); + let account_data = pod_from_bytes::(&account_handle.data).unwrap(); assert_eq!(account_data.authority, new_authority.pubkey()); let new_data = Data { @@ -433,7 +440,7 @@ async fn set_authority_success() { &account.pubkey(), &new_authority.pubkey(), 0, - new_data.try_to_vec().unwrap(), + pod_bytes_of(&new_data), )], Some(&context.payer.pubkey()), &[&context.payer, &new_authority], @@ -445,11 +452,13 @@ async fn set_authority_success() { .await .unwrap(); - let account_data = context + let account_handle = context .banks_client - .get_account_data_with_borsh::(account.pubkey()) + .get_account(account.pubkey()) .await + .unwrap() .unwrap(); + let account_data = pod_from_bytes::(&account_handle.data).unwrap(); assert_eq!(account_data.data, new_data); assert_eq!(account_data.authority, new_authority.pubkey()); assert_eq!(account_data.version, RecordData::CURRENT_VERSION); @@ -468,15 +477,15 @@ async fn set_authority_fail_wrong_authority() { let wrong_authority = Keypair::new(); let transaction = Transaction::new_signed_with_payer( - &[Instruction::new_with_borsh( - id(), - &instruction::RecordInstruction::SetAuthority, - vec![ + &[Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(account.pubkey(), false), AccountMeta::new_readonly(wrong_authority.pubkey(), true), AccountMeta::new(Pubkey::new_unique(), false), ], - )], + data: instruction::RecordInstruction::SetAuthority.pack(), + }], Some(&context.payer.pubkey()), &[&context.payer, &wrong_authority], context.last_blockhash, @@ -507,15 +516,15 @@ async fn set_authority_fail_unsigned() { initialize_storage_account(&mut context, &authority, &account, data).await; let transaction = Transaction::new_signed_with_payer( - &[Instruction::new_with_borsh( - id(), - &instruction::RecordInstruction::SetAuthority, - vec![ + &[Instruction { + program_id: id(), + accounts: vec![ AccountMeta::new(account.pubkey(), false), AccountMeta::new_readonly(authority.pubkey(), false), AccountMeta::new(Pubkey::new_unique(), false), ], - )], + data: instruction::RecordInstruction::SetAuthority.pack(), + }], Some(&context.payer.pubkey()), &[&context.payer], context.last_blockhash,