use std::net::{IpAddr, Ipv4Addr, SocketAddr};
pub fn enabled() -> bool {
std::env::var("BLVM_IBD_SYNTH_WAN")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
pub fn allow_zero_real_peers() -> bool {
enabled()
|| std::env::var("BLVM_IBD_ALLOW_ZERO_PEERS")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
pub fn body_tip_override() -> Option<u64> {
if !enabled() {
return None;
}
std::env::var("BLVM_IBD_SYNTH_WAN_BODY_TIP")
.ok()
.and_then(|s| s.parse().ok())
}
pub fn peer_count() -> usize {
if !enabled() {
return 0;
}
std::env::var("BLVM_IBD_SYNTH_WAN_PEER_COUNT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(4)
.clamp(1, 16)
}
pub fn peer_ids() -> Vec<String> {
if !enabled() {
return Vec::new();
}
(0..peer_count())
.map(|i| {
let octet = (i + 1).min(254);
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, octet as u8)), 8333).to_string()
})
.collect()
}
pub fn is_synthetic_peer(peer_id: &str) -> bool {
if !enabled() {
return false;
}
let Ok(addr) = peer_id.parse::<SocketAddr>() else {
return false;
};
matches!(
addr.ip(),
IpAddr::V4(v4) if v4.octets()[0] == 203 && v4.octets()[1] == 0 && v4.octets()[2] == 113
)
}
pub const REPLAY_180_220K_HERO: &str = "203.0.113.1:8333";
pub const REPLAY_180_220K_HERO_MS: u64 = 3;
pub const REPLAY_180_220K_DRIP: &str = "203.0.113.2:8333";
pub const REPLAY_180_220K_DRIP_MS: u64 = 25;
pub const REPLAY_180_220K_STALL: &str = "203.0.113.3:8333";
pub const REPLAY_180_220K_STALL_MS: u64 = 800;
pub fn replay_fixture() -> Option<&'static str> {
if !enabled() {
return None;
}
let v = std::env::var("BLVM_IBD_SYNTH_REPLAY").ok()?;
match v.trim() {
"180-220k" | "180_220k" | "180220k" => Some("180-220k"),
_ => None,
}
}
fn peer_delay_overrides() -> std::collections::HashMap<String, u64> {
let mut out = std::collections::HashMap::new();
let Ok(s) = std::env::var("BLVM_IBD_SYNTH_GETDATA_DELAY_PEER_MS") else {
return out;
};
for part in s.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
let Some((peer, ms)) = part.rsplit_once('=') else {
continue;
};
let Ok(ms) = ms.trim().parse::<u64>() else {
continue;
};
out.insert(peer.trim().to_string(), ms.min(30_000));
}
out
}
pub fn getdata_delay_ms() -> u64 {
if !enabled() {
return 0;
}
std::env::var("BLVM_IBD_SYNTH_GETDATA_DELAY_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(0)
.min(30_000)
}
pub fn getdata_delay_ms_for_peer(peer_id: &str) -> u64 {
if !enabled() {
return 0;
}
if let Some(&ms) = peer_delay_overrides().get(peer_id) {
return ms;
}
if replay_fixture() == Some("180-220k") {
return match peer_id {
REPLAY_180_220K_HERO => REPLAY_180_220K_HERO_MS,
REPLAY_180_220K_DRIP => REPLAY_180_220K_DRIP_MS,
REPLAY_180_220K_STALL => REPLAY_180_220K_STALL_MS,
_ => getdata_delay_ms().max(50),
};
}
getdata_delay_ms()
}
pub fn injected_ia() -> bool {
if !enabled() {
return false;
}
getdata_delay_ms() > 0 || replay_fixture().is_some() || !peer_delay_overrides().is_empty()
}
pub fn use_fake_download_peers() -> bool {
if !enabled() {
return false;
}
if injected_ia() {
return true;
}
if std::env::var("BLVM_IBD_SYNTH_WAN_FORCE_PEERS")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
return true;
}
peer_count() >= 2
}
pub fn bulk_local_disk_stream() -> bool {
enabled() && !use_fake_download_peers()
}
pub fn effective_wan_body_tip(live_tip: u64) -> u64 {
body_tip_override().unwrap_or(live_tip)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::network::peer_scoring::is_lan_peer;
#[serial_test::serial(ibd)]
#[test]
fn synth_peer_ids_are_non_lan_wan_addrs() {
let _lock = crate::ibd_test_lock::guard();
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN", "1") };
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN_PEER_COUNT", "3") };
let peers = peer_ids();
assert_eq!(peers.len(), 3);
for p in &peers {
assert!(is_synthetic_peer(p));
let addr: SocketAddr = p.parse().expect("parse");
assert!(!is_lan_peer(&addr));
}
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN_PEER_COUNT") };
}
#[serial_test::serial(ibd)]
#[test]
fn bulk_delay_zero_single_peer_uses_local_disk_stream() {
let _lock = crate::ibd_test_lock::guard();
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN", "1") };
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN_PEER_COUNT", "1") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_GETDATA_DELAY_MS") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_GETDATA_DELAY_PEER_MS") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_REPLAY") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN_FORCE_PEERS") };
assert!(!use_fake_download_peers());
assert!(bulk_local_disk_stream());
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN_PEER_COUNT", "4") };
assert!(use_fake_download_peers());
assert!(!bulk_local_disk_stream());
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN_PEER_COUNT", "1") };
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN_FORCE_PEERS", "1") };
assert!(use_fake_download_peers());
assert!(!bulk_local_disk_stream());
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN_PEER_COUNT") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN_FORCE_PEERS") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_GETDATA_DELAY_PEER_MS") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_REPLAY") };
}
#[serial_test::serial(ibd)]
#[test]
fn replay_180_220k_injects_per_peer_ia() {
let _lock = crate::ibd_test_lock::guard();
unsafe { std::env::set_var("BLVM_IBD_SYNTH_WAN", "1") };
unsafe { std::env::set_var("BLVM_IBD_SYNTH_REPLAY", "180-220k") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_GETDATA_DELAY_MS") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_GETDATA_DELAY_PEER_MS") };
assert_eq!(replay_fixture(), Some("180-220k"));
assert!(injected_ia());
assert!(use_fake_download_peers());
assert!(!bulk_local_disk_stream());
assert_eq!(
getdata_delay_ms_for_peer(REPLAY_180_220K_HERO),
REPLAY_180_220K_HERO_MS
);
assert_eq!(
getdata_delay_ms_for_peer(REPLAY_180_220K_DRIP),
REPLAY_180_220K_DRIP_MS
);
assert_eq!(
getdata_delay_ms_for_peer(REPLAY_180_220K_STALL),
REPLAY_180_220K_STALL_MS
);
unsafe { std::env::set_var("BLVM_IBD_SYNTH_GETDATA_DELAY_PEER_MS", "203.0.113.2:8333=2") };
assert_eq!(getdata_delay_ms_for_peer(REPLAY_180_220K_DRIP), 2);
assert_eq!(
getdata_delay_ms_for_peer(REPLAY_180_220K_HERO),
REPLAY_180_220K_HERO_MS
);
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_WAN") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_REPLAY") };
unsafe { std::env::remove_var("BLVM_IBD_SYNTH_GETDATA_DELAY_PEER_MS") };
}
}