From 39e52de91e1c9576846d0abc72f2dea6f56dfba5 Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Tue, 30 Jul 2024 18:39:34 +0000 Subject: [PATCH] update comments --- api/src/instruction.rs | 20 +++++++++------- api/src/state/bus.rs | 6 ++--- api/src/state/config.rs | 2 +- api/src/state/treasury.rs | 4 ++-- program/src/claim.rs | 10 ++++---- program/src/close.rs | 8 +++---- program/src/initialize.rs | 18 +++++++------- program/src/mine.rs | 49 +++++++++++++++++++++++++-------------- program/src/open.rs | 10 +++----- program/src/reset.rs | 22 +++++++++--------- program/src/stake.rs | 10 ++++---- program/src/update.rs | 4 ++-- program/src/upgrade.rs | 2 +- utils/src/lib.rs | 4 ++-- 14 files changed, 91 insertions(+), 78 deletions(-) diff --git a/api/src/instruction.rs b/api/src/instruction.rs index be6a60e..ac0ee6e 100644 --- a/api/src/instruction.rs +++ b/api/src/instruction.rs @@ -16,6 +16,7 @@ use crate::{ #[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)] #[rustfmt::skip] pub enum OreInstruction { + // User Claim = 0, Close = 1, Mine = 2, @@ -25,6 +26,7 @@ pub enum OreInstruction { Update = 6, Upgrade = 7, + // Admin Initialize = 100, } @@ -96,6 +98,15 @@ impl_instruction_from_bytes!(ClaimArgs); impl_instruction_from_bytes!(StakeArgs); impl_instruction_from_bytes!(UpgradeArgs); +/// Builds an auth instruction. +pub fn auth(proof: Pubkey) -> Instruction { + Instruction { + program_id: NOOP_PROGRAM_ID, + accounts: vec![], + data: proof.to_bytes().to_vec(), + } +} + /// Builds a claim instruction. pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction { let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0; @@ -139,15 +150,6 @@ pub fn close(signer: Pubkey) -> Instruction { } } -/// Builds an auth instruction. -pub fn auth(proof: Pubkey) -> Instruction { - Instruction { - program_id: NOOP_PROGRAM_ID, - accounts: vec![], - data: proof.to_bytes().to_vec(), - } -} - /// Builds a mine instruction. pub fn mine( signer: Pubkey, diff --git a/api/src/state/bus.rs b/api/src/state/bus.rs index f8bc481..d69f625 100644 --- a/api/src/state/bus.rs +++ b/api/src/state/bus.rs @@ -4,8 +4,8 @@ use crate::utils::{impl_account_from_bytes, impl_to_bytes, Discriminator}; use super::AccountDiscriminator; -/// Bus accounts are responsible for distributing mining rewards. -/// There are 8 busses total to minimize write-lock contention and allow for parallel mine operations. +/// Bus accounts are responsible for distributing mining rewards. There are 8 busses total +/// to minimize write-lock contention and allow Solana to process mine instructions in parallel. #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Bus { @@ -16,7 +16,7 @@ pub struct Bus { pub rewards: u64, /// The rewards this bus would have paid out in the current epoch if there no limit. - /// Used to calculate the updated reward rate. + /// This is used to calculate the updated reward rate. pub theoretical_rewards: u64, /// The largest known stake balance seen by the bus this epoch. diff --git a/api/src/state/config.rs b/api/src/state/config.rs index a741788..8c50202 100644 --- a/api/src/state/config.rs +++ b/api/src/state/config.rs @@ -4,7 +4,7 @@ use crate::utils::{impl_account_from_bytes, impl_to_bytes, Discriminator}; use super::AccountDiscriminator; -/// Config is a singleton account which manages admin configurable variables. +/// Config is a singleton account which manages program global variables. #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Config { diff --git a/api/src/state/treasury.rs b/api/src/state/treasury.rs index 16a0f60..fcbb96a 100644 --- a/api/src/state/treasury.rs +++ b/api/src/state/treasury.rs @@ -4,8 +4,8 @@ use crate::utils::{impl_account_from_bytes, impl_to_bytes, Discriminator}; use super::AccountDiscriminator; -/// Treasury is a singleton account which manages all program wide variables. -/// It is the mint authority for the Ore token and also the authority of the program-owned token account. +/// Treasury is a singleton account which is the mint authority for the ORE token and the authority of +/// the program's global token account. #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Treasury {} diff --git a/program/src/claim.rs b/program/src/claim.rs index b6b809a..836d3c2 100644 --- a/program/src/claim.rs +++ b/program/src/claim.rs @@ -6,13 +6,13 @@ use solana_program::{ use crate::utils::AccountDeserialize; -/// Claim distributes ORE from the treasury to a miner. +/// Claim distributes claimable ORE from the treasury to a miner. pub fn process_claim<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { - // Parse args + // Parse args. let args = ClaimArgs::try_from_bytes(data)?; let amount = u64::from_le_bytes(args.amount); - // Load accounts + // Load accounts. let [signer, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, token_program] = accounts else { @@ -25,7 +25,7 @@ pub fn process_claim<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) load_treasury_tokens(treasury_tokens_info, true)?; load_program(token_program, spl_token::id())?; - // Update miner balance + // Update miner balance. let mut proof_data = proof_info.data.borrow_mut(); let proof = Proof::try_from_bytes_mut(&mut proof_data)?; proof.balance = proof @@ -33,7 +33,7 @@ pub fn process_claim<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) .checked_sub(amount) .ok_or(OreError::ClaimTooLarge)?; - // Distribute tokens from treasury to beneficiary + // Transfer tokens from treasury to beneficiary. transfer_signed( treasury_info, treasury_tokens_info, diff --git a/program/src/close.rs b/program/src/close.rs index b266329..7f176e1 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -8,7 +8,7 @@ use crate::utils::AccountDeserialize; /// Close closes a proof account and returns the rent to the owner. pub fn process_close<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8]) -> ProgramResult { - // Load accounts + // Load accounts. let [signer, proof_info, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -16,7 +16,7 @@ pub fn process_close<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8] load_proof(proof_info, signer.key, true)?; load_program(system_program, system_program::id())?; - // Validate balance is zero + // Validate balance is zero. let proof_data = proof_info.data.borrow(); let proof = Proof::try_from_bytes(&proof_data)?; if proof.balance.gt(&0) { @@ -24,10 +24,10 @@ pub fn process_close<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8] } drop(proof_data); - // Realloc data to zero + // Realloc data to zero. proof_info.realloc(0, true)?; - // Send lamports to signer + // Send remaining lamports to signer. **signer.lamports.borrow_mut() += proof_info.lamports(); **proof_info.lamports.borrow_mut() = 0; diff --git a/program/src/initialize.rs b/program/src/initialize.rs index a1ac336..5b3ce0b 100644 --- a/program/src/initialize.rs +++ b/program/src/initialize.rs @@ -23,10 +23,10 @@ pub fn process_initialize<'a, 'info>( accounts: &'a [AccountInfo<'info>], data: &[u8], ) -> ProgramResult { - // Parse args + // Parse args. let args = InitializeArgs::try_from_bytes(data)?; - // Load accounts + // 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] = accounts else { @@ -71,12 +71,12 @@ pub fn process_initialize<'a, 'info>( load_program(metadata_program, mpl_token_metadata::ID)?; load_sysvar(rent_sysvar, sysvar::rent::id())?; - // Check signer + // Check signer. if signer.key.ne(&INITIALIZER_ADDRESS) { return Err(ProgramError::MissingRequiredSignature); } - // Initialize bus accounts + // Initialize bus accounts. let bus_infos = [ bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, @@ -107,7 +107,7 @@ pub fn process_initialize<'a, 'info>( bus.rewards = 0; } - // Initialize config + // Initialize config. create_pda( config_info, &ore_api::id(), @@ -124,7 +124,7 @@ pub fn process_initialize<'a, 'info>( config.min_difficulty = INITIAL_MIN_DIFFICULTY as u64; config.top_balance = 0; - // Initialize treasury + // Initialize treasury. create_pda( treasury_info, &ore_api::id(), @@ -137,7 +137,7 @@ pub fn process_initialize<'a, 'info>( treasury_data[0] = Treasury::discriminator() as u8; drop(treasury_data); - // Initialize mint + // Initialize mint. create_pda( mint_info, &spl_token::id(), @@ -163,7 +163,7 @@ pub fn process_initialize<'a, 'info>( &[&[MINT, MINT_NOISE.as_slice(), &[args.mint_bump]]], )?; - // Initialize mint metadata + // Initialize mint metadata. mpl_token_metadata::instructions::CreateMetadataAccountV3Cpi { __program: metadata_program, metadata: metadata_info, @@ -189,7 +189,7 @@ pub fn process_initialize<'a, 'info>( } .invoke_signed(&[&[TREASURY, &[args.treasury_bump]]])?; - // Initialize treasury token account + // Initialize treasury token account. create_ata( signer, treasury_info, diff --git a/program/src/mine.rs b/program/src/mine.rs index 6de4314..e62cfe6 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -28,10 +28,10 @@ use crate::utils::AccountDeserialize; /// Mine validates hashes and increments a miner's collectable balance. pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { - // Parse args + // Parse args. let args = MineArgs::try_from_bytes(data)?; - // Load accounts + // Load accounts. let [signer, bus_info, config_info, proof_info, instructions_sysvar, slot_hashes_sysvar] = accounts else { @@ -44,7 +44,7 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) load_sysvar(instructions_sysvar, sysvar::instructions::id())?; load_sysvar(slot_hashes_sysvar, sysvar::slot_hashes::id())?; - // Authenticate the proof account + // Authenticate the proof account. authenticate(&instructions_sysvar.data.borrow(), proof_info.key)?; // Validate epoch is active. @@ -75,14 +75,17 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) return Err(OreError::Spam.into()); } - // Validate hash satisfies the minimnum difficulty. + // Validate the hash satisfies the minimum difficulty. let hash = solution.to_hash(); let difficulty = hash.difficulty(); if difficulty.lt(&(config.min_difficulty as u32)) { return Err(OreError::HashTooEasy.into()); } - // Normalize difficulty and calculate reward rate + // Normalize difficulty and calculate reward rate. + // + // The reward double for every bit of difficulty (leading zero) on the hash. We use the normalized + // difficulty so the minimum accepted difficulty pays out at the base reward rate. let normalized_difficulty = difficulty .checked_sub(config.min_difficulty as u32) .unwrap(); @@ -92,9 +95,10 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) .unwrap(); // Apply staking multiplier. + // // If user has greater than or equal to the max stake on the network, they receive 2x multiplier. // Any stake less than this will receives between 1x and 2x multipler. The multipler is only active - // if the miner's last stake deposit was more than one minute ago. + // if the miner's last stake deposit was more than one minute ago to protect against flash loan attacks. let mut bus_data = bus_info.data.borrow_mut(); let bus = Bus::try_from_bytes_mut(&mut bus_data)?; if proof.balance.gt(&0) && proof.last_stake_at.saturating_add(ONE_MINUTE).lt(&t) { @@ -115,6 +119,13 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) } // Apply liveness penalty. + // + // The liveness penalty exists to ensure there is no "invisible" hashpower on the network. It + // should not be possible to spend ~1 hour on a given challenge and submit a hash with a large + // difficulty value to earn an outsized reward. + // + // This penalty works by halving the reward amount for every minute late the solution has been submitted. + // This ultimately drives the reward to zero given enough time (10-20 minutes). let t_liveness = t_target.saturating_add(TOLERANCE); if t.gt(&t_liveness) { reward = reward.saturating_sub( @@ -126,15 +137,19 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) ); } - // Limit payout amount to whatever is left in the bus + // Limit payout amount to whatever is left in the bus. + // + // Busses are limited 1 ORE per epoch. This is the maximum amount a miner can earn for any one hash. + // We still track the theoretical rewards that would have been paid out ignoring the bus limit, so the + // base reward rate will be updated to account for the real hashpower on the network. let reward_actual = reward.min(bus.rewards); - // Update balances + // Update balances. bus.theoretical_rewards = bus.theoretical_rewards.checked_add(reward).unwrap(); bus.rewards = bus.rewards.checked_sub(reward_actual).unwrap(); proof.balance = proof.balance.checked_add(reward_actual).unwrap(); - // Hash recent slot hash into the next challenge to prevent pre-mining attacks + // Hash recent slot hash into the next challenge to prevent pre-mining attacks. proof.last_hash = hash.h; proof.challenge = hashv(&[ hash.h.as_slice(), @@ -142,14 +157,14 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) ]) .0; - // Update time trackers + // Update time trackers. proof.last_hash_at = t.max(t_target); - // Update lifetime stats + // Update lifetime stats. proof.total_hashes = proof.total_hashes.saturating_add(1); proof.total_rewards = proof.total_rewards.saturating_add(reward); - // Log the mined rewards + // Log the mined rewards. set_return_data( MineEvent { difficulty: difficulty as u64, @@ -166,12 +181,12 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) /// /// This process is necessary to prevent sybil attacks. If a user can pack multiple hashes into a single /// transaction, then there is a financial incentive to mine across multiple keypairs and submit as many hashes -/// as possible in each transaction to minimize fee / hash. +/// as possible in the same transaction to minimize fee / hash. /// -/// We prevent this by forcing every transaction to declare the proof account being mined with upfont. -/// The authentication process includes passing the 32 byte pubkey address as instruction data to the -/// CU-optimized noop program. We parse this address through transaction introspection and use it to -/// ensure only one proof account can be used for `mine` instructions for a given transaction. +/// This is prevented by forcing every transaction to declare upfront the proof account that will be used for mining. +/// The authentication process includes passing the 32 byte pubkey address as instruction data to a CU-optimized noop +/// program. We parse this address through transaction introspection and use it to ensure the same proof account is +/// used for every `mine` instruction in a given transaction. fn authenticate(data: &[u8], proof_address: &Pubkey) -> ProgramResult { if let Ok(Some(auth_address)) = parse_auth_address(data) { if proof_address.ne(&auth_address) { diff --git a/program/src/open.rs b/program/src/open.rs index 81c88d2..f7390c0 100644 --- a/program/src/open.rs +++ b/program/src/open.rs @@ -15,15 +15,11 @@ use solana_program::{ use crate::utils::{create_pda, AccountDeserialize, Discriminator}; /// Open creates a new proof account to track a miner's state. -/// -/// Safety requirements: -/// - Register is a permissionless instruction and can be invoked by any singer. -/// - Can only succeed if the user does not already have a proof account. pub fn process_open<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { - // Parse args + // Parse args. let args = OpenArgs::try_from_bytes(data)?; - // Load accounts + // Load accounts. let [signer, miner_info, payer_info, proof_info, system_program, slot_hashes_info] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -40,7 +36,7 @@ pub fn process_open<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) load_program(system_program, system_program::id())?; load_sysvar(slot_hashes_info, sysvar::slot_hashes::id())?; - // Initialize proof + // Initialize proof. create_pda( proof_info, &ore_api::id(), diff --git a/program/src/reset.rs b/program/src/reset.rs index aa15315..98a2947 100644 --- a/program/src/reset.rs +++ b/program/src/reset.rs @@ -12,9 +12,9 @@ use spl_token::state::Mint; use crate::utils::AccountDeserialize; -/// Reset tops up the busses, updates the base reward rate, and generally sets up the ORE program for the next epoch. +/// Reset tops up the busses, updates the base reward rate, and sets up the ORE program for the next epoch. pub fn process_reset<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8]) -> ProgramResult { - // Load accounts + // 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] = accounts else { @@ -39,7 +39,7 @@ pub fn process_reset<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8] bus_7_info, ]; - // Validate enough time has passed since last reset + // Validate enough time has passed since last reset. let mut config_data = config_info.data.borrow_mut(); let config = Config::try_from_bytes_mut(&mut config_data)?; let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; @@ -51,10 +51,10 @@ pub fn process_reset<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8] return Ok(()); } - // Update reset timestamp + // Update reset timestamp. config.last_reset_at = clock.unix_timestamp; - // Reset bus accounts and calculate actual rewards mined since last reset + // Reset bus accounts and calculate actual rewards mined since last reset. let mut total_remaining_rewards = 0u64; let mut total_theoretical_rewards = 0u64; let mut top_balance = 0u64; @@ -73,32 +73,32 @@ pub fn process_reset<'a, 'info>(accounts: &'a [AccountInfo<'info>], _data: &[u8] } let total_epoch_rewards = MAX_EPOCH_REWARDS.saturating_sub(total_remaining_rewards); - // Update the top balance + // Update the top balance. config.top_balance = top_balance; - // Update base reward rate for next epoch + // Update base reward rate for next epoch. config.base_reward_rate = calculate_new_reward_rate(config.base_reward_rate, total_theoretical_rewards); - // If base_reward_rate is too low, then increment difficulty by 1 and double base reward rate + // If base_reward_rate is too low, then increment difficulty by 1 and double base reward rate. if config.base_reward_rate.le(&BASE_REWARD_RATE_MIN_THRESHOLD) { config.min_difficulty = config.min_difficulty.checked_add(1).unwrap(); config.base_reward_rate = config.base_reward_rate.checked_mul(2).unwrap(); } - // If base reward rate is too high, then decrement difficulty by 1 and halve base reward rate + // If base reward rate is too high, then decrement difficulty by 1 and halve base reward rate. if config.base_reward_rate.ge(&BASE_REWARD_RATE_MAX_THRESHOLD) && config.min_difficulty.gt(&1) { config.min_difficulty = config.min_difficulty.checked_sub(1).unwrap(); config.base_reward_rate = config.base_reward_rate.checked_div(2).unwrap(); } - // Max supply check + // Max supply check. let mint = Mint::unpack(&mint_info.data.borrow()).expect("Failed to parse mint"); if mint.supply.ge(&MAX_SUPPLY) { return Err(OreError::MaxSupply.into()); } - // Fund treasury token account + // Fund treasury token account. let amount = MAX_SUPPLY .saturating_sub(mint.supply) .min(total_epoch_rewards); diff --git a/program/src/stake.rs b/program/src/stake.rs index b94edf5..4b9ecd1 100644 --- a/program/src/stake.rs +++ b/program/src/stake.rs @@ -9,11 +9,11 @@ use crate::utils::AccountDeserialize; /// Stake deposits ORE into a proof account to earn multiplier. pub fn process_stake<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult { - // Parse args + // Parse args. let args = StakeArgs::try_from_bytes(data)?; let amount = u64::from_le_bytes(args.amount); - // Load accounts + // Load accounts. let [signer, proof_info, sender_info, treasury_tokens_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -23,16 +23,16 @@ pub fn process_stake<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) load_treasury_tokens(treasury_tokens_info, true)?; load_program(token_program, spl_token::id())?; - // Update proof balance + // Update the proof balance. let mut proof_data = proof_info.data.borrow_mut(); let proof = Proof::try_from_bytes_mut(&mut proof_data)?; proof.balance = proof.balance.checked_add(amount).unwrap(); - // Update deposit timestamp + // Update deposit timestamp. let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; proof.last_stake_at = clock.unix_timestamp; - // Distribute tokens from signer to treasury + // Transfer tokens from signer to treasury. transfer( signer, sender_info, diff --git a/program/src/update.rs b/program/src/update.rs index 2b69e47..242c9b0 100644 --- a/program/src/update.rs +++ b/program/src/update.rs @@ -10,7 +10,7 @@ pub fn process_update<'a, 'info>( accounts: &'a [AccountInfo<'info>], _data: &[u8], ) -> ProgramResult { - // Load accounts + // Load accounts. let [signer, miner_info, proof_info] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -18,7 +18,7 @@ pub fn process_update<'a, 'info>( load_system_account(miner_info, false)?; load_proof(proof_info, signer.key, true)?; - // Update the proof + // Update the proof's miner authority. let mut proof_data = proof_info.data.borrow_mut(); let proof = Proof::try_from_bytes_mut(&mut proof_data)?; proof.miner = *miner_info.key; diff --git a/program/src/upgrade.rs b/program/src/upgrade.rs index 48df436..dbd2c3b 100644 --- a/program/src/upgrade.rs +++ b/program/src/upgrade.rs @@ -56,9 +56,9 @@ pub fn process_upgrade<'a, 'info>( if mint.supply.saturating_add(amount_to_mint).gt(&MAX_SUPPLY) { return Err(OreError::MaxSupply.into()); } - drop(mint_data); // Mint to the beneficiary account + drop(mint_data); mint_to_signed( mint_info, beneficiary_info, diff --git a/utils/src/lib.rs b/utils/src/lib.rs index a7088cb..f8bc545 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -6,7 +6,7 @@ use solana_program::{ pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; -/// Creates a new pda +/// Creates a new pda. #[inline(always)] pub fn create_pda<'a, 'info>( target_account: &'a AccountInfo<'info>, @@ -75,7 +75,7 @@ pub fn create_pda<'a, 'info>( } pub trait Discriminator { - fn discriminator() -> u8; //AccountDiscriminator; + fn discriminator() -> u8; } pub trait AccountDeserialize {