This commit is contained in:
Hardhat Chad
2024-06-29 00:59:55 +00:00
parent edc1439712
commit 462a7021e1
29 changed files with 245 additions and 194 deletions

133
core/api/src/consts.rs Normal file
View File

@@ -0,0 +1,133 @@
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 admin allowed to initialize the program.
pub const INITIAL_ADMIN: Pubkey = pubkey!("HBUh9g46wk2X89CvaNN15UmsznP59rh6od1h8JwYAopk");
/// 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 decimal precision of the ORE token.
/// There are 100 billion indivisible units per ORE (called "grains").
pub const TOKEN_DECIMALS: u8 = 11;
/// The decimal precision of the ORE v1 token.
pub const TOKEN_DECIMALS_V1: u8 = 9;
/// One ORE token, denominated in indivisible units.
pub const ONE_ORE: u64 = 10u64.pow(TOKEN_DECIMALS as u32);
/// The duration of one minute, in seconds.
pub const ONE_MINUTE: i64 = 60;
/// The number of minutes in a program epoch.
pub const EPOCH_MINUTES: i64 = 1;
/// The duration of a program epoch, in seconds.
pub const EPOCH_DURATION: i64 = ONE_MINUTE.saturating_mul(EPOCH_MINUTES);
/// The maximum token supply (21 million).
pub const MAX_SUPPLY: u64 = ONE_ORE.saturating_mul(21_000_000);
/// The target quantity of ORE to be mined per epoch.
pub const TARGET_EPOCH_REWARDS: u64 = ONE_ORE.saturating_mul(EPOCH_MINUTES as u64);
/// The maximum quantity of ORE that can be mined per epoch.
/// Inflation rate ≈ 1 ORE / min (min 0, max 8)
pub const MAX_EPOCH_REWARDS: u64 = TARGET_EPOCH_REWARDS.saturating_mul(BUS_COUNT as u64);
/// The quantity of ORE each bus is allowed to issue per epoch.
pub const BUS_EPOCH_REWARDS: u64 = MAX_EPOCH_REWARDS.saturating_div(BUS_COUNT as u64);
/// The number of bus accounts, for parallelizing mine operations.
pub const BUS_COUNT: usize = 8;
/// The smoothing factor for reward rate changes. The reward rate cannot change by more or less
/// than a factor of this constant from one epoch to the next.
pub const SMOOTHING_FACTOR: u64 = 2;
// Assert MAX_EPOCH_REWARDS is evenly divisible by BUS_COUNT.
static_assertions::const_assert!(
(MAX_EPOCH_REWARDS / BUS_COUNT as u64) * BUS_COUNT as u64 == MAX_EPOCH_REWARDS
);
/// The seed of the bus account PDA.
pub const BUS: &[u8] = b"bus";
/// The seed of the config account PDA.
pub const CONFIG: &[u8] = b"config";
/// The seed of the metadata account PDA.
pub const METADATA: &[u8] = b"metadata";
/// The seed of the mint account PDA.
pub const MINT: &[u8] = b"mint";
/// The seed of proof account PDAs.
pub const PROOF: &[u8] = b"proof";
/// The seed of the treasury account PDA.
pub const TREASURY: &[u8] = b"treasury";
/// Noise for deriving the mint pda
pub const MINT_NOISE: [u8; 16] = [
166, 199, 85, 221, 225, 119, 21, 185, 160, 82, 242, 237, 194, 84, 250, 252,
];
/// The name for token metadata.
pub const METADATA_NAME: &str = "ORE";
/// The ticker symbol for token metadata.
pub const METADATA_SYMBOL: &str = "ORE";
/// The uri for token metdata.
pub const METADATA_URI: &str = "https://ore.supply/metadata.json";
/// Program id for const pda derivations
const PROGRAM_ID: [u8; 32] = unsafe { *(&crate::id() as *const Pubkey as *const [u8; 32]) };
/// The addresses of the bus accounts.
pub const BUS_ADDRESSES: [Pubkey; BUS_COUNT] = array_const_fn_init![const_bus_address; 8];
/// Function to derive const bus addresses.
const fn const_bus_address(i: usize) -> Pubkey {
Pubkey::new_from_array(ed25519::derive_program_address(&[BUS, &[i as u8]], &PROGRAM_ID).0)
}
/// The address of the config account.
pub const CONFIG_ADDRESS: Pubkey =
Pubkey::new_from_array(ed25519::derive_program_address(&[CONFIG], &PROGRAM_ID).0);
/// The address of the mint metadata account.
pub const METADATA_ADDRESS: Pubkey = Pubkey::new_from_array(
ed25519::derive_program_address(
&[
METADATA,
unsafe { &*(&mpl_token_metadata::ID as *const Pubkey as *const [u8; 32]) },
unsafe { &*(&MINT_ADDRESS as *const Pubkey as *const [u8; 32]) },
],
unsafe { &*(&mpl_token_metadata::ID as *const Pubkey as *const [u8; 32]) },
)
.0,
);
/// The address of the mint account.
pub const MINT_ADDRESS: Pubkey =
Pubkey::new_from_array(ed25519::derive_program_address(&[MINT, &MINT_NOISE], &PROGRAM_ID).0);
/// The address of the v1 mint account.
pub const MINT_V1_ADDRESS: Pubkey = pubkey!("oreoN2tQbHXVaZsr3pf66A48miqcBXCDJozganhEJgz");
/// The address of the treasury account.
pub const TREASURY_ADDRESS: Pubkey =
Pubkey::new_from_array(ed25519::derive_program_address(&[TREASURY], &PROGRAM_ID).0);
/// The bump of the treasury account, for cpis.
pub const TREASURY_BUMP: u8 = ed25519::derive_program_address(&[TREASURY], &PROGRAM_ID).1;

32
core/api/src/error.rs Normal file
View File

@@ -0,0 +1,32 @@
use num_enum::IntoPrimitive;
use solana_program::program_error::ProgramError;
use thiserror::Error;
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)]
#[repr(u32)]
pub enum OreError {
#[error("The epoch has ended and needs reset")]
NeedsReset = 0,
#[error("The provided hash is invalid")]
HashInvalid = 1,
#[error("The provided hash did not satisfy the minimum required difficulty")]
HashTooEasy = 2,
#[error("The claim amount cannot be greater than the claimable rewards")]
ClaimTooLarge = 3,
#[error("The clock time is invalid")]
ClockInvalid = 4,
#[error("You are trying to submit too soon")]
Spam = 5,
#[error("Only one hash may be validated per transaction")]
TransactionInvalid = 6,
#[error("The tolerance cannot exceed i64 max value")]
ToleranceOverflow = 7,
#[error("The maximum supply has been reached")]
MaxSupply = 8,
}
impl From<OreError> for ProgramError {
fn from(e: OreError) -> Self {
ProgramError::Custom(e as u32)
}
}

439
core/api/src/instruction.rs Normal file
View File

@@ -0,0 +1,439 @@
use bytemuck::{Pod, Zeroable};
use drillx::Solution;
use num_enum::TryFromPrimitive;
use shank::ShankInstruction;
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
system_program, sysvar,
};
use crate::{consts::*, impl_instruction_from_bytes, impl_to_bytes};
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, ShankInstruction, TryFromPrimitive)]
#[rustfmt::skip]
pub enum OreInstruction {
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "beneficiary", desc = "Beneficiary token account", writable)]
#[account(3, name = "proof", desc = "Ore proof account", writable)]
#[account(4, name = "treasury", desc = "Ore treasury account", writable)]
#[account(5, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
#[account(6, name = "token_program", desc = "SPL token program")]
Claim = 0,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "proof", desc = "Ore proof account", writable)]
#[account(3, name = "system_program", desc = "Solana system program")]
Close = 1,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "config", desc = "Ore config account", writable)]
#[account(3, name = "proof", desc = "Ore proof account current top staker")]
#[account(4, name = "proof_new", desc = "Ore proof account new top staker")]
Crown = 2,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "bus", desc = "Ore bus account", writable)]
#[account(3, name = "config", desc = "Ore config account")]
#[account(4, name = "noise", desc = "Ore noise account")]
#[account(5, name = "proof", desc = "Ore proof account", writable)]
#[account(6, name = "slot_hashes", desc = "Solana slot hashes sysvar")]
Mine = 3,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "proof", desc = "Ore proof account", writable)]
#[account(3, name = "system_program", desc = "Solana system program")]
Open = 4,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "bus_0", desc = "Ore bus account 0", writable)]
#[account(3, name = "bus_1", desc = "Ore bus account 1", writable)]
#[account(4, name = "bus_2", desc = "Ore bus account 2", writable)]
#[account(5, name = "bus_3", desc = "Ore bus account 3", writable)]
#[account(6, name = "bus_4", desc = "Ore bus account 4", writable)]
#[account(7, name = "bus_5", desc = "Ore bus account 5", writable)]
#[account(8, name = "bus_6", desc = "Ore bus account 6", writable)]
#[account(9, name = "bus_7", desc = "Ore bus account 7", writable)]
#[account(10, name = "config", desc = "Ore config account")]
#[account(11, name = "mint", desc = "Ore token mint account", writable)]
#[account(12, name = "treasury", desc = "Ore treasury account", writable)]
#[account(13, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
#[account(14, name = "token_program", desc = "SPL token program")]
Reset = 5,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "proof", desc = "Ore proof account", writable)]
#[account(3, name = "sender", desc = "Signer token account", writable)]
#[account(4, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
#[account(5, name = "token_program", desc = "SPL token program")]
Stake = 6,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "proof", desc = "Ore proof account", writable)]
Update = 7,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Signer", signer)]
#[account(2, name = "beneficiary", desc = "Beneficiary token account", writable)]
#[account(3, name = "sender", desc = "Signer token account", writable)]
#[account(4, name = "treasury", desc = "Ore treasury account", writable)]
#[account(5, name = "mint", desc = "Ore token mint account", writable)]
#[account(6, name = "mint_v1", desc = "Ore v1 token mint account", writable)]
#[account(7, name = "token_program", desc = "SPL token program")]
Upgrade = 8,
#[account(0, name = "ore_program", desc = "Ore program")]
#[account(1, name = "signer", desc = "Admin signer", signer)]
#[account(2, name = "bus_0", desc = "Ore bus account 0", writable)]
#[account(3, name = "bus_1", desc = "Ore bus account 1", writable)]
#[account(4, name = "bus_2", desc = "Ore bus account 2", writable)]
#[account(5, name = "bus_3", desc = "Ore bus account 3", writable)]
#[account(6, name = "bus_4", desc = "Ore bus account 4", writable)]
#[account(7, name = "bus_5", desc = "Ore bus account 5", writable)]
#[account(8, name = "bus_6", desc = "Ore bus account 6", writable)]
#[account(9, name = "bus_7", desc = "Ore bus account 7", writable)]
#[account(10, name = "metadata", desc = "Ore mint metadata account", writable)]
#[account(11, name = "mint", desc = "Ore mint account", writable)]
#[account(12, name = "noise", desc = "Ore noise account", writable)]
#[account(13, name = "treasury", desc = "Ore treasury account", writable)]
#[account(14, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
#[account(15, name = "system_program", desc = "Solana system program")]
#[account(16, name = "token_program", desc = "SPL token program")]
#[account(17, name = "associated_token_program", desc = "SPL associated token program")]
#[account(18, name = "mpl_metadata_program", desc = "Metaplex metadata program")]
#[account(19, name = "rent", desc = "Solana rent sysvar")]
Initialize = 100,
}
impl OreInstruction {
pub fn to_vec(&self) -> Vec<u8> {
vec![*self as u8]
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct InitializeArgs {
pub bus_0_bump: u8,
pub bus_1_bump: u8,
pub bus_2_bump: u8,
pub bus_3_bump: u8,
pub bus_4_bump: u8,
pub bus_5_bump: u8,
pub bus_6_bump: u8,
pub bus_7_bump: u8,
pub config_bump: u8,
pub metadata_bump: u8,
pub mint_bump: u8,
pub treasury_bump: u8,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct OpenArgs {
pub bump: u8,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct MineArgs {
pub digest: [u8; 16],
pub nonce: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct ClaimArgs {
pub amount: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct StakeArgs {
pub amount: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct UpgradeArgs {
pub amount: [u8; 8],
}
impl_to_bytes!(InitializeArgs);
impl_to_bytes!(OpenArgs);
impl_to_bytes!(MineArgs);
impl_to_bytes!(ClaimArgs);
impl_to_bytes!(StakeArgs);
impl_to_bytes!(UpgradeArgs);
impl_instruction_from_bytes!(InitializeArgs);
impl_instruction_from_bytes!(OpenArgs);
impl_instruction_from_bytes!(MineArgs);
impl_instruction_from_bytes!(ClaimArgs);
impl_instruction_from_bytes!(StakeArgs);
impl_instruction_from_bytes!(UpgradeArgs);
/// 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;
let treasury_tokens = spl_associated_token_account::get_associated_token_address(
&TREASURY_ADDRESS,
&MINT_ADDRESS,
);
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(beneficiary, false),
AccountMeta::new(MINT_ADDRESS, false),
AccountMeta::new(proof, false),
AccountMeta::new_readonly(TREASURY_ADDRESS, false),
AccountMeta::new(treasury_tokens, false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: [
OreInstruction::Claim.to_vec(),
ClaimArgs {
amount: amount.to_le_bytes(),
}
.to_bytes()
.to_vec(),
]
.concat(),
}
}
/// Builds a close instruction.
pub fn close(signer: Pubkey) -> Instruction {
let proof_pda = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id());
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(proof_pda.0, false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: OreInstruction::Close.to_vec(),
}
}
/// Builds a mine instruction.
pub fn mine(signer: Pubkey, bus: Pubkey, solution: Solution) -> Instruction {
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(bus, false),
AccountMeta::new_readonly(CONFIG_ADDRESS, false),
AccountMeta::new(proof, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
],
data: [
OreInstruction::Mine.to_vec(),
MineArgs {
digest: solution.d,
nonce: solution.n,
}
.to_bytes()
.to_vec(),
]
.concat(),
}
}
/// Builds an open instruction.
pub fn open(signer: Pubkey, miner: Pubkey) -> Instruction {
let proof_pda = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id());
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new_readonly(miner, false),
AccountMeta::new(proof_pda.0, false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
],
data: [
OreInstruction::Open.to_vec(),
OpenArgs { bump: proof_pda.1 }.to_bytes().to_vec(),
]
.concat(),
}
}
/// Builds a reset instruction.
pub fn reset(signer: Pubkey) -> Instruction {
let treasury_tokens = spl_associated_token_account::get_associated_token_address(
&TREASURY_ADDRESS,
&MINT_ADDRESS,
);
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(BUS_ADDRESSES[0], false),
AccountMeta::new(BUS_ADDRESSES[1], false),
AccountMeta::new(BUS_ADDRESSES[2], false),
AccountMeta::new(BUS_ADDRESSES[3], false),
AccountMeta::new(BUS_ADDRESSES[4], false),
AccountMeta::new(BUS_ADDRESSES[5], false),
AccountMeta::new(BUS_ADDRESSES[6], false),
AccountMeta::new(BUS_ADDRESSES[7], false),
AccountMeta::new(CONFIG_ADDRESS, false),
AccountMeta::new(MINT_ADDRESS, false),
AccountMeta::new(TREASURY_ADDRESS, false),
AccountMeta::new(treasury_tokens, false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: OreInstruction::Reset.to_vec(),
}
}
/// Build a stake instruction.
pub fn stake(signer: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
let treasury_tokens = spl_associated_token_account::get_associated_token_address(
&TREASURY_ADDRESS,
&MINT_ADDRESS,
);
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(proof, false),
AccountMeta::new(sender, false),
AccountMeta::new(treasury_tokens, false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: [
OreInstruction::Stake.to_vec(),
StakeArgs {
amount: amount.to_le_bytes(),
}
.to_bytes()
.to_vec(),
]
.concat(),
}
}
// Build an update instruction.
pub fn update(signer: Pubkey, miner: Pubkey) -> Instruction {
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new_readonly(miner, false),
AccountMeta::new(proof, false),
],
data: OreInstruction::Update.to_vec(),
}
}
// Build an upgrade instruction.
pub fn upgrade(signer: Pubkey, beneficiary: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(beneficiary, false),
AccountMeta::new(MINT_ADDRESS, false),
AccountMeta::new(MINT_V1_ADDRESS, false),
AccountMeta::new(sender, false),
AccountMeta::new(TREASURY_ADDRESS, false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: [
OreInstruction::Upgrade.to_vec(),
UpgradeArgs {
amount: amount.to_le_bytes(),
}
.to_bytes()
.to_vec(),
]
.concat(),
}
}
/// Builds an initialize instruction.
pub fn initialize(signer: Pubkey) -> Instruction {
let bus_pdas = [
Pubkey::find_program_address(&[BUS, &[0]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[1]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[2]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[3]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[4]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[5]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[6]], &crate::id()),
Pubkey::find_program_address(&[BUS, &[7]], &crate::id()),
];
let config_pda = Pubkey::find_program_address(&[CONFIG], &crate::id());
let mint_pda = Pubkey::find_program_address(&[MINT, MINT_NOISE.as_slice()], &crate::id());
let treasury_pda = Pubkey::find_program_address(&[TREASURY], &crate::id());
let treasury_tokens =
spl_associated_token_account::get_associated_token_address(&treasury_pda.0, &mint_pda.0);
let metadata_pda = Pubkey::find_program_address(
&[
METADATA,
mpl_token_metadata::ID.as_ref(),
mint_pda.0.as_ref(),
],
&mpl_token_metadata::ID,
);
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(bus_pdas[0].0, false),
AccountMeta::new(bus_pdas[1].0, false),
AccountMeta::new(bus_pdas[2].0, false),
AccountMeta::new(bus_pdas[3].0, false),
AccountMeta::new(bus_pdas[4].0, false),
AccountMeta::new(bus_pdas[5].0, false),
AccountMeta::new(bus_pdas[6].0, false),
AccountMeta::new(bus_pdas[7].0, false),
AccountMeta::new(config_pda.0, false),
AccountMeta::new(metadata_pda.0, false),
AccountMeta::new(mint_pda.0, false),
AccountMeta::new(treasury_pda.0, false),
AccountMeta::new(treasury_tokens, false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(spl_associated_token_account::id(), false),
AccountMeta::new_readonly(mpl_token_metadata::ID, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
],
data: [
OreInstruction::Initialize.to_vec(),
InitializeArgs {
bus_0_bump: bus_pdas[0].1,
bus_1_bump: bus_pdas[1].1,
bus_2_bump: bus_pdas[2].1,
bus_3_bump: bus_pdas[3].1,
bus_4_bump: bus_pdas[4].1,
bus_5_bump: bus_pdas[5].1,
bus_6_bump: bus_pdas[6].1,
bus_7_bump: bus_pdas[7].1,
config_bump: config_pda.1,
metadata_bump: metadata_pda.1,
mint_bump: mint_pda.1,
treasury_bump: treasury_pda.1,
}
.to_bytes()
.to_vec(),
]
.concat(),
}
}

9
core/api/src/lib.rs Normal file
View File

@@ -0,0 +1,9 @@
pub mod consts;
pub mod error;
pub mod instruction;
pub mod state;
pub mod utils;
use solana_program::declare_id;
declare_id!("mineRHF5r6S7HyD9SppBfVMXMavDkJsxwGesEvxZr2A");

32
core/api/src/state/bus.rs Normal file
View File

@@ -0,0 +1,32 @@
use bytemuck::{Pod, Zeroable};
use shank::ShankAccount;
use crate::{
impl_account_from_bytes, impl_to_bytes,
utils::{AccountDiscriminator, Discriminator},
};
/// Bus accounts are responsible for distributing mining rewards.
/// There are 8 busses total to minimize write-lock contention and allow for parallel mine operations.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
pub struct Bus {
/// The ID of the bus account.
pub id: u64,
/// The remaining rewards this bus has left to payout in the current epoch.
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.
pub theoretical_rewards: u64,
}
impl Discriminator for Bus {
fn discriminator() -> AccountDiscriminator {
AccountDiscriminator::Bus
}
}
impl_to_bytes!(Bus);
impl_account_from_bytes!(Bus);

View File

@@ -0,0 +1,37 @@
use bytemuck::{Pod, Zeroable};
use shank::ShankAccount;
use solana_program::pubkey::Pubkey;
use crate::{
impl_account_from_bytes, impl_to_bytes,
utils::{AccountDiscriminator, Discriminator},
};
/// Config is a singleton account which manages admin configurable variables.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
pub struct Config {
/// The admin authority with permission to update the difficulty.
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 address of the proof account with the highest stake balance.
pub top_staker: Pubkey,
}
impl Discriminator for Config {
fn discriminator() -> AccountDiscriminator {
AccountDiscriminator::Config
}
}
impl_to_bytes!(Config);
impl_account_from_bytes!(Config);

View File

@@ -0,0 +1,9 @@
mod bus;
mod config;
mod proof;
mod treasury;
pub use bus::*;
pub use config::*;
pub use proof::*;
pub use treasury::*;

View File

@@ -0,0 +1,50 @@
use bytemuck::{Pod, Zeroable};
use shank::ShankAccount;
use solana_program::pubkey::Pubkey;
use crate::{
impl_account_from_bytes, impl_to_bytes,
utils::{AccountDiscriminator, Discriminator},
};
/// Proof accounts track a miner's current hash, claimable rewards, and lifetime stats.
/// Every miner is allowed one proof account which is required by the program to mine or claim rewards.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
pub struct Proof {
/// The signer authorized to use this proof.
pub authority: Pubkey,
/// The quantity of tokens this miner has staked or earned.
pub balance: u64,
/// The current mining challenge.
pub challenge: [u8; 32],
/// The last hash the miner provided.
pub last_hash: [u8; 32],
/// The last time this account provided a hash.
pub last_hash_at: i64,
/// The last time stake was deposited into this account.
pub last_stake_at: i64,
/// The keypair which has permission to submit hashes for mining.
pub miner: Pubkey,
/// The total lifetime hashes provided by this miner.
pub total_hashes: u64,
/// The total lifetime rewards distributed to this miner.
pub total_rewards: u64,
}
impl Discriminator for Proof {
fn discriminator() -> AccountDiscriminator {
AccountDiscriminator::Proof
}
}
impl_to_bytes!(Proof);
impl_account_from_bytes!(Proof);

View File

@@ -0,0 +1,22 @@
use bytemuck::{Pod, Zeroable};
use shank::ShankAccount;
use crate::{
impl_account_from_bytes, impl_to_bytes,
utils::{AccountDiscriminator, Discriminator},
};
/// 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.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
pub struct Treasury {}
impl Discriminator for Treasury {
fn discriminator() -> AccountDiscriminator {
AccountDiscriminator::Treasury
}
}
impl_to_bytes!(Treasury);
impl_account_from_bytes!(Treasury);

156
core/api/src/utils.rs Normal file
View File

@@ -0,0 +1,156 @@
use bytemuck::{Pod, Zeroable};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
pubkey::Pubkey, rent::Rent, sysvar::Sysvar,
};
/// Creates a new pda
#[inline(always)]
pub fn create_pda<'a, 'info>(
target_account: &'a AccountInfo<'info>,
owner: &Pubkey,
space: usize,
pda_seeds: &[&[u8]],
system_program: &'a AccountInfo<'info>,
payer: &'a AccountInfo<'info>,
) -> ProgramResult {
let rent = Rent::get()?;
if target_account.lamports().eq(&0) {
// If balance is zero, create account
solana_program::program::invoke_signed(
&solana_program::system_instruction::create_account(
payer.key,
target_account.key,
rent.minimum_balance(space),
space as u64,
owner,
),
&[
payer.clone(),
target_account.clone(),
system_program.clone(),
],
&[pda_seeds],
)?;
} else {
// Otherwise, if balance is nonzero:
// 1) transfer sufficient lamports for rent exemption
let rent_exempt_balance = rent
.minimum_balance(space)
.saturating_sub(target_account.lamports());
if rent_exempt_balance.gt(&0) {
solana_program::program::invoke(
&solana_program::system_instruction::transfer(
payer.key,
target_account.key,
rent_exempt_balance,
),
&[
payer.clone(),
target_account.clone(),
system_program.clone(),
],
)?;
}
// 2) allocate space for the account
solana_program::program::invoke_signed(
&solana_program::system_instruction::allocate(target_account.key, space as u64),
&[target_account.clone(), system_program.clone()],
&[pda_seeds],
)?;
// 3) assign our program as the owner
solana_program::program::invoke_signed(
&solana_program::system_instruction::assign(target_account.key, owner),
&[target_account.clone(), system_program.clone()],
&[pda_seeds],
)?;
}
Ok(())
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
pub enum AccountDiscriminator {
Bus = 100,
Config = 101,
Proof = 102,
Treasury = 103,
}
pub trait Discriminator {
fn discriminator() -> AccountDiscriminator;
}
pub trait AccountDeserialize {
fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError>;
fn try_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError>;
}
#[macro_export]
macro_rules! impl_to_bytes {
($struct_name:ident) => {
impl $struct_name {
pub fn to_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
}
};
}
#[macro_export]
macro_rules! impl_account_from_bytes {
($struct_name:ident) => {
impl crate::utils::AccountDeserialize for $struct_name {
fn try_from_bytes(
data: &[u8],
) -> Result<&Self, solana_program::program_error::ProgramError> {
if (Self::discriminator() as u8).ne(&data[0]) {
return Err(solana_program::program_error::ProgramError::InvalidAccountData);
}
bytemuck::try_from_bytes::<Self>(&data[8..]).or(Err(
solana_program::program_error::ProgramError::InvalidAccountData,
))
}
fn try_from_bytes_mut(
data: &mut [u8],
) -> Result<&mut Self, solana_program::program_error::ProgramError> {
if (Self::discriminator() as u8).ne(&data[0]) {
return Err(solana_program::program_error::ProgramError::InvalidAccountData);
}
bytemuck::try_from_bytes_mut::<Self>(&mut data[8..]).or(Err(
solana_program::program_error::ProgramError::InvalidAccountData,
))
}
}
};
}
#[macro_export]
macro_rules! impl_instruction_from_bytes {
($struct_name:ident) => {
impl $struct_name {
pub fn try_from_bytes(
data: &[u8],
) -> Result<&Self, solana_program::program_error::ProgramError> {
bytemuck::try_from_bytes::<Self>(data).or(Err(
solana_program::program_error::ProgramError::InvalidInstructionData,
))
}
}
};
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct MineEvent {
pub difficulty: u64,
pub reward: u64,
pub timing: i64,
}
impl_to_bytes!(MineEvent);