diff --git a/app/igneum-app/src/live.rs b/app/igneum-app/src/live.rs index aaaf69fb3..ba703a2ec 100644 --- a/app/igneum-app/src/live.rs +++ b/app/igneum-app/src/live.rs @@ -36,6 +36,9 @@ pub struct RecentBlock { pub vote_key_hash: String, pub is_chain_block: bool, pub color: String, + /// "" or "merging_block" (the node lane: a merged non-chain block whose own header time was not at hand + /// carries its merging chain block's time, marked so blocks_per_minute stays honest) + pub timestamp_source: String, } pub fn parse_recent(v: &Value) -> Vec { @@ -44,7 +47,7 @@ pub fn parse_recent(v: &Value) -> Vec { v.as_array() .map(|a| { a.iter() - .map(|b| RecentBlock { hash: s(b, "hash"), blue_score: n(b, "blue_score"), daa_score: n(b, "daa_score"), timestamp_ms: n(b, "timestamp_ms"), vote_key_hash: s(b, "vote_key_hash"), is_chain_block: b.get("is_chain_block").and_then(|x| x.as_bool()).unwrap_or(false), color: s(b, "color") }) + .map(|b| RecentBlock { hash: s(b, "hash"), blue_score: n(b, "blue_score"), daa_score: n(b, "daa_score"), timestamp_ms: n(b, "timestamp_ms"), vote_key_hash: s(b, "vote_key_hash"), is_chain_block: b.get("is_chain_block").and_then(|x| x.as_bool()).unwrap_or(false), color: s(b, "color"), timestamp_source: s(b, "timestamp_source") }) .collect() }) .unwrap_or_default() @@ -71,7 +74,7 @@ pub fn shape_from_blocks(blocks: &[RecentBlock], now_ms: u64, window_s: u64) -> let rows: Vec = blocks .iter() .filter(|b| b.timestamp_ms >= win) - .map(|b| json!({ "hash": short(&b.hash), "blue_score": b.blue_score, "daa": b.daa_score, "timestamp_ms": b.timestamp_ms, "is_chain_block": b.is_chain_block, "color": if b.color.is_empty() { "pending" } else { b.color.as_str() }, "vote_key_hash": short(&b.vote_key_hash) })) + .map(|b| json!({ "hash": short(&b.hash), "blue_score": b.blue_score, "daa": b.daa_score, "timestamp_ms": b.timestamp_ms, "is_chain_block": b.is_chain_block, "color": if b.color.is_empty() { "pending" } else { b.color.as_str() }, "vote_key_hash": short(&b.vote_key_hash), "timestamp_source": if b.timestamp_source.is_empty() { "header" } else { b.timestamp_source.as_str() } })) .collect(); json!({ "miners_10m": miners.len(), @@ -165,7 +168,8 @@ fn from_node(shared: &std::sync::Arc) -> Option { } Ok(_) => None, Err(e) => { - if e.contains("not found") || e.contains("-32601") || e.contains("Method") { + // the node's default arm: {"code": -32601, "message": "method igneum_getRecentBlocks not found"} + if e.contains("not found") || e.contains("-32601") { cache().lock().unwrap().node_retry = Some(Instant::now() + Duration::from_secs(600)); } None @@ -188,7 +192,7 @@ mod tests { use super::*; fn b(ts: u64, key: &str, chain: bool) -> RecentBlock { - RecentBlock { hash: format!("0x{:064x}", ts), blue_score: ts / 1000, daa_score: ts / 1000, timestamp_ms: ts, vote_key_hash: key.into(), is_chain_block: chain, color: if chain { "blue".into() } else { String::new() } } + RecentBlock { hash: format!("0x{:064x}", ts), blue_score: ts / 1000, daa_score: ts / 1000, timestamp_ms: ts, vote_key_hash: key.into(), is_chain_block: chain, color: if chain { "blue".into() } else { String::new() }, timestamp_source: String::new() } } #[test] @@ -206,14 +210,16 @@ mod tests { assert_eq!(v["blocks"].as_array().unwrap().len(), 3, "the 120 s window"); assert_eq!(v["blocks"][2]["color"], "pending", "an unmerged block without a colour"); assert_eq!(v["blocks"][0]["color"], "blue"); + assert_eq!(v["blocks"][0]["timestamp_source"], "header"); assert_eq!(v["miners"].as_array().unwrap().len(), 3); } #[test] fn recent_blocks_parse_from_the_node_reply_and_an_empty_reply_is_empty() { - let v: Value = serde_json::from_str(r#"[{"hash":"0xab","blue_score":5,"daa_score":6,"timestamp_ms":7,"vote_key_hash":"cd","is_chain_block":true,"color":"blue"}]"#).unwrap(); + let v: Value = serde_json::from_str(r#"[{"hash":"0xab","blue_score":5,"daa_score":6,"timestamp_ms":7,"vote_key_hash":"cd","is_chain_block":true,"color":"blue"},{"hash":"0xcd","blue_score":5,"daa_score":6,"timestamp_ms":7,"vote_key_hash":"ef","is_chain_block":false,"color":"red","timestamp_source":"merging_block"}]"#).unwrap(); let p = parse_recent(&v); - assert_eq!(p.len(), 1); + assert_eq!(p.len(), 2); + assert_eq!((p[1].color.as_str(), p[1].timestamp_source.as_str()), ("red", "merging_block")); assert_eq!((p[0].blue_score, p[0].is_chain_block, p[0].color.as_str()), (5, true, "blue")); assert!(parse_recent(&json!(null)).is_empty()); assert_eq!(short("0x1234567890abcdef"), "123456..abcdef");