diff --git a/Cargo.lock b/Cargo.lock index 3ffc70d..8758d26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2211,14 +2211,14 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steel" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b09eae425248a8afcef3cf3300dfa548791194fa9fe519dadd5c57d7964dbc" +version = "0.1.5" dependencies = [ "bytemuck", + "num_enum", "solana-program", "spl-associated-token-account", "spl-token", + "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3bb3b18..134951f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,5 @@ steel = { features = ["spl"], version = "0.1.0" } thiserror = "1.0.57" +[patch.crates-io] +steel = { path = "../steel/lib" } diff --git a/api/src/loaders.rs b/api/src/loaders.rs index 925335d..77fa46a 100644 --- a/api/src/loaders.rs +++ b/api/src/loaders.rs @@ -192,34 +192,23 @@ pub fn load_any_proof(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), P Ok(()) } -/// Errors if: -/// - Owner is not Ore program. -/// - Address does not match the expected address. -/// - Data is empty. -/// - Data cannot deserialize into a treasury account. -/// - Expected to be writable, but is not. -pub fn load_treasury(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); +pub trait OreAccountInfoValidation { + fn is_config(&self) -> Result<&Self, ProgramError>; + fn is_treasury(&self) -> Result<&Self, ProgramError>; +} + +impl<'a> OreAccountInfoValidation for AccountInfo<'a> { + fn is_config(&self) -> Result<&Self, ProgramError> { + self.has_address(&CONFIG_ADDRESS)? + .has_owner(&crate::ID)? + .is_type::() } - if info.key.ne(&TREASURY_ADDRESS) { - return Err(ProgramError::InvalidSeeds); + fn is_treasury(&self) -> Result<&Self, ProgramError> { + self.has_address(&TREASURY_ADDRESS)? + .has_owner(&crate::ID)? + .is_type::() } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - if info.data.borrow()[0].ne(&(Treasury::discriminator() as u8)) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) } /// Errors if: diff --git a/api/src/state/bus.rs b/api/src/state/bus.rs index 2c12776..f92f715 100644 --- a/api/src/state/bus.rs +++ b/api/src/state/bus.rs @@ -1,5 +1,3 @@ -use bytemuck::{Pod, Zeroable}; -use solana_program::pubkey::Pubkey; use steel::*; use crate::consts::BUS; @@ -30,4 +28,25 @@ pub fn bus_pda(id: u8) -> (Pubkey, u8) { Pubkey::find_program_address(&[BUS, &[id]], &crate::id()) } +impl<'a> From<&'a [u8]> for &'a Bus { + fn from(value: &'a [u8]) -> &'a Bus { + Bus::try_from_bytes(value).unwrap() + } +} + +impl<'a> From<*const u8> for &'a Bus { + fn from(value: *const u8) -> &'a Bus { + unsafe { + if Bus::discriminator().ne(&value.add(0).read()) { + panic!(""); + } + bytemuck::try_from_bytes::(std::slice::from_raw_parts( + value.add(8), + std::mem::size_of::(), + )) + .expect("") + } + } +} + account!(OreAccount, Bus); diff --git a/api/src/state/config.rs b/api/src/state/config.rs index e35216c..64bbf8c 100644 --- a/api/src/state/config.rs +++ b/api/src/state/config.rs @@ -1,5 +1,3 @@ -use bytemuck::{Pod, Zeroable}; -use solana_program::pubkey::Pubkey; use steel::*; use crate::consts::CONFIG; diff --git a/program/src/claim.rs b/program/src/claim.rs index 7cf33cc..9f564b8 100644 --- a/program/src/claim.rs +++ b/program/src/claim.rs @@ -16,12 +16,12 @@ pub fn process_claim(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; + signer.is_signer()?; load_token_account(beneficiary_info, None, &MINT_ADDRESS, true)?; load_proof(proof_info, signer.key, true)?; - load_treasury(treasury_info, false)?; + treasury_info.is_treasury()?; load_treasury_tokens(treasury_tokens_info, true)?; - load_program(token_program, spl_token::id())?; + token_program.has_address(&spl_token::ID)?; // Update miner balance. let mut proof_data = proof_info.data.borrow_mut(); diff --git a/program/src/close.rs b/program/src/close.rs index 4529e39..4201b5f 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -8,12 +8,12 @@ use steel::*; /// Close closes a proof account and returns the rent to the owner. pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. - let [signer, proof_info, system_program] = accounts else { + let [signer_info, proof_info, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_proof(proof_info, signer.key, true)?; - load_program(system_program, system_program::id())?; + signer_info.is_signer()?; + load_proof(proof_info, signer_info.key, true)?; + system_program.is_program(&system_program::ID)?; // Validate balance is zero. let proof_data = proof_info.data.borrow(); @@ -27,7 +27,7 @@ pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul proof_info.realloc(0, true)?; // Send remaining lamports to signer. - **signer.lamports.borrow_mut() += proof_info.lamports(); + **signer_info.lamports.borrow_mut() += proof_info.lamports(); **proof_info.lamports.borrow_mut() = 0; Ok(()) diff --git a/program/src/initialize.rs b/program/src/initialize.rs index b4255d0..16bd2d0 100644 --- a/program/src/initialize.rs +++ b/program/src/initialize.rs @@ -18,23 +18,22 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR let args = Initialize::try_from_bytes(data)?; // Load accounts. - let [signer, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, metadata_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program, metadata_program, rent_sysvar] = + let [signer_info, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, metadata_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program, metadata_program, rent_sysvar] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_uninitialized_pda(bus_0_info, &[BUS, &[0]], args.bus_0_bump, &ore_api::id())?; - load_uninitialized_pda(bus_1_info, &[BUS, &[1]], args.bus_1_bump, &ore_api::id())?; - load_uninitialized_pda(bus_2_info, &[BUS, &[2]], args.bus_2_bump, &ore_api::id())?; - load_uninitialized_pda(bus_3_info, &[BUS, &[3]], args.bus_3_bump, &ore_api::id())?; - load_uninitialized_pda(bus_4_info, &[BUS, &[4]], args.bus_4_bump, &ore_api::id())?; - load_uninitialized_pda(bus_5_info, &[BUS, &[5]], args.bus_5_bump, &ore_api::id())?; - load_uninitialized_pda(bus_6_info, &[BUS, &[6]], args.bus_6_bump, &ore_api::id())?; - load_uninitialized_pda(bus_7_info, &[BUS, &[7]], args.bus_7_bump, &ore_api::id())?; - load_uninitialized_pda(config_info, &[CONFIG], args.config_bump, &ore_api::id())?; - load_uninitialized_pda( - metadata_info, + signer_info.is_signer()?.has_address(&INITIALIZER_ADDRESS)?; + bus_0_info.is_empty_pda(&[BUS, &[0]], args.bus_0_bump, &ore_api::ID)?; + bus_1_info.is_empty_pda(&[BUS, &[1]], args.bus_1_bump, &ore_api::ID)?; + bus_2_info.is_empty_pda(&[BUS, &[2]], args.bus_2_bump, &ore_api::ID)?; + bus_3_info.is_empty_pda(&[BUS, &[3]], args.bus_3_bump, &ore_api::ID)?; + bus_4_info.is_empty_pda(&[BUS, &[4]], args.bus_4_bump, &ore_api::ID)?; + bus_5_info.is_empty_pda(&[BUS, &[5]], args.bus_5_bump, &ore_api::ID)?; + bus_6_info.is_empty_pda(&[BUS, &[6]], args.bus_6_bump, &ore_api::ID)?; + bus_7_info.is_empty_pda(&[BUS, &[7]], args.bus_7_bump, &ore_api::ID)?; + config_info.is_empty_pda(&[CONFIG], args.config_bump, &ore_api::id())?; + metadata_info.is_empty_pda( &[ METADATA, mpl_token_metadata::ID.as_ref(), @@ -43,29 +42,14 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR args.metadata_bump, &mpl_token_metadata::ID, )?; - load_uninitialized_pda( - mint_info, - &[MINT, MINT_NOISE.as_slice()], - args.mint_bump, - &ore_api::id(), - )?; - load_uninitialized_pda( - treasury_info, - &[TREASURY], - args.treasury_bump, - &ore_api::id(), - )?; - load_system_account(treasury_tokens_info, true)?; - load_program(system_program, system_program::id())?; - load_program(token_program, spl_token::id())?; - load_program(associated_token_program, spl_associated_token_account::id())?; - load_program(metadata_program, mpl_token_metadata::ID)?; - load_sysvar(rent_sysvar, sysvar::rent::id())?; - - // Check signer. - if signer.key.ne(&INITIALIZER_ADDRESS) { - return Err(ProgramError::MissingRequiredSignature); - } + mint_info.is_empty_pda(&[MINT, MINT_NOISE.as_slice()], args.mint_bump, &ore_api::ID)?; + treasury_info.is_empty_pda(&[TREASURY], args.treasury_bump, &ore_api::ID)?; + treasury_tokens_info.is_empty()?; + system_program.is_program(&system_program::ID)?; + token_program.is_program(&spl_token::ID)?; + associated_token_program.is_program(&spl_associated_token_account::ID)?; + metadata_program.is_program(&mpl_token_metadata::ID)?; + rent_sysvar.is_sysvar(&sysvar::rent::ID)?; // Initialize bus accounts. let bus_infos = [ @@ -89,7 +73,7 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR 8 + size_of::(), &[BUS, &[i as u8], &[bus_bumps[i]]], system_program, - signer, + signer_info, )?; let mut bus_data = bus_infos[i].try_borrow_mut_data()?; bus_data[0] = Bus::discriminator() as u8; @@ -107,7 +91,7 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR 8 + size_of::(), &[CONFIG, &[args.config_bump]], system_program, - signer, + signer_info, )?; let mut config_data = config_info.data.borrow_mut(); config_data[0] = Config::discriminator() as u8; @@ -124,7 +108,7 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR 8 + size_of::(), &[TREASURY, &[args.treasury_bump]], system_program, - signer, + signer_info, )?; let mut treasury_data = treasury_info.data.borrow_mut(); treasury_data[0] = Treasury::discriminator() as u8; @@ -137,7 +121,7 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR Mint::LEN, &[MINT, MINT_NOISE.as_slice(), &[args.mint_bump]], system_program, - signer, + signer_info, )?; solana_program::program::invoke_signed( &spl_token::instruction::initialize_mint( @@ -162,8 +146,8 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR metadata: metadata_info, mint: mint_info, mint_authority: treasury_info, - payer: signer, - update_authority: (signer, true), + payer: signer_info, + update_authority: (signer_info, true), system_program, rent: Some(rent_sysvar), __args: mpl_token_metadata::instructions::CreateMetadataAccountV3InstructionArgs { @@ -184,7 +168,7 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR // Initialize treasury token account. create_ata( - signer, + signer_info, treasury_info, treasury_tokens_info, mint_info, diff --git a/program/src/mine.rs b/program/src/mine.rs index 40aa1ee..cf5cf2d 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -26,7 +26,7 @@ use solana_program::{ use steel::*; /// Mine validates hashes and increments a miner's collectable balance. -pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult { +pub fn process_mine(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult { // Parse args. let args = Mine::try_from_bytes(data)?; @@ -36,12 +36,12 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_any_bus(bus_info, true)?; - load_config(config_info, false)?; + signer.is_signer()?; + bus_info.is_type::()?.is_writable()?; + config_info.is_config()?; load_proof_with_miner(proof_info, signer.key, true)?; - load_sysvar(instructions_sysvar, sysvar::instructions::id())?; - load_sysvar(slot_hashes_sysvar, sysvar::slot_hashes::id())?; + instructions_sysvar.is_sysvar(&sysvar::instructions::ID)?; + slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?; // Authenticate the proof account. // diff --git a/program/src/open.rs b/program/src/open.rs index f914af8..e2c99e3 100644 --- a/program/src/open.rs +++ b/program/src/open.rs @@ -19,28 +19,25 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let args = Open::try_from_bytes(data)?; // Load accounts. - let [signer, miner_info, payer_info, proof_info, system_program, slot_hashes_info] = accounts + let [signer_info, miner_info, payer_info, proof_info, system_program, slot_hashes_info] = + accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_any(miner_info, false)?; - load_signer(payer_info)?; - load_uninitialized_pda( - proof_info, - &[PROOF, signer.key.as_ref()], - args.bump, - &ore_api::id(), - )?; - load_program(system_program, system_program::id())?; - load_sysvar(slot_hashes_info, sysvar::slot_hashes::id())?; + signer_info.is_signer()?; + payer_info.is_signer()?; + proof_info + .is_empty_pda(&[PROOF, signer_info.key.as_ref()], args.bump, &ore_api::ID)? + .is_writable()?; + system_program.is_program(&system_program::ID)?; + slot_hashes_info.is_sysvar(&sysvar::slot_hashes::ID)?; // Initialize proof. create_pda( proof_info, &ore_api::id(), 8 + size_of::(), - &[PROOF, signer.key.as_ref(), &[args.bump]], + &[PROOF, signer_info.key.as_ref(), &[args.bump]], system_program, payer_info, )?; @@ -48,10 +45,10 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let mut proof_data = proof_info.data.borrow_mut(); proof_data[0] = Proof::discriminator() as u8; let proof = Proof::try_from_bytes_mut(&mut proof_data)?; - proof.authority = *signer.key; + proof.authority = *signer_info.key; proof.balance = 0; proof.challenge = hashv(&[ - signer.key.as_ref(), + signer_info.key.as_ref(), &slot_hashes_info.data.borrow()[0..size_of::()], ]) .0; diff --git a/program/src/reset.rs b/program/src/reset.rs index a3abcfa..ffb282c 100644 --- a/program/src/reset.rs +++ b/program/src/reset.rs @@ -14,12 +14,12 @@ use steel::*; /// Reset tops up the bus balances, updates the base reward rate, and sets up the ORE program for the next epoch. pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. - let [signer, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, mint_info, treasury_info, treasury_tokens_info, token_program] = + let [signer_info, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, mint_info, treasury_info, treasury_tokens_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; + signer_info.is_signer()?; load_bus(bus_0_info, 0, true)?; load_bus(bus_1_info, 1, true)?; load_bus(bus_2_info, 2, true)?; @@ -28,11 +28,11 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul load_bus(bus_5_info, 5, true)?; load_bus(bus_6_info, 6, true)?; load_bus(bus_7_info, 7, true)?; - load_config(config_info, true)?; + config_info.is_config()?.is_writable()?; load_mint(mint_info, MINT_ADDRESS, true)?; - load_treasury(treasury_info, true)?; + treasury_info.is_treasury()?.is_writable()?; load_treasury_tokens(treasury_tokens_info, true)?; - load_program(token_program, spl_token::id())?; + token_program.is_program(&spl_token::ID)?; let busses: [&AccountInfo; BUS_COUNT] = [ bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, diff --git a/program/src/stake.rs b/program/src/stake.rs index 08f33b5..94fef15 100644 --- a/program/src/stake.rs +++ b/program/src/stake.rs @@ -12,14 +12,15 @@ pub fn process_stake(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let amount = u64::from_le_bytes(args.amount); // Load accounts. - let [signer, proof_info, sender_info, treasury_tokens_info, token_program] = accounts else { + let [signer_info, proof_info, sender_info, treasury_tokens_info, token_program] = accounts + else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_proof(proof_info, signer.key, true)?; - load_token_account(sender_info, Some(signer.key), &MINT_ADDRESS, true)?; + signer_info.is_signer()?; + load_proof(proof_info, signer_info.key, true)?; + load_token_account(sender_info, Some(signer_info.key), &MINT_ADDRESS, true)?; load_treasury_tokens(treasury_tokens_info, true)?; - load_program(token_program, spl_token::id())?; + token_program.is_program(&spl_token::ID)?; // Update the proof balance. let mut proof_data = proof_info.data.borrow_mut(); @@ -32,7 +33,7 @@ pub fn process_stake(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // Transfer tokens from signer to treasury. transfer( - signer, + signer_info, sender_info, treasury_tokens_info, token_program, diff --git a/program/src/update.rs b/program/src/update.rs index 6ffdf3b..849eb1a 100644 --- a/program/src/update.rs +++ b/program/src/update.rs @@ -10,8 +10,8 @@ pub fn process_update(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResu let [signer, miner_info, proof_info] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_any(miner_info, false)?; + signer.is_signer()?; + // load_any(miner_info, false)?; load_proof(proof_info, signer.key, true)?; // Update the proof's miner authority. diff --git a/program/src/upgrade.rs b/program/src/upgrade.rs index 7530117..d3b2220 100644 --- a/program/src/upgrade.rs +++ b/program/src/upgrade.rs @@ -13,17 +13,22 @@ pub fn process_upgrade(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResu let amount = u64::from_le_bytes(args.amount); // Load accounts - let [signer, beneficiary_info, mint_info, mint_v1_info, sender_info, treasury_info, token_program] = + let [signer_info, beneficiary_info, mint_info, mint_v1_info, sender_info, treasury_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_token_account(beneficiary_info, Some(&signer.key), &MINT_ADDRESS, true)?; + signer_info.is_signer()?; + load_token_account( + beneficiary_info, + Some(&signer_info.key), + &MINT_ADDRESS, + true, + )?; load_mint(mint_info, MINT_ADDRESS, true)?; load_mint(mint_v1_info, MINT_V1_ADDRESS, true)?; - load_token_account(sender_info, Some(signer.key), &MINT_V1_ADDRESS, true)?; - load_program(token_program, spl_token::id())?; + load_token_account(sender_info, Some(signer_info.key), &MINT_V1_ADDRESS, true)?; + token_program.is_program(&spl_token::ID)?; // Burn v1 tokens solana_program::program::invoke( @@ -31,15 +36,15 @@ pub fn process_upgrade(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResu &spl_token::id(), sender_info.key, mint_v1_info.key, - signer.key, - &[signer.key], + signer_info.key, + &[signer_info.key], amount, )?, &[ token_program.clone(), sender_info.clone(), mint_v1_info.clone(), - signer.clone(), + signer_info.clone(), ], )?;