fix: correct locktime threshold, PG stats SUM on TEXT, PG locktime width, and translate Italian error strings

- Bug 1: LOCKTIME_THRESHOLD was 5_000_000 instead of BIP-65 value 500_000_000
- Bug 2: PostgreSQL SUM(our_fees) failed on TEXT column; cast to BIGINT
- Bug 3: PostgreSQL locktime INTEGER (i32) widened to BIGINT (i64) with idempotent ALTER
- Translate 4 Italian error strings in xpub.rs to English
This commit is contained in:
2026-08-19 02:22:14 -04:00
parent 5e18a2e06c
commit f106beeb62
4 changed files with 21 additions and 13 deletions

View File

@@ -24,7 +24,8 @@ use reqwest::Client as rClient;
use std::net::SocketAddr; use std::net::SocketAddr;
use url::Url; use url::Url;
const LOCKTIME_THRESHOLD: i64 = 5000000; // BIP-65: locktime values below this are block heights, at or above are UNIX timestamps.
const LOCKTIME_THRESHOLD: i64 = 500_000_000;
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
struct MyConfig { struct MyConfig {

View File

@@ -624,9 +624,9 @@ pub async fn get_pending_txs(
"SELECT txid, tx, locktime, network, status FROM tbl_tx WHERE network = $1 AND status = 0 AND (locktime < $2 OR (locktime > $3 AND locktime < $4))", "SELECT txid, tx, locktime, network, status FROM tbl_tx WHERE network = $1 AND status = 0 AND (locktime < $2 OR (locktime > $3 AND locktime < $4))",
) )
.bind(network) .bind(network)
.bind(bestblock_height as i32) .bind(bestblock_height)
.bind(locktime_threshold as i32) .bind(locktime_threshold)
.bind(bestblock_time as i32) .bind(bestblock_time)
.fetch_all(p) .fetch_all(p)
.await?; .await?;
@@ -634,7 +634,7 @@ pub async fn get_pending_txs(
results.push(TxRow { results.push(TxRow {
txid: row.try_get("txid")?, txid: row.try_get("txid")?,
tx: row.try_get("tx")?, tx: row.try_get("tx")?,
locktime: row.try_get::<i32, _>("locktime").unwrap_or(0) as i64, locktime: row.try_get::<i64, _>("locktime").unwrap_or(0),
network: row.try_get("network")?, network: row.try_get("network")?,
status: row.try_get::<i32, _>("status").unwrap_or(0) as i64, status: row.try_get::<i32, _>("status").unwrap_or(0) as i64,
}); });
@@ -839,9 +839,9 @@ pub async fn calculate_and_upsert_stats(
(SELECT COUNT(*) FROM tbl_tx WHERE status = 0 AND network = $1), (SELECT COUNT(*) FROM tbl_tx WHERE status = 0 AND network = $1),
(SELECT COUNT(*) FROM tbl_tx WHERE status = 1 AND network = $1), (SELECT COUNT(*) FROM tbl_tx WHERE status = 1 AND network = $1),
(SELECT COUNT(*) FROM tbl_tx WHERE status = 2 AND network = $1), (SELECT COUNT(*) FROM tbl_tx WHERE status = 2 AND network = $1),
(SELECT COALESCE(SUM(our_fees),0) FROM tbl_tx WHERE status = 0 AND network = $1), (SELECT COALESCE(SUM(CAST(our_fees AS BIGINT)),0) FROM tbl_tx WHERE status = 0 AND network = $1),
(SELECT COALESCE(SUM(our_fees),0) FROM tbl_tx WHERE status = 1 AND network = $1), (SELECT COALESCE(SUM(CAST(our_fees AS BIGINT)),0) FROM tbl_tx WHERE status = 1 AND network = $1),
(SELECT COALESCE(SUM(our_fees),0) FROM tbl_tx WHERE status = 2 AND network = $1), (SELECT COALESCE(SUM(CAST(our_fees AS BIGINT)),0) FROM tbl_tx WHERE status = 2 AND network = $1),
(SELECT COUNT(DISTINCT tbl_inp.in_txid) (SELECT COUNT(DISTINCT tbl_inp.in_txid)
FROM tbl_inp FROM tbl_inp
JOIN tbl_tx ON tbl_inp.txid = tbl_tx.txid JOIN tbl_tx ON tbl_inp.txid = tbl_tx.txid

View File

@@ -128,7 +128,7 @@ pub async fn create_pg_schema(pool: &PgPool) -> Result<(), sqlx::Error> {
wtxid TEXT, wtxid TEXT,
ntxid TEXT, ntxid TEXT,
tx TEXT, tx TEXT,
locktime INTEGER, locktime BIGINT,
network TEXT, network TEXT,
network_fees TEXT, network_fees TEXT,
reqid TEXT, reqid TEXT,
@@ -141,6 +141,13 @@ pub async fn create_pg_schema(pool: &PgPool) -> Result<(), sqlx::Error> {
.execute(pool) .execute(pool)
.await?; .await?;
// Migrate pre-existing deployments where locktime was created as INTEGER.
// nLockTime is a u32 (up to 4_294_967_295); INTEGER (i32) cannot hold
// timestamp-based locktimes after 2038-01-19.
let _ = sqlx::query("ALTER TABLE tbl_tx ALTER COLUMN locktime TYPE BIGINT")
.execute(pool)
.await;
sqlx::query( sqlx::query(
"CREATE TABLE IF NOT EXISTS tbl_inp ( "CREATE TABLE IF NOT EXISTS tbl_inp (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,

View File

@@ -148,12 +148,12 @@ pub fn calculate_fingerprint(tpub: &str) -> Result<String, String> {
fn base58check_decode(s: &str) -> Result<Vec<u8>, String> { fn base58check_decode(s: &str) -> Result<Vec<u8>, String> {
let data = bs58::decode(s).into_vec().map_err(|e| e.to_string())?; let data = bs58::decode(s).into_vec().map_err(|e| e.to_string())?;
if data.len() < 4 { if data.len() < 4 {
return Err("Data troppo corta".to_string()); return Err("Data too short".to_string());
} }
let (payload, checksum) = data.split_at(data.len() - 4); let (payload, checksum) = data.split_at(data.len() - 4);
let hash = Sha256::digest(Sha256::digest(payload)); let hash = Sha256::digest(Sha256::digest(payload));
if hash[0..4] != checksum[..] { if hash[0..4] != checksum[..] {
return Err("Checksum invalido".to_string()); return Err("Invalid checksum".to_string());
} }
Ok(payload.to_vec()) Ok(payload.to_vec())
} }
@@ -168,7 +168,7 @@ fn convert_to(zpub: &str, prefix: BS58Prefix) -> Result<String, String> {
let mut data = base58check_decode(zpub)?; let mut data = base58check_decode(zpub)?;
if data.len() < 4 { if data.len() < 4 {
return Err("Non è una zpub valida.".to_string()); return Err("Not a valid zpub".to_string());
} }
data.splice( data.splice(
0..4, 0..4,
@@ -207,7 +207,7 @@ pub fn new_address_from_xpub(
fn main() -> Result<(), Box<dyn std::error::Error>>{ fn main() -> Result<(), Box<dyn std::error::Error>>{
match convert_to(zpub,BS58Prefix::Tpub) { match convert_to(zpub,BS58Prefix::Tpub) {
Ok(tpub) => println!("XPUB: {}", tpub), Ok(tpub) => println!("XPUB: {}", tpub),
Err(e) => eprintln!("Errore: {}", e), Err(e) => eprintln!("Error: {}", e),
} }
let fingerprint = base58check_encode(&calculate_fingerprint(zpub)); let fingerprint = base58check_encode(&calculate_fingerprint(zpub));
println!("ZPUB: {}, FINGERPRINT: {}",zpub,fingerprint); println!("ZPUB: {}, FINGERPRINT: {}",zpub,fingerprint);