penumbra_sdk_mock_consensus/builder/
init_chain.rs1use {
2 super::*,
3 anyhow::{anyhow, bail},
4 bytes::Bytes,
5 prost::Message,
6 sha2::Digest as _,
7 std::{collections::BTreeMap, time},
8 tap::TapFallible,
9 tendermint::{
10 abci::request::InitChain,
11 block::{self, Height},
12 consensus::{
13 self,
14 params::{AbciParams, ValidatorParams, VersionParams},
15 },
16 evidence,
17 v0_37::abci::{ConsensusRequest, ConsensusResponse},
18 validator::Update,
19 vote::Power,
20 },
21 tendermint_proto::v0_37::types::HashedParams,
22 tower::{BoxError, Service, ServiceExt},
23 tracing::{debug, error},
24};
25
26impl Builder {
27 pub async fn init_chain<C>(self, mut consensus: C) -> Result<TestNode<C>, anyhow::Error>
34 where
35 C: Service<ConsensusRequest, Response = ConsensusResponse, Error = BoxError>
36 + Send
37 + Clone
38 + 'static,
39 C::Future: Send + 'static,
40 C::Error: Sized,
41 {
42 use tendermint::v0_37::abci::response;
43
44 let Self {
45 app_state: Some(app_state),
46 keyring,
47 on_block,
48 initial_timestamp,
49 ts_callback,
50 chain_id,
51 keys: _,
52 hardcoded_genesis,
53 } = self
54 else {
55 bail!("builder was not fully initialized")
56 };
57
58 let chain_id = tendermint::chain::Id::try_from(
59 chain_id.unwrap_or(TestNode::<()>::CHAIN_ID.to_string()),
60 )?;
61
62 let timestamp = initial_timestamp.unwrap_or(Time::now());
63 let request = match hardcoded_genesis {
64 Some(genesis) => ConsensusRequest::InitChain(InitChain {
66 time: genesis.genesis_time,
67 chain_id: genesis.chain_id.into(),
68 consensus_params: genesis.consensus_params,
69 validators: genesis
70 .validators
71 .iter()
72 .map(|v| Update {
73 pub_key: v.pub_key.clone(),
74 power: v.power,
75 })
76 .collect::<Vec<_>>(),
77 app_state_bytes: serde_json::to_vec(&genesis.app_state).unwrap().into(),
78 initial_height: Height::try_from(genesis.initial_height)?,
79 }),
80 None => {
82 Self::init_chain_request(app_state, &keyring, chain_id.clone(), timestamp.clone())?
83 }
84 };
85 let service = consensus
86 .ready()
87 .await
88 .tap_err(|error| error!(?error, "failed waiting for consensus service"))
89 .map_err(|_| anyhow!("failed waiting for consensus service"))?;
90
91 let response::InitChain {
92 app_hash,
93 consensus_params,
94 validators,
95 ..
96 } = match service
97 .call(request)
98 .await
99 .tap_ok(|resp| debug!(?resp, "received response from consensus service"))
100 .tap_err(|error| error!(?error, "consensus service returned error"))
101 .map_err(|_| anyhow!("consensus service returned error"))?
102 {
103 ConsensusResponse::InitChain(resp) => resp,
104 response => {
105 error!(?response, "unexpected InitChain response");
106 bail!("unexpected InitChain response");
107 }
108 };
109
110 {
112 tracing::debug!(?validators, "init_chain ignoring validator updates");
114 }
115
116 let pk = keyring.iter().next().expect("validator key in keyring").0;
119 let pub_key =
120 tendermint::PublicKey::from_raw_ed25519(pk.as_bytes()).expect("pub key present");
121 let proposer_address = tendermint::validator::Info {
122 address: tendermint::account::Id::new(
123 <sha2::Sha256 as sha2::Digest>::digest(pk).as_slice()[0..20]
124 .try_into()
125 .expect(""),
126 )
127 .try_into()?,
128 pub_key,
129 power: 1i64.try_into()?,
130 name: Some("test validator".to_string()),
131 proposer_priority: 1i64.try_into()?,
132 };
133
134 let hashed_params = HashedParams {
135 block_max_bytes: consensus_params
136 .as_ref()
137 .unwrap()
138 .block
139 .max_bytes
140 .try_into()?,
141 block_max_gas: consensus_params.unwrap().block.max_gas,
142 };
143
144 Ok(TestNode {
145 consensus,
146 height: block::Height::from(0_u8),
147 last_app_hash: app_hash.as_bytes().to_owned(),
148 last_validator_set_hash: Some(
150 tendermint::validator::Set::new(
151 vec![tendermint::validator::Info {
152 address: proposer_address.address,
153 pub_key,
154 power: Power::try_from(25_000 * 10i64.pow(6))?,
155 name: Some("test validator".to_string()),
156 proposer_priority: 1i64.try_into()?,
157 }],
158 Some(tendermint::validator::Info {
160 address: proposer_address.address,
161 pub_key,
162 power: Power::try_from(25_000 * 10i64.pow(6))?,
163 name: Some("test validator".to_string()),
164 proposer_priority: 1i64.try_into()?,
165 }),
166 )
167 .hash(),
168 ),
169 keyring,
170 on_block,
171 timestamp,
172 ts_callback: ts_callback.unwrap_or(Box::new(default_ts_callback)),
173 chain_id,
174 consensus_params_hash: sha2::Sha256::digest(hashed_params.encode_to_vec()).to_vec(),
175 last_commit: None,
177 })
178 }
179
180 fn init_chain_request(
181 app_state_bytes: Bytes,
182 keyring: &BTreeMap<ed25519_consensus::VerificationKey, ed25519_consensus::SigningKey>,
183 chain_id: tendermint::chain::Id,
184 timestamp: Time,
185 ) -> Result<ConsensusRequest, anyhow::Error> {
186 let consensus_params = Self::consensus_params();
187
188 let pub_keys = keyring
190 .iter()
191 .map(|(pk, _)| pk)
192 .map(|pk| {
193 tendermint::PublicKey::from_raw_ed25519(pk.as_bytes()).expect("pub key present")
194 })
195 .collect::<Vec<_>>();
196
197 Ok(ConsensusRequest::InitChain(InitChain {
198 time: timestamp,
199 chain_id: chain_id.into(),
200 consensus_params,
201 validators: pub_keys
202 .into_iter()
203 .map(|pub_key| tendermint::validator::Update {
204 pub_key,
205 power: 1u64.try_into().unwrap(),
206 })
207 .collect::<Vec<_>>(),
208 app_state_bytes,
209 initial_height: 0_u32.into(),
210 }))
211 }
212
213 fn consensus_params() -> consensus::Params {
214 consensus::Params {
215 block: block::Size {
216 max_bytes: 1,
217 max_gas: 1,
218 time_iota_ms: 1,
219 },
220 evidence: evidence::Params {
221 max_age_num_blocks: 1,
222 max_age_duration: evidence::Duration(time::Duration::from_secs(1)),
223 max_bytes: 1,
224 },
225 validator: ValidatorParams {
226 pub_key_types: vec![],
227 },
228 version: Some(VersionParams { app: 1 }),
229 abci: AbciParams {
230 vote_extensions_enable_height: None,
231 },
232 }
233 }
234}