error handling fixes
This commit is contained in:
parent
88b043cf41
commit
7cd421e142
|
|
@ -67,8 +67,47 @@ async fn main() {
|
||||||
};
|
};
|
||||||
let receiver = match normalize_short_address_input(&receiver_input) {
|
let receiver = match normalize_short_address_input(&receiver_input) {
|
||||||
Ok(address) => address,
|
Ok(address) => address,
|
||||||
Err(_) => {
|
Err(err) => {
|
||||||
println!("reciver wallet is not valid");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::common::binary_conversions::binary_to_string;
|
use crate::common::binary_conversions::binary_to_string;
|
||||||
use crate::log::{error, info, warn};
|
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::structs::UndoTransactions;
|
||||||
use crate::orphans::undo_block_transactions::undo_transactions;
|
use crate::orphans::undo_block_transactions::undo_transactions;
|
||||||
use crate::records::memory::connections::live_miner_peer_streams;
|
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)
|
(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(
|
async fn snapshot_has_peer_quorum(
|
||||||
snapshot_height: u32,
|
snapshot_height: u32,
|
||||||
local_hash: &str,
|
local_hash: &str,
|
||||||
map: Arc<Mutex<Command>>,
|
map: Arc<Mutex<Command>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let peers = live_miner_peer_streams().await;
|
let peers = live_miner_peer_streams().await;
|
||||||
let total_voters = peers.len() + 1;
|
|
||||||
|
|
||||||
if total_voters < 2 {
|
if peers.is_empty() {
|
||||||
warn!(
|
warn!(
|
||||||
"[snapshot] not advancing snapshot at height {snapshot_height}: no connected miner peers"
|
"[snapshot] not advancing snapshot at height {snapshot_height}: no connected miner peers"
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let required_votes = required_snapshot_votes(total_voters);
|
|
||||||
let mut handles = Vec::with_capacity(peers.len());
|
let mut handles = Vec::with_capacity(peers.len());
|
||||||
|
|
||||||
for (connections_key, stream) in peers {
|
for (connections_key, stream) in peers {
|
||||||
|
|
@ -68,10 +70,12 @@ async fn snapshot_has_peer_quorum(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut matching_votes = 1usize;
|
let mut matching_votes = 1usize;
|
||||||
|
let mut eligible_peer_votes = 0usize;
|
||||||
|
|
||||||
for handle in handles {
|
for handle in handles {
|
||||||
match handle.await {
|
match handle.await {
|
||||||
Ok((connections_key, Ok(peer_hash))) => {
|
Ok((connections_key, Ok(peer_hash))) => {
|
||||||
|
eligible_peer_votes += 1;
|
||||||
if peer_hash == local_hash {
|
if peer_hash == local_hash {
|
||||||
matching_votes += 1;
|
matching_votes += 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -81,9 +85,15 @@ async fn snapshot_has_peer_quorum(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((connections_key, Err(err))) => {
|
Ok((connections_key, Err(err))) => {
|
||||||
warn!(
|
if peer_is_syncing_vote_error(&err) {
|
||||||
"[snapshot] peer vote failed: height={snapshot_height} peer={connections_key} err={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) => {
|
Err(err) => {
|
||||||
warn!("[snapshot] peer vote task failed at height {snapshot_height}: {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 {
|
if matching_votes >= required_votes {
|
||||||
info!(
|
info!(
|
||||||
"[snapshot] consensus reached: height={snapshot_height} hash={local_hash} votes={matching_votes}/{total_voters} required={required_votes}"
|
"[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,
|
current_height: u32,
|
||||||
map: Arc<Mutex<Command>>,
|
map: Arc<Mutex<Command>>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
if is_syncing_mode() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// Genesis is always a valid snapshot, then later snapshots lag the tip
|
// Genesis is always a valid snapshot, then later snapshots lag the tip
|
||||||
// so normal orphan correction still has room to operate.
|
// so normal orphan correction still has room to operate.
|
||||||
let snapshot_height = if current_height == 0 {
|
let snapshot_height = if current_height == 0 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Load the full block because this path reconstructs both the header and
|
||||||
// every transaction for validation or inspection.
|
// every transaction for validation or inspection.
|
||||||
let binary_data = match fs::read(&file_name) {
|
let binary_data = match fs::read(&file_name) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("{}", &file_name);
|
return Err(format!("Unable to read block {block_number}: {err:?}"));
|
||||||
eprintln!("Error reading file load_block: {err:?}");
|
}
|
||||||
return Err(format!("Unable to read binary file: {err:?}"));
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if binary_data.len() < VRF_BLOCK_BYTES {
|
if binary_data.len() < VRF_BLOCK_BYTES {
|
||||||
return Err("Unable to load block: binary data shorter than VrfBlock header".to_string());
|
return Err("Unable to load block: binary data shorter than VrfBlock header".to_string());
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,14 @@ pub async fn load_block_header(block_number: u32) -> Result<VrfBlock, String> {
|
||||||
.join(format!("{block_number}.{block_ext}"))
|
.join(format!("{block_number}.{block_ext}"))
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.into_owned();
|
||||||
let file = match File::open(&file_name).await {
|
let file = match File::open(&file_name).await {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(format!("Error opening file {}: {:?}", &file_name, err));
|
return Err(format!(
|
||||||
}
|
"Error opening block file for height {block_number}: {err:?}"
|
||||||
};
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut binary_data = Vec::with_capacity(VRF_BLOCK_BYTES);
|
let mut binary_data = Vec::with_capacity(VRF_BLOCK_BYTES);
|
||||||
// Only read the fixed VrfBlock prefix from the block file.
|
// 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)
|
.take(VRF_BLOCK_BYTES as u64)
|
||||||
.read_to_end(&mut binary_data)
|
.read_to_end(&mut binary_data)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Error reading file load_block_head {}: {:?}",
|
"Error reading block header for height {block_number}: {err:?}"
|
||||||
&file_name, err
|
));
|
||||||
));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// The stored header format is the same VrfBlock prefix used at the
|
// The stored header format is the same VrfBlock prefix used at the
|
||||||
// beginning of every block file.
|
// beginning of every block file.
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,8 @@ pub async fn request_block(db: &Db, hash: &str) -> RpcResponse {
|
||||||
match load_block(block_number).await {
|
match load_block(block_number).await {
|
||||||
Ok(block) => match block.to_bytes().await {
|
Ok(block) => match block.to_bytes().await {
|
||||||
Ok(block_bytes) => RpcResponse::Binary(block_bytes),
|
Ok(block_bytes) => RpcResponse::Binary(block_bytes),
|
||||||
Err(err) => RpcResponse::Binary(
|
Err(_) => RpcResponse::Binary(b"error: Unable to serialize block".to_vec()),
|
||||||
format!("error: Failed to convert block to bytes: {err}").into_bytes(),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
Err(err) => RpcResponse::Binary(format!("error: Failed to load block: {err}").into_bytes()),
|
Err(_) => RpcResponse::Binary(b"error: Block not found".to_vec()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,9 @@ pub async fn request_block(height: u32) -> RpcResponse {
|
||||||
let block_bytes = block.to_bytes().await;
|
let block_bytes = block.to_bytes().await;
|
||||||
match block_bytes {
|
match block_bytes {
|
||||||
Ok(result) => RpcResponse::Binary(result),
|
Ok(result) => RpcResponse::Binary(result),
|
||||||
Err(err) => {
|
Err(_) => RpcResponse::Binary(b"error: Unable to serialize block".to_vec()),
|
||||||
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
|
|
||||||
RpcResponse::Binary(msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(_) => RpcResponse::Binary(format!("error: Block {height} not found").into_bytes()),
|
||||||
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
|
|
||||||
RpcResponse::Binary(msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
use crate::decode;
|
use crate::decode;
|
||||||
|
use crate::miner::flag::is_syncing_mode;
|
||||||
use crate::records::unpack_block::unpack_header::load_block_header;
|
use crate::records::unpack_block::unpack_header::load_block_header;
|
||||||
use crate::rpc::responses::RpcResponse;
|
use crate::rpc::responses::RpcResponse;
|
||||||
|
|
||||||
pub async fn request_block_hash_at_height(block_number: u32) -> 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 {
|
match load_block_header(block_number).await {
|
||||||
Ok(header) => {
|
Ok(header) => {
|
||||||
let hash = header.hash().await;
|
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(
|
Err(_) => {
|
||||||
format!("error: Failed to load block header at height {block_number}: {err}")
|
RpcResponse::Binary(format!("error: Block {block_number} not found").into_bytes())
|
||||||
.as_bytes()
|
}
|
||||||
.to_vec(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,9 @@ pub async fn request_latest_block(db: &Db) -> RpcResponse {
|
||||||
let block_bytes = block.to_bytes().await;
|
let block_bytes = block.to_bytes().await;
|
||||||
match block_bytes {
|
match block_bytes {
|
||||||
Ok(block_bytes) => RpcResponse::Binary(block_bytes),
|
Ok(block_bytes) => RpcResponse::Binary(block_bytes),
|
||||||
Err(err) => {
|
Err(_) => RpcResponse::Binary(b"error: Unable to serialize block".to_vec()),
|
||||||
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
|
|
||||||
RpcResponse::Binary(msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(_) => RpcResponse::Binary(format!("error: Block {height} not found").into_bytes()),
|
||||||
let msg = format!("error: {err}").to_string().as_bytes().to_vec();
|
|
||||||
RpcResponse::Binary(msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ pub fn should_trigger_orphan_check(error: &str) -> bool {
|
||||||
|| error.contains("Candidate parent is not current chain parent.")
|
|| error.contains("Candidate parent is not current chain parent.")
|
||||||
|| error.contains("Difficulty mismatch with the blockchain data.")
|
|| error.contains("Difficulty mismatch with the blockchain data.")
|
||||||
|| error.contains("Incoming block is no longer the next expected height.")
|
|| 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 {
|
async fn next_expected_height(db: &Db) -> u32 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue