Skip to content

Commit 70457b6

Browse files
authored
Merge pull request kellymusk#146 from afurious/feat/stellar-confirmation-worker
feat: implement Stellar confirmation polling worker
2 parents 8ab1ad3 + fc1cf98 commit 70457b6

6 files changed

Lines changed: 1011 additions & 0 deletions

File tree

‎Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ crate-type = ["cdylib", "rlib"]
1919
# ---------------------------------------------------------------------------
2020
[features]
2121
default = ["database", "cache"]
22+
integration = []
2223
database = [ "dep:tokio", "dep:async-trait", "dep:uuid", "dep:chrono", "dep:serde", "dep:serde_json", "dep:tracing", "dep:tracing-subscriber", "dep:axum", "dep:tower", "dep:tower-http", "dep:regex", "dep:http", "dep:sqlx", "dep:hmac", "dep:sha2", "dep:hex", "dep:bigdecimal", "dep:rust_decimal", "dep:stellar-strkey", "dep:ed25519-dalek", "dep:stellar-xdr" ]
2324
cache = ["dep:redis", "dep:bb8", "dep:bb8-redis", "dep:moka", "dep:prometheus", "dep:tokio-util", "database"]
2425
database = [ "dep:tokio", "dep:async-trait", "dep:uuid", "dep:chrono", "dep:serde", "dep:serde_json", "dep:tracing", "dep:tracing-subscriber", "dep:axum", "dep:tower", "dep:tower-http", "dep:regex", "dep:http", "dep:sqlx", "dep:hmac", "dep:sha2", "dep:hex", "dep:bigdecimal", "dep:rust_decimal", "dep:stellar-strkey", "dep:ed25519-dalek", "dep:stellar-xdr", "dep:jsonwebtoken" ]
@@ -116,6 +117,7 @@ thiserror = "2.0.18"
116117

117118
# --- Nigerian bank account validation ---
118119
nuban = "1.1.0"
120+
prometheus = { version = "0.13", features = ["process"] }
119121
csv = "1.3"
120122
jsonwebtoken = { version = "9.2.0", optional = true }
121123
serde_yaml = "0.9"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
-- migrate:up
2+
-- Adds columns required by the Stellar Confirmation Polling Worker.
3+
4+
-- stellar_tx_hash: the on-chain hash the worker polls Horizon for.
5+
-- stale_flagged_at: set when a transaction is stuck beyond the stale timeout.
6+
-- state_transitioned_at: timestamp of the last status change (audit trail).
7+
8+
ALTER TABLE transactions
9+
ADD COLUMN IF NOT EXISTS stellar_tx_hash TEXT,
10+
ADD COLUMN IF NOT EXISTS stale_flagged_at TIMESTAMPTZ,
11+
ADD COLUMN IF NOT EXISTS state_transitioned_at TIMESTAMPTZ;
12+
13+
-- Index for the worker's primary query: active txns with a stellar hash.
14+
CREATE INDEX IF NOT EXISTS idx_transactions_stellar_polling
15+
ON transactions (status, stellar_tx_hash)
16+
WHERE status IN ('pending', 'processing') AND stellar_tx_hash IS NOT NULL;
17+
18+
-- Index for stale-transaction detection.
19+
CREATE INDEX IF NOT EXISTS idx_transactions_stale_check
20+
ON transactions (status, created_at)
21+
WHERE status IN ('pending', 'processing');
22+
23+
-- migrate:down
24+
DROP INDEX IF EXISTS idx_transactions_stale_check;
25+
DROP INDEX IF EXISTS idx_transactions_stellar_polling;
26+
ALTER TABLE transactions
27+
DROP COLUMN IF EXISTS state_transitioned_at,
28+
DROP COLUMN IF EXISTS stale_flagged_at,
29+
DROP COLUMN IF EXISTS stellar_tx_hash;

‎src/main.rs‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,41 @@ async fn main() -> anyhow::Result<()> {
463463
info!("Offramp processor worker disabled (OFFRAMP_PROCESSOR_ENABLED=false)");
464464
}
465465

466+
// Start Stellar Confirmation Polling Worker
467+
let stellar_confirm_enabled = std::env::var("STELLAR_CONFIRM_WORKER_ENABLED")
468+
.unwrap_or_else(|_| "true".to_string())
469+
.to_lowercase()
470+
!= "false";
471+
if stellar_confirm_enabled {
472+
if let (Some(pool), Some(client)) = (db_pool.clone(), stellar_client.clone()) {
473+
let confirm_config =
474+
workers::stellar_confirmation_worker::StellarConfirmationConfig::from_env();
475+
let registry = prometheus::default_registry().clone();
476+
match workers::stellar_confirmation_worker::WorkerMetrics::new(&registry) {
477+
Ok(metrics) => {
478+
info!(
479+
poll_interval_secs = confirm_config.poll_interval.as_secs(),
480+
confirmation_threshold = confirm_config.confirmation_threshold,
481+
stale_timeout_secs = confirm_config.stale_timeout.as_secs(),
482+
"Starting Stellar confirmation polling worker"
483+
);
484+
let worker = workers::stellar_confirmation_worker::StellarConfirmationWorker::new(
485+
pool,
486+
client,
487+
confirm_config,
488+
std::sync::Arc::new(metrics),
489+
);
490+
tokio::spawn(worker.run(worker_shutdown_rx.clone()));
491+
}
492+
Err(e) => {
493+
error!(error = %e, "Failed to register Prometheus metrics for Stellar confirmation worker — skipping");
494+
}
495+
}
496+
} else {
497+
info!("Skipping Stellar confirmation worker (missing db pool or stellar client)");
498+
}
499+
} else {
500+
info!("Stellar confirmation worker disabled (STELLAR_CONFIRM_WORKER_ENABLED=false)");
466501
// Start Onramp Processor Worker
467502
let onramp_enabled = std::env::var("ONRAMP_PROCESSOR_ENABLED")
468503
.unwrap_or_else(|_| "true".to_string())

‎src/workers/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct TransactionMonitorWorker {
1717
pub http_client: reqwest::Client,
1818
pub mod maintenance;
1919
pub mod offramp_processor;
20+
pub mod stellar_confirmation_worker;
2021
pub mod onramp_processor;
2122
pub mod payment_poller;
2223
pub mod transaction_monitor;

0 commit comments

Comments
 (0)