mirror of
https://github.com/d0zingcat/ore.git
synced 2026-05-14 15:10:13 +00:00
Merge pull request #68 from regolith-labs/hardhat/min-difficulty
Make min difficulty algorithmic
This commit is contained in:
@@ -19,8 +19,8 @@ const-crypto = "0.1.0"
|
||||
drillx = { version = "2.0.0-beta.1", features = ["solana"] }
|
||||
mpl-token-metadata = "4.1.2"
|
||||
num_enum = "0.7.2"
|
||||
ore-api = { path = "api", version = "2.0.0-beta.2" }
|
||||
ore-utils = { path = "utils", version = "2.0.0-beta.2", features = ["spl"] }
|
||||
ore-api = { path = "api", version = "2.0.0-beta.3" }
|
||||
ore-utils = { path = "utils", version = "2.0.0-beta.3", features = ["spl"] }
|
||||
shank = "0.3.0"
|
||||
solana-program = "^1.18"
|
||||
spl-token = { version = "^4", features = ["no-entrypoint"] }
|
||||
|
||||
@@ -2,17 +2,23 @@ use array_const_fn_init::array_const_fn_init;
|
||||
use const_crypto::ed25519;
|
||||
use solana_program::{pubkey, pubkey::Pubkey};
|
||||
|
||||
/// The reward rate to intialize the program with.
|
||||
pub const INITIAL_BASE_REWARD_RATE: u64 = 10u64.pow(3u32);
|
||||
/// The authority allowed to initialize the program.
|
||||
pub const INITIALIZER_ADDRESS: Pubkey = pubkey!("HBUh9g46wk2X89CvaNN15UmsznP59rh6od1h8JwYAopk");
|
||||
|
||||
/// The admin allowed to initialize the program.
|
||||
pub const INITIAL_ADMIN: Pubkey = pubkey!("HBUh9g46wk2X89CvaNN15UmsznP59rh6od1h8JwYAopk");
|
||||
/// The base reward rate to intialize the program with.
|
||||
pub const INITIAL_BASE_REWARD_RATE: u64 = 2u64.pow(6);
|
||||
|
||||
/// The minimum allowed base reward rate, at which point the min difficulty should be increased
|
||||
pub const BASE_REWARD_RATE_MIN_THRESHOLD: u64 = 2u64.pow(5);
|
||||
|
||||
/// The maximum allowed base reward rate, at which point the min difficulty should be decreased.
|
||||
pub const BASE_REWARD_RATE_MAX_THRESHOLD: u64 = 2u64.pow(8);
|
||||
|
||||
/// The spam/liveness tolerance in seconds.
|
||||
pub const TOLERANCE: i64 = 5;
|
||||
|
||||
/// The minimum difficulty required of all submitted hashes.
|
||||
pub const MIN_DIFFICULTY: u32 = 8;
|
||||
/// The minimum difficulty to initialize the program with.
|
||||
pub const INITIAL_MIN_DIFFICULTY: u32 = 8;
|
||||
|
||||
/// The decimal precision of the ORE token.
|
||||
/// There are 100 billion indivisible units per ORE (called "grains").
|
||||
|
||||
@@ -10,20 +10,20 @@ use super::AccountDiscriminator;
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
|
||||
pub struct Config {
|
||||
// TODO Remove this
|
||||
pub admin: Pubkey,
|
||||
|
||||
/// The base reward rate paid out for a hash of minimum difficulty.
|
||||
pub base_reward_rate: u64,
|
||||
|
||||
/// The timestamp of the last reset.
|
||||
pub last_reset_at: i64,
|
||||
|
||||
/// The largest known stake balance on the network.
|
||||
pub max_stake: u64,
|
||||
/// The minimum accepted difficulty.
|
||||
pub min_difficulty: u64,
|
||||
|
||||
/// The address of the proof account with the highest stake balance.
|
||||
pub top_staker: Pubkey,
|
||||
|
||||
/// The largest known stake balance on the network.
|
||||
pub top_staker_balance: u64,
|
||||
}
|
||||
|
||||
impl Discriminator for Config {
|
||||
|
||||
@@ -56,8 +56,8 @@ pub fn process_crown<'a, 'info>(
|
||||
}
|
||||
|
||||
// Crown the new top staker.
|
||||
config.max_stake = proof_new.balance;
|
||||
config.top_staker = *proof_new_info.key;
|
||||
config.top_staker_balance = proof_new.balance;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ pub fn process_initialize<'a, 'info>(
|
||||
load_sysvar(rent_sysvar, sysvar::rent::id())?;
|
||||
|
||||
// Check signer
|
||||
if signer.key.ne(&INITIAL_ADMIN) {
|
||||
if signer.key.ne(&INITIALIZER_ADDRESS) {
|
||||
return Err(ProgramError::MissingRequiredSignature);
|
||||
}
|
||||
|
||||
@@ -140,8 +140,9 @@ pub fn process_initialize<'a, 'info>(
|
||||
let config = Config::try_from_bytes_mut(&mut config_data)?;
|
||||
config.base_reward_rate = INITIAL_BASE_REWARD_RATE;
|
||||
config.last_reset_at = 0;
|
||||
config.max_stake = 0;
|
||||
config.min_difficulty = INITIAL_MIN_DIFFICULTY as u64;
|
||||
config.top_staker = Pubkey::new_from_array([0; 32]);
|
||||
config.top_staker_balance = 0;
|
||||
|
||||
// Initialize treasury
|
||||
create_pda(
|
||||
|
||||
@@ -97,27 +97,32 @@ pub fn process_mine<'a, 'info>(
|
||||
// Validate hash satisfies the minimnum difficulty.
|
||||
let hash = solution.to_hash();
|
||||
let difficulty = hash.difficulty();
|
||||
sol_log(&format!("Diff {}", difficulty));
|
||||
if difficulty.lt(&MIN_DIFFICULTY) {
|
||||
if difficulty.lt(&(config.min_difficulty as u32)) {
|
||||
return Err(OreError::HashTooEasy.into());
|
||||
}
|
||||
|
||||
// Normalize difficulty and calculate reward rate
|
||||
let normalized_difficulty = difficulty
|
||||
.checked_sub(config.min_difficulty as u32)
|
||||
.unwrap();
|
||||
let mut reward = config
|
||||
.base_reward_rate
|
||||
.checked_mul(2u64.checked_pow(difficulty).unwrap())
|
||||
.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.
|
||||
// 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 config.max_stake.gt(&0)
|
||||
if config.top_staker_balance.gt(&0)
|
||||
&& proof.balance.gt(&0)
|
||||
&& proof.last_stake_at.saturating_add(ONE_MINUTE).le(&t)
|
||||
{
|
||||
let staking_reward = (reward as u128)
|
||||
.checked_mul(proof.balance.min(config.max_stake) as u128)
|
||||
.checked_mul(proof.balance.min(config.top_staker_balance) as u128)
|
||||
.unwrap()
|
||||
.checked_div(config.max_stake as u128)
|
||||
.checked_div(config.top_staker_balance as u128)
|
||||
.unwrap() as u64;
|
||||
reward = reward.checked_add(staking_reward).unwrap();
|
||||
}
|
||||
|
||||
@@ -94,6 +94,18 @@ pub fn process_reset<'a, 'info>(
|
||||
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 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 config.base_reward_rate.ge(&BASE_REWARD_RATE_MAX_THRESHOLD) && config.min_difficulty.gt(&0) {
|
||||
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
|
||||
let mint = Mint::unpack(&mint_info.data.borrow()).expect("Failed to parse mint");
|
||||
if mint.supply.ge(&MAX_SUPPLY) {
|
||||
@@ -160,7 +172,8 @@ mod tests {
|
||||
|
||||
use crate::calculate_new_reward_rate;
|
||||
use ore_api::consts::{
|
||||
BUS_EPOCH_REWARDS, MAX_EPOCH_REWARDS, SMOOTHING_FACTOR, TARGET_EPOCH_REWARDS,
|
||||
BASE_REWARD_RATE_MIN_THRESHOLD, BUS_EPOCH_REWARDS, MAX_EPOCH_REWARDS, SMOOTHING_FACTOR,
|
||||
TARGET_EPOCH_REWARDS,
|
||||
};
|
||||
|
||||
const FUZZ_SIZE: u64 = 10_000;
|
||||
@@ -189,6 +202,13 @@ mod tests {
|
||||
assert!(new_rate.lt(¤t_rate));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_new_reward_rate_lower_edge() {
|
||||
let current_rate = BASE_REWARD_RATE_MIN_THRESHOLD;
|
||||
let new_rate = calculate_new_reward_rate(current_rate, TARGET_EPOCH_REWARDS + 1);
|
||||
assert!(new_rate.lt(¤t_rate));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_new_reward_rate_lower_fuzz() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
Reference in New Issue
Block a user