error handling fixes

This commit is contained in:
viraladmin 2026-06-02 17:05:48 -06:00
parent 88b043cf41
commit 7cd421e142
9 changed files with 105 additions and 55 deletions

View File

@ -67,8 +67,47 @@ async fn main() {
};
let receiver = match normalize_short_address_input(&receiver_input) {
Ok(address) => address,
Err(_) => {
println!("reciver wallet is not valid");
Err(err) => {
let trimmed_receiver = receiver_input.trim();
let escaped_receiver = receiver_input
.chars()
.flat_map(char::escape_default)
.collect::<String>();
let escaped_trimmed_receiver = trimmed_receiver
.chars()
.flat_map(char::escape_default)
.collect::<String>();
let dot_count = trimmed_receiver.matches('.').count();
let (payload_chars, payload_bytes, suffix, payload_is_hex, payload_is_ascii) =
match trimmed_receiver.rsplit_once('.') {
Some((payload, suffix)) => (
payload.chars().count(),
payload.len(),
suffix,
payload.chars().all(|ch| ch.is_ascii_hexdigit()),
payload.is_ascii(),
),
None => (0, 0, "<missing>", false, trimmed_receiver.is_ascii()),
};
println!("receiver wallet is not valid: {err}");
println!(
"receiver diagnostics: raw_chars={} raw_bytes={} trimmed_chars={} trimmed_bytes={} dot_count={} suffix={} payload_chars={} payload_bytes={} payload_hex={} payload_ascii={} raw_ascii={} raw_control_chars={}",
receiver_input.chars().count(),
receiver_input.len(),
trimmed_receiver.chars().count(),
trimmed_receiver.len(),
dot_count,
suffix,
payload_chars,
payload_bytes,
payload_is_hex,
payload_is_ascii,
receiver_input.is_ascii(),
receiver_input.chars().any(char::is_control)
);
println!("receiver escaped raw: {escaped_receiver}");
println!("receiver escaped trimmed: {escaped_trimmed_receiver}");
return;
}
};

View File

@ -1,6 +1,6 @@
use crate::common::binary_conversions::binary_to_string;
use crate::log::{error, info, warn};
use crate::miner::flag::begin_reorg_lock;
use crate::miner::flag::{begin_reorg_lock, is_syncing_mode};
use crate::orphans::structs::UndoTransactions;
use crate::orphans::undo_block_transactions::undo_transactions;
use crate::records::memory::connections::live_miner_peer_streams;
@ -35,22 +35,24 @@ fn required_snapshot_votes(total_voters: usize) -> usize {
(total_voters * 2).div_ceil(3).max(2)
}
fn peer_is_syncing_vote_error(error: &str) -> bool {
error.trim() == "error: Node is syncing"
}
async fn snapshot_has_peer_quorum(
snapshot_height: u32,
local_hash: &str,
map: Arc<Mutex<Command>>,
) -> bool {
let peers = live_miner_peer_streams().await;
let total_voters = peers.len() + 1;
if total_voters < 2 {
if peers.is_empty() {
warn!(
"[snapshot] not advancing snapshot at height {snapshot_height}: no connected miner peers"
);
return false;
}
let required_votes = required_snapshot_votes(total_voters);
let mut handles = Vec::with_capacity(peers.len());
for (connections_key, stream) in peers {
@ -68,10 +70,12 @@ async fn snapshot_has_peer_quorum(
}
let mut matching_votes = 1usize;
let mut eligible_peer_votes = 0usize;
for handle in handles {
match handle.await {
Ok((connections_key, Ok(peer_hash))) => {
eligible_peer_votes += 1;
if peer_hash == local_hash {
matching_votes += 1;
} else {
@ -81,9 +85,15 @@ async fn snapshot_has_peer_quorum(
}
}
Ok((connections_key, Err(err))) => {
warn!(
"[snapshot] peer vote failed: height={snapshot_height} peer={connections_key} err={err}"
);
if peer_is_syncing_vote_error(&err) {
info!(
"[snapshot] skipping syncing peer vote: height={snapshot_height} peer={connections_key}"
);
} else {
warn!(
"[snapshot] peer vote failed: height={snapshot_height} peer={connections_key} err={err}"
);
}
}
Err(err) => {
warn!("[snapshot] peer vote task failed at height {snapshot_height}: {err}");
@ -91,6 +101,15 @@ async fn snapshot_has_peer_quorum(
}
}
let total_voters = eligible_peer_votes + 1;
if total_voters < 2 {
warn!(
"[snapshot] not advancing snapshot at height {snapshot_height}: no eligible non-syncing miner peers"
);
return false;
}
let required_votes = required_snapshot_votes(total_voters);
if matching_votes >= required_votes {
info!(
"[snapshot] consensus reached: height={snapshot_height} hash={local_hash} votes={matching_votes}/{total_voters} required={required_votes}"
@ -117,6 +136,10 @@ pub async fn update_snapshot(
current_height: u32,
map: Arc<Mutex<Command>>,
) -> Result<(), String> {
if is_syncing_mode() {
return Ok(());
}
// Genesis is always a valid snapshot, then later snapshots lag the tip
// so normal orphan correction still has room to operate.
let snapshot_height = if current_height == 0 {

View File

@ -67,14 +67,12 @@ pub async fn load_block(block_number: u32) -> Result<Block, String> {
// Load the full block because this path reconstructs both the header and
// every transaction for validation or inspection.
let binary_data = match fs::read(&file_name) {
Ok(data) => data,
Err(err) => {
println!("{}", &file_name);
eprintln!("Error reading file load_block: {err:?}");
return Err(format!("Unable to read binary file: {err:?}"));
}
};
let binary_data = match fs::read(&file_name) {
Ok(data) => data,
Err(err) => {
return Err(format!("Unable to read block {block_number}: {err:?}"));
}
};
if binary_data.len() < VRF_BLOCK_BYTES {
return Err("Unable to load block: binary data shorter than VrfBlock header".to_string());

View File

@ -22,12 +22,14 @@ pub async fn load_block_header(block_number: u32) -> Result<VrfBlock, String> {
.join(format!("{block_number}.{block_ext}"))
.to_string_lossy()
.into_owned();
let file = match File::open(&file_name).await {
Ok(file) => file,
Err(err) => {
return Err(format!("Error opening file {}: {:?}", &file_name, err));
}
};
let file = match File::open(&file_name).await {
Ok(file) => file,
Err(err) => {
return Err(format!(
"Error opening block file for height {block_number}: {err:?}"
));
}
};
let mut binary_data = Vec::with_capacity(VRF_BLOCK_BYTES);
// Only read the fixed VrfBlock prefix from the block file.
@ -35,12 +37,11 @@ pub async fn load_block_header(block_number: u32) -> Result<VrfBlock, String> {
.take(VRF_BLOCK_BYTES as u64)
.read_to_end(&mut binary_data)
.await
{
return Err(format!(
"Error reading file load_block_head {}: {:?}",
&file_name, err
));
}
{
return Err(format!(
"Error reading block header for height {block_number}: {err:?}"
));
}
// The stored header format is the same VrfBlock prefix used at the
// beginning of every block file.

View File

@ -51,10 +51,8 @@ pub async fn request_block(db: &Db, hash: &str) -> RpcResponse {
match load_block(block_number).await {
Ok(block) => match block.to_bytes().await {
Ok(block_bytes) => RpcResponse::Binary(block_bytes),
Err(err) => RpcResponse::Binary(
format!("error: Failed to convert block to bytes: {err}").into_bytes(),
),
Err(_) => RpcResponse::Binary(b"error: Unable to serialize block".to_vec()),
},
Err(err) => RpcResponse::Binary(format!("error: Failed to load block: {err}").into_bytes()),
Err(_) => RpcResponse::Binary(b"error: Block not found".to_vec()),
}
}

View File

@ -9,15 +9,9 @@ pub async fn request_block(height: u32) -> RpcResponse {
let block_bytes = block.to_bytes().await;
match block_bytes {
Ok(result) => RpcResponse::Binary(result),
Err(err) => {
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
RpcResponse::Binary(msg)
}
Err(_) => RpcResponse::Binary(b"error: Unable to serialize block".to_vec()),
}
}
Err(err) => {
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
RpcResponse::Binary(msg)
}
Err(_) => RpcResponse::Binary(format!("error: Block {height} not found").into_bytes()),
}
}

View File

@ -1,8 +1,13 @@
use crate::decode;
use crate::miner::flag::is_syncing_mode;
use crate::records::unpack_block::unpack_header::load_block_header;
use crate::rpc::responses::RpcResponse;
pub async fn request_block_hash_at_height(block_number: u32) -> RpcResponse {
if is_syncing_mode() {
return RpcResponse::Binary(b"error: Node is syncing".to_vec());
}
match load_block_header(block_number).await {
Ok(header) => {
let hash = header.hash().await;
@ -15,10 +20,8 @@ pub async fn request_block_hash_at_height(block_number: u32) -> RpcResponse {
),
}
}
Err(err) => RpcResponse::Binary(
format!("error: Failed to load block header at height {block_number}: {err}")
.as_bytes()
.to_vec(),
),
Err(_) => {
RpcResponse::Binary(format!("error: Block {block_number} not found").into_bytes())
}
}
}

View File

@ -13,15 +13,9 @@ pub async fn request_latest_block(db: &Db) -> RpcResponse {
let block_bytes = block.to_bytes().await;
match block_bytes {
Ok(block_bytes) => RpcResponse::Binary(block_bytes),
Err(err) => {
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
RpcResponse::Binary(msg)
}
Err(_) => RpcResponse::Binary(b"error: Unable to serialize block".to_vec()),
}
}
Err(err) => {
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
RpcResponse::Binary(msg)
}
Err(_) => RpcResponse::Binary(format!("error: Block {height} not found").into_bytes()),
}
}

View File

@ -33,7 +33,7 @@ pub fn should_trigger_orphan_check(error: &str) -> bool {
|| error.contains("Candidate parent is not current chain parent.")
|| error.contains("Difficulty mismatch with the blockchain data.")
|| error.contains("Incoming block is no longer the next expected height.")
|| error.contains("Error opening file ./testnet_blocks/")
|| error.contains("Unable to read block")
}
async fn next_expected_height(db: &Db) -> u32 {