mirror of
https://github.com/d0zingcat/ore.git
synced 2026-06-09 15:11:40 +00:00
proof of wager
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -1246,9 +1246,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ore-api"
|
name = "ore-api"
|
||||||
version = "3.6.0-beta"
|
version = "3.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eff73b3394583f3df7ce0f58e9ec66d890777c6cc6dd739de63f8ec826fd4baf"
|
checksum = "90933d4deaa73a1c73201d8a1aed50ce88211718370ceb0408ead479cb77e227"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"array-const-fn-init",
|
"array-const-fn-init",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -1283,16 +1283,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ore-boost-api"
|
name = "ore-boost-api"
|
||||||
version = "4.0.0-beta"
|
version = "4.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0d09065544f61448a879ee9cc04e1c45a13ba76c38988fce83faa3f7aa31b1f"
|
checksum = "0b6c68dc61ee91b269520653f607038bb7f6ebf053385b0a896d4e272bc9f485"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"array-const-fn-init",
|
"array-const-fn-init",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"const-crypto",
|
"const-crypto",
|
||||||
"fixed",
|
"fixed",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"ore-api 3.6.0-beta",
|
"ore-api 3.6.0",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
"spl-associated-token-account",
|
"spl-associated-token-account",
|
||||||
"spl-token 4.0.2",
|
"spl-token 4.0.2",
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ num_enum = "0.7.2"
|
|||||||
ore-api = { path = "api" }
|
ore-api = { path = "api" }
|
||||||
ore-boost-api = "4.0.0-alpha"
|
ore-boost-api = "4.0.0-alpha"
|
||||||
solana-program = "^2.1"
|
solana-program = "^2.1"
|
||||||
|
solana-client = "^2.1"
|
||||||
|
solana-sdk = "^2.1"
|
||||||
spl-token = { version = "^4", features = ["no-entrypoint"] }
|
spl-token = { version = "^4", features = ["no-entrypoint"] }
|
||||||
spl-associated-token-account = { version = "^6", features = [ "no-entrypoint" ] }
|
spl-associated-token-account = { version = "^6", features = [ "no-entrypoint" ] }
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
steel = { features = ["spl"], version = "4.0" }
|
steel = { features = ["spl"], version = "4.0" }
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0.57"
|
||||||
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ pub const SMOOTHING_FACTOR: u64 = 2;
|
|||||||
/// The seed of the bus account PDA.
|
/// The seed of the bus account PDA.
|
||||||
pub const BUS: &[u8] = b"bus";
|
pub const BUS: &[u8] = b"bus";
|
||||||
|
|
||||||
|
/// The seed of the block account PDA.
|
||||||
|
pub const BLOCK: &[u8] = b"block";
|
||||||
|
|
||||||
|
/// The seed of the wager account PDA.
|
||||||
|
pub const WAGER: &[u8] = b"wager";
|
||||||
|
|
||||||
/// The seed of the config account PDA.
|
/// The seed of the config account PDA.
|
||||||
pub const CONFIG: &[u8] = b"config";
|
pub const CONFIG: &[u8] = b"config";
|
||||||
|
|
||||||
|
|||||||
@@ -4,59 +4,40 @@ use steel::*;
|
|||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
|
||||||
pub enum OreInstruction {
|
pub enum OreInstruction {
|
||||||
// User
|
// User
|
||||||
Claim = 0,
|
Bet = 0,
|
||||||
Close = 1,
|
Close = 1,
|
||||||
Mine = 2,
|
Payout = 2,
|
||||||
Open = 3,
|
Reset = 3,
|
||||||
Reset = 4,
|
|
||||||
Update = 5,
|
|
||||||
|
|
||||||
// Admin
|
// Admin
|
||||||
Initialize = 100,
|
Initialize = 100,
|
||||||
Migrate = 101,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
pub struct Claim {
|
pub struct Bet {
|
||||||
pub amount: [u8; 8],
|
pub amount: [u8; 8],
|
||||||
|
pub seed: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
pub struct Close {}
|
pub struct Close {}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
|
||||||
pub struct Mine {
|
|
||||||
pub nonce: [u8; 8],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
|
||||||
pub struct Open {}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
pub struct Reset {}
|
pub struct Reset {}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
pub struct Update {}
|
pub struct Payout {}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
pub struct Initialize {}
|
pub struct Initialize {}
|
||||||
|
|
||||||
#[repr(C)]
|
instruction!(OreInstruction, Bet);
|
||||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
|
||||||
pub struct Migrate {}
|
|
||||||
|
|
||||||
instruction!(OreInstruction, Claim);
|
|
||||||
instruction!(OreInstruction, Close);
|
instruction!(OreInstruction, Close);
|
||||||
instruction!(OreInstruction, Mine);
|
instruction!(OreInstruction, Payout);
|
||||||
instruction!(OreInstruction, Open);
|
|
||||||
instruction!(OreInstruction, Reset);
|
instruction!(OreInstruction, Reset);
|
||||||
instruction!(OreInstruction, Update);
|
|
||||||
instruction!(OreInstruction, Initialize);
|
instruction!(OreInstruction, Initialize);
|
||||||
instruction!(OreInstruction, Migrate);
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ pub mod consts;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod loaders;
|
|
||||||
pub mod sdk;
|
pub mod sdk;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
@@ -11,8 +10,6 @@ pub mod prelude {
|
|||||||
pub use crate::error::*;
|
pub use crate::error::*;
|
||||||
pub use crate::event::*;
|
pub use crate::event::*;
|
||||||
pub use crate::instruction::*;
|
pub use crate::instruction::*;
|
||||||
pub use crate::loaders::*;
|
|
||||||
pub use crate::sdk::*;
|
|
||||||
pub use crate::state::*;
|
pub use crate::state::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
use steel::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
consts::*,
|
|
||||||
state::{Config, Treasury},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait OreAccountInfoValidation {
|
|
||||||
fn is_bus(&self) -> Result<&Self, ProgramError>;
|
|
||||||
fn is_config(&self) -> Result<&Self, ProgramError>;
|
|
||||||
fn is_treasury(&self) -> Result<&Self, ProgramError>;
|
|
||||||
fn is_treasury_tokens(&self) -> Result<&Self, ProgramError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OreAccountInfoValidation for AccountInfo<'_> {
|
|
||||||
fn is_bus(&self) -> Result<&Self, ProgramError> {
|
|
||||||
if !BUS_ADDRESSES.contains(self.key) {
|
|
||||||
return Err(ProgramError::InvalidSeeds);
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_config(&self) -> Result<&Self, ProgramError> {
|
|
||||||
self.has_address(&CONFIG_ADDRESS)?
|
|
||||||
.is_type::<Config>(&crate::ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_treasury(&self) -> Result<&Self, ProgramError> {
|
|
||||||
self.has_address(&TREASURY_ADDRESS)?
|
|
||||||
.is_type::<Treasury>(&crate::ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_treasury_tokens(&self) -> Result<&Self, ProgramError> {
|
|
||||||
self.has_address(&TREASURY_TOKENS_ADDRESS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
135
api/src/sdk.rs
135
api/src/sdk.rs
@@ -1,136 +1,3 @@
|
|||||||
use steel::*;
|
use steel::*;
|
||||||
|
|
||||||
use crate::{consts::*, instruction::*, state::proof_pda};
|
use crate::{consts::*, instruction::*, state::*};
|
||||||
|
|
||||||
/// Builds a claim instruction.
|
|
||||||
pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
|
|
||||||
let proof = proof_pda(signer).0;
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(signer, true),
|
|
||||||
AccountMeta::new(beneficiary, false),
|
|
||||||
AccountMeta::new(proof, false),
|
|
||||||
AccountMeta::new_readonly(TREASURY_ADDRESS, false),
|
|
||||||
AccountMeta::new(TREASURY_TOKENS_ADDRESS, false),
|
|
||||||
AccountMeta::new_readonly(spl_token::ID, false),
|
|
||||||
],
|
|
||||||
data: Claim {
|
|
||||||
amount: amount.to_le_bytes(),
|
|
||||||
}
|
|
||||||
.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a close instruction.
|
|
||||||
pub fn close(signer: Pubkey) -> Instruction {
|
|
||||||
let proof = proof_pda(signer).0;
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(signer, true),
|
|
||||||
AccountMeta::new(proof, false),
|
|
||||||
AccountMeta::new_readonly(solana_program::system_program::ID, false),
|
|
||||||
],
|
|
||||||
data: Close {}.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a mine instruction.
|
|
||||||
pub fn mine(
|
|
||||||
signer: Pubkey,
|
|
||||||
authority: Pubkey,
|
|
||||||
bus: Pubkey,
|
|
||||||
nonce: u64,
|
|
||||||
boost_config: Pubkey,
|
|
||||||
) -> Instruction {
|
|
||||||
let proof = proof_pda(authority).0;
|
|
||||||
let 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),
|
|
||||||
AccountMeta::new_readonly(boost_config, false),
|
|
||||||
AccountMeta::new(proof_pda(boost_config).0, false),
|
|
||||||
];
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts,
|
|
||||||
data: Mine {
|
|
||||||
// digest: solution.d,
|
|
||||||
nonce: nonce.to_le_bytes(),
|
|
||||||
}
|
|
||||||
.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds an open instruction.
|
|
||||||
pub fn open(signer: Pubkey, miner: Pubkey, payer: Pubkey) -> Instruction {
|
|
||||||
let proof_pda = proof_pda(signer);
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(signer, true),
|
|
||||||
AccountMeta::new_readonly(miner, false),
|
|
||||||
AccountMeta::new(payer, true),
|
|
||||||
AccountMeta::new(proof_pda.0, false),
|
|
||||||
AccountMeta::new_readonly(solana_program::system_program::ID, false),
|
|
||||||
AccountMeta::new_readonly(sysvar::slot_hashes::ID, false),
|
|
||||||
],
|
|
||||||
data: Open {}.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a reset instruction.
|
|
||||||
pub fn reset(signer: Pubkey, best_proof: Pubkey) -> Instruction {
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(signer, true),
|
|
||||||
AccountMeta::new(CONFIG_ADDRESS, false),
|
|
||||||
AccountMeta::new(MINT_ADDRESS, false),
|
|
||||||
AccountMeta::new(best_proof, false),
|
|
||||||
AccountMeta::new(TREASURY_ADDRESS, false),
|
|
||||||
AccountMeta::new(TREASURY_TOKENS_ADDRESS, false),
|
|
||||||
AccountMeta::new_readonly(spl_token::ID, false),
|
|
||||||
],
|
|
||||||
data: Reset {}.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build an update instruction.
|
|
||||||
pub fn update(signer: Pubkey, miner: Pubkey) -> Instruction {
|
|
||||||
let proof = proof_pda(signer).0;
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(signer, true),
|
|
||||||
AccountMeta::new_readonly(miner, false),
|
|
||||||
AccountMeta::new(proof, false),
|
|
||||||
],
|
|
||||||
data: Update {}.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds an initialize instruction.
|
|
||||||
pub fn initialize(signer: Pubkey) -> Instruction {
|
|
||||||
Instruction {
|
|
||||||
program_id: crate::ID,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(signer, true),
|
|
||||||
AccountMeta::new(CONFIG_ADDRESS, false),
|
|
||||||
AccountMeta::new(METADATA_ADDRESS, false),
|
|
||||||
AccountMeta::new(MINT_ADDRESS, false),
|
|
||||||
AccountMeta::new(TREASURY_ADDRESS, false),
|
|
||||||
AccountMeta::new(TREASURY_TOKENS_ADDRESS, 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: Initialize {}.to_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
36
api/src/state/block.rs
Normal file
36
api/src/state/block.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use steel::*;
|
||||||
|
|
||||||
|
use super::OreAccount;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||||
|
pub struct Block {
|
||||||
|
/// The current round.
|
||||||
|
pub current_round: u64,
|
||||||
|
|
||||||
|
/// The cumulative amount of SOL risked in the current round, up to and including this bet.
|
||||||
|
pub total_bets: u64,
|
||||||
|
|
||||||
|
/// The number of bets made in the current round.
|
||||||
|
pub bet_count: u64,
|
||||||
|
|
||||||
|
/// The time time the current round started.
|
||||||
|
pub started_at: u64,
|
||||||
|
|
||||||
|
/// The slot at which the current round ends.
|
||||||
|
pub ends_at: u64,
|
||||||
|
|
||||||
|
/// Whether or not the current round has ended.
|
||||||
|
pub payed_out: u64,
|
||||||
|
|
||||||
|
/// The mint used to track wagers of the current round.
|
||||||
|
pub mint: Pubkey,
|
||||||
|
|
||||||
|
/// The amount of ORE to distribute to the winner.
|
||||||
|
pub reward: u64,
|
||||||
|
|
||||||
|
/// The noise used for the current round.
|
||||||
|
pub noise: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
account!(OreAccount, Block);
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
use steel::*;
|
|
||||||
|
|
||||||
use super::OreAccount;
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
/// 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.
|
|
||||||
/// This is used to calculate the updated reward rate.
|
|
||||||
pub theoretical_rewards: u64,
|
|
||||||
|
|
||||||
/// The largest known stake balance seen by the bus this epoch.
|
|
||||||
#[deprecated(since = "2.8.0", note = "Top balance is no longer tracked or used")]
|
|
||||||
pub top_balance: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
account!(OreAccount, Bus);
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
use steel::*;
|
|
||||||
|
|
||||||
use super::{OldOreAccount, OreAccount};
|
|
||||||
|
|
||||||
/// Config is a singleton account which manages program global variables.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
|
||||||
pub struct Config {
|
|
||||||
/// The timestamp of the last reset.
|
|
||||||
pub last_reset_at: i64,
|
|
||||||
|
|
||||||
/// The best difficulty score of this epoch.
|
|
||||||
pub best_hash: [u8; 32],
|
|
||||||
|
|
||||||
/// The proof of the best submitted hash of this epoch.
|
|
||||||
pub best_proof: Pubkey,
|
|
||||||
|
|
||||||
/// The challenge of this epoch.
|
|
||||||
pub challenge: [u8; 32],
|
|
||||||
|
|
||||||
/// The target emissions rate in ORE/min.
|
|
||||||
pub block_reward: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Config is a singleton account which manages program global variables.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
|
||||||
pub struct OldConfig {
|
|
||||||
/// 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 minimum accepted difficulty.
|
|
||||||
pub min_difficulty: u64,
|
|
||||||
|
|
||||||
/// The target emissions rate in ORE/min.
|
|
||||||
pub target_emmissions_rate: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
account!(OreAccount, Config);
|
|
||||||
account!(OldOreAccount, OldConfig);
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
mod bus;
|
mod block;
|
||||||
mod config;
|
|
||||||
mod proof;
|
mod proof;
|
||||||
mod treasury;
|
mod treasury;
|
||||||
|
mod wager;
|
||||||
|
|
||||||
pub use bus::*;
|
pub use block::*;
|
||||||
pub use config::*;
|
|
||||||
pub use proof::*;
|
pub use proof::*;
|
||||||
pub use treasury::*;
|
pub use treasury::*;
|
||||||
|
pub use wager::*;
|
||||||
|
|
||||||
use steel::*;
|
use steel::*;
|
||||||
|
|
||||||
@@ -15,29 +15,27 @@ use crate::consts::*;
|
|||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
|
||||||
pub enum OreAccount {
|
pub enum OreAccount {
|
||||||
Bus = 100,
|
|
||||||
Config = 101,
|
|
||||||
Proof = 102,
|
Proof = 102,
|
||||||
Treasury = 103,
|
Treasury = 103,
|
||||||
|
Block = 104,
|
||||||
|
Wager = 105,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive the PDA of the config account.
|
pub fn block_pda() -> (Pubkey, u8) {
|
||||||
pub fn config_pda() -> (Pubkey, u8) {
|
Pubkey::find_program_address(&[BLOCK], &crate::ID)
|
||||||
Pubkey::find_program_address(&[CONFIG], &crate::id())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive the PDA of a proof account.
|
|
||||||
pub fn proof_pda(authority: Pubkey) -> (Pubkey, u8) {
|
pub fn proof_pda(authority: Pubkey) -> (Pubkey, u8) {
|
||||||
Pubkey::find_program_address(&[PROOF, authority.as_ref()], &crate::id())
|
Pubkey::find_program_address(&[PROOF, authority.as_ref()], &crate::id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive the PDA of the treasury account.
|
pub fn wager_pda(round: u64, id: u64) -> (Pubkey, u8) {
|
||||||
pub fn treasury_pda() -> (Pubkey, u8) {
|
Pubkey::find_program_address(
|
||||||
Pubkey::find_program_address(&[TREASURY], &crate::id())
|
&[WAGER, &round.to_le_bytes(), &id.to_le_bytes()],
|
||||||
|
&crate::ID,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
pub fn treasury_pda() -> (Pubkey, u8) {
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
|
Pubkey::find_program_address(&[TREASURY], &crate::ID)
|
||||||
pub enum OldOreAccount {
|
|
||||||
OldConfig = 101,
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
api/src/state/wager.rs
Normal file
27
api/src/state/wager.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use steel::*;
|
||||||
|
|
||||||
|
use super::OreAccount;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||||
|
pub struct Wager {
|
||||||
|
/// The signer authorized to use this wager.
|
||||||
|
pub authority: Pubkey,
|
||||||
|
|
||||||
|
/// The current round this miner is betting in.
|
||||||
|
pub round: u64,
|
||||||
|
|
||||||
|
/// The ID of the bet.
|
||||||
|
pub id: u64,
|
||||||
|
|
||||||
|
/// The quantity of SOL this miner has bet in the current round.
|
||||||
|
pub amount: u64,
|
||||||
|
|
||||||
|
/// The cumulative amount of SOL bet in the current round, up to and including this wager.
|
||||||
|
pub cumulative_bets: u64,
|
||||||
|
|
||||||
|
/// The timestamp of the wager.
|
||||||
|
pub timestamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
account!(OreAccount, Wager);
|
||||||
81
program/src/bet.rs
Normal file
81
program/src/bet.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use ore_api::prelude::*;
|
||||||
|
use solana_program::{keccak::hashv, slot_hashes::SlotHash};
|
||||||
|
use steel::*;
|
||||||
|
|
||||||
|
pub fn process_bet(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
|
||||||
|
// Parse data.
|
||||||
|
let args = Bet::try_from_bytes(data)?;
|
||||||
|
let amount = u64::from_le_bytes(args.amount);
|
||||||
|
|
||||||
|
// Load accounts.
|
||||||
|
let clock = Clock::get()?;
|
||||||
|
let [signer_info, block_info, wager_info, block_bets_info, sender_info, system_program, token_program, slot_hashes_sysvar] =
|
||||||
|
accounts
|
||||||
|
else {
|
||||||
|
return Err(ProgramError::NotEnoughAccountKeys);
|
||||||
|
};
|
||||||
|
signer_info.is_signer()?;
|
||||||
|
let block = block_info
|
||||||
|
.as_account_mut::<Block>(&ore_api::ID)?
|
||||||
|
.assert_mut(|b| b.ends_at > clock.slot)?
|
||||||
|
.assert_mut(|b| b.payed_out != 0)?;
|
||||||
|
block_bets_info.as_associated_token_account(block_info.key, &block.mint)?;
|
||||||
|
sender_info.as_associated_token_account(signer_info.key, &block.mint)?;
|
||||||
|
wager_info.is_writable()?.is_empty()?.has_seeds(
|
||||||
|
&[
|
||||||
|
WAGER,
|
||||||
|
&block.current_round.to_le_bytes(),
|
||||||
|
&block.bet_count.to_le_bytes(),
|
||||||
|
],
|
||||||
|
&ore_api::ID,
|
||||||
|
)?;
|
||||||
|
system_program.is_program(&system_program::ID)?;
|
||||||
|
token_program.is_program(&spl_token::ID)?;
|
||||||
|
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
|
||||||
|
|
||||||
|
// Create wager account.
|
||||||
|
create_program_account::<Wager>(
|
||||||
|
&wager_info,
|
||||||
|
&system_program,
|
||||||
|
&signer_info,
|
||||||
|
&ore_api::ID,
|
||||||
|
&[
|
||||||
|
WAGER,
|
||||||
|
&block.current_round.to_le_bytes(),
|
||||||
|
&block.bet_count.to_le_bytes(),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
let wager = wager_info.as_account_mut::<Wager>(&ore_api::ID)?;
|
||||||
|
wager.amount = amount;
|
||||||
|
wager.authority = *signer_info.key;
|
||||||
|
wager.id = block.bet_count;
|
||||||
|
wager.round = block.current_round;
|
||||||
|
wager.timestamp = clock.unix_timestamp as u64;
|
||||||
|
wager.cumulative_bets = block.total_bets;
|
||||||
|
|
||||||
|
// Update block.
|
||||||
|
block.total_bets += amount;
|
||||||
|
block.bet_count += 1;
|
||||||
|
|
||||||
|
// Hash client seed into block noise. Use a recent slot hash if no seed is provided.
|
||||||
|
// This follows the scheme for provable randomness.
|
||||||
|
let seed: &[u8] = if args.seed == [0; 32] {
|
||||||
|
&slot_hashes_sysvar.data.borrow()[0..size_of::<SlotHash>()]
|
||||||
|
} else {
|
||||||
|
args.seed.as_slice()
|
||||||
|
};
|
||||||
|
block.noise = hashv(&[&block.noise, seed]).to_bytes();
|
||||||
|
|
||||||
|
// Transfer wagers.
|
||||||
|
transfer(
|
||||||
|
&signer_info,
|
||||||
|
&sender_info,
|
||||||
|
&block_bets_info,
|
||||||
|
&token_program,
|
||||||
|
amount,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
use ore_api::prelude::*;
|
|
||||||
use steel::*;
|
|
||||||
|
|
||||||
/// Claim distributes claimable ORE from the treasury to a miner.
|
|
||||||
pub fn process_claim(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
|
|
||||||
// Parse args.
|
|
||||||
let args = Claim::try_from_bytes(data)?;
|
|
||||||
let amount = u64::from_le_bytes(args.amount);
|
|
||||||
|
|
||||||
// Load accounts.
|
|
||||||
let clock = Clock::get()?;
|
|
||||||
let [signer_info, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, token_program] =
|
|
||||||
accounts
|
|
||||||
else {
|
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
|
||||||
};
|
|
||||||
signer_info.is_signer()?;
|
|
||||||
beneficiary_info
|
|
||||||
.is_writable()?
|
|
||||||
.as_token_account()?
|
|
||||||
.assert(|t| t.mint() == MINT_ADDRESS)?;
|
|
||||||
let proof = proof_info
|
|
||||||
.as_account_mut::<Proof>(&ore_api::ID)?
|
|
||||||
.assert_mut_err(
|
|
||||||
|p| p.authority == *signer_info.key,
|
|
||||||
ProgramError::MissingRequiredSignature,
|
|
||||||
)?;
|
|
||||||
treasury_info.is_treasury()?;
|
|
||||||
treasury_tokens_info.is_writable()?.is_treasury_tokens()?;
|
|
||||||
token_program.is_program(&spl_token::ID)?;
|
|
||||||
|
|
||||||
// Update miner balance.
|
|
||||||
proof.balance = proof
|
|
||||||
.balance
|
|
||||||
.checked_sub(amount)
|
|
||||||
.ok_or(OreError::ClaimTooLarge)?;
|
|
||||||
|
|
||||||
// Update last claim timestamp.
|
|
||||||
proof.last_claim_at = clock.unix_timestamp;
|
|
||||||
|
|
||||||
// Transfer tokens from treasury to beneficiary.
|
|
||||||
transfer_signed(
|
|
||||||
treasury_info,
|
|
||||||
treasury_tokens_info,
|
|
||||||
beneficiary_info,
|
|
||||||
token_program,
|
|
||||||
amount,
|
|
||||||
&[TREASURY],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,21 @@
|
|||||||
use ore_api::prelude::*;
|
use ore_api::prelude::*;
|
||||||
use steel::*;
|
use steel::*;
|
||||||
|
|
||||||
/// Close closes a proof account and returns the rent to the owner.
|
|
||||||
pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||||
// Load accounts.
|
// Load accounts.
|
||||||
let [signer_info, proof_info, system_program] = accounts else {
|
let [signer_info, block_info, wager_info, system_program] = accounts else {
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
return Err(ProgramError::NotEnoughAccountKeys);
|
||||||
};
|
};
|
||||||
signer_info.is_signer()?;
|
signer_info.is_signer()?;
|
||||||
proof_info
|
let block = block_info.as_account::<Block>(&ore_api::ID)?;
|
||||||
.is_writable()?
|
wager_info
|
||||||
.as_account::<Proof>(&ore_api::ID)?
|
.as_account_mut::<Wager>(&ore_api::ID)?
|
||||||
.assert_err(
|
.assert_mut(|w| w.authority == *signer_info.key)?
|
||||||
|p| p.authority == *signer_info.key,
|
.assert_mut(|w| w.round < block.current_round)?;
|
||||||
ProgramError::MissingRequiredSignature,
|
|
||||||
)?
|
|
||||||
.assert(|p| p.balance == 0)?;
|
|
||||||
system_program.is_program(&system_program::ID)?;
|
system_program.is_program(&system_program::ID)?;
|
||||||
|
|
||||||
// Return rent to signer.
|
// Close the wager account
|
||||||
proof_info.close(signer_info)?;
|
wager_info.close(&signer_info)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,122 +1,52 @@
|
|||||||
use ore_api::prelude::*;
|
use ore_api::prelude::*;
|
||||||
use solana_program::program_pack::Pack;
|
|
||||||
use spl_token::state::Mint;
|
|
||||||
use steel::*;
|
use steel::*;
|
||||||
|
|
||||||
/// Initialize sets up the ORE program to begin mining.
|
/// Initialize sets up the ORE program to begin mining.
|
||||||
pub fn process_initialize(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
pub fn process_initialize(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||||
// Load accounts.
|
// Load accounts.
|
||||||
let [signer_info, config_info, metadata_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program, metadata_program, rent_sysvar] =
|
let [signer_info, block_info, block_bets_info, sol_mint_info, system_program, token_program, associated_token_program] =
|
||||||
accounts
|
accounts
|
||||||
else {
|
else {
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
return Err(ProgramError::NotEnoughAccountKeys);
|
||||||
};
|
};
|
||||||
signer_info.is_signer()?.has_address(&INITIALIZER_ADDRESS)?;
|
signer_info.is_signer()?.has_address(&INITIALIZER_ADDRESS)?;
|
||||||
config_info
|
block_info
|
||||||
.is_empty()?
|
.is_empty()?
|
||||||
.is_writable()?
|
.is_writable()?
|
||||||
.has_seeds(&[CONFIG], &ore_api::ID)?;
|
.has_seeds(&[BLOCK], &ore_api::ID)?;
|
||||||
metadata_info.is_empty()?.is_writable()?.has_seeds(
|
block_bets_info.is_empty()?.is_writable()?;
|
||||||
&[
|
sol_mint_info
|
||||||
METADATA,
|
.has_address(&spl_token::native_mint::ID)?
|
||||||
mpl_token_metadata::ID.as_ref(),
|
.as_mint()?;
|
||||||
MINT_ADDRESS.as_ref(),
|
|
||||||
],
|
|
||||||
&mpl_token_metadata::ID,
|
|
||||||
)?;
|
|
||||||
mint_info
|
|
||||||
.is_empty()?
|
|
||||||
.is_writable()?
|
|
||||||
.has_seeds(&[MINT, MINT_NOISE.as_slice()], &ore_api::ID)?;
|
|
||||||
treasury_info
|
|
||||||
.is_empty()?
|
|
||||||
.is_writable()?
|
|
||||||
.has_seeds(&[TREASURY], &ore_api::ID)?;
|
|
||||||
treasury_tokens_info.is_empty()?.is_writable()?;
|
|
||||||
system_program.is_program(&system_program::ID)?;
|
system_program.is_program(&system_program::ID)?;
|
||||||
token_program.is_program(&spl_token::ID)?;
|
token_program.is_program(&spl_token::ID)?;
|
||||||
associated_token_program.is_program(&spl_associated_token_account::ID)?;
|
associated_token_program.is_program(&spl_associated_token_account::ID)?;
|
||||||
metadata_program.is_program(&mpl_token_metadata::ID)?;
|
|
||||||
rent_sysvar.is_sysvar(&sysvar::rent::ID)?;
|
|
||||||
|
|
||||||
// Initialize config.
|
// Initialize config.
|
||||||
create_program_account::<Config>(
|
create_program_account::<Block>(
|
||||||
config_info,
|
block_info,
|
||||||
system_program,
|
system_program,
|
||||||
signer_info,
|
signer_info,
|
||||||
&ore_api::ID,
|
&ore_api::ID,
|
||||||
&[CONFIG],
|
&[BLOCK],
|
||||||
)?;
|
)?;
|
||||||
let config = config_info.as_account_mut::<Config>(&ore_api::ID)?;
|
let block = block_info.as_account_mut::<Block>(&ore_api::ID)?;
|
||||||
// config.base_reward_rate = INITIAL_BASE_REWARD_RATE;
|
block.current_round = 0;
|
||||||
config.last_reset_at = 0;
|
block.total_bets = 0;
|
||||||
config.best_hash = [u8::MAX; 32];
|
block.bet_count = 0;
|
||||||
config.best_proof = Pubkey::default();
|
block.started_at = 0;
|
||||||
config.challenge = [0; 32];
|
block.ends_at = 0;
|
||||||
config.block_reward = 0;
|
block.payed_out = 0;
|
||||||
|
block.mint = spl_token::native_mint::ID;
|
||||||
// Initialize treasury.
|
block.reward = 0;
|
||||||
create_program_account::<Treasury>(
|
block.noise = [0; 32];
|
||||||
treasury_info,
|
|
||||||
system_program,
|
|
||||||
signer_info,
|
|
||||||
&ore_api::ID,
|
|
||||||
&[TREASURY],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Initialize mint.
|
|
||||||
allocate_account_with_bump(
|
|
||||||
mint_info,
|
|
||||||
system_program,
|
|
||||||
signer_info,
|
|
||||||
Mint::LEN,
|
|
||||||
&spl_token::ID,
|
|
||||||
&[MINT, MINT_NOISE.as_slice()],
|
|
||||||
MINT_BUMP,
|
|
||||||
)?;
|
|
||||||
initialize_mint_signed_with_bump(
|
|
||||||
mint_info,
|
|
||||||
treasury_info,
|
|
||||||
None,
|
|
||||||
token_program,
|
|
||||||
rent_sysvar,
|
|
||||||
TOKEN_DECIMALS,
|
|
||||||
&[MINT, MINT_NOISE.as_slice()],
|
|
||||||
MINT_BUMP,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Initialize mint metadata.
|
|
||||||
mpl_token_metadata::instructions::CreateMetadataAccountV3Cpi {
|
|
||||||
__program: metadata_program,
|
|
||||||
metadata: metadata_info,
|
|
||||||
mint: mint_info,
|
|
||||||
mint_authority: treasury_info,
|
|
||||||
payer: signer_info,
|
|
||||||
update_authority: (signer_info, true),
|
|
||||||
system_program,
|
|
||||||
rent: Some(rent_sysvar),
|
|
||||||
__args: mpl_token_metadata::instructions::CreateMetadataAccountV3InstructionArgs {
|
|
||||||
data: mpl_token_metadata::types::DataV2 {
|
|
||||||
name: METADATA_NAME.to_string(),
|
|
||||||
symbol: METADATA_SYMBOL.to_string(),
|
|
||||||
uri: METADATA_URI.to_string(),
|
|
||||||
seller_fee_basis_points: 0,
|
|
||||||
creators: None,
|
|
||||||
collection: None,
|
|
||||||
uses: None,
|
|
||||||
},
|
|
||||||
is_mutable: true,
|
|
||||||
collection_details: None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.invoke_signed(&[&[TREASURY, &[TREASURY_BUMP]]])?;
|
|
||||||
|
|
||||||
// Initialize treasury token account.
|
// Initialize treasury token account.
|
||||||
create_associated_token_account(
|
create_associated_token_account(
|
||||||
signer_info,
|
signer_info,
|
||||||
treasury_info,
|
block_info,
|
||||||
treasury_tokens_info,
|
block_bets_info,
|
||||||
mint_info,
|
sol_mint_info,
|
||||||
system_program,
|
system_program,
|
||||||
token_program,
|
token_program,
|
||||||
associated_token_program,
|
associated_token_program,
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
mod claim;
|
mod bet;
|
||||||
mod close;
|
mod close;
|
||||||
mod initialize;
|
mod initialize;
|
||||||
mod migrate;
|
mod payout;
|
||||||
mod mine;
|
|
||||||
mod open;
|
|
||||||
mod reset;
|
mod reset;
|
||||||
mod update;
|
|
||||||
|
|
||||||
use claim::*;
|
use bet::*;
|
||||||
use close::*;
|
use close::*;
|
||||||
use initialize::*;
|
use initialize::*;
|
||||||
use migrate::*;
|
use payout::*;
|
||||||
use mine::*;
|
|
||||||
use open::*;
|
|
||||||
use ore_api::instruction::*;
|
|
||||||
use reset::*;
|
use reset::*;
|
||||||
|
|
||||||
|
use ore_api::instruction::*;
|
||||||
use steel::*;
|
use steel::*;
|
||||||
use update::*;
|
|
||||||
|
|
||||||
pub fn process_instruction(
|
pub fn process_instruction(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
@@ -26,14 +21,11 @@ pub fn process_instruction(
|
|||||||
let (ix, data) = parse_instruction(&ore_api::ID, program_id, data)?;
|
let (ix, data) = parse_instruction(&ore_api::ID, program_id, data)?;
|
||||||
|
|
||||||
match ix {
|
match ix {
|
||||||
OreInstruction::Claim => process_claim(accounts, data)?,
|
OreInstruction::Bet => process_bet(accounts, data)?,
|
||||||
OreInstruction::Close => process_close(accounts, data)?,
|
OreInstruction::Close => process_close(accounts, data)?,
|
||||||
OreInstruction::Mine => process_mine(accounts, data)?,
|
|
||||||
OreInstruction::Open => process_open(accounts, data)?,
|
|
||||||
OreInstruction::Reset => process_reset(accounts, data)?,
|
OreInstruction::Reset => process_reset(accounts, data)?,
|
||||||
OreInstruction::Update => process_update(accounts, data)?,
|
|
||||||
OreInstruction::Initialize => process_initialize(accounts, data)?,
|
OreInstruction::Initialize => process_initialize(accounts, data)?,
|
||||||
OreInstruction::Migrate => process_migrate(accounts, data)?,
|
OreInstruction::Payout => process_payout(accounts, data)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
use ore_api::prelude::*;
|
|
||||||
use solana_program::hash;
|
|
||||||
use steel::*;
|
|
||||||
|
|
||||||
/// Mine validates hashes and increments a miner's claimable balance.
|
|
||||||
pub fn process_migrate(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
|
|
||||||
// Parse args.
|
|
||||||
let args = Migrate::try_from_bytes(data)?;
|
|
||||||
|
|
||||||
// Load accounts.
|
|
||||||
let clock = Clock::get()?;
|
|
||||||
let t: i64 = clock.unix_timestamp;
|
|
||||||
let [signer_info, 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, system_program, slot_hashes_sysvar] =
|
|
||||||
accounts
|
|
||||||
else {
|
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
|
||||||
};
|
|
||||||
signer_info.is_signer()?.has_address(&INITIALIZER_ADDRESS);
|
|
||||||
let config = config_info
|
|
||||||
.as_account_mut::<OldConfig>(&ore_api::ID)?
|
|
||||||
.assert_mut_err(
|
|
||||||
|c| t < c.last_reset_at + EPOCH_DURATION,
|
|
||||||
OreError::NeedsReset.into(),
|
|
||||||
)?;
|
|
||||||
let bus_0 = bus_0_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 0)?;
|
|
||||||
let bus_1 = bus_1_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 1)?;
|
|
||||||
let bus_2 = bus_2_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 2)?;
|
|
||||||
let bus_3 = bus_3_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 3)?;
|
|
||||||
let bus_4 = bus_4_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 4)?;
|
|
||||||
let bus_5 = bus_5_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 5)?;
|
|
||||||
let bus_6 = bus_6_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 6)?;
|
|
||||||
let bus_7 = bus_7_info
|
|
||||||
.as_account_mut::<Bus>(&ore_api::ID)?
|
|
||||||
.assert_mut(|b| b.id == 7)?;
|
|
||||||
mint_info
|
|
||||||
.is_writable()?
|
|
||||||
.has_address(&MINT_ADDRESS)?
|
|
||||||
.as_mint()?;
|
|
||||||
treasury_info
|
|
||||||
.has_address(&TREASURY_ADDRESS)?
|
|
||||||
.is_writable()?;
|
|
||||||
treasury_tokens_info
|
|
||||||
.has_address(&TREASURY_TOKENS_ADDRESS)?
|
|
||||||
.as_associated_token_account(&TREASURY_ADDRESS, &MINT_ADDRESS)?;
|
|
||||||
token_program.is_program(&spl_token::ID)?;
|
|
||||||
system_program.is_program(&system_program::ID)?;
|
|
||||||
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
|
|
||||||
|
|
||||||
let mut total_bus_balance = 0;
|
|
||||||
total_bus_balance += bus_0.rewards;
|
|
||||||
total_bus_balance += bus_1.rewards;
|
|
||||||
total_bus_balance += bus_2.rewards;
|
|
||||||
total_bus_balance += bus_3.rewards;
|
|
||||||
total_bus_balance += bus_4.rewards;
|
|
||||||
total_bus_balance += bus_5.rewards;
|
|
||||||
total_bus_balance += bus_6.rewards;
|
|
||||||
total_bus_balance += bus_7.rewards;
|
|
||||||
|
|
||||||
// Reset bus balances
|
|
||||||
bus_0.rewards = 0;
|
|
||||||
bus_1.rewards = 0;
|
|
||||||
bus_2.rewards = 0;
|
|
||||||
bus_3.rewards = 0;
|
|
||||||
bus_4.rewards = 0;
|
|
||||||
bus_5.rewards = 0;
|
|
||||||
bus_6.rewards = 0;
|
|
||||||
bus_7.rewards = 0;
|
|
||||||
|
|
||||||
// Delete bus accounts
|
|
||||||
bus_0_info.close(signer_info)?;
|
|
||||||
bus_1_info.close(signer_info)?;
|
|
||||||
bus_2_info.close(signer_info)?;
|
|
||||||
bus_3_info.close(signer_info)?;
|
|
||||||
bus_4_info.close(signer_info)?;
|
|
||||||
bus_5_info.close(signer_info)?;
|
|
||||||
bus_6_info.close(signer_info)?;
|
|
||||||
bus_7_info.close(signer_info)?;
|
|
||||||
|
|
||||||
// Burn all tokens in the bus balances
|
|
||||||
burn_signed(
|
|
||||||
treasury_tokens_info,
|
|
||||||
mint_info,
|
|
||||||
treasury_info,
|
|
||||||
token_program,
|
|
||||||
total_bus_balance,
|
|
||||||
&[TREASURY],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// let proof = proof_info
|
|
||||||
// .as_account_mut::<Proof>(&ore_api::ID)?
|
|
||||||
// .assert_mut_err(
|
|
||||||
// |p| p.miner == *signer_info.key,
|
|
||||||
// ProgramError::MissingRequiredSignature,
|
|
||||||
// )?;
|
|
||||||
|
|
||||||
// Compute the hash.
|
|
||||||
// let solution = hash::hashv(&[
|
|
||||||
// args.nonce.as_slice(),
|
|
||||||
// config.challenge.as_slice(),
|
|
||||||
// proof.authority.to_bytes().as_slice(),
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// // Update the best solution.
|
|
||||||
// if solution.to_bytes() < config.best_hash {
|
|
||||||
// config.best_hash = solution.to_bytes();
|
|
||||||
// config.best_proof = *proof_info.key;
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
use ore_api::prelude::*;
|
|
||||||
use solana_program::hash;
|
|
||||||
use steel::*;
|
|
||||||
|
|
||||||
/// Mine validates hashes and increments a miner's claimable balance.
|
|
||||||
pub fn process_mine(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
|
|
||||||
// Parse args.
|
|
||||||
let args = Mine::try_from_bytes(data)?;
|
|
||||||
|
|
||||||
// Load accounts.
|
|
||||||
let clock = Clock::get()?;
|
|
||||||
let t: i64 = clock.unix_timestamp;
|
|
||||||
let [signer_info, config_info, proof_info] = accounts else {
|
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
|
||||||
};
|
|
||||||
signer_info.is_signer()?;
|
|
||||||
let config = config_info.as_account_mut::<Config>(&ore_api::ID)?;
|
|
||||||
let proof = proof_info
|
|
||||||
.as_account_mut::<Proof>(&ore_api::ID)?
|
|
||||||
.assert_mut_err(
|
|
||||||
|p| p.miner == *signer_info.key,
|
|
||||||
ProgramError::MissingRequiredSignature,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Compute the hash.
|
|
||||||
let solution = hash::hashv(&[
|
|
||||||
args.nonce.as_slice(),
|
|
||||||
config.challenge.as_slice(),
|
|
||||||
proof.authority.to_bytes().as_slice(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Update the best solution.
|
|
||||||
if solution.to_bytes() < config.best_hash {
|
|
||||||
config.best_hash = solution.to_bytes();
|
|
||||||
config.best_proof = *proof_info.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the proof.
|
|
||||||
proof.last_hash = solution.to_bytes();
|
|
||||||
proof.last_hash_at = t;
|
|
||||||
proof.total_hashes += 1;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
use std::mem::size_of;
|
|
||||||
|
|
||||||
use ore_api::prelude::*;
|
|
||||||
use solana_program::{keccak::hashv, slot_hashes::SlotHash};
|
|
||||||
use steel::*;
|
|
||||||
|
|
||||||
/// Open creates a new proof account to track a miner's state.
|
|
||||||
pub fn process_open(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
|
||||||
// Load accounts.
|
|
||||||
let [signer_info, miner_info, payer_info, proof_info, system_program, slot_hashes_info] =
|
|
||||||
accounts
|
|
||||||
else {
|
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
|
||||||
};
|
|
||||||
signer_info.is_signer()?;
|
|
||||||
payer_info.is_signer()?;
|
|
||||||
proof_info
|
|
||||||
.is_empty()?
|
|
||||||
.is_writable()?
|
|
||||||
.has_seeds(&[PROOF, signer_info.key.as_ref()], &ore_api::ID)?;
|
|
||||||
system_program.is_program(&system_program::ID)?;
|
|
||||||
slot_hashes_info.is_sysvar(&sysvar::slot_hashes::ID)?;
|
|
||||||
|
|
||||||
// Initialize proof.
|
|
||||||
create_program_account::<Proof>(
|
|
||||||
proof_info,
|
|
||||||
system_program,
|
|
||||||
payer_info,
|
|
||||||
&ore_api::ID,
|
|
||||||
&[PROOF, signer_info.key.as_ref()],
|
|
||||||
)?;
|
|
||||||
let clock = Clock::get()?;
|
|
||||||
let proof = proof_info.as_account_mut::<Proof>(&ore_api::ID)?;
|
|
||||||
proof.authority = *signer_info.key;
|
|
||||||
proof.balance = 0;
|
|
||||||
proof.challenge = hashv(&[
|
|
||||||
signer_info.key.as_ref(),
|
|
||||||
&slot_hashes_info.data.borrow()[0..size_of::<SlotHash>()],
|
|
||||||
])
|
|
||||||
.0;
|
|
||||||
proof.last_hash = [0; 32];
|
|
||||||
proof.last_hash_at = clock.unix_timestamp;
|
|
||||||
proof.miner = *miner_info.key;
|
|
||||||
proof.total_hashes = 0;
|
|
||||||
proof.total_rewards = 0;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
62
program/src/payout.rs
Normal file
62
program/src/payout.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
use ore_api::prelude::*;
|
||||||
|
use solana_program::{keccak::hashv, slot_hashes::SlotHash};
|
||||||
|
use steel::*;
|
||||||
|
|
||||||
|
pub fn process_payout(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||||
|
// Load accounts.
|
||||||
|
let clock = Clock::get()?;
|
||||||
|
let [signer_info, block_info, wager_info, recipient_info, treasury_info, treasury_tokens_info, system_program, token_program, slot_hashes_sysvar] =
|
||||||
|
accounts
|
||||||
|
else {
|
||||||
|
return Err(ProgramError::NotEnoughAccountKeys);
|
||||||
|
};
|
||||||
|
signer_info.is_signer()?;
|
||||||
|
let block = block_info
|
||||||
|
.as_account_mut::<Block>(&ore_api::ID)?
|
||||||
|
.assert_mut(|b| b.ends_at < clock.slot)?
|
||||||
|
.assert_mut(|b| b.payed_out == 0)?;
|
||||||
|
let wager = wager_info.as_account::<Wager>(&ore_api::ID)?;
|
||||||
|
recipient_info.as_associated_token_account(&wager.authority, &MINT_ADDRESS)?;
|
||||||
|
treasury_info.has_address(&TREASURY_ADDRESS)?;
|
||||||
|
treasury_tokens_info
|
||||||
|
.has_address(&TREASURY_TOKENS_ADDRESS)?
|
||||||
|
.is_writable()?;
|
||||||
|
system_program.is_program(&system_program::ID)?;
|
||||||
|
token_program.is_program(&spl_token::ID)?;
|
||||||
|
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
|
||||||
|
|
||||||
|
// Select the slothash from the slot at when the round ended.
|
||||||
|
// The represents the server seed for the provably fair random number.
|
||||||
|
let offset = clock.slot - block.ends_at;
|
||||||
|
let size = size_of::<SlotHash>();
|
||||||
|
let i = offset as usize * size;
|
||||||
|
let slot_hash = &slot_hashes_sysvar.data.borrow()[i..i + size];
|
||||||
|
block.noise = hashv(&[&block.noise, slot_hash]).to_bytes();
|
||||||
|
|
||||||
|
// Calculate the random number.
|
||||||
|
let x = u64::from_le_bytes(block.noise[0..8].try_into().unwrap());
|
||||||
|
let y = u64::from_le_bytes(block.noise[8..16].try_into().unwrap());
|
||||||
|
let z = u64::from_le_bytes(block.noise[16..24].try_into().unwrap());
|
||||||
|
let w = u64::from_le_bytes(block.noise[24..32].try_into().unwrap());
|
||||||
|
let roll = (x ^ y ^ z ^ w) % block.total_bets;
|
||||||
|
|
||||||
|
// Assert that the wager account passed in is the winner.
|
||||||
|
assert!(roll >= wager.cumulative_bets && roll < wager.cumulative_bets + wager.amount);
|
||||||
|
|
||||||
|
// Mark the block as paid out.
|
||||||
|
block.payed_out = 1;
|
||||||
|
|
||||||
|
// Transfer the winnings to the recipient.
|
||||||
|
transfer_signed(
|
||||||
|
&treasury_info,
|
||||||
|
&treasury_tokens_info,
|
||||||
|
&recipient_info,
|
||||||
|
&token_program,
|
||||||
|
ONE_ORE / 2,
|
||||||
|
&[TREASURY],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,102 +1,72 @@
|
|||||||
use drillx::difficulty;
|
|
||||||
use ore_api::prelude::*;
|
use ore_api::prelude::*;
|
||||||
use ore_boost_api::state::Config as BoostConfig;
|
use ore_boost_api::{consts::DENOMINATOR_BPS, prelude::Config as BoostConfig};
|
||||||
use solana_program::{hash::hashv, slot_hashes::SlotHash};
|
|
||||||
use steel::*;
|
use steel::*;
|
||||||
|
|
||||||
/// Reset tops up the bus balances and updates the emissions and reward rates.
|
/// Reset tops up the bus balances and updates the emissions and reward rates.
|
||||||
pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||||
// Load accounts.
|
// Load accounts.
|
||||||
let clock = Clock::get()?;
|
let clock = Clock::get()?;
|
||||||
let (required_accounts, boost_accounts) = accounts.split_at(7);
|
let (required_accounts, boost_accounts) = accounts.split_at(6);
|
||||||
let [signer_info, config_info, mint_info, proof_info, treasury_info, treasury_tokens_info, token_program, slot_hashes_sysvar] =
|
let [signer_info, block_info, mint_info, treasury_info, treasury_tokens_info, token_program, slot_hashes_sysvar] =
|
||||||
required_accounts
|
required_accounts
|
||||||
else {
|
else {
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
return Err(ProgramError::NotEnoughAccountKeys);
|
||||||
};
|
};
|
||||||
signer_info.is_signer()?;
|
signer_info.is_signer()?;
|
||||||
let config = config_info
|
let block = block_info
|
||||||
.is_config()?
|
.as_account_mut::<Block>(&ore_api::ID)?
|
||||||
.as_account_mut::<Config>(&ore_api::ID)?;
|
.assert_mut(|b| b.ends_at < clock.slot)?
|
||||||
|
.assert_mut(|b| b.payed_out != 0)?;
|
||||||
let mint = mint_info
|
let mint = mint_info
|
||||||
.has_address(&MINT_ADDRESS)?
|
.has_address(&MINT_ADDRESS)?
|
||||||
.is_writable()?
|
.is_writable()?
|
||||||
.as_mint()?;
|
.as_mint()?;
|
||||||
let proof = proof_info
|
treasury_info.has_address(&TREASURY_ADDRESS)?;
|
||||||
.as_account_mut::<Proof>(&ore_api::ID)?
|
treasury_tokens_info.has_address(&TREASURY_TOKENS_ADDRESS)?;
|
||||||
.assert_mut(|p| p.authority == config.best_proof)?;
|
|
||||||
treasury_info.is_treasury()?.is_writable()?;
|
|
||||||
treasury_tokens_info.is_treasury_tokens()?.is_writable()?;
|
|
||||||
token_program.is_program(&spl_token::ID)?;
|
token_program.is_program(&spl_token::ID)?;
|
||||||
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
|
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
|
||||||
|
|
||||||
// Parse boost accounts.
|
|
||||||
let [boost_config_info, boost_proof_info] = boost_accounts else {
|
let [boost_config_info, boost_proof_info] = boost_accounts else {
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
return Err(ProgramError::NotEnoughAccountKeys);
|
||||||
};
|
};
|
||||||
let boost_config = boost_config_info.as_account::<BoostConfig>(&ore_api::ID)?;
|
let boost_config = boost_config_info.as_account::<BoostConfig>(&ore_boost_api::ID)?;
|
||||||
let boost_proof = boost_proof_info
|
let boost_proof = boost_proof_info
|
||||||
.as_account_mut::<Proof>(&ore_api::ID)?
|
.as_account_mut::<Proof>(&ore_api::ID)?
|
||||||
.assert_mut(|p| p.authority == *boost_config_info.key)?;
|
.assert_mut(|p| p.authority == *boost_config_info.key)?;
|
||||||
|
|
||||||
// Validate enough time has passed since the last reset.
|
// Payout to boosts.
|
||||||
if clock.unix_timestamp < config.last_reset_at + EPOCH_DURATION {
|
let net_emissions = get_target_emissions_rate(mint.supply());
|
||||||
return Ok(());
|
let boost_reward =
|
||||||
}
|
(net_emissions as u128 * boost_config.take_rate as u128 / DENOMINATOR_BPS as u128) as u64;
|
||||||
|
|
||||||
// Record difficulty.
|
|
||||||
let score = difficulty(config.best_hash) as u64;
|
|
||||||
|
|
||||||
// Reset the challenge.
|
|
||||||
config.challenge = hashv(&[
|
|
||||||
config.challenge.as_slice(),
|
|
||||||
&slot_hashes_sysvar.data.borrow()[0..size_of::<SlotHash>()],
|
|
||||||
])
|
|
||||||
.to_bytes();
|
|
||||||
|
|
||||||
// Reset the config.
|
|
||||||
let block_reward = get_block_reward(mint.supply());
|
|
||||||
config.block_reward = block_reward;
|
|
||||||
config.best_proof = Pubkey::default();
|
|
||||||
config.best_hash = [u8::MAX; 32];
|
|
||||||
config.last_reset_at = clock.unix_timestamp;
|
|
||||||
|
|
||||||
// Calculate boost reward.
|
|
||||||
let take_rate = boost_config.take_rate.min(9900); // Cap at 99%
|
|
||||||
let boost_reward = block_reward * take_rate / ore_boost_api::consts::DENOMINATOR_BPS;
|
|
||||||
let miner_reward = block_reward - boost_reward;
|
|
||||||
|
|
||||||
// Update proof balances.
|
|
||||||
proof.balance += miner_reward;
|
|
||||||
proof.total_rewards += miner_reward;
|
|
||||||
boost_proof.balance += boost_reward;
|
boost_proof.balance += boost_reward;
|
||||||
boost_proof.total_rewards += boost_reward;
|
boost_proof.total_rewards += boost_reward;
|
||||||
|
|
||||||
|
// Reset the block.
|
||||||
|
block.reward = net_emissions - boost_reward;
|
||||||
|
block.started_at = clock.slot;
|
||||||
|
block.ends_at = clock.slot + 150; // 60 seconds
|
||||||
|
block.payed_out = 0;
|
||||||
|
block.total_bets = 0;
|
||||||
|
block.bet_count = 0;
|
||||||
|
block.noise = [0; 32];
|
||||||
|
block.current_round += 1;
|
||||||
|
|
||||||
// Fund the treasury.
|
// Fund the treasury.
|
||||||
mint_to_signed(
|
mint_to_signed(
|
||||||
mint_info,
|
mint_info,
|
||||||
treasury_tokens_info,
|
treasury_tokens_info,
|
||||||
treasury_info,
|
treasury_info,
|
||||||
token_program,
|
token_program,
|
||||||
block_reward,
|
net_emissions,
|
||||||
&[TREASURY],
|
&[TREASURY],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Emit event.
|
|
||||||
BlockEvent {
|
|
||||||
score,
|
|
||||||
block_reward,
|
|
||||||
boost_reward,
|
|
||||||
ts: clock.unix_timestamp as u64,
|
|
||||||
}
|
|
||||||
.log_return();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function calculates the block reward (ORE / min) based on the current supply.
|
/// This function calculates the target emissions rate (ORE / min) based on the current supply.
|
||||||
/// It is designed to reduce emissions by 10% approximately every 12 months with a hard stop at 5 million ORE.
|
/// It is designed to reduce emissions by 10% approximately every 12 months with a hardcap at 5 million ORE.
|
||||||
pub(crate) fn get_block_reward(current_supply: u64) -> u64 {
|
pub(crate) fn get_target_emissions_rate(current_supply: u64) -> u64 {
|
||||||
match current_supply {
|
match current_supply {
|
||||||
n if n < ONE_ORE * 525_600 => 100_000_000_000, // Year ~1
|
n if n < ONE_ORE * 525_600 => 100_000_000_000, // Year ~1
|
||||||
n if n < ONE_ORE * 998_640 => 90_000_000_000, // Year ~2
|
n if n < ONE_ORE * 998_640 => 90_000_000_000, // Year ~2
|
||||||
@@ -126,52 +96,7 @@ pub(crate) fn get_block_reward(current_supply: u64) -> u64 {
|
|||||||
n if n < ONE_ORE * 4_916_405 => 7_178_979_874, // Year ~26
|
n if n < ONE_ORE * 4_916_405 => 7_178_979_874, // Year ~26
|
||||||
n if n < ONE_ORE * 4_950_365 => 6_461_081_886, // Year ~27
|
n if n < ONE_ORE * 4_950_365 => 6_461_081_886, // Year ~27
|
||||||
n if n < ONE_ORE * 4_980_928 => 5_814_973_607, // Year ~28
|
n if n < ONE_ORE * 4_980_928 => 5_814_973_607, // Year ~28
|
||||||
n if n < MAX_SUPPLY => 5_233_476_327.min(MAX_SUPPLY - current_supply), // Year ~29
|
n if n < ONE_ORE * 5_000_000 => 5_233_476_327, // Year ~29
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_reward_max_supply() {
|
|
||||||
let max_supply = ONE_ORE * 5_000_000;
|
|
||||||
|
|
||||||
// Test at max supply
|
|
||||||
assert_eq!(get_block_reward(max_supply), 0);
|
|
||||||
|
|
||||||
// Test slightly below max supply
|
|
||||||
let near_max = max_supply - 1;
|
|
||||||
assert_eq!(get_block_reward(near_max), 1);
|
|
||||||
|
|
||||||
// Test at max supply - 1000
|
|
||||||
let below_max = max_supply - 1000;
|
|
||||||
assert_eq!(get_block_reward(below_max), 1000);
|
|
||||||
|
|
||||||
// Test that reward never exceeds remaining supply
|
|
||||||
let supply_4_999_990 = ONE_ORE * 4_999_990;
|
|
||||||
assert!(get_block_reward(supply_4_999_990) <= max_supply - supply_4_999_990);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_reward_boundaries() {
|
|
||||||
// Test first tier boundary
|
|
||||||
let year1_supply = ONE_ORE * 525_599;
|
|
||||||
assert_eq!(get_block_reward(year1_supply), 100_000_000_000);
|
|
||||||
|
|
||||||
// Test middle tier boundary
|
|
||||||
let year15_supply = ONE_ORE * 4_173_835;
|
|
||||||
assert_eq!(get_block_reward(year15_supply), 22_876_792_454);
|
|
||||||
|
|
||||||
// Test last tier boundary before max supply logic
|
|
||||||
let last_tier_supply = ONE_ORE * 4_980_927;
|
|
||||||
assert_eq!(get_block_reward(last_tier_supply), 5_814_973_607);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_reward_zero_supply() {
|
|
||||||
assert_eq!(get_block_reward(0), 100_000_000_000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
use ore_api::prelude::*;
|
|
||||||
use steel::*;
|
|
||||||
|
|
||||||
/// Update changes the miner authority on a proof account.
|
|
||||||
pub fn process_update(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
|
||||||
// Load accounts.
|
|
||||||
let [signer_info, miner_info, proof_info] = accounts else {
|
|
||||||
return Err(ProgramError::NotEnoughAccountKeys);
|
|
||||||
};
|
|
||||||
signer_info.is_signer()?;
|
|
||||||
let proof = proof_info
|
|
||||||
.as_account_mut::<Proof>(&ore_api::ID)?
|
|
||||||
.assert_mut_err(
|
|
||||||
|p| p.authority == *signer_info.key,
|
|
||||||
ProgramError::MissingRequiredSignature,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Update the proof's miner authority.
|
|
||||||
proof.miner = *miner_info.key;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user