Skip to content

Commit 232c179

Browse files
committed
refactor and address all warnings
1 parent e9bc350 commit 232c179

File tree

10 files changed

+663
-625
lines changed

10 files changed

+663
-625
lines changed

src/api.rs

Lines changed: 30 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,25 @@
1-
// shadowharvester/src/api.rs
1+
// src/api.rs
22

3-
use serde::{Deserialize, Serialize};
4-
use reqwest;
53
use reqwest::blocking;
6-
use serde_json;
74

8-
// FIX: Import MOCK constants from the new module
9-
use crate::constants::USER_AGENT;
5+
// FIX: Import structs from the new module location
6+
use crate::data_types::{
7+
TandCResponse, RegistrationReceipt, ChallengeData, ChallengeResponse,
8+
SolutionReceipt, DonateResponse, Statistics, StatisticsApiResponse, CliChallengeData, ApiErrorResponse
9+
};
1010

11-
// --- RESPONSE STRUCTS ---
12-
13-
#[derive(Debug, Deserialize)]
14-
pub struct TandCResponse {
15-
pub version: String,
16-
pub content: String,
17-
pub message: String,
18-
}
19-
20-
#[derive(Debug, Deserialize)]
21-
pub struct RegistrationReceipt {
22-
#[serde(rename = "registrationReceipt")]
23-
pub registration_receipt: serde_json::Value,
24-
}
25-
26-
#[derive(Debug, Deserialize, Serialize, Clone)]
27-
pub struct ChallengeData {
28-
pub challenge_id: String,
29-
pub difficulty: String,
30-
#[serde(rename = "no_pre_mine")]
31-
pub no_pre_mine_key: String,
32-
#[serde(rename = "no_pre_mine_hour")]
33-
pub no_pre_mine_hour_str: String,
34-
pub latest_submission: String,
35-
// NEW: Fields for listing command
36-
pub challenge_number: u16,
37-
pub day: u8,
38-
pub issued_at: String,
39-
}
40-
41-
#[derive(Debug, Deserialize)]
42-
pub struct ChallengeResponse { // Made struct public for use in main.rs
43-
pub code: String,
44-
pub challenge: Option<ChallengeData>,
45-
pub starts_at: Option<String>,
46-
// NEW: Fields for listing command (overall status)
47-
pub mining_period_ends: Option<String>,
48-
pub max_day: Option<u8>,
49-
pub total_challenges: Option<u16>,
50-
pub current_day: Option<u8>,
51-
pub next_challenge_starts_at: Option<String>,
52-
}
53-
54-
#[derive(Debug, Deserialize, PartialEq)]
55-
pub struct SolutionReceipt {
56-
#[serde(rename = "crypto_receipt")]
57-
pub crypto_receipt: serde_json::Value,
58-
}
59-
60-
#[derive(Debug, Deserialize, PartialEq)]
61-
pub struct DonateResponse {
62-
pub status: String,
63-
#[serde(rename = "donation_id")]
64-
pub donation_id: String,
65-
}
66-
67-
68-
#[derive(Debug, Deserialize)]
69-
struct ApiErrorResponse {
70-
pub message: String,
71-
pub error: Option<String>,
72-
pub statusCode: Option<u16>,
73-
}
11+
// --- API FUNCTIONS ---
7412

75-
#[derive(Debug, Deserialize)]
76-
pub struct GlobalStatistics {
77-
pub wallets: u32,
78-
pub challenges: u16,
79-
#[serde(rename = "total_challenges")]
80-
pub total_challenges: u16,
81-
#[serde(rename = "total_crypto_receipts")]
82-
pub total_crypto_receipts: u32,
83-
#[serde(rename = "recent_crypto_receipts")]
84-
pub recent_crypto_receipts: u32,
85-
}
13+
/// Fetches the T&C from the API, returning the full response object.
14+
pub fn fetch_tandc(client: &blocking::Client, api_url: &str) -> Result<TandCResponse, reqwest::Error> {
15+
let url = format!("{}/TandC/1-0", api_url);
16+
println!("-> Fetching Terms and Conditions from: {}", url);
8617

87-
// NEW: Struct for the statistics under the "local" key
88-
#[derive(Debug, Deserialize)]
89-
pub struct LocalStatistics {
90-
pub crypto_receipts: u32,
91-
pub night_allocation: u32,
92-
}
18+
let response = client.get(url).send()?;
9319

94-
// NEW: Struct representing the entire JSON response from the /statistics/:address endpoint
95-
#[derive(Debug, Deserialize)]
96-
pub struct StatisticsApiResponse {
97-
pub global: GlobalStatistics,
98-
pub local: LocalStatistics,
99-
}
20+
let response = response.error_for_status()?;
10021

101-
#[derive(Debug)]
102-
pub struct Statistics {
103-
// Local Address (Added by the client)
104-
pub local_address: String,
105-
// Global fields
106-
pub wallets: u32,
107-
pub challenges: u16,
108-
pub total_challenges: u16,
109-
pub total_crypto_receipts: u32,
110-
pub recent_crypto_receipts: u32,
111-
// Local fields
112-
pub crypto_receipts: u32,
113-
pub night_allocation: u32,
114-
}
115-
// NEW: Struct for the challenge parameters provided via CLI
116-
#[derive(Debug, Clone)]
117-
pub struct CliChallengeData {
118-
pub challenge_id: String,
119-
pub no_pre_mine_key: String,
120-
pub difficulty: String,
121-
pub no_pre_mine_hour_str: String,
122-
pub latest_submission: String,
22+
response.json()
12323
}
12424

12525
// NEW FUNCTION: Parses the comma-separated CLI challenge string
@@ -143,20 +43,6 @@ pub fn parse_cli_challenge_string(challenge_str: &str) -> Result<CliChallengeDat
14343
}
14444

14545

146-
// --- API FUNCTIONS ---
147-
148-
/// Fetches the T&C from the API, returning the full response object.
149-
pub fn fetch_tandc(client: &blocking::Client, api_url: &str) -> Result<TandCResponse, reqwest::Error> {
150-
let url = format!("{}/TandC/1-0", api_url);
151-
println!("-> Fetching Terms and Conditions from: {}", url);
152-
153-
let response = client.get(url).send()?;
154-
155-
let response = response.error_for_status()?;
156-
157-
response.json()
158-
}
159-
16046
/// Performs the POST /register call using key/signature arguments.
16147
// RENAMED from register_address_mock
16248
pub fn register_address(
@@ -191,34 +77,17 @@ pub fn register_address(
19177
Ok(())
19278
}
19379

194-
/// Fetches the current challenge parameters from the API.
195-
pub fn fetch_challenge(client: &blocking::Client, api_url: &str) -> Result<ChallengeData, String> {
196-
let url = format!("{}/challenge", api_url);
197-
println!("-> Fetching current challenge from: {}", url);
198-
199-
let response = client.get(url).send().map_err(|e| format!("API request failed: {}", e))?;
80+
/// Helper to format a detailed error message from the API response body.
81+
fn format_detailed_api_error(err: ApiErrorResponse, status: reqwest::StatusCode) -> String {
82+
let mut msg = format!("(Status {}) {}", status.as_u16(), err.message);
20083

201-
if !response.status().is_success() {
202-
return Err(format!("Challenge API returned non-success status: {}", response.status()));
84+
if let Some(e) = err.error {
85+
msg.push_str(&format!(" [Type: {}]", e));
20386
}
204-
205-
let challenge_response: ChallengeResponse = response.json().map_err(|e| format!("JSON parsing failed: {}", e))?;
206-
207-
match challenge_response.code.as_str() {
208-
"active" => {
209-
Ok(challenge_response.challenge.unwrap())
210-
}
211-
"before" => {
212-
let start_time = challenge_response.starts_at.unwrap_or_default();
213-
Err(format!("MINING IS NOT YET ACTIVE. Starts at: {}", start_time))
214-
}
215-
"after" => {
216-
Err("MINING PERIOD HAS ENDED.".to_string())
217-
}
218-
_ => {
219-
Err(format!("Received unexpected challenge code: {}", challenge_response.code))
220-
}
87+
if let Some(code) = err.status_code {
88+
msg.push_str(&format!(" [API Code: {}]", code));
22189
}
90+
msg
22291
}
22392

22493
/// Performs the POST /solution call.
@@ -258,8 +127,8 @@ pub fn submit_solution(
258127

259128
match api_error {
260129
Ok(err) => {
261-
// API returned a structured error message (e.g., Not registered, Invalid difficulty)
262-
Err(format!("API Validation Failed (Status {}): {}", status.as_u16(), err.message))
130+
// FIX: Use all error fields for detailed reporting
131+
Err(format!("API Validation Failed: {}", format_detailed_api_error(err, status)))
263132
}
264133
Err(_) => {
265134
// API returned a non-structured error (e.g., plain text or unreadable JSON)
@@ -288,7 +157,6 @@ pub fn donate_to(
288157
);
289158

290159
println!("-> Donating funds from {} to {}", original_address, destination_address);
291-
println!("url: {}", &url);
292160

293161
let response = client
294162
.post(&url)
@@ -308,7 +176,8 @@ pub fn donate_to(
308176

309177
match api_error {
310178
Ok(err) => {
311-
Err(format!("Donation Failed (Status {}): {}", status.as_u16(), err.message))
179+
// FIX: Use all error fields for detailed reporting
180+
Err(format!("Donation Failed: {}", format_detailed_api_error(err, status)))
312181
}
313182
Err(_) => {
314183
Err(format!("HTTP Error {} with unparseable body: {}", status.as_u16(), body_text))
@@ -335,7 +204,7 @@ pub fn fetch_challenge_status(client: &blocking::Client, api_url: &str) -> Resul
335204
/// Fetches and validates the active challenge parameters, returning data only if active.
336205
// RENAMED from fetch_challenge
337206
pub fn get_active_challenge_data(client: &blocking::Client, api_url: &str) -> Result<ChallengeData, String> {
338-
let challenge_response = fetch_challenge_status(&client, api_url)?;
207+
let challenge_response = fetch_challenge_status(client, api_url)?;
339208

340209
match challenge_response.code.as_str() {
341210
"active" => {
@@ -378,19 +247,19 @@ pub fn fetch_statistics(client: &blocking::Client, api_url: &str, address: &str)
378247
wallets: api_data.global.wallets,
379248
challenges: api_data.global.challenges,
380249
total_challenges: api_data.global.total_challenges,
381-
total_crypto_receipts: api_data.global.total_crypto_receipts,
382250
recent_crypto_receipts: api_data.global.recent_crypto_receipts,
251+
total_crypto_receipts: api_data.global.total_crypto_receipts,
383252
crypto_receipts: api_data.local.crypto_receipts,
384253
night_allocation: api_data.local.night_allocation,
385254
})
386255
} else {
387256
let body_text = response.text().unwrap_or_else(|_| format!("(Could not read response body for status {})", status));
388-
// ... (error handling omitted)
389257
let api_error: Result<ApiErrorResponse, _> = serde_json::from_str(&body_text);
390258

391259
match api_error {
392260
Ok(err) => {
393-
Err(format!("API Error (Status {}): {}", status.as_u16(), err.message))
261+
// FIX: Use all error fields for detailed reporting
262+
Err(format!("API Error: {}", format_detailed_api_error(err, status)))
394263
}
395264
Err(_) => {
396265
Err(format!("HTTP Error {} with unparseable body: {}", status.as_u16(), body_text))

src/cardano.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use cryptoxide::{hmac::Hmac, pbkdf2::pbkdf2, sha2::Sha512};
1111
use minicbor::*;
1212

1313
use rand_core::{OsRng};
14-
use bip39::{Language, Mnemonic};
15-
use ed25519_bip32::{self, XPrv, XPub, XPRV_SIZE};
14+
use bip39::Mnemonic;
15+
use ed25519_bip32::{self, XPrv, XPRV_SIZE};
1616

1717
pub enum FlexibleSecretKey {
1818
Standard(SecretKey),
@@ -128,7 +128,7 @@ pub struct CoseSignData<'a> {
128128
pub payload: &'a [u8],
129129
}
130130

131-
impl<'a, C> Encode<C> for CoseSignData<'a>
131+
impl<C> Encode<C> for CoseSignData<'_>
132132
where
133133
C: Default,
134134
{
@@ -150,11 +150,11 @@ pub struct CoseSign1<'a> {
150150
pub signature: &'a [u8],
151151
}
152152

153-
impl<'a, C> Encode<C> for CoseSign1<'a>
153+
impl<C> Encode<C> for CoseSign1<'_>
154154
where
155155
C: Default,
156156
{
157-
fn encode<W: encode::Write>(&self, e: &mut Encoder<W>, ctx: &mut C) -> Result<(), encode::Error<W::Error>> {
157+
fn encode<W: encode::Write>(&self, e: &mut Encoder<W>, _ctx: &mut C) -> Result<(), encode::Error<W::Error>> {
158158
e.array(4)?;
159159
e.bytes(self.protected_header)?;
160160
e.map(1)?;
@@ -176,7 +176,7 @@ where
176176
/// but the signature and public key components are guaranteed to be unique.
177177
pub fn cip8_sign(kp: &KeyPairAndAddress, message: &str) -> (String, String) {
178178

179-
let pubkey = hex::encode(&kp.1.as_ref());
179+
let pubkey = hex::encode(kp.1.as_ref());
180180
let prot_header = CoseProtHeader {
181181
address: kp.2.to_vec(),
182182
};
@@ -185,7 +185,7 @@ pub fn cip8_sign(kp: &KeyPairAndAddress, message: &str) -> (String, String) {
185185
label: "Signature1",
186186
protected_header: &cose_prot_cbor,
187187
external_aad: b"" ,
188-
payload: &message.as_bytes(),
188+
payload: message.as_bytes(),
189189
};
190190
let to_sign_cbor = pallas::codec::minicbor::to_vec(&to_sign).unwrap();
191191
// The specific error type the compiler couldn't figure out. Let's use &'static str
@@ -207,8 +207,8 @@ pub fn cip8_sign(kp: &KeyPairAndAddress, message: &str) -> (String, String) {
207207

208208
let cose_struct = CoseSign1 {
209209
protected_header: &cose_prot_cbor,
210-
payload: &message.as_bytes(),
211-
signature: &sig.as_ref(),
210+
payload: message.as_bytes(),
211+
signature: sig.as_ref(),
212212
};
213213
let cose_sign1_cbor = pallas::codec::minicbor::to_vec(&cose_struct).unwrap();
214214

src/cli.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// shadowharvester/src/cli.rs
1+
// src/cli.rs
22

33
use clap::{Parser, Subcommand};
44

@@ -24,15 +24,19 @@ pub struct Cli {
2424
#[arg(long, default_value_t = 24)]
2525
pub threads: u32,
2626

27-
/// NEW: Optional secret key (hex-encoded) to mine with. If passed, only solves once.
27+
/// Optional secret key (hex-encoded) to mine with.
2828
#[arg(long)]
2929
pub payment_key: Option<String>,
3030

31-
/// NEW: Cardano address (bech32) to donate all accumulated rewards to.
31+
/// NEW: Automatically generate a new ephemeral key pair for every mining cycle.
32+
#[arg(long)]
33+
pub ephemeral_key: bool,
34+
35+
/// Cardano address (bech32) to donate all accumulated rewards to.
3236
#[arg(long)]
3337
pub donate_to: Option<String>,
3438

35-
/// NEW: 24-word BIP39 mnemonic phrase for sequential address generation.
39+
/// 24-word BIP39 mnemonic phrase for sequential address generation.
3640
#[arg(long)]
3741
pub mnemonic: Option<String>,
3842

@@ -56,7 +60,7 @@ pub struct Cli {
5660

5761
#[derive(Subcommand, Debug, Clone)]
5862
pub enum Commands {
59-
/// NEW: Lists the current status and details of the mining challenge.
63+
/// Lists the current status and details of the mining challenge.
6064
#[command(author, about = "List current challenge status")]
6165
Challenges,
6266
}

src/constants.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
11
// shadowharvester/src/constants.rs
22

3-
/// Placeholder Ed25519 Public Key (64-character hex string) for mock registration.
4-
pub const MOCK_PUBKEY: &str = "009236f53f5fbb1056defb64d623f7508bc1b61822016d43faf62070f1489ff5";
5-
6-
/// Placeholder CIP-30 Signature (long CBOR hex string) for mock registration.
7-
/// This signature must be generated over the official T&C message.
8-
pub const MOCK_SIGNATURE: &str = "845882a30127045839001c2e057143337716055394074256b79df7fc36051802ccefc1b2d3bf2a77372b1cff16a2cfe1e9a634bfaae3c74e8c8188e6043f572295f067616464726573735839001c2e057143337716055394074256b79df7fc36051802ccefc1b2d3bf2a77372b1cff16a2cfe1e9a634bfaae3c74e8c8188e6043f572295f0a166686173686564f458b34920616772656520746f20616269646520627920746865207465726d7320616e6420636f6e646974696f6e732061732064657363726962656420696e2076657273696f6e20312d30206f6620746865204d69646e696768742073636176656e676572206d96e69676874";
9-
10-
/// Placeholder CIP-30 Signature for the donation message.
11-
pub const DONATE_MESSAGE_SIG: &str = "845882a3012704583900d91c05fb2f3c47e7fbb3c657775d9d189daaa468683ff7852c8ce674cc91d284f1635432c18613b194bd1900db1bf11e4bb9fb40dc0a70da6761646472657373583900d91c05fb2f3c47e7fbb3c657775d9d189daaa468683ff7852c8ce674cc91d284f1635432c18613b194bd1900db1bf11e4bb9fb40dc0a70daa166686173686564f4589441737369676e20616363756d756c617465642053636176656e6765722072696768747320746f3a20616464725f7465737431717134646c336e6872306178757267637270756e39787970303470643272326477753578376565616d3938707376366468786c6465387563636c7632703436686d303737647334767a656c66353536356667336b79373934756872713575703068655840514b869f06b1d86b61781291bef81753b118cf9f11a13ee4824ff151cda4529c1580ab465c1aae5153bc18d94de71978221e6d714dd2329634e5733946c39103";
12-
133
// UserAgent String
144
pub const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36";

0 commit comments

Comments
 (0)