Support configurable records (#200)

* support configurable records
This commit is contained in:
Al Liu 2023-06-21 16:31:36 +08:00 committed by GitHub
parent c74b53be2e
commit 4ccc19f5a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 129 additions and 7 deletions

View File

@ -97,7 +97,11 @@ impl SimulationApp {
);
CarnotNode::<FlatOverlay<RoundRobin>>::new(
node_id,
CarnotSettings::new(nodes, simulation_settings.node_settings.timeout),
CarnotSettings::new(
nodes,
simulation_settings.node_settings.timeout,
simulation_settings.record_settings.clone(),
),
overlay_settings,
genesis,
network_interface,

View File

@ -30,12 +30,38 @@ use nomos_consensus::{
network::messages::{NewViewMsg, TimeoutMsg, VoteMsg},
};
#[derive(Serialize)]
const CURRENT_VIEW: &str = "current_view";
const HIGHEST_VOTED_VIEW: &str = "highest_voted_view";
const LOCAL_HIGH_QC: &str = "local_high_qc";
const SAFE_BLOCKS: &str = "safe_blocks";
const LAST_VIEW_TIMEOUT_QC: &str = "last_view_timeout_qc";
const LATEST_COMMITTED_BLOCK: &str = "latest_committed_block";
const LATEST_COMMITTED_VIEW: &str = "latest_committed_view";
const ROOT_COMMITTEE: &str = "root_committee";
const PARENT_COMMITTEE: &str = "parent_committee";
const CHILD_COMMITTEES: &str = "child_committees";
const COMMITTED_BLOCKS: &str = "committed_blocks";
pub const CARNOT_RECORD_KEYS: &[&str] = &[
CURRENT_VIEW,
HIGHEST_VOTED_VIEW,
LOCAL_HIGH_QC,
SAFE_BLOCKS,
LAST_VIEW_TIMEOUT_QC,
LATEST_COMMITTED_BLOCK,
LATEST_COMMITTED_VIEW,
ROOT_COMMITTEE,
PARENT_COMMITTEE,
CHILD_COMMITTEES,
COMMITTED_BLOCKS,
];
static RECORD_SETTINGS: std::sync::OnceLock<HashMap<String, bool>> = std::sync::OnceLock::new();
pub struct CarnotState {
current_view: View,
highest_voted_view: View,
local_high_qc: StandardQc,
#[serde(serialize_with = "serialize_blocks")]
safe_blocks: HashMap<BlockId, Block>,
last_view_timeout_qc: Option<TimeoutQc>,
latest_committed_block: Block,
@ -46,6 +72,81 @@ pub struct CarnotState {
committed_blocks: Vec<BlockId>,
}
impl serde::Serialize for CarnotState {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
if let Some(rs) = RECORD_SETTINGS.get() {
let keys = rs
.iter()
.filter_map(|(k, v)| {
if CARNOT_RECORD_KEYS.contains(&k.trim()) && *v {
Some(k)
} else {
None
}
})
.collect::<Vec<_>>();
let mut ser = serializer.serialize_struct("CarnotState", keys.len())?;
for k in keys {
match k.trim() {
CURRENT_VIEW => ser.serialize_field(CURRENT_VIEW, &self.current_view)?,
HIGHEST_VOTED_VIEW => {
ser.serialize_field(HIGHEST_VOTED_VIEW, &self.highest_voted_view)?
}
LOCAL_HIGH_QC => ser.serialize_field(LOCAL_HIGH_QC, &self.local_high_qc)?,
SAFE_BLOCKS => {
#[derive(Serialize)]
#[serde(transparent)]
struct SafeBlockHelper<'a> {
#[serde(serialize_with = "serialize_blocks")]
safe_blocks: &'a HashMap<BlockId, Block>,
}
ser.serialize_field(
SAFE_BLOCKS,
&SafeBlockHelper {
safe_blocks: &self.safe_blocks,
},
)?;
}
LAST_VIEW_TIMEOUT_QC => {
ser.serialize_field(LAST_VIEW_TIMEOUT_QC, &self.last_view_timeout_qc)?
}
LATEST_COMMITTED_BLOCK => {
ser.serialize_field(LATEST_COMMITTED_BLOCK, &self.latest_committed_block)?
}
LATEST_COMMITTED_VIEW => {
ser.serialize_field(LATEST_COMMITTED_VIEW, &self.latest_committed_view)?
}
ROOT_COMMITTEE => ser.serialize_field(ROOT_COMMITTEE, &self.root_committe)?,
PARENT_COMMITTEE => {
ser.serialize_field(PARENT_COMMITTEE, &self.parent_committe)?
}
CHILD_COMMITTEES => {
ser.serialize_field(CHILD_COMMITTEES, &self.child_committees)?
}
COMMITTED_BLOCKS => {
ser.serialize_field(COMMITTED_BLOCKS, &self.committed_blocks)?
}
_ => {}
}
}
ser.end()
} else {
serializer.serialize_none()
}
}
}
impl CarnotState {
const fn keys() -> &'static [&'static str] {
CARNOT_RECORD_KEYS
}
}
/// Have to implement this manually because of the `serde_json` will panic if the key of map
/// is not a string.
fn serialize_blocks<S>(blocks: &HashMap<BlockId, Block>, serializer: S) -> Result<S::Ok, S::Error>
@ -87,11 +188,20 @@ impl<O: Overlay> From<&Carnot<O>> for CarnotState {
pub struct CarnotSettings {
nodes: Vec<consensus_engine::NodeId>,
timeout: Duration,
record_settings: HashMap<String, bool>,
}
impl CarnotSettings {
pub fn new(nodes: Vec<consensus_engine::NodeId>, timeout: Duration) -> Self {
Self { nodes, timeout }
pub fn new(
nodes: Vec<consensus_engine::NodeId>,
timeout: Duration,
record_settings: HashMap<String, bool>,
) -> Self {
Self {
nodes,
timeout,
record_settings,
}
}
}
@ -120,11 +230,12 @@ impl<O: Overlay> CarnotNode<O> {
let engine = Carnot::from_genesis(id, genesis.header().clone(), overlay);
let state = CarnotState::from(&engine);
let timeout = settings.timeout;
RECORD_SETTINGS.get_or_init(|| settings.record_settings.clone());
// pk is generated in an insecure way, but for simulation purpouses using a rng like smallrng is more useful
let mut pk_buff = [0; 32];
rng.fill_bytes(&mut pk_buff);
let random_beacon_pk = PrivateKey::new(pk_buff);
Self {
let mut this = Self {
id,
state,
settings,
@ -133,7 +244,9 @@ impl<O: Overlay> CarnotNode<O> {
event_builder: event_builder::EventBuilder::new(id, timeout),
engine,
random_beacon_pk,
}
};
this.state = CarnotState::from(&this.engine);
this
}
pub(crate) fn send_message(&self, message: NetworkMessage<CarnotMessage>) {

View File

@ -149,6 +149,7 @@ where
leaders_count: _,
network_settings: _,
step_time: _,
record_settings: _,
} = settings;
Ok(Self {
runner_settings,

View File

@ -1,3 +1,5 @@
use std::collections::HashMap;
use crate::network::NetworkSettings;
use crate::overlay::OverlaySettings;
use crate::streaming::StreamSettings;
@ -31,6 +33,8 @@ pub struct NodeSettings {
pub struct SimulationSettings {
#[serde(default)]
pub wards: Vec<Ward>,
#[serde(default)]
pub record_settings: HashMap<String, bool>,
pub network_settings: NetworkSettings,
pub overlay_settings: OverlaySettings,
pub node_settings: NodeSettings,