layer_climb_core/querier/
validator.rs

1use tracing::instrument;
2
3use crate::prelude::*;
4
5use super::basic::BlockHeightReq;
6
7impl QueryClient {
8    #[instrument]
9    pub async fn validator_set(
10        &self,
11        height: Option<u64>,
12        proposer_address: Option<&[u8]>,
13    ) -> Result<layer_climb_proto::tendermint::ValidatorSet> {
14        self.run_with_middleware(ValidatorSetReq {
15            height,
16            proposer_address,
17        })
18        .await
19    }
20}
21
22#[derive(Clone, Debug)]
23pub struct ValidatorSetReq<'a> {
24    pub height: Option<u64>,
25    pub proposer_address: Option<&'a [u8]>,
26}
27
28impl QueryRequest for ValidatorSetReq<'_> {
29    type QueryResponse = layer_climb_proto::tendermint::ValidatorSet;
30
31    async fn request(
32        &self,
33        client: QueryClient,
34    ) -> Result<layer_climb_proto::tendermint::ValidatorSet> {
35        let height = match self.height {
36            Some(height) => height,
37            None => BlockHeightReq {}.request(client.clone()).await?,
38        };
39
40        let mut validators = Vec::new();
41        let mut pagination = None;
42
43        let mut proposer = None;
44
45        let proposer_address = self
46            .proposer_address
47            .map(|addr| tendermint::account::Id::try_from(addr.to_vec()))
48            .transpose()?;
49
50        let mut grpc_query_client = match client.get_connection_mode() {
51            ConnectionMode::Grpc => Some(
52                layer_climb_proto::tendermint::service_client::ServiceClient::new(
53                    client.clone_grpc_channel()?,
54                ),
55            ),
56            ConnectionMode::Rpc => None,
57        };
58
59        loop {
60            let req = layer_climb_proto::tendermint::GetValidatorSetByHeightRequest {
61                height: height.try_into()?,
62                pagination,
63            };
64
65            let resp = match client.get_connection_mode() {
66                ConnectionMode::Grpc => grpc_query_client
67                    .as_mut()
68                    .unwrap()
69                    .get_validator_set_by_height(req)
70                    .await
71                    .map(|res| res.into_inner())
72                    .context("couldn't get validator set")?,
73                ConnectionMode::Rpc => client
74                    .rpc_client()?
75                    .abci_protobuf_query(
76                        "/cosmos.base.tendermint.v1beta1.Service/GetValidatorSetByHeight",
77                        req,
78                        Some(height),
79                    )
80                    .await
81                    .context("couldn't get validator set")?,
82            };
83
84            for validator in resp.validators {
85                let pub_key = validator.pub_key.context("couldn't get public key")?;
86
87                let pub_key = match pub_key.type_url.as_str() {
88                    "/cosmos.crypto.ed25519.PubKey" => {
89                        let key = layer_climb_proto::crypto::ed25519::PubKey::decode(
90                            pub_key.value.as_slice(),
91                        )?
92                        .key;
93                        tendermint::public_key::PublicKey::Ed25519(key.as_slice().try_into()?)
94                    }
95                    _ => {
96                        return Err(anyhow::anyhow!(
97                            "unsupported public key type: {}",
98                            pub_key.type_url
99                        ))
100                    }
101                };
102
103                let info = tendermint::validator::Info {
104                    name: None,
105                    pub_key,
106                    address: tendermint::account::Id::from(pub_key),
107                    power: validator.voting_power.try_into()?,
108                    proposer_priority: validator.proposer_priority.into(),
109                };
110
111                if Some(info.address) == proposer_address {
112                    proposer = Some(info.clone());
113                }
114
115                validators.push(info);
116            }
117
118            if let Some(resp_pagination) = resp.pagination {
119                if resp_pagination.next_key.is_empty() {
120                    break;
121                } else {
122                    pagination = Some(layer_climb_proto::query::PageRequest {
123                        key: resp_pagination.next_key,
124                        offset: 0,
125                        limit: 0,
126                        count_total: false,
127                        reverse: false,
128                    });
129                }
130            } else {
131                break;
132            }
133        }
134
135        let validator_set =
136            convert_raw_validator_set(tendermint::validator::Set::new(validators, proposer));
137
138        // validators.sort_by_key(|v| (core::cmp::Reverse(v.voting_power), v.address.clone()));
139        // let validator_set = layer_climb_proto::ValidatorSet {
140        //     validators,
141        //     proposer,
142        //     total_voting_power,
143        // };
144
145        Ok(validator_set)
146    }
147}
148
149// yes, this is ridiculous
150fn convert_raw_validator_set(
151    validators: tendermint::validator::Set,
152) -> layer_climb_proto::tendermint::ValidatorSet {
153    layer_climb_proto::tendermint::ValidatorSet {
154        validators: validators
155            .validators()
156            .iter()
157            .map(|validator| layer_climb_proto::tendermint::Validator {
158                address: validator.address.into(),
159                pub_key: Some(layer_climb_proto::tendermint::crypto::PublicKey {
160                    sum: Some(
161                        layer_climb_proto::tendermint::crypto::public_key::Sum::Ed25519(
162                            validator.pub_key.to_bytes(),
163                        ),
164                    ),
165                }),
166                voting_power: validator.power.into(),
167                proposer_priority: validator.proposer_priority.into(),
168            })
169            .collect(),
170
171        proposer: validators.proposer().as_ref().map(|validator| {
172            layer_climb_proto::tendermint::Validator {
173                address: validator.address.into(),
174                pub_key: Some(layer_climb_proto::tendermint::crypto::PublicKey {
175                    sum: Some(
176                        layer_climb_proto::tendermint::crypto::public_key::Sum::Ed25519(
177                            validator.pub_key.to_bytes(),
178                        ),
179                    ),
180                }),
181                voting_power: validator.power.into(),
182                proposer_priority: validator.proposer_priority.into(),
183            }
184        }),
185        total_voting_power: validators.total_voting_power().into(),
186    }
187}