Ember /api/live: the node lane's shape (timestamp_source passed through, header by default; the -32601 "method ... not found" message is the not-carried signal); tests
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
0276163301
commit
f9c4ac3701
1 changed files with 12 additions and 6 deletions
|
|
@ -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<RecentBlock> {
|
||||
|
|
@ -44,7 +47,7 @@ pub fn parse_recent(v: &Value) -> Vec<RecentBlock> {
|
|||
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<Value> = 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<Shared>) -> Option<Value> {
|
|||
}
|
||||
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");
|
||||
|
|
|
|||
Loading…
Reference in a new issue