diff --git a/programs/bpf/rust/sysvar/src/lib.rs b/programs/bpf/rust/sysvar/src/lib.rs index 7bdea1bb73..9513405f52 100644 --- a/programs/bpf/rust/sysvar/src/lib.rs +++ b/programs/bpf/rust/sysvar/src/lib.rs @@ -7,11 +7,12 @@ use solana_program::{ entrypoint, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent, sysvar::{ - self, clock::Clock, fees::Fees, rent::Rent, slot_hashes::SlotHashes, - stake_history::StakeHistory, Sysvar, + self, clock::Clock, fees::Fees, instructions, rent::Rent, slot_hashes::SlotHashes, + slot_history::SlotHistory, stake_history::StakeHistory, Sysvar, }, }; @@ -25,37 +26,54 @@ fn process_instruction( // Clock msg!("Clock identifier:"); sysvar::clock::id().log(); - let clock = Clock::from_account_info(&accounts[2]).expect("clock"); + let clock = Clock::from_account_info(&accounts[2]).unwrap(); assert_eq!(clock.slot, DEFAULT_SLOTS_PER_EPOCH + 1); // Fees msg!("Fees identifier:"); sysvar::fees::id().log(); - let fees = Fees::from_account_info(&accounts[3]).expect("fees"); + let fees = Fees::from_account_info(&accounts[3]).unwrap(); let fee_calculator = fees.fee_calculator; assert_eq!(fee_calculator.lamports_per_signature, 0); + // Instructions + msg!("Instructions identifier:"); + sysvar::instructions::id().log(); + let index = instructions::load_current_index(&accounts[4].try_borrow_data()?); + assert_eq!(0, index); + msg!( + "instruction {:?}", + instructions::load_instruction_at(index as usize, &accounts[4].try_borrow_data()?) + ); + + let due = Rent::from_account_info(&accounts[5]).unwrap().due( + rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR * rent::DEFAULT_EXEMPTION_THRESHOLD as u64, + 1, + 1.0, + ); + assert_eq!(due, (0, true)); + // Slot Hashes msg!("SlotHashes identifier:"); sysvar::slot_hashes::id().log(); - let slot_hashes = SlotHashes::from_account_info(&accounts[4]).expect("slot_hashes"); - assert!(slot_hashes.len() >= 1); + assert_eq!( + Err(ProgramError::UnsupportedSysvar), + SlotHashes::from_account_info(&accounts[6]) + ); + + // Slot History + msg!("SlotHistory identifier:"); + sysvar::slot_history::id().log(); + assert_eq!( + Err(ProgramError::UnsupportedSysvar), + SlotHistory::from_account_info(&accounts[7]) + ); // Stake History msg!("StakeHistory identifier:"); sysvar::stake_history::id().log(); - let stake_history = StakeHistory::from_account_info(&accounts[5]).expect("stake_history"); + let stake_history = StakeHistory::from_account_info(&accounts[8]).unwrap(); assert!(stake_history.len() >= 1); - let rent = Rent::from_account_info(&accounts[6]).unwrap(); - assert_eq!( - rent.due( - rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR * rent::DEFAULT_EXEMPTION_THRESHOLD as u64, - 1, - 1.0 - ), - (0, true) - ); - Ok(()) } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index e89620083b..0169b98caf 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -35,7 +35,7 @@ use solana_sdk::{ pubkey::Pubkey, signature::{keypair_from_seed, Keypair, Signer}, system_instruction, - sysvar::{clock, fees, rent, slot_hashes, stake_history}, + sysvar::{clock, fees, rent, slot_hashes, slot_history, stake_history, instructions}, transaction::{Transaction, TransactionError}, }; use solana_transaction_status::{ @@ -465,11 +465,14 @@ fn test_program_bpf_sanity() { let account_metas = vec![ AccountMeta::new(mint_keypair.pubkey(), true), AccountMeta::new(Keypair::new().pubkey(), false), - AccountMeta::new(clock::id(), false), - AccountMeta::new(fees::id(), false), - AccountMeta::new(slot_hashes::id(), false), - AccountMeta::new(stake_history::id(), false), - AccountMeta::new(rent::id(), false), + AccountMeta::new_readonly(clock::id(), false), + AccountMeta::new_readonly(fees::id(), false), + AccountMeta::new_readonly(instructions::id(), false), + AccountMeta::new_readonly(rent::id(), false), + AccountMeta::new_readonly(slot_hashes::id(), false), + AccountMeta::new_readonly(slot_history::id(), false), + AccountMeta::new_readonly(stake_history::id(), false), + ]; let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 375813914e..5db227584a 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -114,7 +114,7 @@ impl ExecuteTimings { } type BankStatusCache = StatusCache>; -#[frozen_abi(digest = "EcB9J7sm37t1R47vLcvGuNeiRciB4Efq1EDWDWL6Bp5h")] +#[frozen_abi(digest = "4mSWwHd4RrLjCXH7RFrm6K3wZSsi9DfVJK3Ngz9jKk7D")] pub type BankSlotDelta = SlotDelta>; type TransactionAccountRefCells = Vec>>; type TransactionAccountDepRefCells = Vec<(Pubkey, Rc>)>; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 4a7eb0da2d..de25f9fde2 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -24,6 +24,7 @@ use solana_sdk::{ pubkey::Pubkey, rent::Rent, system_program, + sysvar::instructions, transaction::TransactionError, }; use std::{ @@ -1049,9 +1050,9 @@ impl MessageProcessor { // before the account pre-values are taken care of if feature_set.is_active(&instructions_sysvar_enabled::id()) { for (i, key) in message.account_keys.iter().enumerate() { - if solana_sdk::sysvar::instructions::check_id(key) { + if instructions::check_id(key) { let mut mut_account_ref = accounts[i].borrow_mut(); - solana_sdk::sysvar::instructions::store_current_index( + instructions::store_current_index( mut_account_ref.data_as_mut_slice(), instruction_index as u16, ); diff --git a/sdk/program/src/instruction.rs b/sdk/program/src/instruction.rs index 041889020f..eb54966798 100644 --- a/sdk/program/src/instruction.rs +++ b/sdk/program/src/instruction.rs @@ -211,6 +211,10 @@ pub enum InstructionError { /// Program arithmetic overflowed #[error("Program arithmetic overflowed")] ArithmeticOverflow, + + /// Unsupported sysvar + #[error("Unsupported sysvar")] + UnsupportedSysvar, } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] diff --git a/sdk/program/src/message.rs b/sdk/program/src/message.rs index d14b43b387..2ff13c86aa 100644 --- a/sdk/program/src/message.rs +++ b/sdk/program/src/message.rs @@ -384,15 +384,15 @@ impl Message { // [0..2 - num_instructions // // Then a table of offsets of where to find them in the data - // 3..2*num_instructions table of instruction offsets + // 3..2 * num_instructions table of instruction offsets // // Each instruction is then encoded as: // 0..2 - num_accounts - // 3 - meta_byte -> (bit 0 signer, bit 1 is_writable) - // 4..36 - pubkey - 32 bytes - // 36..64 - program_id - // 33..34 - data len - u16 - // 35..data_len - data + // 2 - meta_byte -> (bit 0 signer, bit 1 is_writable) + // 3..35 - pubkey - 32 bytes + // 35..67 - program_id + // 67..69 - data len - u16 + // 69..data_len - data pub fn serialize_instructions(&self, demote_sysvar_write_locks: bool) -> Vec { // 64 bytes is a reasonable guess, calculating exactly is slower in benchmarks let mut data = Vec::with_capacity(self.instructions.len() * (32 * 2)); diff --git a/sdk/program/src/program_error.rs b/sdk/program/src/program_error.rs index 2df4a78f12..7d7aa47180 100644 --- a/sdk/program/src/program_error.rs +++ b/sdk/program/src/program_error.rs @@ -43,6 +43,8 @@ pub enum ProgramError { BorshIoError(String), #[error("An account does not have enough lamports to be rent-exempt")] AccountNotRentExempt, + #[error("Unsupported sysvar")] + UnsupportedSysvar, } pub trait PrintProgramError { @@ -79,6 +81,7 @@ impl PrintProgramError for ProgramError { Self::InvalidSeeds => msg!("Error: InvalidSeeds"), Self::BorshIoError(_) => msg!("Error: BorshIoError"), Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"), + Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"), } } } @@ -107,6 +110,7 @@ pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13); pub const INVALID_SEEDS: u64 = to_builtin!(14); pub const BORSH_IO_ERROR: u64 = to_builtin!(15); pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16); +pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17); impl From for u64 { fn from(error: ProgramError) -> Self { @@ -126,6 +130,7 @@ impl From for u64 { ProgramError::InvalidSeeds => INVALID_SEEDS, ProgramError::BorshIoError(_) => BORSH_IO_ERROR, ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT, + ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR, ProgramError::Custom(error) => { if error == 0 { @@ -154,6 +159,7 @@ impl From for ProgramError { ACCOUNT_BORROW_FAILED => ProgramError::AccountBorrowFailed, MAX_SEED_LENGTH_EXCEEDED => ProgramError::MaxSeedLengthExceeded, INVALID_SEEDS => ProgramError::InvalidSeeds, + UNSUPPORTED_SYSVAR => ProgramError::UnsupportedSysvar, CUSTOM_ZERO => ProgramError::Custom(0), _ => ProgramError::Custom(error as u32), } @@ -180,6 +186,7 @@ impl TryFrom for ProgramError { Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded), Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)), Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt), + Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar), _ => Err(error), } } @@ -206,6 +213,7 @@ where ACCOUNT_BORROW_FAILED => InstructionError::AccountBorrowFailed, MAX_SEED_LENGTH_EXCEEDED => InstructionError::MaxSeedLengthExceeded, INVALID_SEEDS => InstructionError::InvalidSeeds, + UNSUPPORTED_SYSVAR => InstructionError::UnsupportedSysvar, _ => { // A valid custom error has no bits set in the upper 32 if error >> BUILTIN_BIT_SHIFT == 0 { diff --git a/sdk/program/src/sysvar/instructions.rs b/sdk/program/src/sysvar/instructions.rs index c05c1bb75a..c211299df1 100644 --- a/sdk/program/src/sysvar/instructions.rs +++ b/sdk/program/src/sysvar/instructions.rs @@ -1,14 +1,14 @@ #![allow(clippy::integer_arithmetic)] //! This account contains the serialized transaction instructions -use crate::{instruction::Instruction, sanitize::SanitizeError, sysvar::Sysvar}; +use crate::{instruction::Instruction, sanitize::SanitizeError}; -pub type Instructions = Vec; +// Instructions Sysvar, dummy type, use the associated helpers instead of the Sysvar trait +pub struct Instructions(); crate::declare_sysvar_id!("Sysvar1nstructions1111111111111111111111111", Instructions); -impl Sysvar for Instructions {} - +/// Load the current instruction's index from the Instructions Sysvar data pub fn load_current_index(data: &[u8]) -> u16 { let mut instr_fixed_data = [0u8; 2]; let len = data.len(); @@ -16,11 +16,13 @@ pub fn load_current_index(data: &[u8]) -> u16 { u16::from_le_bytes(instr_fixed_data) } +/// Store the current instruction's index in the Instructions Sysvar data pub fn store_current_index(data: &mut [u8], instruction_index: u16) { let last_index = data.len() - 2; data[last_index..last_index + 2].copy_from_slice(&instruction_index.to_le_bytes()); } +/// Load an instruction at the specified index pub fn load_instruction_at(index: usize, data: &[u8]) -> Result { crate::message::Message::deserialize_instruction(index, data) } diff --git a/sdk/program/src/sysvar/slot_hashes.rs b/sdk/program/src/sysvar/slot_hashes.rs index 71865d04d8..d10cb57d8a 100644 --- a/sdk/program/src/sysvar/slot_hashes.rs +++ b/sdk/program/src/sysvar/slot_hashes.rs @@ -4,7 +4,7 @@ //! pub use crate::slot_hashes::SlotHashes; -use crate::sysvar::Sysvar; +use crate::{account_info::AccountInfo, program_error::ProgramError, sysvar::Sysvar}; crate::declare_sysvar_id!("SysvarS1otHashes111111111111111111111111111", SlotHashes); @@ -14,6 +14,10 @@ impl Sysvar for SlotHashes { // hard-coded so that we don't have to construct an empty 20_488 // golden, update if MAX_ENTRIES changes } + fn from_account_info(_account_info: &AccountInfo) -> Result { + // This sysvar is too large to bincode::deserialize in-program + Err(ProgramError::UnsupportedSysvar) + } } #[cfg(test)] diff --git a/sdk/program/src/sysvar/slot_history.rs b/sdk/program/src/sysvar/slot_history.rs index 7aafa02d8a..fae51b2f26 100644 --- a/sdk/program/src/sysvar/slot_history.rs +++ b/sdk/program/src/sysvar/slot_history.rs @@ -1,9 +1,11 @@ //! named accounts for synthesized data accounts for bank state, etc. //! //! this account carries a bitvector of slots present over the past -//! epoch +//! epoch //! -pub use crate::slot_history::SlotHistory; +pub use crate::{ + account_info::AccountInfo, program_error::ProgramError, slot_history::SlotHistory, +}; use crate::sysvar::Sysvar; @@ -15,6 +17,10 @@ impl Sysvar for SlotHistory { // hard-coded so that we don't have to construct an empty 131_097 // golden, update if MAX_ENTRIES changes } + fn from_account_info(_account_info: &AccountInfo) -> Result { + // This sysvar is too large to bincode::deserialize in-program + Err(ProgramError::UnsupportedSysvar) + } } #[cfg(test)] diff --git a/storage-proto/proto/solana.storage.transaction_by_addr.rs b/storage-proto/proto/solana.storage.transaction_by_addr.rs index ed7aebb9ba..692a9b939e 100644 --- a/storage-proto/proto/solana.storage.transaction_by_addr.rs +++ b/storage-proto/proto/solana.storage.transaction_by_addr.rs @@ -118,4 +118,5 @@ pub enum InstructionErrorType { AccountNotRentExempt = 45, InvalidAccountOwner = 46, ArithmeticOverflow = 47, + UnsupportedSysvar = 48, } diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 708a866d59..9e18f29df9 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -510,6 +510,7 @@ impl TryFrom for TransactionError { 45 => InstructionError::AccountNotRentExempt, 46 => InstructionError::InvalidAccountOwner, 47 => InstructionError::ArithmeticOverflow, + 48 => InstructionError::UnsupportedSysvar, _ => return Err("Invalid InstructionError"), }; @@ -739,6 +740,9 @@ impl From for tx_by_addr::TransactionError { InstructionError::ArithmeticOverflow => { tx_by_addr::InstructionErrorType::ArithmeticOverflow } + InstructionError::UnsupportedSysvar => { + tx_by_addr::InstructionErrorType::UnsupportedSysvar + } } as i32, custom: match instruction_error { InstructionError::Custom(custom) => {