-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wip: generating election data from storage.
wip. wip: rudimentary tracking of elections. wip: fighting with trace construction. wip: first tracing of elections! wip: tracing elections and properties updates! wip: cleanup. wip: refactor to produce nice order of spans. wip: extract span sending function. wip: generate traces with multiple levels of detail. temp: add debug logging in elections pallet wip: better trace names.
- Loading branch information
Showing
15 changed files
with
1,434 additions
and
34 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
[package] | ||
name = "chainflip-state-observer" | ||
version = "0.1.0" | ||
edition = "2024" | ||
|
||
[dependencies] | ||
# chainflip-state-observer-framework = {git = "https://github.com/chainflip-io/chainflip-state-observer-framework.git", branch = "feat/PRO-2014/create-observable-trait"} | ||
tracing = "0.1.41" | ||
tracing-core = "0.1.33" | ||
tracing-opentelemetry = "0.28.0" | ||
tracing-subscriber = "0.3.19" | ||
opentelemetry = "0.27.1" | ||
opentelemetry_sdk = { version = "0.27.1", features = ["async-std", "rt-tokio", "trace"] } | ||
opentelemetry-otlp = { version = "0.27.0", features = ["grpc-tonic"] } | ||
opentelemetry-stdout = "0.27.0" | ||
tokio = { version = "*", features = ["rt-multi-thread"]} | ||
static_str_ops = "0.1.2" | ||
|
||
# workspaced deps | ||
bitvec = { workspace = true, default-features = false } | ||
codec = { workspace = true, default-features = false } | ||
futures-util = { workspace = true } | ||
futures = { workspace = true } | ||
futures-core = { workspace = true } | ||
serde = { workspace = true, features = ["derive"] } | ||
|
||
# local dependencies | ||
pallet-cf-elections = { workspace = true, default-features = true } | ||
pallet-cf-validator = { workspace = true, default-features = true } | ||
chainflip-engine = { workspace = true } | ||
custom-rpc = { workspace = true } | ||
state-chain-runtime = { workspace = true, default-features = true } | ||
cf-utilities = { workspace = true, default-features = true } | ||
|
||
[lints] | ||
workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
|
||
use std::{collections::BTreeMap, fmt::{format, Display}, hash::{DefaultHasher, Hash, Hasher}}; | ||
|
||
use crate::{trace::Trace, ElectionData}; | ||
use codec::{Decode, Encode}; | ||
use pallet_cf_elections::{bitmap_components::ElectionBitmapComponents, electoral_system::BitmapComponentOf, vote_storage::VoteStorage, ElectionIdentifierOf, ElectoralSystemTypes, IndividualComponentOf, SharedDataHash, UniqueMonotonicIdentifier}; | ||
use bitvec::prelude::*; | ||
|
||
|
||
// NOTE! the order is important for ordering the traces! | ||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] | ||
pub enum Category { | ||
Properties, | ||
NoVote, | ||
Vote(String), | ||
} | ||
use self::Category::*; | ||
|
||
impl Display for Category { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
NoVote => write!(f, "Computing vote"), | ||
Vote(s) => write!(f, "Election unchanged: {s}"), | ||
Properties => write!(f, "New properties"), | ||
// IndividualVote(s) => write!(f, "Individual: {s}"), | ||
// PartialVote(s) => write!(f, "Partial: {s}"), | ||
} | ||
} | ||
} | ||
|
||
|
||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] | ||
pub enum Key { | ||
RootBlockHeight(u32), | ||
ElectoralSystem(String), | ||
Election(String), | ||
Category(String, Category), | ||
Validator(u32), | ||
State{summary: String}, | ||
} | ||
|
||
impl Display for Key { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
RootBlockHeight(h) => write!(f, "blocks {h}-..."), | ||
Election(e) => write!(f, "{e}"), | ||
Key::Category(extra, category) => write!(f, "[{extra}] {category}"), | ||
Validator(x) => write!(f, "Validator {x}"), | ||
ElectoralSystem(name) => write!(f, "ES {name}"), | ||
State { summary } => write!(f, "{summary}"), | ||
} | ||
} | ||
} | ||
|
||
use Key::*; | ||
|
||
pub fn cloned_vec<'a, XS: IntoIterator<Item = &'a X>, X>(xs: XS) -> Vec<X> | ||
where X : Clone + 'a | ||
{ | ||
xs.into_iter().cloned().collect() | ||
} | ||
|
||
/// Initial value from which the trace state will be created | ||
#[derive(Clone)] | ||
pub struct TraceInit { | ||
pub end_immediately: bool, | ||
pub attributes: Vec<(String, String)> | ||
} | ||
|
||
impl TraceInit { | ||
pub fn with_attribute(&self, key: String, value: String) -> Self { | ||
let mut result = self.clone(); | ||
result.attributes.push((key, value)); | ||
result | ||
} | ||
} | ||
|
||
|
||
struct AsHex(Vec<u8>); | ||
|
||
impl Display for AsHex { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
for b in &self.0 { | ||
write!(f, "{b:x}")?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
struct ElectoralSystemStats { | ||
ongoing_count: u32 | ||
} | ||
|
||
pub fn add_ES_stats(mut trace: TraceInit, stats: &BTreeMap<String,ElectoralSystemStats>) -> TraceInit { | ||
for (name, stat) in stats { | ||
trace = trace.with_attribute(format!("z_count_{name}"), format!("{}", stat.ongoing_count)); | ||
} | ||
trace | ||
} | ||
|
||
pub fn make_traces<ES: ElectoralSystemTypes>(data: ElectionData<ES>) -> Trace<Key, TraceInit> | ||
where IndividualComponentOf<ES>: Encode | ||
{ | ||
|
||
let mut votes: BTreeMap<(ElectionIdentifierOf<ES>, u32), (String, String)> = BTreeMap::new(); | ||
|
||
for (identifier, (name, properties)) in &data.elections { | ||
|
||
if let Some(bitmaps) = data.bitmaps.get(identifier.unique_monotonic()) { | ||
|
||
for (component, bitmap) in bitmaps { | ||
for (id, bit) in bitmap.iter().enumerate() { | ||
let key3 = Validator(id as u32); | ||
if *bit { | ||
votes.insert((*identifier, id as u32), (AsHex(component.encode()).to_string(), format!("{component:?}"))); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if let Some(individual_components) = data.individual_components.get(identifier.unique_monotonic()) { | ||
for (authority_index, component) in individual_components { | ||
votes.insert((*identifier, *authority_index as u32), (AsHex(component.encode()).to_string(), format!("{component:?}"))); | ||
} | ||
} | ||
} | ||
|
||
let end = TraceInit { | ||
end_immediately: true, | ||
attributes: Vec::new() | ||
}; | ||
let start = TraceInit { | ||
end_immediately: false, | ||
attributes: Vec::new() | ||
}; | ||
|
||
let mut trace = Trace::new(); | ||
|
||
let root_height = data.height - (data.height % 50); | ||
let key0 = RootBlockHeight(root_height); | ||
trace.insert(vec![key0.clone()], end.with_attribute("height".into(), format!("{root_height}"))); | ||
|
||
let mut electoral_system_stats : BTreeMap<String,ElectoralSystemStats> = BTreeMap::new(); | ||
|
||
for name in data.electoral_system_names { | ||
trace.insert(vec![key0.clone(), ElectoralSystem(name.clone())], end.with_attribute("height".into(), format!("{root_height}"))); | ||
|
||
let current_elections_for_name : Vec<_> = data.elections.iter().filter(|(_, (cur_name, _))| *cur_name == name).collect(); | ||
electoral_system_stats.insert(name, ElectoralSystemStats { ongoing_count: current_elections_for_name.len() as u32 }); | ||
} | ||
|
||
for (identifier, (name, properties)) in &data.elections { | ||
|
||
let input = identifier.encode(); | ||
let mut other: &[u8] = &input; | ||
let id: u64 = Decode::decode(&mut other).unwrap(); | ||
let extra = format!("{:?}", identifier.extra()); | ||
|
||
let key1 = ElectoralSystem(name.clone()); | ||
let key2 = Election(format!("{name} ({id})")); | ||
|
||
// election id | ||
trace.insert(cloned_vec([&key0, &key1, &key2]), end.clone()); | ||
|
||
// properties | ||
let key3 = Category(extra.clone(), Properties); | ||
trace.insert( | ||
cloned_vec([&key0, &key1, &key2, &key3]), | ||
{ | ||
let init = end.with_attribute("Properties".into(), format!("{properties:#?}")); | ||
// add_ES_stats(init, &electoral_system_stats) | ||
init | ||
} | ||
); | ||
|
||
// no votes | ||
for authority_id in 0..data.validators { | ||
if votes.get(&(*identifier, authority_id)).is_none() { | ||
trace.insert(cloned_vec([&key0, &key1, &key2, &Category(extra.clone(), NoVote)]), start.clone()); | ||
trace.insert(cloned_vec([&key0, &key1, &key2, &Category(extra.clone(), NoVote), &Validator(authority_id)]), start.clone()); | ||
} | ||
} | ||
|
||
// votes | ||
for authority_id in 0..data.validators { | ||
if let Some(s) = votes.get(&(*identifier, authority_id)) { | ||
trace.insert(cloned_vec([&key0, &key1, &key2, &Category(extra.clone(), Vote(s.0.clone()))]), start.with_attribute("vote".into(), s.1.clone())); | ||
trace.insert(cloned_vec([&key0, &key1, &key2, &Category(extra.clone(), Vote(s.0.clone())), &Validator(authority_id)]), start.with_attribute("vote".into(), s.1.clone())); | ||
} | ||
} | ||
|
||
} | ||
|
||
trace | ||
|
||
} | ||
|
Oops, something went wrong.