use std::process::ExitCode;
use std::time::Duration;
use serde_json::{json, Map, Value};
use chia_query::coinset::CoinsetClient;
const SNAPSHOT_PATH: &str = "tests/fixtures/coinset-api-snapshot.json";
const BASE_URL: &str = "https://api.coinset.org";
struct Probe {
name: &'static str,
endpoint: &'static str,
body: Value,
}
fn probes() -> Vec<Probe> {
vec![
Probe {
name: "get_blockchain_state",
endpoint: "get_blockchain_state",
body: json!({}),
},
Probe {
name: "get_network_info",
endpoint: "get_network_info",
body: json!({}),
},
Probe {
name: "get_block_record_by_height",
endpoint: "get_block_record_by_height",
body: json!({ "height": 1 }),
},
Probe {
name: "get_coin_record_by_name__error",
endpoint: "get_coin_record_by_name",
body: json!({ "name": format!("0x{}", "0".repeat(64)) }),
},
]
}
#[tokio::main]
async fn main() -> ExitCode {
let mode = std::env::args().nth(1).unwrap_or_else(|| "check".into());
match run(&mode).await {
Ok(code) => code,
Err(err) => {
eprintln!("coinset_drift: {err}");
ExitCode::from(2)
}
}
}
async fn run(mode: &str) -> Result<ExitCode, Box<dyn std::error::Error>> {
let live = collect_shapes().await?;
match mode {
"update" => {
let serialized = format!("{}\n", serde_json::to_string_pretty(&live)?);
std::fs::write(SNAPSHOT_PATH, serialized)?;
println!("wrote {SNAPSHOT_PATH}");
Ok(ExitCode::SUCCESS)
}
"check" => {
let baseline: Value = serde_json::from_str(&std::fs::read_to_string(SNAPSHOT_PATH)?)?;
let drifts = chia_query::drift::diff_shapes("", &baseline, &live);
if drifts.is_empty() {
println!("no drift: coinset.org matches the committed snapshot");
Ok(ExitCode::SUCCESS)
} else {
println!("DRIFT DETECTED against {SNAPSHOT_PATH}:");
for line in &drifts {
println!("- {line}");
}
Ok(ExitCode::FAILURE)
}
}
other => Err(format!("unknown mode `{other}` (expected `check` or `update`)").into()),
}
}
async fn collect_shapes() -> Result<Value, Box<dyn std::error::Error>> {
let client = CoinsetClient::new(BASE_URL, Duration::from_secs(30))?;
let mut shapes = Map::new();
for probe in probes() {
let response = client.post_raw(probe.endpoint, &probe.body).await?;
shapes.insert(
probe.name.to_string(),
chia_query::drift::shape_of(&response),
);
}
Ok(Value::Object(shapes))
}