use primitive_types::U256;
use serde;
use crate::api::Hash;
use crate::net::ProtoAddr;
use crate::node::{HashedBlock, Peer};
use crate::runtime::{StatementErr, StatementInfo};
fn show_opt<T: std::fmt::Display>(x: Option<T>) -> String {
match x {
None => "~".to_string(),
Some(x) => x.to_string(),
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct NodeEvent<A: ProtoAddr> {
pub time: u128,
pub addr: A,
pub event: NodeEventType,
}
#[derive(Debug, Clone, serde::Serialize)]
pub enum NodeEventType {
AddBlock {
block: BlockInfo,
event: Box<AddBlockEvent>, },
Mining {
event: MiningEvent,
},
Peers {
event: PeersEvent,
},
HandleMessage {
event: HandleMessageEvent,
},
Heartbeat {
peers: HeartbeatPeers,
tip: HeartbeatTip,
blocks: HeartbeatBlocks,
runtime: HeartbeatRuntime,
tip_blocks: Vec<Hash>,
},
}
pub type NodeEventEmittedInfo = (NodeEventType, u128);
#[derive(Debug, Clone, serde::Serialize)]
pub struct BlockInfo {
hash: Hash,
parent: Hash,
height: Option<u128>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, serde::Serialize)]
pub enum AddBlockEvent {
AlreadyIncluded,
NotEnoughWork,
Reorg {
old_tip: BlockInfo, common_block: BlockInfo, rollback: Option<RollbackInfo>, work: Hash,
},
Included {
siblings: Vec<Hash>,
work: Hash,
},
Computed {
block: BlockInfo,
results: Vec<Result<StatementInfo, StatementErr>>,
},
MissingParent {
parent: Hash,
},
TooLate,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct RollbackInfo {
runtime_tick: u128,
common_tick: u128,
rolled_to: u128,
}
#[derive(Debug, Clone, serde::Serialize)]
pub enum MiningEvent {
Success { block: Hash, target: Hash },
Failure { target: Hash },
AskMine { target: Hash },
Stop,
}
#[derive(Debug, Clone, serde::Serialize)]
pub enum PeersEvent {
SeePeer { addr: String, seen_at: u128, result: SeePeerResult },
Timeout { addr: String, seen_at: u128 },
}
#[derive(Debug, Clone, serde::Serialize)]
pub enum SeePeerResult {
NotSeenBefore,
Activated,
AlreadyActive { new_seen_at: u128 },
}
#[derive(Debug, Clone, serde::Serialize)]
pub enum HandleMessageEvent {
NoticeTheseBlocks {
magic: u32,
gossip: bool,
blocks: Vec<Hash>,
peers: Vec<String>, },
GiveMeThatBlock {
magic: u32,
bhash: Hash,
},
PleaseMineThisTransaction {
magic: u32,
trans: Hash, },
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct HeartbeatPeers {
pub num: usize,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct HeartbeatTip {
pub height: u64,
pub difficulty: u64,
pub work: Hash,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct HeartbeatBlocks {
pub missing: u64,
pub pending: u64,
pub included: usize,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct HeartbeatRuntime {
pub mana: HeartbeatStatInfo,
pub size: HeartbeatStatInfo,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct HeartbeatStatInfo {
pub current: i64,
pub limit: i64,
pub available: i64,
}
#[derive(PartialEq, Eq, Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum NodeEventDiscriminant {
AddBlock,
HandleMessage,
Heartbeat,
Mining,
Peers,
}
impl std::str::FromStr for NodeEventDiscriminant {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"add_block" => Ok(NodeEventDiscriminant::AddBlock),
"mining" => Ok(NodeEventDiscriminant::Mining),
"peers" => Ok(NodeEventDiscriminant::Peers),
"handle_message" => Ok(NodeEventDiscriminant::HandleMessage),
"heartbeat" => Ok(NodeEventDiscriminant::Heartbeat),
_ => Err(format!(
"Was not possible to convert from {} to an event discriminant",
s
)),
}
}
}
impl From<NodeEventType> for NodeEventDiscriminant {
fn from(event: NodeEventType) -> Self {
match event {
NodeEventType::AddBlock { .. } => NodeEventDiscriminant::AddBlock,
NodeEventType::Mining { .. } => NodeEventDiscriminant::Mining,
NodeEventType::Peers { .. } => NodeEventDiscriminant::Peers,
NodeEventType::HandleMessage { .. } => {
NodeEventDiscriminant::HandleMessage
}
NodeEventType::Heartbeat { .. } => NodeEventDiscriminant::Heartbeat,
}
}
}
impl std::fmt::Display for HeartbeatPeers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("peers: {}", self.num))
}
}
impl std::fmt::Display for HeartbeatTip {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"tip: {{ height: {} | difficulty: {} }}",
self.height, self.difficulty
))
}
}
impl std::fmt::Display for HeartbeatBlocks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"blocks: {{ included: {} | missing: {} | pending: {} }}",
self.included, self.missing, self.pending
))
}
}
impl std::fmt::Display for HeartbeatRuntime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("runtime: {{ size: {} }}", self.size))
}
}
impl std::fmt::Display for HeartbeatStatInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"current: {} | limit: {} | available: {}",
self.current, self.limit, self.available
))
}
}
impl std::fmt::Display for HandleMessageEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match self {
HandleMessageEvent::NoticeTheseBlocks {
magic,
gossip,
blocks,
peers,
} => {
let blocks =
blocks.iter().map(|h| h.to_string()).collect::<Vec<_>>().join(", ");
let peers = peers.join(", ");
format!(
"[notice_blocks] magic: {} | gossip: {} | blocks: {} | peers: {}",
magic, gossip, blocks, peers
)
}
HandleMessageEvent::GiveMeThatBlock { magic, bhash } => {
format!("[give_me_that_block] magic: {} | block: {}", magic, bhash)
}
HandleMessageEvent::PleaseMineThisTransaction { magic, trans } => {
format!("[mine_trans] magic: {} | trans: {}", magic, trans)
}
};
f.write_fmt(format_args!("{}", message))
}
}
impl std::fmt::Display for PeersEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let formatted = match self {
PeersEvent::SeePeer { addr, seen_at, result } => {
let formatted_result = match result {
SeePeerResult::NotSeenBefore => "[not_seen_before]".to_string(),
SeePeerResult::Activated => "[activated]".to_string(),
SeePeerResult::AlreadyActive { new_seen_at } => {
format!("[activated] new_seen_at: {}", new_seen_at)
}
};
format!(
"[see_peer] addr: {} | seen_at: {} | result: {}",
addr, seen_at, formatted_result
)
}
PeersEvent::Timeout { addr, seen_at } => {
format!("[timeout] addr: {} | seen_at: {}", addr, seen_at)
}
};
f.write_fmt(format_args!("{}", formatted))
}
}
impl std::fmt::Display for RollbackInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"common_tick: {} | runtime_tick: {}",
self.common_tick, self.runtime_tick
))
}
}
impl std::fmt::Display for BlockInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.hash))
}
}
impl std::fmt::Display for NodeEventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str_res = match self {
NodeEventType::AddBlock { block, event } => match &**event {
AddBlockEvent::AlreadyIncluded => {
format!(
"[add_block] [already_included] {} was alredy included",
block
)
}
AddBlockEvent::NotEnoughWork => {
format!(
"[add_block] [not_enough_work] {} didn't have enough work",
block
)
}
AddBlockEvent::Reorg { old_tip, rollback, .. } => {
let rollback = if let Some(rollback) = rollback {
format!("rollback: {}", rollback)
} else {
String::new()
};
format!("[add_block] [reorg] old_block {} was reorganized in favor of new_block {}; old height: {}; new height: {} {}", old_tip.hash, block, show_opt(old_tip.height), show_opt(block.height), rollback)
}
AddBlockEvent::MissingParent { parent } => {
format!(
"[add_block] [missing_parent] block {} has missing parent {}",
block, parent
)
}
AddBlockEvent::TooLate => {
format!(
"[add_block] [too_late] block {} was too late to be added",
block
)
}
AddBlockEvent::Included { .. } => {
format!("[included] block {}", block)
}
AddBlockEvent::Computed { block, results } => {
let results: String = results
.iter()
.map(|statement_result| match statement_result {
Ok(statement_info) => format!("{}", statement_info),
Err(statement_err) => statement_err.err.to_string(),
})
.collect();
format!("[computed] block {} | results: {}", block, results)
}
},
NodeEventType::Mining { event } => match event {
MiningEvent::Success { block, target } => {
format!(
"[mining] [success] block {} was mined successfully with target {}",
block, target
)
}
MiningEvent::Failure { target } => {
format!("[mining] [failure] failed in mining with target {}", target)
}
MiningEvent::AskMine { target } => {
format!(
"[mining] [ask_mine] asking miner to mine with target {}",
target
)
}
MiningEvent::Stop => "[mining] [stop] stopping mining".to_string(),
},
NodeEventType::Peers { event } => {
format!("[peers] {}", event)
}
NodeEventType::HandleMessage { event } => {
format!("[handle_message] {}", event)
}
NodeEventType::Heartbeat { peers, tip, blocks, runtime, .. } => {
format!("[heartbeat] {} {} {} {}", peers, tip, blocks, runtime)
}
};
f.write_fmt(format_args!("{}", str_res))
}
}
impl<A: ProtoAddr> std::fmt::Display for NodeEvent<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("[node] {} [event] {}", self.addr, self.event))
}
}
impl NodeEventType {
pub fn not_enough_work(block: &HashedBlock) -> Self {
let hash = U256::from(block.get_hash());
NodeEventType::AddBlock {
block: BlockInfo {
hash: hash.into(),
parent: block.prev.into(),
height: None,
},
event: Box::new(AddBlockEvent::NotEnoughWork),
}
}
pub fn included(
block: &HashedBlock,
height: Option<u128>,
siblings: &[U256],
work: U256,
) -> Self {
let hash = U256::from(block.get_hash());
let siblings = siblings.iter().map(|h| (*h).into()).collect();
NodeEventType::AddBlock {
block: BlockInfo { hash: hash.into(), parent: block.prev.into(), height },
event: Box::new(AddBlockEvent::Included { siblings, work: work.into() }),
}
}
pub fn computed(
added_block: (&HashedBlock, u128),
block: (&HashedBlock, u128),
results: &[Result<StatementInfo, StatementErr>],
) -> Self {
let (added_block, added_height) = added_block;
let (block, height) = block;
let added_hash = U256::from(added_block.get_hash());
let hash = U256::from(block.get_hash());
NodeEventType::AddBlock {
block: BlockInfo {
hash: added_hash.into(),
parent: block.prev.into(),
height: Some(added_height),
},
event: Box::new(AddBlockEvent::Computed {
block: BlockInfo {
hash: hash.into(),
parent: block.prev.into(),
height: Some(height),
},
results: results.to_vec(),
}),
}
}
pub fn reorg(
old: (&HashedBlock, u128),
new: (&HashedBlock, u128),
common: (&HashedBlock, u128),
ticks: (u128, u128), work: U256,
) -> Self {
let (old_block, old_height) = old;
let (new_block, new_height) = new;
let (common_block, common_height) = common;
let (old_runtime_tick, new_runtime_tick) = ticks;
let common_tick = common_height;
let rollback = {
if common_tick < old_runtime_tick {
Some(RollbackInfo {
common_tick,
runtime_tick: old_runtime_tick,
rolled_to: new_runtime_tick,
})
} else {
None
}
};
let new_hash = U256::from(new_block.get_hash());
let old_hash = U256::from(old_block.get_hash());
let common_hash = U256::from(common_block.get_hash());
NodeEventType::AddBlock {
block: BlockInfo {
hash: new_hash.into(),
parent: new_block.prev.into(),
height: Some(new_height),
},
event: Box::new(AddBlockEvent::Reorg {
old_tip: BlockInfo {
hash: old_hash.into(),
parent: old_block.prev.into(),
height: Some(old_height),
},
common_block: BlockInfo {
hash: common_hash.into(),
parent: common_block.prev.into(),
height: Some(common_height),
},
rollback,
work: work.into(),
}),
}
}
pub fn already_included(block: &HashedBlock, height: u128) -> Self {
let bhash = U256::from(block.get_hash());
NodeEventType::AddBlock {
block: BlockInfo {
hash: bhash.into(),
parent: block.prev.into(),
height: Some(height),
},
event: Box::new(AddBlockEvent::AlreadyIncluded),
}
}
pub fn missing_parent(block: &HashedBlock) -> Self {
let bhash = U256::from(block.get_hash());
let parent: Hash = block.prev.into();
NodeEventType::AddBlock {
block: BlockInfo { hash: bhash.into(), parent, height: None },
event: Box::new(AddBlockEvent::MissingParent { parent }),
}
}
pub fn too_late(block: &HashedBlock) -> Self {
let bhash = U256::from(block.get_hash());
NodeEventType::AddBlock {
block: BlockInfo {
hash: bhash.into(),
parent: block.prev.into(),
height: None,
},
event: Box::new(AddBlockEvent::TooLate),
}
}
pub fn mined(block: U256, target: U256) -> Self {
NodeEventType::Mining {
event: MiningEvent::Success {
block: block.into(),
target: target.into(),
},
}
}
pub fn failed_mined(target: U256) -> Self {
NodeEventType::Mining {
event: MiningEvent::Failure { target: target.into() },
}
}
pub fn ask_mine(target: U256) -> Self {
NodeEventType::Mining {
event: MiningEvent::AskMine { target: target.into() },
}
}
pub fn stop_mining() -> Self {
NodeEventType::Mining { event: MiningEvent::Stop }
}
pub fn timeout<A: ProtoAddr>(peer: &Peer<A>) -> Self {
NodeEventType::Peers {
event: PeersEvent::Timeout {
addr: peer.address.to_string(),
seen_at: peer.seen_at,
},
}
}
pub fn see_peer_activated<A: ProtoAddr>(peer: &Peer<A>) -> Self {
NodeEventType::Peers {
event: PeersEvent::SeePeer {
addr: peer.address.to_string(),
seen_at: peer.seen_at,
result: SeePeerResult::Activated,
},
}
}
pub fn see_peer_not_seen<A: ProtoAddr>(peer: &Peer<A>) -> Self {
NodeEventType::Peers {
event: PeersEvent::SeePeer {
addr: peer.address.to_string(),
seen_at: peer.seen_at,
result: SeePeerResult::NotSeenBefore,
},
}
}
pub fn see_peer_already_active<A: ProtoAddr>(
peer: &Peer<A>,
new_seen_at: u128,
) -> Self {
NodeEventType::Peers {
event: PeersEvent::SeePeer {
addr: peer.address.to_string(),
seen_at: peer.seen_at,
result: SeePeerResult::AlreadyActive { new_seen_at },
},
}
}
pub fn notice_blocks<A: ProtoAddr>(
magic: u32,
gossip: bool,
blocks: &[HashedBlock],
peers: &[Peer<A>],
) -> Self {
let event = HandleMessageEvent::NoticeTheseBlocks {
magic,
gossip,
blocks: blocks.iter().map(|b| U256::from(b.get_hash()).into()).collect(),
peers: peers.iter().map(|p| format!("{}", p.address)).collect(),
};
NodeEventType::HandleMessage { event }
}
pub fn give_me_block(magic: u32, block: U256) -> Self {
let event =
HandleMessageEvent::GiveMeThatBlock { magic, bhash: block.into() };
NodeEventType::HandleMessage { event }
}
pub fn mine_trans(magic: u32, trans: U256) -> Self {
let event = HandleMessageEvent::PleaseMineThisTransaction {
magic,
trans: trans.into(),
};
NodeEventType::HandleMessage { event }
}
}
#[macro_export]
macro_rules! heartbeat {
(
peers: { num: $peers_num:expr },
tip: {
height: $tip_height:expr,
difficulty: $difficulty:expr,
work: $work: expr,
},
blocks: {
missing: $missing_count:expr,
pending: $pending_count:expr,
included: $included_count:expr,
},
runtime: {
mana: {
current: $mana_cur:expr,
limit: $mana_lim:expr,
available: $mana_avail:expr,
},
size: {
current: $size_cur:expr,
limit: $size_lim:expr,
available: $size_avail:expr,
}
},
tip_blocks: $tip_blocks:expr
) => {
NodeEventType::Heartbeat {
peers: $crate::events::HeartbeatPeers { num: $peers_num },
tip: $crate::events::HeartbeatTip {
height: $tip_height,
difficulty: $difficulty,
work: $work.into(),
},
blocks: $crate::events::HeartbeatBlocks {
missing: $missing_count,
pending: $pending_count,
included: $included_count,
},
runtime: $crate::events::HeartbeatRuntime {
mana: $crate::events::HeartbeatStatInfo {
current: $mana_cur,
limit: $mana_lim,
available: $mana_avail,
},
size: $crate::events::HeartbeatStatInfo {
current: $size_cur,
limit: $size_lim,
available: $size_avail,
},
},
tip_blocks: $tip_blocks.iter().map(|x: &U256| (*x).into()).collect(),
}
};
}