Skip to content

Commit

Permalink
Summonerd: add web view for queue state
Browse files Browse the repository at this point in the history
This is just the number of connected participants, the top, and median
bid, but nothing else for now
  • Loading branch information
cronokirby committed Nov 1, 2023
1 parent b4375fb commit 9eca034
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 44 deletions.
5 changes: 3 additions & 2 deletions tools/summonerd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,15 @@ impl Opt {
tokio::spawn(coordinator.run::<Phase2>().instrument(coordinator_span))
}
};
let service = CoordinatorService::new(knower, storage.clone(), queue, marker);
let service =
CoordinatorService::new(knower, storage.clone(), queue.clone(), marker);
let grpc_server = Server::builder().add_service(
CeremonyCoordinatorServiceServer::new(service)
.max_encoding_message_size(max_message_size(marker))
.max_decoding_message_size(max_message_size(marker)),
);

let web_app = web_app(storage);
let web_app = web_app(marker, queue, storage);

let router = grpc_server.into_router().merge(web_app);

Expand Down
23 changes: 23 additions & 0 deletions tools/summonerd/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ use tokio::sync::RwLock;

use crate::participant::Participant;

/// A snapshot of the queue for public consumption.
pub struct Snapshot {
pub top_bid: Option<Amount>,
pub median_bid: Option<Amount>,
pub connected_participants: u64,
}

/// The inner struct for "normal" manipulation outside of the concurrent data structure
struct Inner {
// Invariant: this is always sorted by the amount.
Expand Down Expand Up @@ -90,6 +97,17 @@ impl Inner {
fn iter(&self) -> impl Iterator<Item = &(Participant, Amount)> {
self.sorted.iter()
}

fn snapshot(&self) -> Snapshot {
Snapshot {
top_bid: self.top_bid(),
median_bid: self
.sorted
.get(self.sorted.len() / 2)
.map(|(_, amount)| *amount),
connected_participants: self.sorted.len() as u64,
}
}
}

/// A thread safe queue of participants, sorted by bid amounts.
Expand Down Expand Up @@ -155,4 +173,9 @@ impl ParticipantQueue {
pub async fn inform_all(&self) -> Result<()> {
self.inform(None).await
}

/// Return a snapshot of this queue.
pub async fn snapshot(&self) -> Snapshot {
self.participants.read().await.snapshot()
}
}
20 changes: 13 additions & 7 deletions tools/summonerd/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,29 +353,35 @@ impl Storage {
&self,
marker: PhaseMarker,
n: u64,
) -> Result<Vec<(String, String, String)>> {
) -> Result<Vec<(u64, String, String, String)>> {
let mut conn = self.pool.get()?;
let tx = conn.transaction()?;
let query = match marker {
PhaseMarker::P1 =>
"SELECT hash, time, address from phase1_contributions WHERE address IS NOT NULL ORDER BY slot DESC LIMIT ?1",
"SELECT slot, hash, time, address from phase1_contributions WHERE address IS NOT NULL ORDER BY slot DESC LIMIT ?1",
PhaseMarker::P2 =>
"SELECT hash, time, address from phase2_contributions WHERE address IS NOT NULL ORDER BY slot DESC LIMIT ?1",
"SELECT slot, hash, time, address from phase2_contributions WHERE address IS NOT NULL ORDER BY slot DESC LIMIT ?1",
};

let mut out = Vec::new();
let mut stmt = tx.prepare(query)?;
let mut rows = stmt.query([n])?;
while let Some(row) = rows.next()? {
let hash_bytes: Vec<u8> = row.get(0)?;
let unix_timestamp: u64 = row.get(1)?;
let slot: u64 = row.get(0)?;
let hash_bytes: Vec<u8> = row.get(1)?;
let unix_timestamp: u64 = row.get(2)?;
// Convert unix timestamp to date time
let date_time =
chrono::DateTime::from_timestamp(unix_timestamp as i64, 0).unwrap_or_default();
let hash: String = hex::encode_upper(hash_bytes);
let address_bytes: Vec<u8> = row.get(2)?;
let address_bytes: Vec<u8> = row.get(3)?;
let address: Address = address_bytes.try_into()?;
out.push((hash, date_time.to_string(), address.display_short_form()));
out.push((
slot,
hash,
date_time.to_string(),
address.display_short_form(),
));
}

Ok(out)
Expand Down
73 changes: 50 additions & 23 deletions tools/summonerd/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@ use axum::{
};
use std::sync::Arc;

use crate::storage::Storage;
use crate::PhaseMarker;
use crate::{queue::ParticipantQueue, storage::Storage};

/// The number of previous contributions to display
const LAST_N: u64 = 10_000;

/// Represents the storage used by the web application.
pub struct WebAppState {
phase: PhaseMarker,
queue: ParticipantQueue,
storage: Storage,
}

pub fn web_app(storage: Storage) -> Router {
let shared_state = Arc::new(WebAppState { storage });
pub fn web_app(phase: PhaseMarker, queue: ParticipantQueue, storage: Storage) -> Router {
let shared_state = Arc::new(WebAppState {
phase,
queue,
storage,
});

Router::new()
.route("/", get(main_page).with_state(shared_state.clone()))
Expand All @@ -26,18 +35,10 @@ pub fn web_app(storage: Storage) -> Router {
}

pub async fn main_page(State(state): State<Arc<WebAppState>>) -> impl IntoResponse {
let has_transitioned = state
.storage
.transition_extra_information()
.await
.expect("Can get transition status");

let phase_number: u64;
if has_transitioned.is_some() {
phase_number = 2;
} else {
phase_number = 1;
}
let phase_number = match state.phase {
PhaseMarker::P1 => 1,
PhaseMarker::P2 => 2,
};

let template = MainTemplate { phase_number };
HtmlTemplate(template)
Expand All @@ -52,15 +53,27 @@ pub async fn phase_1(State(state): State<Arc<WebAppState>>) -> impl IntoResponse
.await
.expect("Can get contributions so far");

let contributions_by_hash_time_shortaddr = state
let contributions_by_slot_hash_time_shortaddr = state
.storage
.last_n_contributors(PhaseMarker::P1, 5)
.last_n_contributors(PhaseMarker::P1, LAST_N)
.await
.expect("Can get top N contributors");

let snapshot_participants_top_median = if state.phase == PhaseMarker::P1 {
let snapshot = state.queue.snapshot().await;
Some((
snapshot.connected_participants,
snapshot.top_bid.unwrap_or(0u64.into()).to_string(),
snapshot.median_bid.unwrap_or(0u64.into()).to_string(),
))
} else {
None
};

let template = Phase1Template {
snapshot_participants_top_median,
num_contributions_so_far_phase_1,
contributions_by_hash_time_shortaddr,
contributions_by_slot_hash_time_shortaddr,
};
HtmlTemplate(template)
}
Expand All @@ -74,15 +87,27 @@ pub async fn phase_2(State(state): State<Arc<WebAppState>>) -> impl IntoResponse
.await
.expect("Can get contributions so far");

let contributions_by_hash_time_shortaddr = state
let contributions_by_slot_hash_time_shortaddr = state
.storage
.last_n_contributors(PhaseMarker::P2, 5)
.last_n_contributors(PhaseMarker::P2, LAST_N)
.await
.expect("Can get top N contributors");

let snapshot_participants_top_median = if state.phase == PhaseMarker::P2 {
let snapshot = state.queue.snapshot().await;
Some((
snapshot.connected_participants,
snapshot.top_bid.unwrap_or(0u64.into()).to_string(),
snapshot.median_bid.unwrap_or(0u64.into()).to_string(),
))
} else {
None
};

let template = Phase2Template {
snapshot_participants_top_median,
num_contributions_so_far_phase_2,
contributions_by_hash_time_shortaddr,
contributions_by_slot_hash_time_shortaddr,
};
HtmlTemplate(template)
}
Expand All @@ -96,15 +121,17 @@ struct MainTemplate {
#[derive(Template)]
#[template(path = "phase1.html")]
struct Phase1Template {
snapshot_participants_top_median: Option<(u64, String, String)>,
num_contributions_so_far_phase_1: u64,
contributions_by_hash_time_shortaddr: Vec<(String, String, String)>,
contributions_by_slot_hash_time_shortaddr: Vec<(u64, String, String, String)>,
}

#[derive(Template)]
#[template(path = "phase2.html")]
struct Phase2Template {
snapshot_participants_top_median: Option<(u64, String, String)>,
num_contributions_so_far_phase_2: u64,
contributions_by_hash_time_shortaddr: Vec<(String, String, String)>,
contributions_by_slot_hash_time_shortaddr: Vec<(u64, String, String, String)>,
}

struct HtmlTemplate<T>(T);
Expand Down
33 changes: 27 additions & 6 deletions tools/summonerd/templates/phase1.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
<h1>✨✨✨✨ Penumbra Summoning Ceremony ✨✨✨✨</h1>

{% if snapshot_participants_top_median.is_some() %}
{% let value = snapshot_participants_top_median.as_ref().unwrap() %}
<span>Waiting:</span>
<span>{{ value.0 }}</span>
<span>Top Bid:</span>
<span>{{ value.1 }}</span>
<span>Median Bid:</span>
<span>{{ value.2 }}</span>
{% endif %}

<h2>Previous Contributions in phase 1: {{ num_contributions_so_far_phase_1 }}</h2>

{% if contributions_by_hash_time_shortaddr.len() != 0 %}
{% if contributions_by_slot_hash_time_shortaddr.len() != 0 %}
Recent phase 1 contributors:
<ul>
{% for contribution in contributions_by_hash_time_shortaddr %}
<li>{{ contribution.2 }} contributed {{ contribution.0 }} at {{ contribution.1 }}</li>
<table>
<tr>
<td>Slot</td>
<td>Who</td>
<td>What</td>
<td>When</td>
</tr>
{% for contribution in contributions_by_slot_hash_time_shortaddr.iter() %}
<tr>
<td>{{ contribution.0 }}</td>
<td>{{ contribution.3 }}</td>
<td>{{ contribution.1 }}</td>
<td>{{ contribution.2 }}</td>
</tr>
{% endfor %}
</ul>
{% endif %}
</table>
{% endif %}
33 changes: 27 additions & 6 deletions tools/summonerd/templates/phase2.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
<h1>✨✨✨✨ Penumbra Summoning Ceremony ✨✨✨✨</h1>

{% if snapshot_participants_top_median.is_some() %}
{% let value = snapshot_participants_top_median.as_ref().unwrap() %}
<span>Waiting:</span>
<span>{{ value.0 }}</span>
<span>Top Bid:</span>
<span>{{ value.1 }}</span>
<span>Median Bid:</span>
<span>{{ value.2 }}</span>
{% endif %}

<h2>Previous Contributions in phase 2: {{ num_contributions_so_far_phase_2 }}</h2>

{% if contributions_by_hash_time_shortaddr.len() != 0 %}
{% if contributions_by_slot_hash_time_shortaddr.len() != 0 %}
Recent phase 2 contributors:
<ul>
{% for contribution in contributions_by_hash_time_shortaddr %}
<li>{{ contribution.2 }} contributed {{ contribution.0 }} at {{ contribution.1 }}</li>
<table>
<tr>
<td>Slot</td>
<td>Who</td>
<td>What</td>
<td>When</td>
</tr>
{% for contribution in contributions_by_slot_hash_time_shortaddr.iter() %}
<tr>
<td>{{ contribution.0 }}</td>
<td>{{ contribution.3 }}</td>
<td>{{ contribution.1 }}</td>
<td>{{ contribution.2 }}</td>
</tr>
{% endfor %}
</ul>
{% endif %}
</table>
{% endif %}

0 comments on commit 9eca034

Please sign in to comment.