1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use futures::{TryStream, TryStreamExt};
use std::{boxed::Box, fmt, sync::Arc};
use subxt::{
ext::{sp_core::H256, sp_runtime::traits::Header},
metadata::DecodeStaticType,
storage::{address::Yes, StaticStorageAddress},
Error, OnlineClient, PolkadotConfig as DefaultConfig,
};
const POV_MAX: u64 = 5_242_880 / 2;
#[derive(Debug)]
pub struct BlockStats {
pub hash: H256,
pub number: u32,
pub pov_len: u64,
pub witness_len: u64,
pub len: u64,
pub weight: u64,
pub num_extrinsics: u64,
pub max_pov: u64,
pub max_weight: u64,
}
impl fmt::Display for BlockStats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{:04}: PoV Size={:04}KiB({:03}%) Weight={:07}ms({:03}%) Witness={:04}KiB Block={:04}KiB NumExtrinsics={:04}",
self.number,
self.pov_len / 1024,
self.pov_len * 100 / self.max_pov,
self.weight / 1_000_000_000,
self.weight * 100 / self.max_weight,
self.witness_len / 1024,
self.len / 1024,
self.num_extrinsics,
)
}
}
pub async fn subscribe_stats(
url: &str,
) -> Result<impl TryStream<Ok = BlockStats, Error = Error> + Unpin, Error> {
let client = OnlineClient::<DefaultConfig>::from_url(url).await?;
let client = Arc::new(client);
let blocks = client.rpc().subscribe_blocks().await?;
let max_block_weights: BlockWeights = {
let metadata = client.metadata();
let pallet = metadata.pallet("System")?;
let constant = pallet.constant("BlockWeights")?;
codec::Decode::decode(&mut &constant.value[..])?
};
Ok(Box::pin(blocks.map_err(Into::into).and_then(
move |block| {
let client = client.clone();
let block_weight_address =
StaticStorageAddress::<DecodeStaticType<PerDispatchClass<u64>>, Yes, Yes, ()>::new(
"System",
"BlockWeight",
vec![],
Default::default(),
)
.unvalidated();
async move {
let stats = client
.rpc()
.block_stats(block.hash())
.await?
.ok_or_else(|| Error::Other("Block not available.".to_string()))?;
let weight: PerDispatchClass<u64> = client
.storage()
.fetch_or_default(&block_weight_address, Some(block.hash()))
.await?;
let pov_len = stats.witness_len + stats.block_len;
let total_weight = weight.normal + weight.operational + weight.mandatory;
Ok(BlockStats {
hash: block.hash(),
number: *block.number(),
pov_len,
witness_len: stats.witness_len,
len: stats.block_len,
weight: total_weight,
num_extrinsics: stats.num_extrinsics,
max_pov: POV_MAX,
max_weight: max_block_weights.max_block,
})
}
},
)))
}
#[derive(codec::Encode, codec::Decode)]
struct BlockWeights {
pub base_block: u64,
pub max_block: u64,
pub per_class: PerDispatchClass<WeightsPerClass>,
}
#[derive(codec::Encode, codec::Decode)]
struct PerDispatchClass<T> {
normal: T,
operational: T,
mandatory: T,
}
#[derive(codec::Encode, codec::Decode)]
struct WeightsPerClass {
pub base_extrinsic: u64,
pub max_extrinsic: Option<u64>,
pub max_total: Option<u64>,
pub reserved: Option<u64>,
}