riftenlabs-indexer/src/bcmr/wellknowndowloader.rs

147 lines
5.3 KiB
Rust
Raw Normal View History

use crate::{
bcmr::{parsedbcmr::parse_all_bcmr_identities, utilurl::get_url},
db::{
bcmr::{delete_entries_for_well_known, insert_well_known_bcmr},
DBPool,
},
};
use anyhow::Result;
use log::{info, warn};
use serde_json::Value;
use std::{
sync::{atomic::AtomicBool, Arc},
thread::{self, JoinHandle},
time::{Duration, Instant},
};
// List of 'trust me bro' sources of truth
const WELL_KNOWN: [(&str, &str); 1] = [(
"otr",
"https://otr.cash/.well-known/bitcoin-cash-metadata-registry.json",
)];
pub struct WellKnownDownloader {
db: DBPool,
keep_running: Arc<AtomicBool>,
download_thread: Option<JoinHandle<()>>,
}
const LOOP_SLEEP_TIME: Duration = Duration::from_secs(2);
const UPDATE_INTERVAL: Duration = Duration::from_secs(3600 * 24); // once per day
const DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(120);
const MAX_BCMR_SIZE: usize = 1024 * 1024 * 500; // 500 MB
impl WellKnownDownloader {
pub fn new(db: DBPool) -> Self {
Self {
db,
keep_running: Arc::new(AtomicBool::new(true)),
download_thread: None,
}
}
pub fn start(&mut self) -> Result<()> {
let db_cpy = self.db.clone();
let keep_running_cpy = self.keep_running.clone();
self.download_thread = Some(
thread::Builder::new()
.name("wellknown downloader".to_string())
.spawn(move || {
let mut last_update: Option<Instant> = None;
loop {
thread::sleep(LOOP_SLEEP_TIME);
if !keep_running_cpy.load(std::sync::atomic::Ordering::Relaxed) {
info!("Exiting bcmr well known thread");
return;
}
if let Some(last_time) = last_update {
if Instant::now().duration_since(last_time) < UPDATE_INTERVAL {
continue;
}
}
last_update = Some(Instant::now());
for (source, url) in WELL_KNOWN {
info!("bcmr: Fetching well known '{}' token list", source);
let json_str: String = match get_url(url, DOWNLOAD_TIMEOUT, MAX_BCMR_SIZE) {
Ok((json_str, _)) => json_str,
Err(e) => {
info!("bcmr: Failed to download well known url {}: {}", url, e);
continue;
}
};
let json: Value = match serde_json::from_str(&json_str) {
Ok(j) => j,
Err(e) => {
info!("bcmr: Failed to parse well known url {}: {}", url, e);
continue;
}
};
let bcmr_entries = match parse_all_bcmr_identities(&json, true, source) {
Ok(entries) => entries,
Err(e) => {
info!("bcmr: Failed to parse bcmr from well known url {}: {}", url, e);
continue;
}
};
let mut tx = match db_cpy.get() {
Ok(conn) => conn,
Err(e) => {
warn!("bcmr: Failed to get DB connection: {}", e);
continue;
}
};
let tx = match tx.transaction() {
Ok(tx) => tx,
Err(e) => {
warn!("bcmr: Failed to initiate DB transaction: {}", e);
continue;
}
};
if let Err(e) = delete_entries_for_well_known(&tx, source) {
warn!("bcmr: Failed to delete entries for well known source {}: {}", source, e);
}
for bcmr in bcmr_entries {
if let Err(e) = insert_well_known_bcmr(&tx, source, &bcmr) {
warn!("bcmr: Failed to insert an entry from well known source {}: {}", source, e);
}
}
if let Err(e) = tx.commit() {
warn!("bcmr: Failed to update DB entries for {}: {}", source, e);
}
}
}})
.expect("failed to start bcmr wellknown thread"),
);
Ok(())
}
}
impl Drop for WellKnownDownloader {
fn drop(&mut self) {
self.keep_running
.store(false, std::sync::atomic::Ordering::SeqCst);
if let Some(thread) = self.download_thread.take() {
// Wake the thread in case it is sleeping
thread.thread().unpark();
match thread.join() {
Ok(_) => info!("bcmr well knownthread done"),
Err(e) => warn!("Failed to join bcmr wellknown thread: {:?}", e),
}
}
}
}