diff --git a/Cargo.toml b/Cargo.toml index 1038238..4d231e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ description = "Digial gold, unchained." documentation = "https://docs.rs/ore-api/latest/ore_api/" repository = "https://github.com/regolith-labs/ore" readme = "./README.md" -keywords = ["solana", "crypto", "mining"] +keywords = ["solana"] [workspace.dependencies] anyhow = "1.0" diff --git a/api/src/instruction.rs b/api/src/instruction.rs index e4891ea..e240062 100644 --- a/api/src/instruction.rs +++ b/api/src/instruction.rs @@ -19,6 +19,9 @@ pub enum OreInstruction { SetFeeCollector = 10, SetFeeRate = 11, SetSniperFeeDuration = 12, + + // Migration + MigrateMinerAccount = 13, } #[repr(C)] @@ -100,6 +103,12 @@ pub struct SetSniperFeeDuration { pub sniper_fee_duration: [u8; 8], } +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct MigrateMinerAccount { + pub authority: [u8; 32], +} + instruction!(OreInstruction, Claim); instruction!(OreInstruction, Open); instruction!(OreInstruction, Close); @@ -113,3 +122,4 @@ instruction!(OreInstruction, SetBlockDuration); instruction!(OreInstruction, SetFeeCollector); instruction!(OreInstruction, SetFeeRate); instruction!(OreInstruction, SetSniperFeeDuration); +instruction!(OreInstruction, MigrateMinerAccount); diff --git a/api/src/sdk.rs b/api/src/sdk.rs index 66b8526..82d1d03 100644 --- a/api/src/sdk.rs +++ b/api/src/sdk.rs @@ -287,3 +287,20 @@ pub fn set_sniper_fee_duration(signer: Pubkey, sniper_fee_duration: u64) -> Inst .to_bytes(), } } + +pub fn migrate_miner_account(signer: Pubkey, miner: Pubkey) -> Instruction { + let config_address = config_pda().0; + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(config_address, false), + AccountMeta::new(miner, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + data: MigrateMinerAccount { + authority: miner.to_bytes(), + } + .to_bytes(), + } +} diff --git a/api/src/state/miner.rs b/api/src/state/miner.rs index 5d790de..adfb53f 100644 --- a/api/src/state/miner.rs +++ b/api/src/state/miner.rs @@ -1,9 +1,31 @@ use steel::*; -use crate::state::miner_pda; +use crate::state::{miner_pda, OreAccountOLD}; use super::OreAccount; +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] +pub struct MinerOLD { + /// The authority of this miner account. + pub authority: Pubkey, + + /// The ID of the last block this miner mined in. + pub block_id: u64, + + /// The amount of hashpower this miner has committed to the current block. + pub hashpower: u64, + + /// A user-supplied seed for random number generation. + pub seed: [u8; 32], + + /// The total amount of hashpower this miner has committed across all blocks. + pub total_hashpower: u64, + + /// The total amount of ORE this miner has mined across all blocks. + pub total_rewards: u64, +} + #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Miner { @@ -13,6 +35,9 @@ pub struct Miner { /// The ID of the last block this miner mined in. pub block_id: u64, + /// An account authorized to execute actions on behalf of this miner. + pub executor: Pubkey, + /// The amount of hashpower this miner has committed to the current block. pub hashpower: u64, @@ -33,3 +58,4 @@ impl Miner { } account!(OreAccount, Miner); +account!(OreAccountOLD, MinerOLD); diff --git a/api/src/state/mod.rs b/api/src/state/mod.rs index 7b25496..d8e9a81 100644 --- a/api/src/state/mod.rs +++ b/api/src/state/mod.rs @@ -24,6 +24,12 @@ pub enum OreAccount { Treasury = 104, } +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] +pub enum OreAccountOLD { + MinerOLD = 103, +} + pub fn block_pda(id: u64) -> (Pubkey, u8) { Pubkey::find_program_address(&[BLOCK, &id.to_le_bytes()], &crate::ID) } diff --git a/cli/src/main.rs b/cli/src/main.rs index 834b69e..081fb14 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -81,6 +81,9 @@ async fn main() { "set_fee_collector" => { set_fee_collector(&rpc, &payer).await.unwrap(); } + "migrate" => { + migrate(&rpc, &payer).await.unwrap(); + } "benchmark" => { benchmark_keccak().await.unwrap(); } @@ -241,6 +244,36 @@ async fn set_fee_collector( Ok(()) } +async fn migrate( + rpc: &RpcClient, + payer: &solana_sdk::signer::keypair::Keypair, +) -> Result<(), anyhow::Error> { + let address = std::env::var("ADDRESS").expect("Missing ADDRESS env var"); + let address = Pubkey::from_str(&address).expect("Invalid ADDRESS"); + let ix = ore_api::sdk::migrate_miner_account(payer.pubkey(), address); + simulate_transaction(rpc, payer, &[ix]).await; + Ok(()) +} + +async fn migrate_all( + rpc: &RpcClient, + payer: &solana_sdk::signer::keypair::Keypair, +) -> Result<(), anyhow::Error> { + let old_miners = get_old_miners(rpc).await?; + println!("Found {} old miners", old_miners.len()); + for (i, (miner_address, _)) in old_miners.iter().enumerate() { + println!( + "[{} / {}] Migrating miner {}", + i, + old_miners.len(), + miner_address + ); + let ix = ore_api::sdk::migrate_miner_account(payer.pubkey(), *miner_address); + simulate_transaction(rpc, payer, &[ix]).await; + } + Ok(()) +} + async fn log_treasury(_rpc: &RpcClient) -> Result<(), anyhow::Error> { let treasury_address = ore_api::state::treasury_pda().0; println!("Treasury"); @@ -367,10 +400,10 @@ async fn get_market(rpc: &RpcClient) -> Result { Ok(*market) } -async fn get_miner(rpc: &RpcClient, authority: Pubkey) -> Result { +async fn get_miner(rpc: &RpcClient, authority: Pubkey) -> Result { let miner_pda = ore_api::state::miner_pda(authority); let account = rpc.get_account(&miner_pda.0).await?; - let miner = Miner::try_from_bytes(&account.data)?; + let miner = MinerOLD::try_from_bytes(&account.data)?; Ok(*miner) } @@ -385,7 +418,18 @@ async fn get_blocks(rpc: &RpcClient) -> Result, anyhow::Err Ok(blocks) } -async fn _simulate_transaction( +async fn get_old_miners(rpc: &RpcClient) -> Result, anyhow::Error> { + let miners = get_program_accounts::(rpc, ore_api::ID, vec![]).await?; + Ok(miners) +} + +async fn get_miners(rpc: &RpcClient) -> Result, anyhow::Error> { + let miners = get_program_accounts::(rpc, ore_api::ID, vec![]).await?; + Ok(miners) +} + +#[allow(dead_code)] +async fn simulate_transaction( rpc: &RpcClient, payer: &solana_sdk::signer::keypair::Keypair, instructions: &[solana_sdk::instruction::Instruction], diff --git a/program/src/lib.rs b/program/src/lib.rs index 7279005..31a2352 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -2,6 +2,7 @@ mod claim; mod close; mod initialize; mod log; +mod migrate_miner_account; mod mine; mod open; mod reset; @@ -17,6 +18,7 @@ use claim::*; use close::*; use initialize::*; use log::*; +use migrate_miner_account::*; use mine::*; use open::*; use reset::*; @@ -54,6 +56,9 @@ pub fn process_instruction( OreInstruction::SetFeeCollector => process_set_fee_collector(accounts, data)?, OreInstruction::SetFeeRate => process_set_fee_rate(accounts, data)?, OreInstruction::SetSniperFeeDuration => process_set_sniper_fee_duration(accounts, data)?, + + // Migration + OreInstruction::MigrateMinerAccount => process_migrate_miner_account(accounts, data)?, } Ok(()) diff --git a/program/src/migrate_miner_account.rs b/program/src/migrate_miner_account.rs new file mode 100644 index 0000000..ef8d769 --- /dev/null +++ b/program/src/migrate_miner_account.rs @@ -0,0 +1,42 @@ +use std::mem::size_of; + +use ore_api::prelude::*; +use solana_program::log::sol_log; +use steel::*; + +/// Migrates a miner account from the old format to the new format. +pub fn process_migrate_miner_account(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { + // Load accounts. + let [signer_info, config_info, miner_info, system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + signer_info.is_signer()?; + config_info + .as_account_mut::(&ore_api::ID)? + .assert_mut(|c| c.admin == *signer_info.key)?; + let miner = miner_info.as_account_mut::(&ore_api::ID)?; + system_program.is_program(&system_program::ID)?; + + // Copy old data. + let authority = miner.authority; + let block_id = miner.block_id; + let hashpower = miner.hashpower; + let seed = miner.seed; + let total_hashpower = miner.total_hashpower; + let total_rewards = miner.total_rewards; + + // Reallocate new account. + miner_info.realloc(8 + size_of::(), false)?; + + // Copy new data. + let miner = miner_info.as_account_mut::(&ore_api::ID)?; + miner.authority = authority; + miner.block_id = block_id; + miner.executor = Pubkey::default(); + miner.hashpower = hashpower; + miner.seed = seed; + miner.total_hashpower = total_hashpower; + miner.total_rewards = total_rewards; + + Ok(()) +}