update comments

This commit is contained in:
Hardhat Chad
2024-07-30 18:39:34 +00:00
parent ff56065044
commit 39e52de91e
14 changed files with 91 additions and 78 deletions

View File

@@ -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,

View File

@@ -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;

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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(),

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;

View File

@@ -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,