mirror of
https://github.com/d0zingcat/ore.git
synced 2026-05-16 15:10:20 +00:00
comments
This commit is contained in:
@@ -6,14 +6,7 @@ use solana_program::{
|
||||
|
||||
use crate::utils::AccountDeserialize;
|
||||
|
||||
/// Claim distributes Ore from the treasury to a miner. Its responsibilies include:
|
||||
/// 1. Decrement the miner's claimable balance.
|
||||
/// 2. Transfer tokens from the treasury to the miner.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Claim is a permissionless instruction and can be called by any user.
|
||||
/// - Can only succeed if the claimed amount is less than or equal to the miner's claimable rewards.
|
||||
/// - The provided beneficiary, token account, treasury, treasury token account, and token program must be valid.
|
||||
/// Claim distributes ORE from the treasury to a miner.
|
||||
pub fn process_claim<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult {
|
||||
// Parse args
|
||||
let args = ClaimArgs::try_from_bytes(data)?;
|
||||
|
||||
@@ -6,14 +6,7 @@ use solana_program::{
|
||||
|
||||
use crate::utils::AccountDeserialize;
|
||||
|
||||
/// Close closes a proof account and returns the rent to the owner. Its responsibilities include:
|
||||
/// 1. Realloc proof account size to 0.
|
||||
/// 2. Transfer lamports to the owner.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Deregister is a permissionless instruction and can be invoked by any singer.
|
||||
/// - Can only succeed if the provided proof acount PDA is valid (associated with the signer).
|
||||
/// - The provided system program must be valid.
|
||||
/// 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
|
||||
let [signer, proof_info, system_program] = accounts else {
|
||||
|
||||
@@ -18,24 +18,7 @@ use spl_token::state::Mint;
|
||||
|
||||
use crate::utils::{create_pda, AccountDeserialize, Discriminator};
|
||||
|
||||
/// Initialize sets up the Ore program. Its responsibilities include:
|
||||
/// 1. Initialize the 8 bus accounts.
|
||||
/// 2. Initialize the treasury account.
|
||||
/// 3. Initialize the Ore mint account.
|
||||
/// 4. Initialize the mint metadata account.
|
||||
/// 5. Initialize the treasury token account.
|
||||
/// 6. Set the signer as the program admin.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Initialize is a permissionless instruction and can be called by anyone.
|
||||
/// - Can only succeed once for the entire lifetime of the program.
|
||||
/// - Can only succeed if all provided PDAs match their expected values.
|
||||
/// - Can only succeed if provided system program, token program,
|
||||
/// associated token program, metadata program, and rent sysvar are valid.
|
||||
///
|
||||
/// Discussion
|
||||
/// - The signer of this instruction is set as the program admin and the
|
||||
/// upgrade authority of the mint metadata account.
|
||||
/// Initialize sets up the ORE program to begin mining.
|
||||
pub fn process_initialize<'a, 'info>(
|
||||
accounts: &'a [AccountInfo<'info>],
|
||||
data: &[u8],
|
||||
|
||||
@@ -16,7 +16,6 @@ use solana_program::{
|
||||
blake3::hashv,
|
||||
clock::Clock,
|
||||
entrypoint::ProgramResult,
|
||||
log::sol_log,
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
sanitize::SanitizeError,
|
||||
@@ -27,20 +26,7 @@ use solana_program::{
|
||||
|
||||
use crate::utils::AccountDeserialize;
|
||||
|
||||
/// Mine is the primary workhorse instruction of the Ore program. Its responsibilities include:
|
||||
/// 1. Calculate the hash from the provided nonce.
|
||||
/// 2. Payout rewards based on difficulty, staking multiplier, and liveness penalty.
|
||||
/// 3. Generate a new challenge for the miner.
|
||||
/// 4. Update the miner's lifetime stats.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Mine is a permissionless instruction and can be called by any signer.
|
||||
/// - Can only succeed if mining is not paused.
|
||||
/// - Can only succeed if the last reset was less than 60 seconds ago.
|
||||
/// - Can only succeed if the provided hash satisfies the minimum difficulty requirement.
|
||||
/// - Can only succeed if the miners proof pubkey matches the declared proof pubkey.
|
||||
/// - The provided proof account must be associated with the signer.
|
||||
/// - The provided bus, config, noise, stake, and slot hash sysvar must be valid.
|
||||
/// 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
|
||||
let args = MineArgs::try_from_bytes(data)?;
|
||||
@@ -59,13 +45,7 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8])
|
||||
load_sysvar(slot_hashes_sysvar, sysvar::slot_hashes::id())?;
|
||||
|
||||
// Authenticate the proof account
|
||||
if let Ok(Some(auth_address)) = authenticate(&instructions_sysvar.data.borrow()) {
|
||||
if auth_address.ne(proof_info.key) {
|
||||
return Err(OreError::AuthFailed.into());
|
||||
}
|
||||
} else {
|
||||
return Err(OreError::AuthFailed.into());
|
||||
}
|
||||
authenticate(&instructions_sysvar.data.borrow(), proof_info.key)?;
|
||||
|
||||
// Validate epoch is active.
|
||||
let config_data = config_info.data.borrow();
|
||||
@@ -110,7 +90,6 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8])
|
||||
.base_reward_rate
|
||||
.checked_mul(2u64.checked_pow(normalized_difficulty).unwrap())
|
||||
.unwrap();
|
||||
sol_log(&format!("Diff {}", difficulty));
|
||||
|
||||
// Apply staking multiplier.
|
||||
// If user has greater than or equal to the max stake on the network, they receive 2x multiplier.
|
||||
@@ -183,24 +162,29 @@ pub fn process_mine<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8])
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the authenticated pubkey.
|
||||
/// Authenticate the proof account.
|
||||
///
|
||||
/// The intent here is to disincentivize sybil. If a user can fit multiple hashes into a single
|
||||
/// transaction, there is a financial incentive to sybil multiple keypairs and pack as many hashes
|
||||
/// as possible into each transaction to minimize fee / hash.
|
||||
/// 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.
|
||||
///
|
||||
/// If each transaction is limited to one hash only, then a user will minimize their fee / hash
|
||||
/// by allocating all their hashpower to finding the single most difficult hash they can.
|
||||
///
|
||||
/// We solve this by "authenticating" the proof account on every mine instruction. That is,
|
||||
/// every transaction with a `mine` instruction needs to include an `auth` instruction that
|
||||
/// specifies the proof account that will be used. The `auth` instruction must be first ORE
|
||||
/// instruction in the transaction. The `mine` instruction should error out if the provided proof
|
||||
/// account doesn't match the authenticated address.
|
||||
///
|
||||
/// Errors if:
|
||||
/// - Fails to find and parse an authentication address.
|
||||
fn authenticate(data: &[u8]) -> Result<Option<Pubkey>, SanitizeError> {
|
||||
/// 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.
|
||||
fn authenticate(data: &[u8], proof_address: &Pubkey) -> ProgramResult {
|
||||
if let Ok(Some(auth_address)) = parse_auth_address(data) {
|
||||
if proof_address.ne(&auth_address) {
|
||||
return Err(OreError::AuthFailed.into());
|
||||
}
|
||||
} else {
|
||||
return Err(OreError::AuthFailed.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Use transaction introspection to parse the authenticated pubkey.
|
||||
fn parse_auth_address(data: &[u8]) -> Result<Option<Pubkey>, SanitizeError> {
|
||||
// Start the current byte index at 0
|
||||
let mut curr = 0;
|
||||
let num_instructions = read_u16(&mut curr, data)?;
|
||||
|
||||
@@ -14,15 +14,11 @@ use solana_program::{
|
||||
|
||||
use crate::utils::{create_pda, AccountDeserialize, Discriminator};
|
||||
|
||||
/// Register generates a new hash chain for a prospective miner. Its responsibilities include:
|
||||
/// 1. Initialize a new proof account.
|
||||
/// 2. Generate an initial hash from the signer's key.
|
||||
/// 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 provided proof acount PDA is valid (associated with the signer).
|
||||
/// - Can only succeed if the user does not already have a proof account.
|
||||
/// - The provided system program must be valid.
|
||||
pub fn process_open<'a, 'info>(accounts: &'a [AccountInfo<'info>], data: &[u8]) -> ProgramResult {
|
||||
// Parse args
|
||||
let args = OpenArgs::try_from_bytes(data)?;
|
||||
|
||||
@@ -12,25 +12,7 @@ use spl_token::state::Mint;
|
||||
|
||||
use crate::utils::AccountDeserialize;
|
||||
|
||||
/// Reset sets up the Ore program for the next epoch. Its responsibilities include:
|
||||
/// 1. Reset bus account rewards counters.
|
||||
/// 2. Adjust the reward rate to stabilize inflation.
|
||||
/// 3. Top up the treasury token account to fund claims.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Reset is a permissionless instruction and can be invoked by any signer.
|
||||
/// - Can only succeed if START_AT has passed.
|
||||
/// - Can only succeed if more tha 60 seconds or more have passed since the last successful reset.
|
||||
/// - The busses, mint, treasury, treasury token account, and token program must all be valid.
|
||||
///
|
||||
/// Discussion:
|
||||
/// - It is important that `reset` can only be invoked once per 60 second period to ensure the supply growth rate
|
||||
/// stays within the guaranteed bounds of 0 ≤ R ≤ 2 ORE/min.
|
||||
/// - The reward rate is dynamically adjusted based on last epoch's theoretical reward rate to target an average
|
||||
/// supply growth rate of 1 ORE/min.
|
||||
/// - The "theoretical" reward rate refers to the amount that would have been paid out if rewards were not capped by
|
||||
/// the bus limits. It's necessary to use this value to ensure the reward rate update calculation accurately
|
||||
/// accounts for the difficulty of submitted hashes.
|
||||
/// Reset tops up the busses, updates the base reward rate, and generally sets up the ORE program for the next epoch.
|
||||
pub fn process_reset<'a, 'info>(accounts: &'a [AccountInfo<'info>], _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] =
|
||||
|
||||
@@ -7,14 +7,7 @@ use solana_program::{
|
||||
|
||||
use crate::utils::AccountDeserialize;
|
||||
|
||||
/// Stake deposits Ore into a miner's proof account to earn multiplier. Its responsibilies include:
|
||||
/// 1. Transfer tokens from the miner to the treasury account.
|
||||
/// 2. Increment the miner's claimable balance.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Stake is a permissionless instruction and can be called by any user.
|
||||
/// - Can only succeed if the amount is less than or equal to the miner's transferable tokens.
|
||||
/// - The provided beneficiary, proof, sender, treasury token account, and token program must be valid.
|
||||
/// 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
|
||||
let args = StakeArgs::try_from_bytes(data)?;
|
||||
|
||||
@@ -6,13 +6,7 @@ use solana_program::{
|
||||
};
|
||||
use spl_token::state::Mint;
|
||||
|
||||
/// Upgrade allows a user to migrate a v1 token to a v2 token one-for-one. Its responsibilies include:
|
||||
/// 1. Burns the v1 tokens.
|
||||
/// 2. Mints an equivalent number of v2 tokens to the user.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Upgrade is a permissionless instruction and can be called by any user.
|
||||
/// - The provided beneficiary, mint, mint v1, sender, and token program must be valid.
|
||||
/// Upgrade allows a user to migrate a v1 token to a v2 token at a 1:1 exchange rate.
|
||||
pub fn process_upgrade<'a, 'info>(
|
||||
accounts: &'a [AccountInfo<'info>],
|
||||
data: &[u8],
|
||||
|
||||
Reference in New Issue
Block a user