Skip to content

Commit

Permalink
wip: generating election data from storage.
Browse files Browse the repository at this point in the history
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
MxmUrw committed Feb 14, 2025
1 parent f233e94 commit 00b3e2e
Show file tree
Hide file tree
Showing 15 changed files with 1,434 additions and 34 deletions.
462 changes: 455 additions & 7 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ members = [
"api/bin/chainflip-broker-api",
"api/bin/chainflip-cli",
"api/bin/chainflip-ingress-egress-tracker",
"api/bin/chainflip-lp-api",
"api/bin/chainflip-lp-api", "api/bin/chainflip-state-observer",
"api/lib",
"engine",
"engine-proc-macros",
Expand Down
36 changes: 36 additions & 0 deletions api/bin/chainflip-state-observer/Cargo.toml
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
197 changes: 197 additions & 0 deletions api/bin/chainflip-state-observer/src/elections.rs
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

}

Loading

0 comments on commit 00b3e2e

Please sign in to comment.