From f106beeb62547d038c0b7194fe1fe733c496851b Mon Sep 17 00:00:00 2001 From: svatantrya Date: Wed, 19 Aug 2026 02:22:14 -0400 Subject: [PATCH] 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 --- src/bin/bal-pusher.rs | 3 ++- src/db/mod.rs | 14 +++++++------- src/db/schema.rs | 9 ++++++++- src/xpub.rs | 8 ++++---- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/bin/bal-pusher.rs b/src/bin/bal-pusher.rs index 123aa45..c8285c8 100644 --- a/src/bin/bal-pusher.rs +++ b/src/bin/bal-pusher.rs @@ -24,7 +24,8 @@ use reqwest::Client as rClient; use std::net::SocketAddr; 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"); #[derive(Debug, Clone, Serialize, Deserialize)] struct MyConfig { diff --git a/src/db/mod.rs b/src/db/mod.rs index 0bb38f4..a6591b8 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -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))", ) .bind(network) - .bind(bestblock_height as i32) - .bind(locktime_threshold as i32) - .bind(bestblock_time as i32) + .bind(bestblock_height) + .bind(locktime_threshold) + .bind(bestblock_time) .fetch_all(p) .await?; @@ -634,7 +634,7 @@ pub async fn get_pending_txs( results.push(TxRow { txid: row.try_get("txid")?, tx: row.try_get("tx")?, - locktime: row.try_get::("locktime").unwrap_or(0) as i64, + locktime: row.try_get::("locktime").unwrap_or(0), network: row.try_get("network")?, status: row.try_get::("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 = 1 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(our_fees),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 = 0 AND network = $1), + (SELECT COALESCE(SUM(CAST(our_fees AS BIGINT)),0) FROM tbl_tx WHERE status = 1 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) FROM tbl_inp JOIN tbl_tx ON tbl_inp.txid = tbl_tx.txid diff --git a/src/db/schema.rs b/src/db/schema.rs index 53ebd46..bfe0a47 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -128,7 +128,7 @@ pub async fn create_pg_schema(pool: &PgPool) -> Result<(), sqlx::Error> { wtxid TEXT, ntxid TEXT, tx TEXT, - locktime INTEGER, + locktime BIGINT, network TEXT, network_fees TEXT, reqid TEXT, @@ -141,6 +141,13 @@ pub async fn create_pg_schema(pool: &PgPool) -> Result<(), sqlx::Error> { .execute(pool) .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( "CREATE TABLE IF NOT EXISTS tbl_inp ( id SERIAL PRIMARY KEY, diff --git a/src/xpub.rs b/src/xpub.rs index 0874926..7b5b686 100644 --- a/src/xpub.rs +++ b/src/xpub.rs @@ -148,12 +148,12 @@ pub fn calculate_fingerprint(tpub: &str) -> Result { fn base58check_decode(s: &str) -> Result, String> { let data = bs58::decode(s).into_vec().map_err(|e| e.to_string())?; 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 hash = Sha256::digest(Sha256::digest(payload)); if hash[0..4] != checksum[..] { - return Err("Checksum invalido".to_string()); + return Err("Invalid checksum".to_string()); } Ok(payload.to_vec()) } @@ -168,7 +168,7 @@ fn convert_to(zpub: &str, prefix: BS58Prefix) -> Result { let mut data = base58check_decode(zpub)?; if data.len() < 4 { - return Err("Non รจ una zpub valida.".to_string()); + return Err("Not a valid zpub".to_string()); } data.splice( 0..4, @@ -207,7 +207,7 @@ pub fn new_address_from_xpub( fn main() -> Result<(), Box>{ match convert_to(zpub,BS58Prefix::Tpub) { Ok(tpub) => println!("XPUB: {}", tpub), - Err(e) => eprintln!("Errore: {}", e), + Err(e) => eprintln!("Error: {}", e), } let fingerprint = base58check_encode(&calculate_fingerprint(zpub)); println!("ZPUB: {}, FINGERPRINT: {}",zpub,fingerprint);