Skip to content

Commit 6a2ccfa

Browse files
authored
Metrics in separated files. (#12)
* Metrics and Checks are separated files and work in a similar way - checks are calculated based on pre/post states: `pub async fn run(&self, pre_state: &State, post_state: &State)` - metrics are updated the same way: `pub fn update(&self, pre_state: &State, post_state: &State)` * A single current_state is maintained in the mainloop * Initial height from config or tip * States attempt to be at the blockchain latest height (Expects blocks are consecutive (can not skip blocks)) * lru of blocks disabled.
1 parent fdc0a6d commit 6a2ccfa

19 files changed

+1365
-7940
lines changed

Cargo.lock

Lines changed: 0 additions & 7301 deletions
This file was deleted.

src/checks/block.rs

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,49 @@
1-
use anyhow::anyhow;
1+
use crate::state::State;
2+
use anyhow::{anyhow, Ok};
23

34
#[derive(Clone, Debug, Default)]
45
pub struct BlockHeigtCheck {}
56

67
impl BlockHeigtCheck {
7-
pub async fn run(
8-
&self,
9-
pre_state: &crate::state::State,
10-
post_state: &crate::state::State,
11-
) -> anyhow::Result<()> {
12-
match (
13-
pre_state.latest_block_height,
14-
post_state.latest_block_height,
15-
) {
16-
(None, None) => Ok(()), // nothing happen yet
17-
(None, Some(_)) => Ok(()), // first block
18-
(Some(pre_block_height), None) => Err(anyhow!(
19-
"Invalid state: pre {} -> post None",
20-
pre_block_height
21-
)), // should never happen
22-
(Some(pre_block_height), Some(post_block_height)) => {
23-
if pre_block_height < post_block_height {
24-
Ok(())
25-
} else {
26-
Err(anyhow!(
27-
"Invalid block height: pre {} -> post {}",
28-
pre_block_height,
29-
post_block_height
30-
))
31-
}
8+
pub async fn run(&self, pre_state: &State, post_state: &State) -> anyhow::Result<()> {
9+
let pre_block = pre_state.get_last_block();
10+
let post_block = post_state.get_last_block();
11+
if pre_block.height + 1 != post_block.height {
12+
Err(anyhow!(
13+
"Blocks are not consecutive: pre {} -> post {}",
14+
pre_block.height,
15+
post_block.height
16+
))
17+
} else {
18+
Ok(())
19+
}
20+
}
21+
}
22+
23+
pub struct BlockTimeCheck {
24+
max_time: u64,
25+
}
26+
impl BlockTimeCheck {
27+
pub async fn run(&self, pre_state: &State, post_state: &State) -> anyhow::Result<()> {
28+
let pre_block = pre_state.get_last_block();
29+
let post_block = post_state.get_last_block();
30+
let max_estimated_block_time = pre_state.get_max_block_time_estimate() as u64;
31+
if max_estimated_block_time > self.max_time {
32+
Err(anyhow!(
33+
"Max estimated block time is too high: {}",
34+
max_estimated_block_time
35+
))
36+
} else {
37+
let estimated_post_time = pre_state.max_next_block_timestamp_estimate();
38+
if estimated_post_time < post_block.timestamp {
39+
Err(anyhow!(
40+
"Block at height {} took too long to be produced {} (vs {:?})",
41+
post_block.height,
42+
post_block.timestamp - pre_block.timestamp,
43+
pre_state.get_max_block_time_estimate()
44+
))
45+
} else {
46+
Ok(())
3247
}
3348
}
3449
}

src/checks/epoch.rs

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,21 @@
1+
use crate::state::State;
12
use anyhow::anyhow;
23

34
#[derive(Clone, Debug, Default)]
45
pub struct EpochCheck {}
56

67
impl EpochCheck {
7-
pub async fn run(
8-
&self,
9-
pre_state: &crate::state::State,
10-
post_state: &crate::state::State,
11-
) -> anyhow::Result<()> {
12-
match (pre_state.latest_epoch, post_state.latest_epoch) {
13-
(None, None) => Ok(()), // nothing happen yet
14-
(None, Some(_)) => Ok(()), // first epoch
15-
(Some(pre_epoch), None) => {
16-
Err(anyhow!("Invalid state: pre {} -> post None", pre_epoch))
17-
} // should never happen
18-
(Some(pre_epoch), Some(post_epoch)) => {
19-
if pre_epoch <= post_epoch {
20-
Ok(())
21-
} else {
22-
Err(anyhow!(
23-
"Invalid epoch: pre {} -> post {}",
24-
pre_epoch,
25-
post_epoch
26-
))
27-
}
28-
}
8+
pub async fn run(&self, pre_state: &State, post_state: &State) -> anyhow::Result<()> {
9+
let pre_epoch = pre_state.get_last_block().epoch;
10+
let post_epoch = post_state.get_last_block().epoch;
11+
if pre_epoch == post_epoch || pre_epoch.checked_add(1).unwrap_or_default() == post_epoch {
12+
Ok(())
13+
} else {
14+
Err(anyhow!(
15+
"Invalid epoch: pre {} -> post {}",
16+
pre_epoch,
17+
post_epoch
18+
))
2919
}
3020
}
3121
}

src/checks/mod.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,56 @@ pub mod epoch;
33
pub mod total_supply_native;
44
pub mod tx_size;
55

6+
use crate::config::AppConfig;
7+
use crate::state::State;
68
use block::BlockHeigtCheck;
9+
use block::BlockTimeCheck;
710
use epoch::EpochCheck;
811
use total_supply_native::TotalSupplyNativeCheck;
912
use tx_size::TxSizeCheck;
1013

1114
pub enum Checks {
1215
BlockHeightCheck(BlockHeigtCheck),
16+
BlockTimeCheck(BlockTimeCheck),
1317
EpochCheck(EpochCheck),
1418
TotalSupplyNative(TotalSupplyNativeCheck),
1519
TxSize(TxSizeCheck),
1620
}
1721

18-
pub fn all_checks() -> Vec<Checks> {
19-
vec![
20-
Checks::BlockHeightCheck(BlockHeigtCheck::default()),
21-
Checks::EpochCheck(EpochCheck::default()),
22-
Checks::TotalSupplyNative(TotalSupplyNativeCheck::default()),
23-
Checks::TxSize(TxSizeCheck::default()),
24-
]
22+
impl Checks {
23+
pub async fn run(&self, pre_state: &State, post_state: &State) -> anyhow::Result<()> {
24+
match self {
25+
Checks::BlockHeightCheck(check) => check.run(pre_state, post_state).await,
26+
Checks::BlockTimeCheck(check) => check.run(pre_state, post_state).await,
27+
Checks::EpochCheck(check) => check.run(pre_state, post_state).await,
28+
Checks::TotalSupplyNative(check) => check.run(pre_state, post_state).await,
29+
Checks::TxSize(check) => check.run(pre_state, post_state).await,
30+
}
31+
}
32+
}
33+
34+
pub struct CheckCollection {
35+
checks: Vec<Checks>,
36+
}
37+
38+
impl CheckCollection {
39+
pub fn new(config: &AppConfig) -> Self {
40+
let checks = vec![
41+
Checks::BlockHeightCheck(BlockHeigtCheck::default()),
42+
Checks::EpochCheck(EpochCheck::default()),
43+
Checks::TotalSupplyNative(TotalSupplyNativeCheck::default()),
44+
Checks::TxSize(TxSizeCheck::new(
45+
config.max_tx_size,
46+
config.max_tx_inner_len,
47+
)),
48+
];
49+
Self { checks }
50+
}
51+
52+
pub async fn run(&self, pre_state: &State, post_state: &State) -> anyhow::Result<()> {
53+
for check in &self.checks {
54+
check.run(pre_state, post_state).await?;
55+
}
56+
Ok(())
57+
}
2558
}

src/checks/total_supply_native.rs

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,29 @@
1+
use crate::shared::namada::Address;
2+
use crate::state::State;
13
use anyhow::anyhow;
24

35
#[derive(Clone, Debug, Default)]
4-
pub struct TotalSupplyNativeCheck {}
6+
pub struct TotalSupplyNativeCheck {
7+
token: Address,
8+
}
59

610
impl TotalSupplyNativeCheck {
7-
pub async fn run(
8-
&self,
9-
pre_state: &crate::state::State,
10-
post_state: &crate::state::State,
11-
) -> anyhow::Result<()> {
12-
match (
13-
pre_state.latest_total_supply_native,
14-
post_state.latest_total_supply_native,
15-
) {
16-
(None, None) => Ok(()), // nothing happen yet
17-
(None, Some(_)) => Ok(()), // first epoch
18-
(Some(pre_total_supply), None) => Err(anyhow!(
19-
"Invalid state: pre {} -> post None",
20-
pre_total_supply
21-
)), // should never happen
22-
(Some(pre_total_supply), Some(post_total_supply)) => {
23-
if pre_total_supply <= post_total_supply {
24-
Ok(())
25-
} else {
26-
Err(anyhow!(
27-
"Invalid total supply: pre {} -> post {}. Could be valid in case of slashes or rejected governance proposal.",
28-
pre_total_supply,
29-
post_total_supply
30-
))
31-
}
32-
}
11+
pub fn new(token: Address) -> Self {
12+
Self { token }
13+
}
14+
15+
pub async fn run(&self, pre_state: &State, post_state: &State) -> anyhow::Result<()> {
16+
let pre_total_supply = pre_state.get_total_supply(&self.token).unwrap_or_default();
17+
let post_total_supply = post_state.get_total_supply(&self.token).unwrap_or_default();
18+
19+
if pre_total_supply <= post_total_supply {
20+
Ok(())
21+
} else {
22+
Err(anyhow!(
23+
"Invalid total supply: pre {} -> post {}. Could be valid in case of slashes or rejected governance proposal.",
24+
pre_total_supply,
25+
post_total_supply
26+
))
3327
}
3428
}
3529
}

src/checks/tx_size.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
1-
#[derive(Clone, Debug, Default)]
2-
pub struct TxSizeCheck {}
1+
use crate::state::State;
2+
use anyhow::anyhow;
3+
4+
#[derive(Clone, Debug)]
5+
pub struct TxSizeCheck {
6+
max_tx_size: u64,
7+
max_inner_len: u64,
8+
}
39

410
impl TxSizeCheck {
5-
pub async fn run(
6-
&self,
7-
pre_state: &crate::state::State,
8-
post_state: &crate::state::State,
9-
) -> anyhow::Result<()> {
11+
pub fn new(max_tx_size: u64, max_inner_len: u64) -> Self {
12+
Self {
13+
max_tx_size,
14+
max_inner_len,
15+
}
16+
}
17+
18+
pub async fn run(&self, _pre_state: &State, post_state: &State) -> anyhow::Result<()> {
19+
for tx in &post_state.get_last_block().transactions {
20+
if tx.inners.len() > self.max_inner_len as usize {
21+
return Err(anyhow!(
22+
"Transaction inner length is too large: {}",
23+
tx.inners.len()
24+
));
25+
}
26+
for inner in &tx.inners {
27+
if inner.size > self.max_tx_size as usize {
28+
return Err(anyhow!("Transaction size is too large: {}", inner.size));
29+
}
30+
}
31+
}
1032
Ok(())
1133
}
1234
}

src/config.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::u64;
2+
13
use crate::log::LogConfig;
24

35
#[derive(clap::Parser)]
@@ -6,7 +8,7 @@ pub struct AppConfig {
68
pub cometbft_urls: Vec<String>,
79

810
#[clap(long, env)]
9-
pub chain_id: String,
11+
pub chain_id: Option<String>,
1012

1113
#[clap(long, env)]
1214
pub slack_token: Option<String>,
@@ -20,12 +22,18 @@ pub struct AppConfig {
2022
#[clap(long, env, default_value_t = 9184)]
2123
pub prometheus_port: u64,
2224

23-
#[clap(long, env, default_value_t = 1)]
25+
#[clap(long, env, default_value_t = u64::MAX)]
2426
pub initial_block_height: u64,
2527

26-
#[clap(long, env, default_value_t = 5)]
28+
#[clap(long, env, default_value_t = 1000)]
2729
pub sleep_for: u64,
2830

2931
#[clap(flatten)]
3032
pub log: LogConfig,
33+
34+
#[clap(long, env, default_value_t = 100000)]
35+
pub max_tx_size: u64,
36+
37+
#[clap(long, env, default_value_t = 100)]
38+
pub max_tx_inner_len: u64,
3139
}

0 commit comments

Comments
 (0)