Skip to main content

miner_api/
sdk.rs

1//! Instruction builders for clients and tests.
2
3use solana_program::{
4    instruction::{AccountMeta, Instruction},
5    pubkey::Pubkey,
6};
7
8use crate::{consts::*, instruction::MinerInstruction, pda};
9
10pub fn initialize(admin: Pubkey, mint: Pubkey) -> Instruction {
11    let (config, _) = pda::config_pda();
12    let (round0, _) = pda::round_pda(0);
13    Instruction {
14        program_id: crate::id(),
15        accounts: vec![
16            AccountMeta::new(admin, true),
17            AccountMeta::new(config, false),
18            AccountMeta::new_readonly(mint, false),
19            AccountMeta::new(round0, false),
20            AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
21        ],
22        data: vec![MinerInstruction::Initialize as u8],
23    }
24}
25
26pub fn register(authority: Pubkey) -> Instruction {
27    let (miner, _) = pda::miner_pda(&authority);
28    Instruction {
29        program_id: crate::id(),
30        accounts: vec![
31            AccountMeta::new(authority, true),
32            AccountMeta::new(miner, false),
33            AccountMeta::new_readonly(SLOT_HASHES_SYSVAR_ID, false),
34            AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
35        ],
36        data: vec![MinerInstruction::Register as u8],
37    }
38}
39
40pub fn authorize_session(authority: Pubkey, session_key: Pubkey) -> Instruction {
41    let (miner, _) = pda::miner_pda(&authority);
42    let mut data = vec![MinerInstruction::AuthorizeSession as u8];
43    data.extend_from_slice(session_key.as_ref());
44    Instruction {
45        program_id: crate::id(),
46        accounts: vec![
47            AccountMeta::new_readonly(authority, true),
48            AccountMeta::new(miner, false),
49        ],
50        data,
51    }
52}
53
54pub fn mine(
55    signer: Pubkey,
56    authority: Pubkey,
57    mint: Pubkey,
58    current_round_index: u64,
59    prev_round_index: u64,
60    nonce: u64,
61) -> Instruction {
62    let (miner, _) = pda::miner_pda(&authority);
63    let (config, _) = pda::config_pda();
64    let (current_round, _) = pda::round_pda(current_round_index);
65    let (prev_round, _) = pda::round_pda(prev_round_index);
66    let token_account = pda::ata(&authority, &mint);
67    let mut data = vec![MinerInstruction::Mine as u8];
68    data.extend_from_slice(&nonce.to_le_bytes());
69    Instruction {
70        program_id: crate::id(),
71        accounts: vec![
72            AccountMeta::new_readonly(signer, true),
73            AccountMeta::new(miner, false),
74            AccountMeta::new_readonly(config, false),
75            AccountMeta::new(current_round, false),
76            AccountMeta::new_readonly(prev_round, false),
77            AccountMeta::new_readonly(token_account, false),
78            AccountMeta::new_readonly(SLOT_HASHES_SYSVAR_ID, false),
79        ],
80        data,
81    }
82}
83
84pub fn claim(authority: Pubkey, mint: Pubkey, prev_round_index: u64) -> Instruction {
85    let (miner, _) = pda::miner_pda(&authority);
86    let (config, _) = pda::config_pda();
87    let (prev_round, _) = pda::round_pda(prev_round_index);
88    let (treasury, _) = pda::treasury_pda();
89    let token_account = pda::ata(&authority, &mint);
90    Instruction {
91        program_id: crate::id(),
92        accounts: vec![
93            AccountMeta::new_readonly(authority, true),
94            AccountMeta::new(miner, false),
95            AccountMeta::new_readonly(config, false),
96            AccountMeta::new_readonly(prev_round, false),
97            AccountMeta::new(mint, false),
98            AccountMeta::new_readonly(treasury, false),
99            AccountMeta::new(token_account, false),
100            AccountMeta::new_readonly(SPL_TOKEN_PROGRAM_ID, false),
101        ],
102        data: vec![MinerInstruction::Claim as u8],
103    }
104}
105
106pub fn crank(payer: Pubkey, new_round_index: u64) -> Instruction {
107    let (config, _) = pda::config_pda();
108    let (new_round, _) = pda::round_pda(new_round_index);
109    Instruction {
110        program_id: crate::id(),
111        accounts: vec![
112            AccountMeta::new(payer, true),
113            AccountMeta::new(config, false),
114            AccountMeta::new(new_round, false),
115            AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
116        ],
117        data: vec![MinerInstruction::Crank as u8],
118    }
119}
120
121pub fn close_round(recipient: Pubkey, round_index: u64) -> Instruction {
122    let (config, _) = pda::config_pda();
123    let (round, _) = pda::round_pda(round_index);
124    Instruction {
125        program_id: crate::id(),
126        accounts: vec![
127            AccountMeta::new(recipient, true),
128            AccountMeta::new_readonly(config, false),
129            AccountMeta::new(round, false),
130        ],
131        data: vec![MinerInstruction::CloseRound as u8],
132    }
133}
134
135pub fn update_config(
136    admin: Pubkey,
137    min_difficulty: u64,
138    base_weight: u64,
139    round_seconds: u64,
140) -> Instruction {
141    let (config, _) = pda::config_pda();
142    let mut data = vec![MinerInstruction::UpdateConfig as u8];
143    data.extend_from_slice(&min_difficulty.to_le_bytes());
144    data.extend_from_slice(&base_weight.to_le_bytes());
145    data.extend_from_slice(&round_seconds.to_le_bytes());
146    Instruction {
147        program_id: crate::id(),
148        accounts: vec![
149            AccountMeta::new_readonly(admin, true),
150            AccountMeta::new(config, false),
151        ],
152        data,
153    }
154}
155
156pub fn set_admin(admin: Pubkey, new_admin: Pubkey) -> Instruction {
157    let (config, _) = pda::config_pda();
158    let mut data = vec![MinerInstruction::SetAdmin as u8];
159    data.extend_from_slice(new_admin.as_ref());
160    Instruction {
161        program_id: crate::id(),
162        accounts: vec![
163            AccountMeta::new_readonly(admin, true),
164            AccountMeta::new(config, false),
165        ],
166        data,
167    }
168}
169
170/// Client-side hash verification (identical logic to the on-chain program).
171pub fn hash_meets_difficulty(
172    challenge: &[u8; 32],
173    authority: &Pubkey,
174    nonce: u64,
175    min_difficulty: u64,
176) -> bool {
177    let hash = solana_program::keccak::hashv(&[
178        challenge.as_slice(),
179        authority.as_ref(),
180        &nonce.to_le_bytes(),
181    ]);
182    leading_zero_bits(hash.as_ref()) >= min_difficulty
183}
184
185pub fn leading_zero_bits(bytes: &[u8]) -> u64 {
186    let mut bits: u64 = 0;
187    for b in bytes {
188        if *b == 0 {
189            bits += 8;
190        } else {
191            bits += b.leading_zeros() as u64;
192            break;
193        }
194    }
195    bits
196}